/* The thing that makes children, remembers them, and contains wait loops. */ /* This file works under BSD, System V, minix, and Posix systems. */ /* Copyright (C) 1987,1989 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include "config.h" #include "general.h" #include "filecntl.h" #include "jobs.h" #if !defined (USG) && !defined (_POSIX_VERSION) # include #else # if defined (_POSIX_VERSION) # include # else # include # include # endif /* !POSIX_VERSION */ #endif /* USG && _POSIX_VERSION */ #if !defined (SIGABRT) # define SIGABRT SIGIOT #endif /* !SIGABRT */ #if defined (USG) || defined (_POSIX_VERSION) # define killpg(pg, sig) kill(-(pg),(sig)) #endif /* USG || _POSIX_VERSION */ #if defined (USG) # define siginterrupt(sig, code) #endif /* USG */ #if defned (_POSIX_VERSION) # define WAITPID(pid, statusp, options) waitpid (pid, statusp, options) #else # define WAITPID(pid, statusp, options) wait (statusp) #endif /* !_POSIX_VERSION */ pid_t last_made_pid = (pid_t)-1; pid_t last_asynchronous_pid = (pid_t)-1; extern int errno; /* Initialize the job control mechanism, and set up the tty stuff. */ initialize_jobs () { get_tty_state (); } /* Setup this shell to handle C-C, etc. */ initialize_job_signals () { extern int login_shell; extern sighandler sigint_sighandler (); signal (SIGINT, sigint_sighandler); /* If this is a login shell we don't wish to be disturbed by stop signals. */ if (login_shell) { #ifdef SIGTSTP signal (SIGTSTP, SIG_IGN); signal (SIGTTOU, SIG_IGN); signal (SIGTTIN, SIG_IGN); #endif } } #if defined (_POSIX_VERSION) /* Collect the status of all zombie children so that their system resources can be deallocated. */ static void reap_zombie_children () { #if defined (WNOHANG) while (waitpid (-1, (int *)NULL, WNOHANG) != -1) ; #endif /* WNOHANG */ } #endif /* _POSIX_VERSION */ /* 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; { pid_t pid; #if defined (_POSIX_VERSION) int retry = 1; #endif /* _POSIX_VERSION */ /* Discard saved memory. */ if (command) free (command); /* Make new environment array if neccessary. */ maybe_make_export_env (); /* Create the child, handle severe errors. */ #if defined (_POSIX_VERSION) retry_fork: #endif /* _POSIX_VERSION */ if ((pid = fork ()) < 0) { #if defined (_POSIX_VERSION) /* Posix systems with a non-blocking waitpid () system call available get another chance after zombies are reaped. */ if (errno == EAGAIN && retry) { reap_zombie_chilren (); retry = 0; goto retry_fork; } #endif /* _POSIX_VERSION */ report_error ("fork: %s", strerror (errno)); throw_to_top_level (); } if (!pid) { /* Cancel shell traps. */ restore_original_signals (); /* Ignore INT and QUIT in asynchronous children. */ if (async_p) { signal (SIGINT, SIG_IGN); signal (SIGQUIT, SIG_IGN); last_asynchronous_pid = getpid (); } else { #if defined (SIGTSTP) signal (SIGTSTP, SIG_DFL); signal (SIGTTIN, SIG_DFL); signal (SIGTTOU, SIG_DFL); #endif } } else { /* In the parent. */ last_made_pid = pid; if (async_p) last_asynchronous_pid = pid; } return (pid); } /* Wait for a single pid (PID) and return its exit status. */ wait_for_single_pid (pid) pid_t pid; { pid_t got_pid; WAIT status; /* Make sure that the process we are waiting for is valid. This check is not necessary on Posix systems. */ #if !defined (_POSIX_VERSION) if ((kill (pid, 0) < 0) && errno == ESRCH) return (127); #endif /* !_POSIX_VERSION */ siginterrupt (SIGINT, 1); while ((got_pid = WAITPID (pid, &status, 0)) != pid) { if (got_pid < 0) { if (errno != EINTR && errno != ECHILD) { siginterrupt (SIGINT, 0); file_error ("wait"); } break; } } siginterrupt (SIGINT, 0); QUIT; } /* Wait for all of the shell's children to exit. */ wait_for_background_pids () { /* If we aren't using job control, we let the kernel take care of the bookkeeping for us. wait () will return -1 and set errno to ECHILD when there are no more unwaited-for child processes on both 4.2 BSD-based and System V-based systems. */ siginterrupt (SIGINT, 1); while (1) { pid_t got_pid; WAIT status; /* Wait for ECHILD */ while ((got_pid = WAITPID (-1, &status, 0)) != -1) ; if (errno != EINTR && errno != ECHILD) { siginterrupt (SIGINT, 0); file_error("wait"); } break; } siginterrupt (SIGINT, 0); QUIT; } /* Wait for pid (one of our children) to terminate. */ int wait_for (pid) pid_t pid; { extern int interactive; int return_val; pid_t got_pid; WAIT status; /* Make sure that the process we are waiting for is valid. This check is not necessary on Posix systems. */ #if !defined (_POSIX_VERSION) if ((kill (pid, 0) < 0) && (errno == ESRCH)) return (0); #endif /* !_POSIX_VERSION */ siginterrupt (SIGINT, 1); while ((got_pid = WAITPID (pid, &status, 0)) != pid) { if (got_pid < 0 && errno == ECHILD) { #if !defined (_POSIX_VERSION) status.w_termsig = status.w_retcode = 0; #else status = 0; #endif /* _POSIX_VERSION */ break; } else if (got_pid < 0 && errno != EINTR) programming_error ("got errno %d while waiting for %d", errno, pid); } siginterrupt (SIGINT, 0); /* Default return value. */ /* ``a full 8 bits of status is returned'' */ if (WIFSIGNALED (status)) return_val = 128 + WTERMSIG (status); else return_val = WEXITSTATUS (status); if (!WIFSTOPPED (status) && WIFSIGNALED (status) && (WTERMSIG (status) != SIGINT)) { extern char *sys_siglist[]; fprintf (stderr, "%s", sys_siglist[WTERMSIG (status)]); if (WIFCORED (status)) fprintf (stderr, " (core dumped)"); fprintf (stderr, "\n"); } if (WIFSIGNALED (status) || WIFSTOPPED (status)) set_tty_state (); else get_tty_state (); return (return_val); } /* Give PID SIGNAL. This determines what job the pid belongs to (if any). If PID does belong to a job, and the job is stopped, then CONTinue the job after giving it SIGNAL. Returns -1 on failure. If GROUP is non-null, then kill the process group associated with PID. */ int kill_pid (pid, signal, group) pid_t pid; int signal, group; { int result; if (group) result = killpg (pid, signal); else result = kill (pid, signal); return (result); } #if defined (_POSIX_VERSION) static struct termios shell_tty_info; #else # if defined (USG) static struct termio shell_tty_info; # else static struct sgttyb shell_tty_info; # endif /* USG */ #endif /* _POSIX_VERSION */ static int got_tty_state = 0; /* Fill the contents of shell_tty_info with the current tty info. */ get_tty_state () { int tty = open ("/dev/tty", O_RDONLY); if (tty != -1) { #if defined (_POSIX_VERSION) tcgetattr (tty, &shell_tty_info); #else # if defined (USG) ioctl (tty, TCGETA, &shell_tty_info); # else ioctl (tty, TIOCGETP, &shell_tty_info); # endif #endif close (tty); got_tty_state = 1; } } /* Make the current tty use the state in shell_tty_info. */ set_tty_state () { int tty = open ("/dev/tty", O_RDONLY); if (tty != -1) { if (!got_tty_state) { close (tty); return; } #if defined (_POSIX_VERSION) tcsetattr (tty, TCSADRAIN, &shell_tty_info); #else # if defined (USG) ioctl (tty, TCSETAW, &shell_tty_info); /* Wait for output, no flush */ # else ioctl (tty, TIOCSETN, &shell_tty_info); # endif #endif close (tty); } } /* Give the terminal to PGRP. */ give_terminal_to (pgrp) pid_t pgrp; { } /* Stop a pipeline. */ stop_pipeline (async, ignore) int async; char *ignore; { } /* Print descriptive information about the job with leader pid PID. */ describe_pid (pid) pid_t pid; { fprintf (stderr, "<%d>\n", (int) pid); }