Skip to content

Commit 0216fd2

Browse files
committed
Use fork and execve instead
1 parent 13db301 commit 0216fd2

File tree

3 files changed

+89
-9
lines changed

3 files changed

+89
-9
lines changed

Sources/TSCBasic/Process.swift

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
4+
Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -658,6 +658,7 @@ public final class Process {
658658
posix_spawn_file_actions_init(&fileActions)
659659
defer { posix_spawn_file_actions_destroy(&fileActions) }
660660

661+
var spawnFunc: SpawnFunc = .posix_spawn
661662
if let workingDirectory = workingDirectory?.pathString {
662663
#if canImport(Darwin)
663664
// The only way to set a workingDirectory is using an availability-gated initializer, so we don't need
@@ -667,11 +668,11 @@ public final class Process {
667668
posix_spawn_file_actions_addchdir_np(&fileActions, workingDirectory)
668669
}
669670
#elseif os(Linux)
670-
guard SPM_posix_spawn_file_actions_addchdir_np_supported() else {
671-
throw Process.Error.workingDirectoryNotSupported
671+
if SPM_posix_spawn_file_actions_addchdir_np_supported() {
672+
SPM_posix_spawn_file_actions_addchdir_np(&fileActions, workingDirectory)
673+
} else {
674+
spawnFunc = .fork_exec(workingDirectory: workingDirectory)
672675
}
673-
674-
SPM_posix_spawn_file_actions_addchdir_np(&fileActions, workingDirectory)
675676
#else
676677
throw Process.Error.workingDirectoryNotSupported
677678
#endif
@@ -725,8 +726,13 @@ public final class Process {
725726
}
726727
let argv = CStringArray(resolvedArgs)
727728
let env = CStringArray(environment.map({ "\($0.0)=\($0.1)" }))
728-
let rv = posix_spawnp(&processID, argv.cArray[0]!, &fileActions, &attributes, argv.cArray, env.cArray)
729-
729+
let rv = switch spawnFunc {
730+
case .posix_spawn:
731+
posix_spawnp(&processID, argv.cArray[0]!, &fileActions, &attributes, argv.cArray, env.cArray)
732+
case .fork_exec(let workingDirectory):
733+
SPM_fork_exec_chdir(&processID, workingDirectory, argv.cArray[0]!, argv.cArray, env.cArray, &stdinPipe, &outputPipe, &stderrPipe, outputRedirection.redirectsOutput, outputRedirection.redirectStderr)
734+
}
735+
730736
guard rv == 0 else {
731737
throw SystemError.posix_spawn(rv, arguments)
732738
}
@@ -1349,3 +1355,10 @@ extension Process {
13491355
stdoutStream.flush()
13501356
}
13511357
}
1358+
1359+
extension Process {
1360+
enum SpawnFunc {
1361+
case posix_spawn
1362+
case fork_exec(workingDirectory: String)
1363+
}
1364+
}

Sources/TSCclibc/include/process.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ int SPM_posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *restric
99
// Runtime check for the availability of posix_spawn_file_actions_addchdir_np. Returns 0 if the method is available, -1 if not.
1010
bool SPM_posix_spawn_file_actions_addchdir_np_supported();
1111

12+
// Alternative for posix_spawn_file_actions_addchdir_np on Linux that mimics its behavior using fork, exec*, and chdir.
13+
int SPM_fork_exec_chdir(pid_t *pid, const char *target_directory, const char *cmd, char *const argv[], char *const envp[],
14+
int in_pipe[], int out_pipe[], int err_pipe[], bool redirect_out, bool redirect_err);
15+
1216
#endif

Sources/TSCclibc/process.c

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
#endif
66

77
#include <errno.h>
8+
#include <stdio.h>
9+
#include <stdlib.h>
810
#include <unistd.h>
11+
#include <sys/wait.h>
912

1013
#include "process.h"
1114

@@ -14,8 +17,7 @@ int SPM_posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *restric
1417
# if __GLIBC_PREREQ(2, 29)
1518
return posix_spawn_file_actions_addchdir_np(file_actions, path);
1619
# else
17-
// Change working directory which child process will inherit
18-
return chdir(path);
20+
return ENOSYS;
1921
# endif
2022
#else
2123
return ENOSYS;
@@ -24,10 +26,71 @@ int SPM_posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *restric
2426

2527
bool SPM_posix_spawn_file_actions_addchdir_np_supported() {
2628
#if defined(__GLIBC__)
29+
# if __GLIBC_PREREQ(2, 29)
2730
return true;
31+
# else
32+
return false;
33+
# endif
2834
#else
2935
return false;
3036
#endif
3137
}
3238

39+
int SPM_fork_exec_chdir(pid_t *pid, const char *cwd,
40+
const char *cmd, char *const argv[], char *const envp[],
41+
int in_pipe[], int out_pipe[], int err_pipe[], bool redirect_out, bool redirect_err) {
42+
*pid = fork();
43+
44+
if (*pid < 0) {
45+
perror("fork() failed");
46+
_exit(EXIT_FAILURE);
47+
} else if (*pid > 0) { // Parent process
48+
// Wait for child process to finish
49+
int status;
50+
waitpid(*pid, &status, 0);
51+
return status;
52+
} else { // Child process
53+
// Change working directory then execute cmd
54+
if (chdir(cwd)) {
55+
_exit(EXIT_FAILURE);
56+
}
57+
58+
// Replicate pipe logic in TSCBasic.Process.launch()
59+
60+
// Dupe the read portion of the remote to 0.
61+
dup2(in_pipe[0], 0);
62+
// Close the other side's pipe since it was duped to 0.
63+
close(in_pipe[0]);
64+
close(in_pipe[1]);
65+
66+
if (redirect_out) {
67+
// Open the write end of the pipe.
68+
dup2(out_pipe[1], 1);
69+
// Close the other ends of the pipe since they were duped to 1.
70+
close(out_pipe[0]);
71+
close(out_pipe[1]);
72+
73+
if (redirect_err) {
74+
// If merged was requested, send stderr to stdout.
75+
dup2(1, 2);
76+
} else {
77+
// If no redirect was requested, open the pipe for stderr.
78+
dup2(err_pipe[1], 2);
79+
// Close the other ends of the pipe since they were dupped to 2.
80+
close(err_pipe[0]);
81+
close(err_pipe[1]);
82+
}
83+
} else {
84+
dup2(1, 1);
85+
dup2(2, 2);
86+
}
87+
88+
execve(cmd, argv, envp);
89+
90+
// If execve returns, it must have failed.
91+
perror(cmd);
92+
_exit(EXIT_FAILURE);
93+
}
94+
}
95+
3396
#endif

0 commit comments

Comments
 (0)