* job.c (child_execute_job): Prefer posix_spawn() over fork()/exec()

This commit is contained in:
Aron Barath 2018-07-09 09:05:31 +02:00 committed by Paul Smith
parent fa937343f5
commit 749a54d7a4

166
src/job.c
View File

@ -137,6 +137,10 @@ extern int wait3 ();
# endif /* Have wait3. */
#endif /* Have waitpid. */
#ifdef HAVE_SPAWN_H
# include <spawn.h>
#endif /* have spawn.h */
#if !defined (wait) && !defined (POSIX)
int wait ();
#endif
@ -2232,6 +2236,11 @@ child_execute_job (struct output *out, int good_stdin, char **argv, char **envp)
int fderr = FD_STDERR;
int r;
pid_t pid;
#if HAVE_POSIX_SPAWN
short flags = 0;
posix_spawnattr_t attr;
posix_spawn_file_actions_t fa;
#endif /* have posix_spawn() */
/* Divert child output if we want to capture it. */
if (out && out->syncout)
@ -2242,6 +2251,9 @@ child_execute_job (struct output *out, int good_stdin, char **argv, char **envp)
fderr = out->err;
}
#if ! HAVE_POSIX_SPAWN
/* does not have posix_spawn() */
pid = vfork();
if (pid != 0)
return pid;
@ -2266,6 +2278,160 @@ child_execute_job (struct output *out, int good_stdin, char **argv, char **envp)
/* Run the command. */
exec_command (argv, envp);
#else /* have posix_spawn() */
if (posix_spawnattr_init (&attr) != 0)
return -1;
if (posix_spawn_file_actions_init (&fa) != 0) {
posix_spawnattr_destroy (&attr);
return -1;
error:;
posix_spawn_file_actions_destroy (&fa);
posix_spawnattr_destroy (&attr);
return -1;
}
/* Unblock all signals. */
#ifdef HAVE_POSIX_SPAWNATTR_SETSIGMASK
{
sigset_t mask;
sigemptyset (&mask);
if (posix_spawnattr_setsigmask (&attr, &mask) != 0)
goto error;
flags |= POSIX_SPAWN_SETSIGMASK;
}
#endif /* have posix_spawnattr_setsigmask() */
#ifdef SET_STACK_SIZE
/* Do not bother about stack limit. */
#endif
/* For any redirected FD, dup2() it to the standard FD.
They are all marked close-on-exec already. */
if (fdin >= 0 && fdin != FD_STDIN)
if (posix_spawn_file_actions_adddup2 (&fa, fdin, FD_STDIN) != 0)
goto error;
if (fdout != FD_STDOUT)
if (posix_spawn_file_actions_adddup2 (&fa, fdout, FD_STDOUT) != 0)
goto error;
if (fderr != FD_STDERR)
if (posix_spawn_file_actions_adddup2 (&fa, fderr, FD_STDERR) != 0)
goto error;
/* -------- Here start the replacement for exec_command() */
/* Be the user, permanently. */
flags |= POSIX_SPAWN_RESETIDS;
/* Apply the spawn flags. */
if (posix_spawnattr_setflags(&attr, flags) != 0)
goto error;
/* Run the program. */
r = posix_spawnp(&pid, argv[0], &fa, &attr, argv, envp);
if (r == 0) {
posix_spawn_file_actions_destroy (&fa);
posix_spawnattr_destroy (&attr);
return pid;
}
errno = r; /* for later perror()s */
switch (r)
{
case ENOENT:
/* We are in the child: don't use the output buffer.
It's not right to run fprintf() here! */
if (makelevel == 0)
fprintf (stderr, _("%s: %s: Command not found\n"), program, argv[0]);
else
fprintf (stderr, _("%s[%u]: %s: Command not found\n"),
program, makelevel, argv[0]);
break;
case ENOEXEC:
{
/* The file is not executable. Try it as a shell script. */
const char *shell;
char **new_argv;
int argc;
int i=1;
# ifdef __EMX__
/* Do not use $SHELL from the environment */
struct variable *p = lookup_variable ("SHELL", 5);
if (p)
shell = p->value;
else
shell = 0;
# else
shell = getenv ("SHELL");
# endif
if (shell == 0)
shell = default_shell;
argc = 1;
while (argv[argc] != 0)
++argc;
# ifdef __EMX__
if (!unixy_shell)
++argc;
# endif
new_argv = alloca ((1 + argc + 1) * sizeof (char *));
new_argv[0] = (char *)shell;
# ifdef __EMX__
if (!unixy_shell)
{
new_argv[1] = "/c";
++i;
--argc;
}
# endif
new_argv[i] = argv[0];
while (argc > 0)
{
new_argv[i + argc] = argv[argc];
--argc;
}
r = posix_spawnp(&pid, shell, &fa, &attr, new_argv, envp);
if (r == 0) {
posix_spawn_file_actions_destroy (&fa);
posix_spawnattr_destroy (&attr);
return pid;
}
errno = r; /* for later perror()s */
if (errno == ENOENT)
OS (error, NILF, _("%s: Shell program not found"), shell);
else
perror_with_name ("execvp: ", shell);
break;
}
# ifdef __EMX__
case EINVAL:
/* this nasty error was driving me nuts :-( */
O (error, NILF, _("spawnvpe: environment space might be exhausted"));
/* FALLTHROUGH */
# endif
default:
perror_with_name ("execvp: ", argv[0]);
break;
}
/* We could not spawn our program. Let's clean up, and exit. */
posix_spawn_file_actions_destroy (&fa);
posix_spawnattr_destroy (&attr);
return -1;
#endif /* have posix_spawn() */
}
#endif /* !AMIGA && !__MSDOS__ && !VMS */
#endif /* !WINDOWS32 */