From 596e6ed83a35f12ac9fcc01ac7d5ddc39981ea00 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Sat, 2 Nov 2019 18:44:36 +0100 Subject: [PATCH 1/2] SR-11699: Process: Closing standardInput before calling run() aborts with EBADF - Check the file descriptor passed to _CFPosixSpawnFileActionsAddClose() is valid. --- Foundation/Process.swift | 2 +- TestFoundation/TestProcess.swift | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Foundation/Process.swift b/Foundation/Process.swift index 201aaaf6ac..3f87d6caf4 100644 --- a/Foundation/Process.swift +++ b/Foundation/Process.swift @@ -860,7 +860,7 @@ open class Process: NSObject { for (new, old) in adddup2 { posix(_CFPosixSpawnFileActionsAddDup2(fileActions, old, new)) } - for fd in addclose { + for fd in addclose.filter({ $0 >= 0 }) { posix(_CFPosixSpawnFileActionsAddClose(fileActions, fd)) } diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift index e33c056710..b375080a69 100644 --- a/TestFoundation/TestProcess.swift +++ b/TestFoundation/TestProcess.swift @@ -696,6 +696,33 @@ class TestProcess : XCTestCase { } } + func test_pipeCloseBeforeLaunch() { + let process = Process() + let stdInput = Pipe() + let stdOutput = Pipe() + + process.executableURL = xdgTestHelperURL() + process.arguments = ["--cat"] + process.standardInput = stdInput + process.standardOutput = stdOutput + + let string = "Hello, World" + let stdInputPipe = stdInput.fileHandleForWriting + XCTAssertNoThrow(try stdInputPipe.write(XCTUnwrap(string.data(using: .utf8)))) + stdInputPipe.closeFile() + + XCTAssertNoThrow(try process.run()) + process.waitUntilExit() + + let stdOutputPipe = stdOutput.fileHandleForReading + do { + let readData = try XCTUnwrap(stdOutputPipe.readToEnd()) + let readString = String(data: readData, encoding: .utf8) + XCTAssertEqual(string, readString) + } catch { + XCTFail("\(error)") + } + } static var allTests: [(String, (TestProcess) -> () throws -> Void)] { var tests = [ @@ -724,6 +751,7 @@ class TestProcess : XCTestCase { ("test_redirect_all_using_nil", test_redirect_all_using_nil), ("test_plutil", test_plutil), ("test_currentDirectory", test_currentDirectory), + ("test_pipeCloseBeforeLaunch", test_pipeCloseBeforeLaunch) ] #if !os(Windows) From f73518eeb9e9dda0b6c860d10531a1f3fe6805f8 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Sun, 3 Nov 2019 01:31:09 +0100 Subject: [PATCH 2/2] SR-10639: Add test for connecting two Process with Pipe. - The fix for SR-11699 also fixes connecting two processes with pipes. --- TestFoundation/TestProcess.swift | 43 +++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift index b375080a69..c203c5abde 100644 --- a/TestFoundation/TestProcess.swift +++ b/TestFoundation/TestProcess.swift @@ -724,6 +724,46 @@ class TestProcess : XCTestCase { } } + func test_multiProcesses() { + let source = Process() + source.executableURL = xdgTestHelperURL() + source.arguments = [ "--getcwd" ] + + let cat1 = Process() + cat1.executableURL = xdgTestHelperURL() + cat1.arguments = [ "--cat" ] + + let cat2 = Process() + cat2.executableURL = xdgTestHelperURL() + cat2.arguments = [ "--cat" ] + + let pipe1 = Pipe() + source.standardOutput = pipe1 + cat1.standardInput = pipe1 + + let pipe2 = Pipe() + cat1.standardOutput = pipe2 + cat2.standardInput = pipe2 + + let pipe3 = Pipe() + cat2.standardOutput = pipe3 + + XCTAssertNoThrow(try source.run()) + XCTAssertNoThrow(try cat1.run()) + XCTAssertNoThrow(try cat2.run()) + cat2.waitUntilExit() + cat1.waitUntilExit() + source.waitUntilExit() + + do { + let data = try XCTUnwrap(pipe3.fileHandleForReading.readToEnd()) + let pwd = String.init(decoding: data, as: UTF8.self).trimmingCharacters(in: CharacterSet(["\n", "\r"])) + XCTAssertEqual(pwd, FileManager.default.currentDirectoryPath.standardizePath()) + } catch { + XCTFail("\(error)") + } + } + static var allTests: [(String, (TestProcess) -> () throws -> Void)] { var tests = [ ("test_exit0" , test_exit0), @@ -751,7 +791,8 @@ class TestProcess : XCTestCase { ("test_redirect_all_using_nil", test_redirect_all_using_nil), ("test_plutil", test_plutil), ("test_currentDirectory", test_currentDirectory), - ("test_pipeCloseBeforeLaunch", test_pipeCloseBeforeLaunch) + ("test_pipeCloseBeforeLaunch", test_pipeCloseBeforeLaunch), + ("test_multiProcesses", test_multiProcesses), ] #if !os(Windows)