/* The thing that makes children, remembers them, and contains wait loops. */ /* this will probably only work with System V */ /* 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 #include #include #include "config.h" #include "general.h" #include "jobs.h" #ifndef SIGABRT #define SIGABRT SIGIOT #endif #ifdef SYSV #define killpg(pg, sig) kill(-(pg),(sig)) #endif /* SYSV */ #if defined(SYSV) || defined(HPUX) int siginterrupt (sig, flag) int sig, flag; { return 0; } #endif int last_made_pid = -1; int last_asynchronous_pid = -1; /* * Initialize the job control mechanism, and set up the tty stuff. */ initialize_jobs () { } /* * Setup this shell to handle C-C, etc. */ initialize_job_signals () { extern int login_shell; extern sighandler throw_to_top_level (); signal (SIGINT, throw_to_top_level); signal (SIGQUIT, SIG_IGN); /* If this is a login shell we don't wish to be disturbed by stop signals. */ if (login_shell) { #ifdef SIGSTOP signal (SIGSTOP, SIG_IGN); #endif #ifdef SIGTSTP signal (SIGTSTP, SIG_IGN); signal (SIGTTOU, SIG_IGN); signal (SIGTTIN, SIG_IGN); #endif } } /* * 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. */ int make_child (command, async_p) char *command; int async_p; { int pid; /* Discard saved memory. */ if (command) free (command); /* Make new environment array if neccessary. */ maybe_make_export_env (); /* Create the child, handle severe errors. */ if ((pid = fork ()) < 0) { report_error ("Memory exhausted or process overflow!"); throw_to_top_level (); } if (!pid) { /* * Ignore INT and QUIT in asynchronous children. */ if (async_p) { signal (SIGINT, SIG_IGN); signal (SIGQUIT, SIG_IGN); last_asynchronous_pid = getpid (); } else { signal (SIGINT, SIG_DFL); signal (SIGQUIT, SIG_DFL); } /* Children are easily terminated with SIGTERM. */ signal (SIGTERM, SIG_DFL); /* Set the resource limits for this child. (In ulimit.c). */ set_process_resource_limits (); } 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) int pid; { int got_pid; union wait status; while ((got_pid = wait (&status)) != pid) { if (got_pid < 0) { if (errno != EINTR && errno != ECHILD) file_error ("wait"); break; } } 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.2BSD-based and System V-based systems. */ while (1) { int got_pid; union wait status; while ((got_pid = wait(&status)) != -1) /* wait for ECHILD */ ; if (errno != EINTR && errno != ECHILD) file_error("wait"); break; } QUIT; } /* * Wait for pid (one of our children) to terminate. */ int wait_for (pid) int pid; { extern int interactive; int got_pid, return_val, oldmask; union wait status; while ((got_pid = wait (&status)) != pid) { if (got_pid < 0 && errno == ECHILD) { status.w_termsig = status.w_retcode = 0; break; } else if (got_pid < 0 && errno != EINTR) programming_error ("got errno %d while waiting for %d", errno, pid); } if (interactive) /* allow the user to ^C out of the builtin wait */ QUIT; /* Default return value. */ return_val = status.w_retcode & 0x7f; if (status.w_termsig != 0 && status.w_termsig != WSTOPPED) { extern char *sys_siglist[]; fprintf (stderr, "%s", sys_siglist[status.w_termsig]); if (status.w_coredump) fprintf (stderr, " (core dumped)"); fprintf (stderr, "\n"); return_val = status.w_termsig + 128; } if (status.w_termsig != 0) 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) int pid, signal, group; { int result; if (group) result = killpg (pid, signal); else result = kill (pid, signal); return (result); } static struct termio shell_tty_info; /* 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) { ioctl (tty, TCGETA, &shell_tty_info); close (tty); } } /* Make the current tty use the state in shell_tty_info. */ set_tty_state () { int tty = open ("/dev/tty", O_RDONLY); if (tty != -1) { ioctl (tty, TCSETAW, &shell_tty_info); /* Wait for output, no flush */ close (tty); } } /* Give the terminal to PGRP. */ give_terminal_to (pgrp) int 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) int pid; { fprintf (stderr, "<%d>\n", pid); }