AC_CHECK_DECLS([bsd_signal], [], [], [[#define _GNU_SOURCE 1
#include <signal.h>]])
+AC_FUNC_FORK
+
AC_FUNC_SETVBUF_REVERSED
# Rumor has it that strcasecmp lives in -lresolv on some odd systems.
perror_with_name (error_prefix, "pipe");
return o;
}
+
#elif defined(WINDOWS32)
windows32_openpipe (pipedes, errfd, &pid, command_argv, envp);
/* Restore the value of just_print_flag. */
perror_with_name (error_prefix, "pipe");
return o;
}
- else
+
#else
if (pipe (pipedes) < 0)
{
return o;
}
-# ifdef __EMX__
- /* close some handles that are unnecessary for the child process */
+ /* Close handles that are unnecessary for the child process. */
CLOSE_ON_EXEC(pipedes[1]);
CLOSE_ON_EXEC(pipedes[0]);
- /* Never use fork()/exec() here! Use spawn() instead in exec_command() */
- pid = child_execute_job (FD_STDIN, pipedes[1], errfd, command_argv, envp);
- if (pid < 0)
- perror_with_name (error_prefix, "spawn");
-# else /* ! __EMX__ */
- pid = fork ();
+
+ {
+ struct output out;
+ out.syncout = 1;
+ out.out = pipedes[1];
+ out.err = errfd;
+
+ pid = child_execute_job (&out, 1, command_argv, envp);
+ }
+
if (pid < 0)
- perror_with_name (error_prefix, "fork");
- else if (pid == 0)
{
-# ifdef SET_STACK_SIZE
- /* Reset limits, if necessary. */
- if (stack_limit.rlim_cur)
- setrlimit (RLIMIT_STACK, &stack_limit);
-# endif
- child_execute_job (FD_STDIN, pipedes[1], errfd, command_argv, envp);
+ perror_with_name (error_prefix, "fork");
+ return o;
}
- else
-# endif
#endif
- {
- /* We are the parent. */
- char *buffer;
- unsigned int maxlen, i;
- int cc;
- /* Record the PID for reap_children. */
- shell_function_pid = pid;
+ {
+ char *buffer;
+ unsigned int maxlen, i;
+ int cc;
+
+ /* Record the PID for reap_children. */
+ shell_function_pid = pid;
#ifndef __MSDOS__
- shell_function_completed = 0;
+ shell_function_completed = 0;
- /* Free the storage only the child needed. */
- free (command_argv[0]);
- free (command_argv);
+ /* Free the storage only the child needed. */
+ free (command_argv[0]);
+ free (command_argv);
- /* Close the write side of the pipe. We test for -1, since
- pipedes[1] is -1 on MS-Windows, and some versions of MS
- libraries barf when 'close' is called with -1. */
- if (pipedes[1] >= 0)
- close (pipedes[1]);
+ /* Close the write side of the pipe. We test for -1, since
+ pipedes[1] is -1 on MS-Windows, and some versions of MS
+ libraries barf when 'close' is called with -1. */
+ if (pipedes[1] >= 0)
+ close (pipedes[1]);
#endif
- /* Set up and read from the pipe. */
+ /* Set up and read from the pipe. */
- maxlen = 200;
- buffer = xmalloc (maxlen + 1);
+ maxlen = 200;
+ buffer = xmalloc (maxlen + 1);
- /* Read from the pipe until it gets EOF. */
- for (i = 0; ; i += cc)
- {
- if (i == maxlen)
- {
- maxlen += 512;
- buffer = xrealloc (buffer, maxlen + 1);
- }
+ /* Read from the pipe until it gets EOF. */
+ for (i = 0; ; i += cc)
+ {
+ if (i == maxlen)
+ {
+ maxlen += 512;
+ buffer = xrealloc (buffer, maxlen + 1);
+ }
- EINTRLOOP (cc, read (pipedes[0], &buffer[i], maxlen - i));
- if (cc <= 0)
- break;
- }
- buffer[i] = '\0';
+ EINTRLOOP (cc, read (pipedes[0], &buffer[i], maxlen - i));
+ if (cc <= 0)
+ break;
+ }
+ buffer[i] = '\0';
- /* Close the read side of the pipe. */
+ /* Close the read side of the pipe. */
#ifdef __MSDOS__
- if (fpipe)
- {
- int st = pclose (fpipe);
- shell_completed (st, 0);
- }
+ if (fpipe)
+ {
+ int st = pclose (fpipe);
+ shell_completed (st, 0);
+ }
#else
- (void) close (pipedes[0]);
+ (void) close (pipedes[0]);
#endif
- /* Loop until child_handler or reap_children() sets
- shell_function_completed to the status of our child shell. */
- while (shell_function_completed == 0)
- reap_children (1, 0);
+ /* Loop until child_handler or reap_children() sets
+ shell_function_completed to the status of our child shell. */
+ while (shell_function_completed == 0)
+ reap_children (1, 0);
- if (batch_filename)
- {
- DB (DB_VERBOSE, (_("Cleaning up temporary batch file %s\n"),
- batch_filename));
- remove (batch_filename);
- free (batch_filename);
- }
- shell_function_pid = 0;
+ if (batch_filename)
+ {
+ DB (DB_VERBOSE, (_("Cleaning up temporary batch file %s\n"),
+ batch_filename));
+ remove (batch_filename);
+ free (batch_filename);
+ }
+ shell_function_pid = 0;
- /* shell_completed() will set shell_function_completed to 1 when the
- child dies normally, or to -1 if it dies with status 127, which is
- most likely an exec fail. */
+ /* shell_completed() will set shell_function_completed to 1 when the
+ child dies normally, or to -1 if it dies with status 127, which is
+ most likely an exec fail. */
- if (shell_function_completed == -1)
- {
- /* This likely means that the execvp failed, so we should just
- write the error message in the pipe from the child. */
- fputs (buffer, stderr);
- fflush (stderr);
- }
- else
- {
- /* The child finished normally. Replace all newlines in its output
- with spaces, and put that in the variable output buffer. */
- fold_newlines (buffer, &i, trim_newlines);
- o = variable_buffer_output (o, buffer, i);
- }
+ if (shell_function_completed == -1)
+ {
+ /* This likely means that the execvp failed, so we should just
+ write the error message in the pipe from the child. */
+ fputs (buffer, stderr);
+ fflush (stderr);
+ }
+ else
+ {
+ /* The child finished normally. Replace all newlines in its output
+ with spaces, and put that in the variable output buffer. */
+ fold_newlines (buffer, &i, trim_newlines);
+ o = variable_buffer_output (o, buffer, i);
+ }
- free (buffer);
- }
+ free (buffer);
+ }
return o;
}
static void
start_job_command (struct child *child)
{
-#if !defined(_AMIGA) && !defined(WINDOWS32)
- static int bad_stdin = -1;
-#endif
int flags;
char *p;
#ifdef VMS
char *argv;
#else
char **argv;
- int outfd = FD_STDOUT;
- int errfd = FD_STDERR;
#endif
/* If we have a completely empty commandset, stop now. */
fflush (stdout);
fflush (stderr);
-#ifndef VMS
-#if !defined(WINDOWS32) && !defined(_AMIGA) && !defined(__MSDOS__)
-
- /* Set up a bad standard input that reads from a broken pipe. */
-
- if (bad_stdin == -1)
- {
- /* Make a file descriptor that is the read end of a broken pipe.
- This will be used for some children's standard inputs. */
- int pd[2];
- if (pipe (pd) == 0)
- {
- /* Close the write side. */
- (void) close (pd[1]);
- /* Save the read side. */
- bad_stdin = pd[0];
-
- /* Set the descriptor to close on exec, so it does not litter any
- child's descriptor table. When it is dup2'd onto descriptor 0,
- that descriptor will not close on exec. */
- CLOSE_ON_EXEC (bad_stdin);
- }
- }
-
-#endif /* !WINDOWS32 && !_AMIGA && !__MSDOS__ */
-
/* Decide whether to give this child the 'good' standard input
(one that points to the terminal or whatever), or the 'bad' one
that points to the read side of a broken pipe. */
if (child->good_stdin)
good_stdin_used = 1;
-#endif /* !VMS */
-
child->deleted = 0;
#ifndef _AMIGA
{
int is_remote, id, used_stdin;
if (start_remote_job (argv, child->environment,
- child->good_stdin ? 0 : bad_stdin,
+ child->good_stdin ? 0 : get_bad_stdin (),
&is_remote, &id, &used_stdin))
/* Don't give up; remote execution may fail for various reasons. If
so, simply run the job locally. */
child->remote = 0;
#ifdef VMS
- if (!child_execute_job (argv, child))
+ if (!child_execute_job (child, argv))
{
/* Fork failed! */
perror_with_name ("fork", "");
parent_environ = environ;
-#ifndef NO_OUTPUT_SYNC
- /* Divert child output if output_sync in use. */
- if (child->output.syncout)
- {
- if (child->output.out >= 0)
- outfd = child->output.out;
- if (child->output.err >= 0)
- errfd = child->output.err;
- }
-#endif
-# ifdef __EMX__
- /* If we aren't running a recursive command and we have a jobserver
- pipe, close it before exec'ing. */
- if (!(flags & COMMANDS_RECURSE) && jobserver_enabled ())
- jobserver_pre_child ();
-
- /* Never use fork()/exec() here! Use spawn() instead in exec_command() */
- child->pid = child_execute_job (child->good_stdin ? FD_STDIN : bad_stdin,
- outfd, errfd,
- argv, child->environment);
- if (child->pid < 0)
- {
- /* spawn failed! */
- unblock_sigs ();
- perror_with_name ("spawn", "");
- goto error;
- }
+ jobserver_pre_child (flags & COMMANDS_RECURSE);
- /* undo CLOSE_ON_EXEC() after the child process has been started */
- if (!(flags & COMMANDS_RECURSE) && jobserver_enabled ())
- jobserver_post_child ();
+ child->pid = child_execute_job (&child->output, child->good_stdin, argv, child->environment);
-#else /* !__EMX__ */
-
- child->pid = fork ();
environ = parent_environ; /* Restore value child may have clobbered. */
- if (child->pid == 0)
- {
- /* We are the child side. */
- unblock_sigs ();
-
- /* If we AREN'T running a recursive command and we have a jobserver,
- clear it before exec'ing. */
- if (!(flags & COMMANDS_RECURSE) && jobserver_enabled ())
- jobserver_clear ();
+ jobserver_post_child (flags & COMMANDS_RECURSE);
-#ifdef SET_STACK_SIZE
- /* Reset limits, if necessary. */
- if (stack_limit.rlim_cur)
- setrlimit (RLIMIT_STACK, &stack_limit);
-#endif
- child_execute_job (child->good_stdin ? FD_STDIN : bad_stdin,
- outfd, errfd, argv, child->environment);
- }
- else if (child->pid < 0)
+ if (child->pid < 0)
{
/* Fork failed! */
unblock_sigs ();
perror_with_name ("fork", "");
goto error;
}
-# endif /* !__EMX__ */
#endif /* !VMS */
}
{
HANDLE hPID;
char* arg0;
+ int outfd = FD_STDOUT;
+ int errfd = FD_STDERR;
/* make UNC paths safe for CreateProcess -- backslash format */
arg0 = argv[0];
/* EMX: Start a child process. This function returns the new pid. */
# if defined __EMX__
int
-child_execute_job (int stdin_fd, int stdout_fd, int stderr_fd,
- char **argv, char **envp)
+child_execute_job (struct output *out, int good_stdin, char **argv, char **envp)
{
int pid;
- int save_stdin = -1;
- int save_stdout = -1;
- int save_stderr = -1;
+ int fdin = good_stdin ? FD_STDIN : get_bad_stdin ();
+ int fdout = FD_STDOUT;
+ int fderr = FD_STDERR;
+ int save_fdin = -1;
+ int save_fdout = -1;
+ int save_fderr = -1;
+
+#ifndef NO_OUTPUT_SYNC
+ /* Divert child output if output_sync in use. */
+ if (out && out->syncout)
+ {
+ if (out->out >= 0)
+ fdout = out->out;
+ if (out->err >= 0)
+ fderr = out->err;
+ }
+#endif
/* For each FD which needs to be redirected first make a dup of the standard
FD to save and mark it close on exec so our child won't see it. Then
dup2() the standard FD to the redirect FD, and also mark the redirect FD
as close on exec. */
- if (stdin_fd != FD_STDIN)
+ if (fdin != FD_STDIN)
{
- save_stdin = dup (FD_STDIN);
- if (save_stdin < 0)
+ save_fdin = dup (FD_STDIN);
+ if (save_fdin < 0)
O (fatal, NILF, _("no more file handles: could not duplicate stdin\n"));
- CLOSE_ON_EXEC (save_stdin);
+ CLOSE_ON_EXEC (save_fdin);
- dup2 (stdin_fd, FD_STDIN);
- CLOSE_ON_EXEC (stdin_fd);
+ dup2 (fdin, FD_STDIN);
+ CLOSE_ON_EXEC (fdin);
}
- if (stdout_fd != FD_STDOUT)
+ if (fdout != FD_STDOUT)
{
- save_stdout = dup (FD_STDOUT);
- if (save_stdout < 0)
+ save_fdout = dup (FD_STDOUT);
+ if (save_fdout < 0)
O (fatal, NILF,
_("no more file handles: could not duplicate stdout\n"));
- CLOSE_ON_EXEC (save_stdout);
+ CLOSE_ON_EXEC (save_fdout);
- dup2 (stdout_fd, FD_STDOUT);
- CLOSE_ON_EXEC (stdout_fd);
+ dup2 (fdout, FD_STDOUT);
+ CLOSE_ON_EXEC (fdout);
}
- if (stderr_fd != FD_STDERR)
+ if (fderr != FD_STDERR)
{
- if (stderr_fd != stdout_fd)
+ if (fderr != fdout)
{
- save_stderr = dup (FD_STDERR);
- if (save_stderr < 0)
+ save_fderr = dup (FD_STDERR);
+ if (save_fderr < 0)
O (fatal, NILF,
_("no more file handles: could not duplicate stderr\n"));
- CLOSE_ON_EXEC (save_stderr);
+ CLOSE_ON_EXEC (save_fderr);
}
- dup2 (stderr_fd, FD_STDERR);
- CLOSE_ON_EXEC (stderr_fd);
+ dup2 (fderr, FD_STDERR);
+ CLOSE_ON_EXEC (fderr);
}
/* Run the command. */
pid = exec_command (argv, envp);
/* Restore stdout/stdin/stderr of the parent and close temporary FDs. */
- if (save_stdin >= 0)
+ if (save_fdin >= 0)
{
- if (dup2 (save_stdin, FD_STDIN) != FD_STDIN)
+ if (dup2 (save_fdin, FD_STDIN) != FD_STDIN)
O (fatal, NILF, _("Could not restore stdin\n"));
else
- close (save_stdin);
+ close (save_fdin);
}
- if (save_stdout >= 0)
+ if (save_fdout >= 0)
{
- if (dup2 (save_stdout, FD_STDOUT) != FD_STDOUT)
+ if (dup2 (save_fdout, FD_STDOUT) != FD_STDOUT)
O (fatal, NILF, _("Could not restore stdout\n"));
else
- close (save_stdout);
+ close (save_fdout);
}
- if (save_stderr >= 0)
+ if (save_fderr >= 0)
{
- if (dup2 (save_stderr, FD_STDERR) != FD_STDERR)
+ if (dup2 (save_fderr, FD_STDERR) != FD_STDERR)
O (fatal, NILF, _("Could not restore stderr\n"));
else
- close (save_stderr);
+ close (save_fderr);
}
return pid;
#elif !defined (_AMIGA) && !defined (__MSDOS__) && !defined (VMS)
-/* UNIX:
- Replace the current process with one executing the command in ARGV.
- STDIN_FD/STDOUT_FD/STDERR_FD are used as the process's stdin/stdout/stderr;
- ENVP is the environment of the new program. This function does not return. */
-void
-child_execute_job (int stdin_fd, int stdout_fd, int stderr_fd,
- char **argv, char **envp)
+/* POSIX:
+ Create a child process executing the command in ARGV.
+ ENVP is the environment of the new program. Returns the PID or -1. */
+int
+child_execute_job (struct output *out, int good_stdin, char **argv, char **envp)
{
int r;
+ int pid;
+ int fdin = good_stdin ? FD_STDIN : get_bad_stdin ();
+ int fdout = FD_STDOUT;
+ int fderr = FD_STDERR;
- /* For any redirected FD, dup2() it to the standard FD then close it. */
- if (stdin_fd != FD_STDIN)
+#ifndef NO_OUTPUT_SYNC
+ /* Divert child output if output_sync in use. */
+ if (out && out->syncout)
{
- EINTRLOOP (r, dup2 (stdin_fd, FD_STDIN));
- close (stdin_fd);
+ if (out->out >= 0)
+ fdout = out->out;
+ if (out->err >= 0)
+ fderr = out->err;
}
+#endif
+
+ pid = vfork();
+ if (pid != 0)
+ return pid;
- if (stdout_fd != FD_STDOUT)
- EINTRLOOP (r, dup2 (stdout_fd, FD_STDOUT));
- if (stderr_fd != FD_STDERR)
- EINTRLOOP (r, dup2 (stderr_fd, FD_STDERR));
+ /* We are the child. */
+ unblock_sigs ();
+
+#ifdef SET_STACK_SIZE
+ /* Reset limits, if necessary. */
+ if (stack_limit.rlim_cur)
+ setrlimit (RLIMIT_STACK, &stack_limit);
+#endif
- if (stdout_fd != FD_STDOUT)
- close (stdout_fd);
- if (stderr_fd != FD_STDERR && stderr_fd != stdout_fd)
- close (stderr_fd);
+ /* For any redirected FD, dup2() it to the standard FD.
+ They are all marked close-on-exec already. */
+ if (fdin != FD_STDIN)
+ EINTRLOOP (r, dup2 (fdin, FD_STDIN));
+ if (fdout != FD_STDOUT)
+ EINTRLOOP (r, dup2 (fdout, FD_STDOUT));
+ if (fderr != FD_STDERR)
+ EINTRLOOP (r, dup2 (fderr, FD_STDERR));
/* Run the command. */
exec_command (argv, envp);
/* How to set close-on-exec for a file descriptor. */
-#if !defined F_SETFD
+#if !defined(F_SETFD) || !defined(F_GETFD)
# ifdef WINDOWS32
# define CLOSE_ON_EXEC(_d) process_noinherit(_d)
# else
char **construct_command_argv (char *line, char **restp, struct file *file,
int cmd_flags, char** batch_file);
+
#ifdef VMS
-int child_execute_job (char *argv, struct child *child);
+int child_execute_job (struct child *child, char *argv);
#else
# define FD_STDIN (fileno (stdin))
# define FD_STDOUT (fileno (stdout))
# define FD_STDERR (fileno (stderr))
-# if defined(__EMX__)
-int child_execute_job (int stdin_fd, int stdout_fd, int stderr_fd,
- char **argv, char **envp);
-# else
-void child_execute_job (int stdin_fd, int stdout_fd, int stderr_fd,
- char **argv, char **envp) __attribute__ ((noreturn));
-# endif
+int child_execute_job (struct output *out, int good_stdin, char **argv, char **envp);
#endif
+
#ifdef _AMIGA
void exec_command (char **argv) __attribute__ ((noreturn));
#elif defined(__EMX__)
termination. */
int pid;
int r;
- pid = child_execute_job (FD_STDIN, FD_STDOUT, FD_STDERR,
- nargv, environ);
+ pid = child_execute_job (NULL, 1, nargv, environ);
/* is this loop really necessary? */
do {
# define SA_RESTART 0
#endif
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+#if !HAVE_WORKING_VFORK
+# define vfork fork
+#endif
+
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
#define jobserver_release(_fatal) (void)(0)
#define jobserver_acquire_all() (0)
#define jobserver_signal() (void)(0)
-#define jobserver_pre_child() (void)(0)
-#define jobserver_post_child() (void)(0)
+#define jobserver_pre_child(_r) (void)(0)
+#define jobserver_post_child(_r) (void)(0)
#define jobserver_pre_acquire() (void)(0)
#define jobserver_acquire(_tmout) (0)
#endif
+
+/* Create a "bad" file descriptor for stdin when parallel jobs are run. */
+#if !defined(VMD) && !defined(WINDOWS32) && !defined(_AMIGA) && !defined(__MSDOS__)
+int get_bad_stdin ();
+#else
+# define get_bad_stdin() (-1)
+#endif
}
}
-/* This is really only invoked on OS/2.
- On POSIX we just call jobserver_clear() in the child process. */
-void jobserver_pre_child ()
+/* Prepare the jobserver to start a child process. */
+void jobserver_pre_child (int recursive)
{
- CLOSE_ON_EXEC (job_fds[0]);
- CLOSE_ON_EXEC (job_fds[1]);
+ /* If it's not a recursive make, avoid polutting the jobserver pipes. */
+ if (!recursive && job_fds[0] >= 0)
+ {
+ CLOSE_ON_EXEC (job_fds[0]);
+ CLOSE_ON_EXEC (job_fds[1]);
+ }
}
-void jobserver_post_child ()
+void jobserver_post_child (int recursive)
{
#if defined(F_GETFD) && defined(F_SETFD)
- for (int i = 0; i < 2; ++i)
- {
- int flags;
- EINTRLOOP (flags, fcntl (job_fds[i], F_GETFD));
- if (flags >= 0)
- {
- int r;
- EINTRLOOP (r, fcntl (job_fds[i], F_SETFD, flags & ~FD_CLOEXEC));
- }
- }
+ if (!recursive && job_fds[0] >= 0)
+ for (int i = 0; i < 2; ++i)
+ {
+ int flags;
+ EINTRLOOP (flags, fcntl (job_fds[i], F_GETFD));
+ if (flags >= 0)
+ {
+ int r;
+ EINTRLOOP (r, fcntl (job_fds[i], F_SETFD, flags & ~FD_CLOEXEC));
+ }
+ }
#endif
}
#endif
#endif /* MAKE_JOBSERVER */
+
+/* Create a "bad" file descriptor for stdin when parallel jobs are run. */
+int
+get_bad_stdin ()
+{
+ static int bad_stdin = -1;
+
+ /* Set up a bad standard input that reads from a broken pipe. */
+
+ if (bad_stdin == -1)
+ {
+ /* Make a file descriptor that is the read end of a broken pipe.
+ This will be used for some children's standard inputs. */
+ int pd[2];
+ if (pipe (pd) == 0)
+ {
+ /* Close the write side. */
+ (void) close (pd[1]);
+ /* Save the read side. */
+ bad_stdin = pd[0];
+
+ /* Set the descriptor to close on exec, so it does not litter any
+ child's descriptor table. When it is dup2'd onto descriptor 0,
+ that descriptor will not close on exec. */
+ CLOSE_ON_EXEC (bad_stdin);
+ }
+ }
+
+ return bad_stdin;
+}
fflush (stderr);
}
- pid = fork ();
+ pid = vfork ();
if (pid < 0)
{
/* The fork failed! */
}
int
-child_execute_job (char *argv, struct child *child)
+child_execute_job (struct child *child, char *argv)
{
int i;
{
}
-void jobserver_pre_child ()
+void jobserver_pre_child (int recursive)
{
}
-void jobserver_post_child ()
+void jobserver_post_child (int recursive)
{
}