civodul pushed a commit to branch wip-posix-spawn in repository guile. commit d4ece4b74d9920bd07e5bfe875ce85abe623f98e Author: Ludovic Courtès <l...@gnu.org> AuthorDate: Fri Dec 23 11:26:55 2022 +0100
fixup! Make system* and piped-process internally use spawn. Avoid extra 'fork' upon error in 'system*'. --- libguile/posix.c | 117 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 45 deletions(-) diff --git a/libguile/posix.c b/libguile/posix.c index de2a31aae..79b066bb3 100644 --- a/libguile/posix.c +++ b/libguile/posix.c @@ -78,6 +78,7 @@ #include "threads.h" #include "values.h" #include "vectors.h" +#include "verify.h" #include "version.h" #if (SCM_ENABLE_DEPRECATED == 1) @@ -96,6 +97,13 @@ # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif +#ifndef W_EXITCODE +/* Macro for constructing a status value. Found in glibc. */ +# define W_EXITCODE(ret, sig) ((ret) << 8 | (sig)) +#endif +verify (WEXITSTATUS (W_EXITCODE (127, 0)) == 127); + + #include <signal.h> #ifdef HAVE_GRP_H @@ -1424,15 +1432,14 @@ SCM_DEFINE (scm_spawn_process, "spawn*", 5, 0, 0, #undef FUNC_NAME #ifdef HAVE_FORK -static SCM -scm_piped_process (SCM prog, SCM args, SCM from, SCM to) +static int +piped_process (pid_t *pid, SCM prog, SCM args, SCM from, SCM to) #define FUNC_NAME "piped-process" { int reading, writing; int c2p[2]; /* Child to parent. */ int p2c[2]; /* Parent to child. */ int in = -1, out = -1, err = -1; - int pid; char *exec_file; char **exec_argv; char **exec_env = environ; @@ -1468,48 +1475,57 @@ scm_piped_process (SCM prog, SCM args, SCM from, SCM to) in = SCM_FPORT_FDES (port); } - pid = do_spawn (exec_file, exec_argv, exec_env, in, out, err); - int errno_save = errno; - - if (pid == -1) - { - /* TODO This is a compatibility shim until the next major release */ - switch (errno) - { - /* If the error seemingly comes from fork */ - case EAGAIN: - case ENOMEM: - case ENOSYS: - free (exec_file); - - if (reading) - close (c2p[0]); - if (writing) - close (p2c[1]); - - errno = errno_save; - SCM_SYSERROR; - break; - - default: - /* Else create a dummy process that exits with value 127 */ - dprintf (err, "In execvp of %s: %s\n", exec_file, - strerror (errno_save)); - pid = fork (); - if (pid == -1) - SCM_SYSERROR; - if (pid == 0) - _exit (127); - } - } - - free (exec_file); + *pid = do_spawn (exec_file, exec_argv, exec_env, in, out, err); + int errno_save = (*pid < 0) ? errno : 0; if (reading) close (c2p[1]); if (writing) close (p2c[0]); + if (*pid == -1) + switch (errno_save) + { + /* Errors that seemingly come from fork. */ + case EAGAIN: + case ENOMEM: + case ENOSYS: + errno = err; + free (exec_file); + SCM_SYSERROR; + break; + + default: /* ENOENT, etc. */ + /* Create a dummy process that exits with value 127. */ + dprintf (err, "In execvp of %s: %s\n", exec_file, + strerror (errno_save)); + } + + free (exec_file); + + return errno_save; +} +#undef FUNC_NAME + +static SCM +scm_piped_process (SCM prog, SCM args, SCM from, SCM to) +#define FUNC_NAME "piped-process" +{ + pid_t pid; + + (void) piped_process (&pid, prog, args, from, to); + if (pid == -1) + { + /* Create a dummy process that exits with value 127 to mimic the + previous fork + exec implementation. TODO: This is a + compatibility shim to remove in the next stable series. */ + pid = fork (); + if (pid == -1) + SCM_SYSERROR; + if (pid == 0) + _exit (127); + } + return scm_from_int (pid); } #undef FUNC_NAME @@ -1555,8 +1571,9 @@ SCM_DEFINE (scm_system_star, "system*", 0, 0, 1, "Example: (system* \"echo\" \"foo\" \"bar\")") #define FUNC_NAME s_scm_system_star { - SCM prog, pid; - int status, wait_result; + SCM prog; + pid_t pid; + int err, status, wait_result; if (scm_is_null (args)) SCM_WRONG_NUM_ARGS (); @@ -1574,10 +1591,20 @@ SCM_DEFINE (scm_system_star, "system*", 0, 0, 1, SCM_UNDEFINED); #endif - pid = scm_piped_process (prog, args, SCM_UNDEFINED, SCM_UNDEFINED); - SCM_SYSCALL (wait_result = waitpid (scm_to_int (pid), &status, 0)); - if (wait_result == -1) - SCM_SYSERROR; + err = piped_process (&pid, prog, args, + SCM_UNDEFINED, SCM_UNDEFINED); + if (err != 0) + /* ERR might be ENOENT or similar. For backward compatibility with + the previous implementation based on fork + exec, pretend the + child process exited with code 127. TODO: Remove this + compatibility shim in the next stable series. */ + status = W_EXITCODE (127, 0); + else + { + SCM_SYSCALL (wait_result = waitpid (pid, &status, 0)); + if (wait_result == -1) + SCM_SYSERROR; + } scm_dynwind_end ();