?? jobs.c
字號:
/* jobs.c - functions that make children, remember them, and handle their termination. *//* This file works with both POSIX and BSD systems. It implements job control. *//* Copyright (C) 1989-2010 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>.*/#include "config.h"#include "bashtypes.h"#include "trap.h"#include <stdio.h>#include <signal.h>#include <errno.h>#if defined (HAVE_UNISTD_H)# include <unistd.h>#endif#include "posixtime.h"#if defined (HAVE_SYS_RESOURCE_H) && defined (HAVE_WAIT3) && !defined (_POSIX_VERSION) && !defined (RLIMTYPE)# include <sys/resource.h>#endif /* !_POSIX_VERSION && HAVE_SYS_RESOURCE_H && HAVE_WAIT3 && !RLIMTYPE */#if defined (HAVE_SYS_FILE_H)# include <sys/file.h>#endif#include "filecntl.h"#include <sys/ioctl.h>#include <sys/param.h>#if defined (BUFFERED_INPUT)# include "input.h"#endif/* Need to include this up here for *_TTY_DRIVER definitions. */#include "shtty.h"/* Define this if your output is getting swallowed. It's a no-op on machines with the termio or termios tty drivers. *//* #define DRAIN_OUTPUT *//* For the TIOCGPGRP and TIOCSPGRP ioctl parameters on HP-UX */#if defined (hpux) && !defined (TERMIOS_TTY_DRIVER)# include <bsdtty.h>#endif /* hpux && !TERMIOS_TTY_DRIVER */#include "bashansi.h"#include "bashintl.h"#include "shell.h"#include "jobs.h"#include "execute_cmd.h"#include "flags.h"#include "builtins/builtext.h"#include "builtins/common.h"#if !defined (errno)extern int errno;#endif /* !errno */#if !defined (HAVE_KILLPG)extern int killpg __P((pid_t, int));#endif#define DEFAULT_CHILD_MAX 32#if !defined (DEBUG)#define MAX_JOBS_IN_ARRAY 4096 /* production */#else#define MAX_JOBS_IN_ARRAY 128 /* testing */#endif/* Flag values for second argument to delete_job */#define DEL_WARNSTOPPED 1 /* warn about deleting stopped jobs */#define DEL_NOBGPID 2 /* don't add pgrp leader to bgpids *//* Take care of system dependencies that must be handled when waiting for children. The arguments to the WAITPID macro match those to the Posix.1 waitpid() function. */#if defined (ultrix) && defined (mips) && defined (_POSIX_VERSION)# define WAITPID(pid, statusp, options) \ wait3 ((union wait *)statusp, options, (struct rusage *)0)#else# if defined (_POSIX_VERSION) || defined (HAVE_WAITPID)# define WAITPID(pid, statusp, options) \ waitpid ((pid_t)pid, statusp, options)# else# if defined (HAVE_WAIT3)# define WAITPID(pid, statusp, options) \ wait3 (statusp, options, (struct rusage *)0)# else# define WAITPID(pid, statusp, options) \ wait3 (statusp, options, (int *)0)# endif /* HAVE_WAIT3 */# endif /* !_POSIX_VERSION && !HAVE_WAITPID*/#endif /* !(Ultrix && mips && _POSIX_VERSION) *//* getpgrp () varies between systems. Even systems that claim to be Posix.1 compatible lie sometimes (Ultrix, SunOS4, apollo). */#if defined (GETPGRP_VOID)# define getpgid(p) getpgrp ()#else# define getpgid(p) getpgrp (p)#endif /* !GETPGRP_VOID *//* If the system needs it, REINSTALL_SIGCHLD_HANDLER will reinstall the handler for SIGCHLD. */#if defined (MUST_REINSTALL_SIGHANDLERS)# define REINSTALL_SIGCHLD_HANDLER signal (SIGCHLD, sigchld_handler)#else# define REINSTALL_SIGCHLD_HANDLER#endif /* !MUST_REINSTALL_SIGHANDLERS *//* Some systems let waitpid(2) tell callers about stopped children. */#if !defined (WCONTINUED) || defined (WCONTINUED_BROKEN)# undef WCONTINUED# define WCONTINUED 0#endif#if !defined (WIFCONTINUED)# define WIFCONTINUED(s) (0)#endif/* The number of additional slots to allocate when we run out. */#define JOB_SLOTS 8typedef int sh_job_map_func_t __P((JOB *, int, int, int));/* Variables used here but defined in other files. */extern int subshell_environment, line_number;extern int posixly_correct, shell_level;extern int last_command_exit_value, last_command_exit_signal;extern int loop_level, breaking;extern int executing_list;extern int sourcelevel;extern int running_trap;extern sh_builtin_func_t *this_shell_builtin;extern char *shell_name, *this_command_name;extern sigset_t top_level_mask;extern procenv_t wait_intr_buf;extern int wait_signal_received;extern WORD_LIST *subst_assign_varlist;static struct jobstats zerojs = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB, 0, 0 };struct jobstats js = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB, 0, 0 };struct bgpids bgpids = { 0, 0, 0 };/* The array of known jobs. */JOB **jobs = (JOB **)NULL;#if 0/* The number of slots currently allocated to JOBS. */int job_slots = 0;#endif/* The controlling tty for this shell. */int shell_tty = -1;/* The shell's process group. */pid_t shell_pgrp = NO_PID;/* The terminal's process group. */pid_t terminal_pgrp = NO_PID;/* The process group of the shell's parent. */pid_t original_pgrp = NO_PID;/* The process group of the pipeline currently being made. */pid_t pipeline_pgrp = (pid_t)0;#if defined (PGRP_PIPE)/* Pipes which each shell uses to communicate with the process group leader until all of the processes in a pipeline have been started. Then the process leader is allowed to continue. */int pgrp_pipe[2] = { -1, -1 };#endif#if 0/* The job which is current; i.e. the one that `%+' stands for. */int current_job = NO_JOB;/* The previous job; i.e. the one that `%-' stands for. */int previous_job = NO_JOB;#endif/* Last child made by the shell. */pid_t last_made_pid = NO_PID;/* Pid of the last asynchronous child. */pid_t last_asynchronous_pid = NO_PID;/* The pipeline currently being built. */PROCESS *the_pipeline = (PROCESS *)NULL;/* If this is non-zero, do job control. */int job_control = 1;/* Call this when you start making children. */int already_making_children = 0;/* If this is non-zero, $LINES and $COLUMNS are reset after every process exits from get_tty_state(). */int check_window_size;/* Functions local to this file. */static sighandler wait_sigint_handler __P((int));static sighandler sigchld_handler __P((int));static sighandler sigcont_sighandler __P((int));static sighandler sigstop_sighandler __P((int));static int waitchld __P((pid_t, int));static PROCESS *find_pipeline __P((pid_t, int, int *));static PROCESS *find_process __P((pid_t, int, int *));static char *current_working_directory __P((void));static char *job_working_directory __P((void));static char *j_strsignal __P((int));static char *printable_job_status __P((int, PROCESS *, int));static PROCESS *find_last_proc __P((int, int));static pid_t find_last_pid __P((int, int));static int set_new_line_discipline __P((int));static int map_over_jobs __P((sh_job_map_func_t *, int, int));static int job_last_stopped __P((int));static int job_last_running __P((int));static int most_recent_job_in_state __P((int, JOB_STATE));static int find_job __P((pid_t, int, PROCESS **));static int print_job __P((JOB *, int, int, int));static int process_exit_status __P((WAIT));static int process_exit_signal __P((WAIT));static int set_job_status_and_cleanup __P((int));static WAIT job_signal_status __P((int));static WAIT raw_job_exit_status __P((int));static void notify_of_job_status __P((void));static void reset_job_indices __P((void));static void cleanup_dead_jobs __P((void));static int processes_in_job __P((int));static void realloc_jobs_list __P((void));static int compact_jobs_list __P((int));static int discard_pipeline __P((PROCESS *));static void add_process __P((char *, pid_t));static void print_pipeline __P((PROCESS *, int, int, FILE *));static void pretty_print_job __P((int, int, FILE *));static void set_current_job __P((int));static void reset_current __P((void));static void set_job_running __P((int));static void setjstatus __P((int));static int maybe_give_terminal_to __P((pid_t, pid_t, int));static void mark_all_jobs_as_dead __P((void));static void mark_dead_jobs_as_notified __P((int));static void restore_sigint_handler __P((void));#if defined (PGRP_PIPE)static void pipe_read __P((int *));#endifstatic struct pidstat *bgp_alloc __P((pid_t, int));static struct pidstat *bgp_add __P((pid_t, int));static int bgp_delete __P((pid_t));static void bgp_clear __P((void));static int bgp_search __P((pid_t));static void bgp_prune __P((void));#if defined (ARRAY_VARS)static int *pstatuses; /* list of pipeline statuses */static int statsize;#endif/* Used to synchronize between wait_for and other functions and the SIGCHLD signal handler. */static int sigchld;static int queue_sigchld;#define QUEUE_SIGCHLD(os) (os) = sigchld, queue_sigchld++#define UNQUEUE_SIGCHLD(os) \ do { \ queue_sigchld--; \ if (queue_sigchld == 0 && os != sigchld) \ waitchld (-1, 0); \ } while (0)static SigHandler *old_tstp, *old_ttou, *old_ttin;static SigHandler *old_cont = (SigHandler *)SIG_DFL;/* A place to temporarily save the current pipeline. */static PROCESS *saved_pipeline;static int saved_already_making_children;/* Set this to non-zero whenever you don't want the jobs list to change at all: no jobs deleted and no status change notifications. This is used, for example, when executing SIGCHLD traps, which may run arbitrary commands. */static int jobs_list_frozen;static char retcode_name_buffer[64];/* flags to detect pid wraparound */static pid_t first_pid = NO_PID;static int pid_wrap = -1;#if !defined (_POSIX_VERSION)/* These are definitions to map POSIX 1003.1 functions onto existing BSD library functions and system calls. */#define setpgid(pid, pgrp) setpgrp (pid, pgrp)#define tcsetpgrp(fd, pgrp) ioctl ((fd), TIOCSPGRP, &(pgrp))pid_ttcgetpgrp (fd) int fd;{ pid_t pgrp; /* ioctl will handle setting errno correctly. */ if (ioctl (fd, TIOCGPGRP, &pgrp) < 0) return (-1); return (pgrp);}#endif /* !_POSIX_VERSION *//* Initialize the global job stats structure and other bookkeeping variables */voidinit_job_stats (){ js = zerojs; first_pid = NO_PID; pid_wrap = -1;}/* Return the working directory for the current process. Unlike job_working_directory, this does not call malloc (), nor do any of the functions it calls. This is so that it can safely be called from a signal handler. */static char *current_working_directory (){ char *dir; static char d[PATH_MAX]; dir = get_string_value ("PWD"); if (dir == 0 && the_current_working_directory && no_symbolic_links) dir = the_current_working_directory; if (dir == 0) { dir = getcwd (d, sizeof(d)); if (dir) dir = d; } return (dir == 0) ? "<unknown>" : dir;}/* Return the working directory for the current process. */static char *job_working_directory (){ char *dir; dir = get_string_value ("PWD"); if (dir) return (savestring (dir)); dir = get_working_directory ("job-working-directory"); if (dir) return (dir); return (savestring ("<unknown>"));}voidmaking_children (){ if (already_making_children) return; already_making_children = 1; start_pipeline ();}voidstop_making_children (){ already_making_children = 0;}voidcleanup_the_pipeline (){ PROCESS *disposer; sigset_t set, oset; BLOCK_CHILD (set, oset); disposer = the_pipeline; the_pipeline = (PROCESS *)NULL; UNBLOCK_CHILD (oset); if (disposer) discard_pipeline (disposer);}voidsave_pipeline (clear) int clear;{ saved_pipeline = the_pipeline; if (clear) the_pipeline = (PROCESS *)NULL; saved_already_making_children = already_making_children;}voidrestore_pipeline (discard) int discard;{ PROCESS *old_pipeline; old_pipeline = the_pipeline; the_pipeline = saved_pipeline; already_making_children = saved_already_making_children; if (discard && old_pipeline) discard_pipeline (old_pipeline);}/* Start building a pipeline. */voidstart_pipeline (){ if (the_pipeline) { cleanup_the_pipeline (); pipeline_pgrp = 0;#if defined (PGRP_PIPE) sh_closepipe (pgrp_pipe);#endif }#if defined (PGRP_PIPE) if (job_control) { if (pipe (pgrp_pipe) == -1) sys_error (_("start_pipeline: pgrp pipe")); }#endif}/* Stop building a pipeline. Install the process list in the job array. This returns the index of the newly installed job. DEFERRED is a command structure to be executed upon satisfactory execution exit of this pipeline. */intstop_pipeline (async, deferred) int async; COMMAND *deferred;{ register int i, j; JOB *newjob; sigset_t set, oset; BLOCK_CHILD (set, oset);#if defined (PGRP_PIPE) /* The parent closes the process group synchronization pipe. */ sh_closepipe (pgrp_pipe);#endif cleanup_dead_jobs (); if (js.j_jobslots == 0) { js.j_jobslots = JOB_SLOTS; jobs = (JOB **)xmalloc (js.j_jobslots * sizeof (JOB *)); /* Now blank out these new entries. */ for (i = 0; i < js.j_jobslots; i++) jobs[i] = (JOB *)NULL; js.j_firstj = js.j_lastj = js.j_njobs = 0; } /* Scan from the last slot backward, looking for the next free one. */ /* XXX - revisit this interactive assumption */ /* XXX - this way for now */ if (interactive) {
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -