From 6cd24ba1aa45f0e4c30cc8b6c5c65163d5a8771b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 15 Jun 2023 14:26:04 +0100 Subject: [PATCH 1/3] Process: check for `posix_spawn_file_actions_addchdir_np` --- CoreFoundation/Base.subproj/CFPlatform.c | 22 +++++++- .../Base.subproj/ForSwiftFoundationOnly.h | 1 + Sources/Foundation/Process.swift | 55 +++++++++++++++---- 3 files changed, 66 insertions(+), 12 deletions(-) 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..fc6153eba9 100644 --- a/Sources/Foundation/Process.swift +++ b/Sources/Foundation/Process.swift @@ -944,11 +944,37 @@ 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) + } + + 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 @@ -961,18 +987,26 @@ open class Process: NSObject { throw _NSErrorWithErrno(errno, reading: true, url: currentDirectoryURL) } - defer { - // Reset the previous working directory path. - fileManager.changeCurrentDirectoryPath(previousDirectoryPath) - } - #endif + try closure() + // Reset the previous working directory path. + fileManager.changeCurrentDirectoryPath(previousDirectoryPath) + } + + 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,7 +1038,6 @@ open class Process: NSObject { isRunning = true self.processIdentifier = pid -#endif } open func interrupt() { From 990cf3820d6f16ff3fdd608e1470975b8249ed77 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 15 Jun 2023 15:55:17 +0100 Subject: [PATCH 2/3] Fix windows build --- Sources/Foundation/Process.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/Foundation/Process.swift b/Sources/Foundation/Process.swift index fc6153eba9..c8afb63183 100644 --- a/Sources/Foundation/Process.swift +++ b/Sources/Foundation/Process.swift @@ -974,6 +974,7 @@ open class Process: NSObject { #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 @@ -1039,7 +1040,8 @@ open class Process: NSObject { self.processIdentifier = pid } - +#endif + open func interrupt() { precondition(hasStarted, "task not launched") #if os(Windows) From 01583402d8e8c9cdf2733ac524cd886f7d63a714 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 15 Jun 2023 15:56:36 +0100 Subject: [PATCH 3/3] Restore use of `defer` --- Sources/Foundation/Process.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Sources/Foundation/Process.swift b/Sources/Foundation/Process.swift index c8afb63183..d07254b4a0 100644 --- a/Sources/Foundation/Process.swift +++ b/Sources/Foundation/Process.swift @@ -988,10 +988,12 @@ open class Process: NSObject { throw _NSErrorWithErrno(errno, reading: true, url: currentDirectoryURL) } - try closure() + defer { + // Reset the previous working directory path. + fileManager.changeCurrentDirectoryPath(previousDirectoryPath) + } - // Reset the previous working directory path. - fileManager.changeCurrentDirectoryPath(previousDirectoryPath) + try closure() } private func posixLaunch(