diff --git a/CoreFoundation/Base.subproj/CFPlatform.c b/CoreFoundation/Base.subproj/CFPlatform.c index 3c13da72c8..e7f1385dd3 100644 --- a/CoreFoundation/Base.subproj/CFPlatform.c +++ b/CoreFoundation/Base.subproj/CFPlatform.c @@ -2210,8 +2210,28 @@ CF_EXPORT int _CFPosixSpawnFileActionsAddClose(_CFPosixSpawnFileActionsRef file_ return posix_spawn_file_actions_addclose((posix_spawn_file_actions_t *)file_actions, filedes); } +CF_EXPORT bool _CFPosixSpawnFileActionsAddChdirNPIsSupported() { +#if defined(__GLIBC__) +# if __GLIBC_PREREQ(2, 29) + return true; +# else + return false; +# endif +#else + return false; +#endif +} + CF_EXPORT int _CFPosixSpawnFileActionsAddChdirNP(_CFPosixSpawnFileActionsRef file_actions, const char *path) { - return posix_spawn_file_actions_addchdir_np((posix_spawn_file_actions_t *)file_actions, path); +#if defined(__GLIBC__) +# if __GLIBC_PREREQ(2, 29) + return posix_spawn_file_actions_addchdir_np((posix_spawn_file_actions_t *)file_actions, path); +# else + return ENOSYS; +# endif +#else + return ENOSYS; +#endif } CF_EXPORT int _CFPosixSpawn(pid_t *_CF_RESTRICT pid, const char *_CF_RESTRICT path, _CFPosixSpawnFileActionsRef file_actions, _CFPosixSpawnAttrRef _Nullable _CF_RESTRICT attrp, char *_Nullable const argv[_Nullable _CF_RESTRICT], char *_Nullable const envp[_Nullable _CF_RESTRICT]) { diff --git a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h index 811f3fd6d7..145ac92bc8 100644 --- a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h +++ b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h @@ -711,6 +711,7 @@ CF_EXPORT int _CFPosixSpawn(pid_t *_CF_RESTRICT pid, const char *_CF_RESTRICT pa #endif // __cplusplus #if TARGET_OS_LINUX +CF_EXPORT bool _CFPosixSpawnFileActionsAddChdirNPIsSupported(void); CF_EXPORT int _CFPosixSpawnFileActionsAddChdirNP(_CFPosixSpawnFileActionsRef file_actions, const char *path); #endif // TARGET_OS_LINUX #endif // !TARGET_OS_WIN32 diff --git a/Sources/Foundation/Process.swift b/Sources/Foundation/Process.swift index 3d0b481bd3..d07254b4a0 100644 --- a/Sources/Foundation/Process.swift +++ b/Sources/Foundation/Process.swift @@ -944,11 +944,38 @@ open class Process: NSObject { } #endif - #if os(Linux) - if let dir = currentDirectoryURL?.path { +#if os(Linux) + if let dir = currentDirectoryURL?.path, _CFPosixSpawnFileActionsAddChdirNPIsSupported() { try _throwIfPosixError(_CFPosixSpawnFileActionsAddChdirNP(fileActions, dir)) + } else { + try withCurrentDirectoryPath { + try posixLaunch( + launchPath: launchPath, + taskSocketPair: taskSocketPair, + fileActions: fileActions, + spawnAttrs: &spawnAttrs, + argv: argv, + envp: envp + ) + } } - #else +#else + try withCurrentDirectoryPath { + try posixLaunch( + launchPath: launchPath, + taskSocketPair: taskSocketPair, + fileActions: fileActions, + spawnAttrs: &spawnAttrs, + argv: argv, + envp: envp + ) + } +#endif // os(Linux) +#endif // os(Windows) + } + +#if !os(Windows) + private func withCurrentDirectoryPath(_ closure: () throws -> ()) throws { // This is an unfortunate workaround: posix_spawn has no POSIX-specified way to set the working directory // of the child process. glibc has a non-POSIX API option, which we use above. Here we take a brute-force // approach of just changing our current working directory. This is not a great implementation and it's likely @@ -965,14 +992,24 @@ open class Process: NSObject { // Reset the previous working directory path. fileManager.changeCurrentDirectoryPath(previousDirectoryPath) } - #endif + try closure() + } + + private func posixLaunch( + launchPath: String, + taskSocketPair: [Int32], + fileActions: _CFPosixSpawnFileActionsRef, + spawnAttrs: UnsafeMutablePointer, + argv: UnsafeMutablePointer?>, + envp: UnsafeMutablePointer?> + ) throws { // Launch var pid = pid_t() - guard _CFPosixSpawn(&pid, launchPath, fileActions, &spawnAttrs, argv, envp) == 0 else { + guard _CFPosixSpawn(&pid, launchPath, fileActions, spawnAttrs, argv, envp) == 0 else { throw _NSErrorWithErrno(errno, reading: true, path: launchPath) } - posix_spawnattr_destroy(&spawnAttrs) + posix_spawnattr_destroy(spawnAttrs) // Close the write end of the input and output pipes. if let pipe = standardInput as? Pipe { @@ -1004,9 +1041,9 @@ open class Process: NSObject { isRunning = true self.processIdentifier = pid -#endif } - +#endif + open func interrupt() { precondition(hasStarted, "task not launched") #if os(Windows)