Skip to content

Commit 6f0bc88

Browse files
committed
Add support for thread-safe chdir in process spawning
This is needed as a fallback to support Amazon Linux 2 and OpenBSD.
1 parent 72b0343 commit 6f0bc88

File tree

1 file changed

+56
-60
lines changed

1 file changed

+56
-60
lines changed

lib/Basic/Subprocess.cpp

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -48,34 +48,21 @@
4848
#ifdef __APPLE__
4949
#include <pthread/spawn.h>
5050
#include "TargetConditionals.h"
51-
#if !TARGET_OS_IPHONE
52-
extern "C" {
53-
// Provided by System.framework's libsystem_kernel interface
54-
extern int __pthread_chdir(const char *path);
55-
extern int __pthread_fchdir(int fd);
56-
}
57-
58-
/// Set the thread specific working directory to the given path.
59-
int pthread_chdir_np(const char *path)
60-
{
61-
return __pthread_chdir(path);
62-
}
63-
64-
/// Set the thread specific working directory to that of the given file
65-
/// descriptor. Passing -1 clears the thread specific working directory,
66-
/// returning it to the process level working directory.
67-
int pthread_fchdir_np(int fd)
68-
{
69-
return __pthread_fchdir(fd);
70-
}
71-
#endif
7251
#endif
7352

7453
#ifndef __GLIBC_PREREQ
7554
#define __GLIBC_PREREQ(maj, min) 0
7655
#endif
7756

7857
#if !defined(_WIN32) && defined(HAVE_POSIX_SPAWN)
58+
static bool posix_spawn_file_actions_addchdir_supported() {
59+
#if (defined(__GLIBC__) && !__GLIBC_PREREQ(2, 29)) || (defined(__OpenBSD__)) || (defined(__ANDROID__) && __ANDROID_API__ < 34) || (defined(__QNX__))
60+
return false;
61+
#else
62+
return true;
63+
#endif
64+
}
65+
7966
// Implementation mostly copied from _CFPosixSpawnFileActionsChdir in swift-corelibs-foundation
8067
static int posix_spawn_file_actions_addchdir_polyfill(posix_spawn_file_actions_t * __restrict file_actions,
8168
const char * __restrict path) {
@@ -775,11 +762,16 @@ void llbuild::basic::spawnProcess(
775762
posix_spawn_file_actions_t fileActions;
776763
posix_spawn_file_actions_init(&fileActions);
777764

778-
bool usePosixSpawnChdirFallback = true;
779765
const auto workingDir = attr.workingDir.str();
780-
if (!workingDir.empty() &&
781-
posix_spawn_file_actions_addchdir_polyfill(&fileActions, workingDir.c_str()) != ENOSYS) {
782-
usePosixSpawnChdirFallback = false;
766+
if (!workingDir.empty()
767+
&& posix_spawn_file_actions_addchdir_supported()
768+
&& posix_spawn_file_actions_addchdir_polyfill(&fileActions, workingDir.c_str()) != 0) {
769+
auto result = ProcessResult::makeFailed();
770+
delegate.processStarted(ctx, handle, pid);
771+
delegate.processHadError(ctx, handle, Twine("failed to set the working directory"));
772+
delegate.processFinished(ctx, handle, result);
773+
completionFn(result);
774+
return;
783775
}
784776

785777
#endif
@@ -865,36 +857,6 @@ void llbuild::basic::spawnProcess(
865857

866858
int result = 0;
867859

868-
bool workingDirectoryUnsupported = false;
869-
870-
#if !defined(_WIN32)
871-
if (usePosixSpawnChdirFallback) {
872-
#if defined(__APPLE__)
873-
thread_local std::string threadWorkingDir;
874-
875-
if (workingDir.empty()) {
876-
if (!threadWorkingDir.empty()) {
877-
pthread_fchdir_np(-1);
878-
threadWorkingDir.clear();
879-
}
880-
} else {
881-
if (threadWorkingDir != workingDir) {
882-
if (pthread_chdir_np(workingDir.c_str()) == -1) {
883-
result = errno;
884-
} else {
885-
threadWorkingDir = workingDir;
886-
}
887-
}
888-
}
889-
#else
890-
if (!workingDir.empty()) {
891-
workingDirectoryUnsupported = true;
892-
result = -1;
893-
}
894-
#endif // if defined(__APPLE__)
895-
}
896-
#endif // else !defined(_WIN32)
897-
898860
if (result == 0) {
899861
#if defined(_WIN32)
900862
auto unicodeEnv = environment.getWindowsEnvp();
@@ -909,10 +871,47 @@ void llbuild::basic::spawnProcess(
909871
: (LPWSTR)u16Cwd.data(),
910872
&startupInfo, &processInfo);
911873
#else
912-
result =
874+
// For platforms missing posix_spawn_file_actions_addchdir{_np}, we need to fork in order to thread-safely set the wd
875+
if (!workingDir.empty()
876+
&& !posix_spawn_file_actions_addchdir_supported()) {
877+
pid_t childPid = fork();
878+
switch (childPid) {
879+
case -1:
880+
// Fail
881+
result = errno;
882+
break;
883+
case 0:
884+
// Child
885+
pthread_sigmask(SIG_BLOCK, &mostSignals, NULL);
886+
if (chdir(workingDir.c_str()) != 0) {
887+
_exit(EXIT_FAILURE);
888+
}
889+
if (attr.connectToConsole) {
890+
dup2(STDIN_FILENO, STDIN_FILENO);
891+
dup2(STDOUT_FILENO, STDOUT_FILENO);
892+
dup2(STDERR_FILENO, STDERR_FILENO);
893+
} else {
894+
setpgid(0, 0);
895+
dup2(open("/dev/null", O_RDONLY, 0), STDIN_FILENO);
896+
dup2(outputPipeChildEnd.unsafeDescriptor(), STDOUT_FILENO);
897+
dup2(outputPipeChildEnd.unsafeDescriptor(), STDERR_FILENO);
898+
close(outputPipeChildEnd.unsafeDescriptor());
899+
}
900+
if (attr.controlEnabled) {
901+
dup2(controlPipeChildEnd.unsafeDescriptor(), controlPipeChildEnd.unsafeDescriptor());
902+
}
903+
_exit(execve(args[0], const_cast<char**>(args.data()), const_cast<char* const*>(environment.getEnvp())));
904+
default:
905+
// Parent
906+
pid = childPid;
907+
break;
908+
}
909+
} else {
910+
result =
913911
posix_spawn(&pid, args[0], /*file_actions=*/&fileActions,
914912
/*attrp=*/&attributes, const_cast<char**>(args.data()),
915913
const_cast<char* const*>(environment.getEnvp()));
914+
}
916915
#endif
917916
}
918917

@@ -925,10 +924,7 @@ void llbuild::basic::spawnProcess(
925924
#endif
926925
delegate.processHadError(
927926
ctx, handle,
928-
workingDirectoryUnsupported
929-
? Twine("working-directory unsupported on this platform")
930-
: Twine("unable to spawn process '") + argsStorage[0] + "' (" + sys::strerror(result) +
931-
")");
927+
Twine("unable to spawn process '") + argsStorage[0] + "' (" + sys::strerror(result) + ")");
932928
delegate.processFinished(ctx, handle, processResult);
933929
pid = (llbuild_pid_t)-1;
934930
} else {

0 commit comments

Comments
 (0)