Skip to content

Commit 81fc87f

Browse files
committed
Properly revert the pgrp of the intput fd to the previous value instead of the one of the process when subprocess ends
1 parent d224aec commit 81fc87f

File tree

1 file changed

+21
-10
lines changed

1 file changed

+21
-10
lines changed

Sources/ProcessInvocation/ProcessInvocation.swift

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -447,34 +447,42 @@ public struct ProcessInvocation : AsyncSequence {
447447
}
448448
}
449449

450-
let fdWhoseFgPgIDShouldBeSet: FileDescriptor?
451450
var fdsToCloseAfterRun = Set<FileDescriptor>()
452451
var fdRedirects = [FileDescriptor: FileDescriptor]()
453452
var outputFileDescriptors = additionalOutputFileDescriptors
453+
let fgPgIDSetInfo: (destFd: FileDescriptor, originalValue: Int32)?
454454
switch stdinRedirect {
455455
case .none(let setFgPgID):
456456
p.standardInput = FileHandle.standardInput /* Already the case in theory as it is the default, but let’s be explicit. */
457-
fdWhoseFgPgIDShouldBeSet = (setFgPgID ? FileDescriptor.standardInput : nil)
457+
let originalPgrp = tcgetpgrp(FileDescriptor.standardInput.rawValue)
458+
if originalPgrp == -1 {
459+
Conf.logger?.warning("Cannot determine the process group ID of the process that owns standard input; skipping foreground process group ID setting.", metadata: ["error": "\(Errno(rawValue: errno))"])
460+
}
461+
fgPgIDSetInfo = (setFgPgID && originalPgrp != -1 ? (FileDescriptor.standardInput, originalPgrp) : nil)
458462

459463
case .fromNull:
460464
p.standardInput = nil
461-
fdWhoseFgPgIDShouldBeSet = nil
465+
fgPgIDSetInfo = nil
462466

463467
case .fromFd(let fd, let shouldClose, let setFgPgID):
464468
p.standardInput = FileHandle(fileDescriptor: fd.rawValue, closeOnDealloc: false)
465469
if shouldClose {
466470
assert(fileDescriptorsToSend.isEmpty, "Giving ownership to fd on stdin is not allowed when launching the process via the bridge. This is because stdin has to be sent via the bridge and we get only pain and race conditions to properly close the fd.")
467471
fdsToCloseAfterRun.insert(fd)
468472
}
469-
fdWhoseFgPgIDShouldBeSet = (setFgPgID ? fd : nil)
473+
let originalPgrp = tcgetpgrp(fd.rawValue)
474+
if originalPgrp == -1 {
475+
Conf.logger?.warning("Cannot determine the process group ID of the process that owns the given fd; skipping foreground process group ID setting.", metadata: ["error": "\(Errno(rawValue: errno))", "fd": "\(fd)"])
476+
}
477+
fgPgIDSetInfo = (setFgPgID && originalPgrp != -1 ? (fd, originalPgrp) : nil)
470478

471479
case .sendFromReader(let reader):
472480
assert(fileDescriptorsToSend.isEmpty, "Sending data to stdin via a reader is not allowed when launching the process via the bridge. This is because stdin has to be sent via the bridge and we get only pain and race conditions to properly close the fd.")
473481
let fd = try Self.readFdOfPipeForStreaming(dataFromReader: reader, maxCacheSize: 32 * 1024 * 1024)
474482
fdsToCloseAfterRun.insert(fd)
475483
p.standardInput = FileHandle(fileDescriptor: fd.rawValue, closeOnDealloc: false)
476484
/* TODO: If we fail later, should the write end of the pipe be closed? */
477-
fdWhoseFgPgIDShouldBeSet = nil
485+
fgPgIDSetInfo = nil
478486
}
479487
switch stdoutRedirect {
480488
case .none: p.standardOutput = FileHandle.standardOutput /* Already the case in theory as it is the default, but let’s be explicit. */
@@ -691,9 +699,9 @@ public struct ProcessInvocation : AsyncSequence {
691699

692700
let additionalTerminationHandler: @Sendable (Process) -> Void = { _ in
693701
Conf.logger?.debug("Called in termination handler of process.")
694-
if let fdWhoseFgPgIDShouldBeSet = fdWhoseFgPgIDShouldBeSet {
695-
/* Let’s revert the fg pg ID back to our pg ID. */
696-
if tcsetpgrp(fdWhoseFgPgIDShouldBeSet.rawValue, getpgrp()) != 0 && errno != ENOTTY {
702+
if let fgPgIDSetInfo = fgPgIDSetInfo {
703+
/* Let’s revert the fg pg ID back to the original value. */
704+
if tcsetpgrp(fgPgIDSetInfo.destFd.rawValue, fgPgIDSetInfo.originalValue) != 0 && errno != ENOTTY {
697705
Conf.logger?.error("Failed setting foreground process group ID of controlling terminal of stdin back to our process group.")
698706
}
699707
}
@@ -785,8 +793,11 @@ public struct ProcessInvocation : AsyncSequence {
785793
/* The executable is now launched.
786794
* We must not fail after this, so we wrap the rest of the function in a non-throwing block. */
787795
return {
788-
if let fdWhoseFgPgIDShouldBeSet = fdWhoseFgPgIDShouldBeSet {
789-
if tcsetpgrp(fdWhoseFgPgIDShouldBeSet.rawValue, getpgid(p.processIdentifier)) != 0 && errno != ENOTTY {
796+
if let fgPgIDSetInfo = fgPgIDSetInfo {
797+
let pgid = getpgid(p.processIdentifier)
798+
if pgid == -1 {
799+
Conf.logger?.error("Failed retrieving the process group ID of the child process.", metadata: ["error": "\(Errno(rawValue: errno))"])
800+
} else if tcsetpgrp(fgPgIDSetInfo.destFd.rawValue, pgid) != 0 && errno != ENOTTY {
790801
Conf.logger?.error("Failed setting the foreground group ID to the child process group ID.", metadata: ["error": "\(Errno(rawValue: errno))"])
791802
}
792803
}

0 commit comments

Comments
 (0)