Skip to content

Commit e380e4c

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

File tree

3 files changed

+92
-8
lines changed

3 files changed

+92
-8
lines changed

Sources/TSCBasic/Process.swift

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ 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+
int SPM_fork_exec_chdir(pid_t *pid, const char *target_directory, const char *cmd, char *const argv[], char *const envp[],
13+
int in_pipe[], int out_pipe[], int err_pipe[], bool redirect_out, bool redirect_err);
14+
1215
#endif

Sources/TSCclibc/process.c

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

0 commit comments

Comments
 (0)