Skip to content

SR-11699: Process: Closing standardInput before calling run() aborts with EBADF #2553

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Foundation/Process.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

Expand Down
69 changes: 69 additions & 0 deletions TestFoundation/TestProcess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,73 @@ 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)")
}
}

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 = [
Expand Down Expand Up @@ -724,6 +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_multiProcesses", test_multiProcesses),
]

#if !os(Windows)
Expand Down