diff --git a/Sources/Foundation/Process.swift b/Sources/Foundation/Process.swift index a085c5d849..484a59a96f 100644 --- a/Sources/Foundation/Process.swift +++ b/Sources/Foundation/Process.swift @@ -944,11 +944,17 @@ open class Process: NSObject { try _throwIfPosixError(_CFPosixSpawnFileActionsAddClose(fileActions, fd)) } - #if canImport(Darwin) +#if canImport(Darwin) var spawnAttrs: posix_spawnattr_t? = nil try _throwIfPosixError(posix_spawnattr_init(&spawnAttrs)) + try _throwIfPosixError(posix_spawnattr_setflags(&spawnAttrs, .init(POSIX_SPAWN_SETPGROUP))) try _throwIfPosixError(posix_spawnattr_setflags(&spawnAttrs, .init(POSIX_SPAWN_CLOEXEC_DEFAULT))) - #else +#else + var spawnAttrs: posix_spawnattr_t = posix_spawnattr_t() + try _throwIfPosixError(posix_spawnattr_init(&spawnAttrs)) + try _throwIfPosixError(posix_spawnattr_setflags(&spawnAttrs, .init(POSIX_SPAWN_SETPGROUP))) + + // POSIX_SPAWN_CLOEXEC_DEFAULT is an Apple extension so emulate it. for fd in 3 ... findMaximumOpenFD() { guard adddup2[fd] == nil && !addclose.contains(fd) && @@ -957,7 +963,7 @@ open class Process: NSObject { } try _throwIfPosixError(_CFPosixSpawnFileActionsAddClose(fileActions, fd)) } - #endif +#endif let fileManager = FileManager() let previousDirectoryPath = fileManager.currentDirectoryPath @@ -972,16 +978,10 @@ open class Process: NSObject { // Launch var pid = pid_t() - #if os(macOS) guard _CFPosixSpawn(&pid, launchPath, fileActions, &spawnAttrs, argv, envp) == 0 else { throw _NSErrorWithErrno(errno, reading: true, path: launchPath) } - #else - guard _CFPosixSpawn(&pid, launchPath, fileActions, nil, argv, envp) == 0 else { - throw _NSErrorWithErrno(errno, reading: true, path: launchPath) - } - #endif - + posix_spawnattr_destroy(&spawnAttrs) // Close the write end of the input and output pipes. if let pipe = standardInput as? Pipe { diff --git a/Tests/Foundation/Tests/TestProcess.swift b/Tests/Foundation/Tests/TestProcess.swift index 7c8e0e989d..54f01bf488 100644 --- a/Tests/Foundation/Tests/TestProcess.swift +++ b/Tests/Foundation/Tests/TestProcess.swift @@ -806,6 +806,35 @@ class TestProcess : XCTestCase { } } + func test_processGroup() throws { + // The process group of the child process should be different to the parent's. + let process = Process() + + process.executableURL = xdgTestHelperURL() + process.arguments = ["--pgrp"] + let pipe = Pipe() + process.standardOutput = pipe + process.standardError = nil + + try process.run() + process.waitUntilExit() + XCTAssertEqual(process.terminationStatus, 0) + + let data = pipe.fileHandleForReading.availableData + guard let string = String(data: data, encoding: .ascii) else { + XCTFail("Could not read stdout") + return + } + + let parts = string.trimmingCharacters(in: .newlines).components(separatedBy: ": ") + guard parts.count == 2, parts[0] == "pgrp", let childPgrp = Int(parts[1]) else { + XCTFail("Could not pgrp fron stdout") + return + } + let parentPgrp = Int(getpgrp()) + XCTAssertNotEqual(parentPgrp, childPgrp, "Child process group \(parentPgrp) should not equal parent process group \(childPgrp)") + } + static var allTests: [(String, (TestProcess) -> () throws -> Void)] { var tests = [ ("test_exit0" , test_exit0), @@ -835,6 +864,7 @@ class TestProcess : XCTestCase { ("test_currentDirectory", test_currentDirectory), ("test_pipeCloseBeforeLaunch", test_pipeCloseBeforeLaunch), ("test_multiProcesses", test_multiProcesses), + ("test_processGroup", test_processGroup), ] #if !os(Windows) diff --git a/Tests/Tools/XDGTestHelper/main.swift b/Tests/Tools/XDGTestHelper/main.swift index 53fff4ac18..d2a36e2b11 100644 --- a/Tests/Tools/XDGTestHelper/main.swift +++ b/Tests/Tools/XDGTestHelper/main.swift @@ -274,6 +274,10 @@ case "--signal-test": case "--print-open-file-descriptors": printOpenFileDescriptors() + +case "--pgrp": + print("pgrp: \(getpgrp())") + #endif default: