Skip to content

Instantly share code, notes, and snippets.

@Ludo6431
Created October 24, 2011 21:12
Show Gist options
  • Save Ludo6431/1310338 to your computer and use it in GitHub Desktop.
Save Ludo6431/1310338 to your computer and use it in GitHub Desktop.
/* Fork, handling errors. Returns the pid of the newly made child, or 0.
COMMAND is just for remembering the name of the command; we don't do
anything else with it. ASYNC_P says what to do with the tty. If
non-zero, then don't give it away. */
pid_t
make_child (command, async_p)
char *command;
int async_p;
{
int forksleep;
sigset_t set, oset;
pid_t pid;
sigemptyset (&set);
sigaddset (&set, SIGCHLD);
sigaddset (&set, SIGINT);
sigemptyset (&oset);
sigprocmask (SIG_BLOCK, &set, &oset);
making_children ();
forksleep = 1;
#if defined (BUFFERED_INPUT)
/* If default_buffered_input is active, we are reading a script. If
the command is asynchronous, we have already duplicated /dev/null
as fd 0, but have not changed the buffered stream corresponding to
the old fd 0. We don't want to sync the stream in this case. */
if (default_buffered_input != -1 &&
(!async_p || default_buffered_input > 0))
sync_buffered_stream (default_buffered_input);
#endif /* BUFFERED_INPUT */
/* Create the child, handle severe errors. Retry on EAGAIN. */
while ((pid = fork ()) < 0 && errno == EAGAIN && forksleep < FORKSLEEP_MAX)
{
/* bash-4.2 */
/* If we can't create any children, try to reap some dead ones. */
waitchld (-1, 0);
sys_error ("fork: retry");
if (sleep (forksleep) != 0)
break;
forksleep <<= 1;
}
if (pid < 0)
{
sys_error ("fork");
/* Kill all of the processes in the current pipeline. */
terminate_current_pipeline ();
/* Discard the current pipeline, if any. */
if (the_pipeline)
kill_current_pipeline ();
last_command_exit_value = EX_NOEXEC;
throw_to_top_level (); /* Reset signals, etc. */
}
if (pid == 0)
{
/* In the child. Give this child the right process group, set the
signals to the default state for a new process. */
pid_t mypid;
mypid = getpid ();
#if defined (BUFFERED_INPUT)
/* Close default_buffered_input if it's > 0. We don't close it if it's
0 because that's the file descriptor used when redirecting input,
and it's wrong to close the file in that case. */
unset_bash_input (0);
#endif /* BUFFERED_INPUT */
/* Restore top-level signal mask. */
sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
if (job_control)
{
/* All processes in this pipeline belong in the same
process group. */
if (pipeline_pgrp == 0) /* This is the first child. */
pipeline_pgrp = mypid;
/* Check for running command in backquotes. */
if (pipeline_pgrp == shell_pgrp)
ignore_tty_job_signals ();
else
default_tty_job_signals ();
/* Set the process group before trying to mess with the terminal's
process group. This is mandated by POSIX. */
/* This is in accordance with the Posix 1003.1 standard,
section B.7.2.4, which says that trying to set the terminal
process group with tcsetpgrp() to an unused pgrp value (like
this would have for the first child) is an error. Section
B.4.3.3, p. 237 also covers this, in the context of job control
shells. */
if (setpgid (mypid, pipeline_pgrp) < 0)
sys_error (_("child setpgid (%ld to %ld)"), (long)mypid, (long)pipeline_pgrp);
/* By convention (and assumption above), if
pipeline_pgrp == shell_pgrp, we are making a child for
command substitution.
In this case, we don't want to give the terminal to the
shell's process group (we could be in the middle of a
pipeline, for example). */
if (async_p == 0 && pipeline_pgrp != shell_pgrp && ((subshell_environment&SUBSHELL_ASYNC) == 0))
give_terminal_to (pipeline_pgrp, 0);
#if defined (PGRP_PIPE)
if (pipeline_pgrp == mypid)
pipe_read (pgrp_pipe);
#endif
}
else /* Without job control... */
{
if (pipeline_pgrp == 0)
pipeline_pgrp = shell_pgrp;
/* If these signals are set to SIG_DFL, we encounter the curious
situation of an interactive ^Z to a running process *working*
and stopping the process, but being unable to do anything with
that process to change its state. On the other hand, if they
are set to SIG_IGN, jobs started from scripts do not stop when
the shell running the script gets a SIGTSTP and stops. */
default_tty_job_signals ();
}
#if defined (PGRP_PIPE)
/* Release the process group pipe, since our call to setpgid ()
is done. The last call to sh_closepipe is done in stop_pipeline. */
sh_closepipe (pgrp_pipe);
#endif /* PGRP_PIPE */
#if 0
/* Don't set last_asynchronous_pid in the child */
if (async_p)
last_asynchronous_pid = mypid; /* XXX */
else
#endif
#if defined (RECYCLES_PIDS)
if (last_asynchronous_pid == mypid)
/* Avoid pid aliasing. 1 seems like a safe, unusual pid value. */
last_asynchronous_pid = 1;
#endif
}
else
{
/* In the parent. Remember the pid of the child just created
as the proper pgrp if this is the first child. */
if (first_pid == NO_PID)
first_pid = pid;
else if (pid_wrap == -1 && pid < first_pid)
pid_wrap = 0;
else if (pid_wrap == 0 && pid >= first_pid)
pid_wrap = 1;
if (job_control)
{
if (pipeline_pgrp == 0)
{
pipeline_pgrp = pid;
/* Don't twiddle terminal pgrps in the parent! This is the bug,
not the good thing of twiddling them in the child! */
/* give_terminal_to (pipeline_pgrp, 0); */
}
/* This is done on the recommendation of the Rationale section of
the POSIX 1003.1 standard, where it discusses job control and
shells. It is done to avoid possible race conditions. (Ref.
1003.1 Rationale, section B.4.3.3, page 236). */
setpgid (pid, pipeline_pgrp);
}
else
{
if (pipeline_pgrp == 0)
pipeline_pgrp = shell_pgrp;
}
/* Place all processes into the jobs array regardless of the
state of job_control. */
add_process (command, pid);
if (async_p)
last_asynchronous_pid = pid;
#if defined (RECYCLES_PIDS)
else if (last_asynchronous_pid == pid)
/* Avoid pid aliasing. 1 seems like a safe, unusual pid value. */
last_asynchronous_pid = 1;
#endif
if (pid_wrap > 0)
delete_old_job (pid);
#if !defined (RECYCLES_PIDS)
/* Only check for saved status if we've saved more than CHILD_MAX
statuses, unless the system recycles pids. */
if ((js.c_reaped + bgpids.npid) >= js.c_childmax)
#endif
bgp_delete (pid); /* new process, discard any saved status */
last_made_pid = pid;
/* keep stats */
js.c_totforked++;
js.c_living++;
/* Unblock SIGINT and SIGCHLD unless creating a pipeline, in which case
SIGCHLD remains blocked until all commands in the pipeline have been
created. */
sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
}
return (pid);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment