From: Chet Ramey Date: Thu, 18 Jul 2024 20:48:17 +0000 (-0400) Subject: job control cleanups; wait -n can return terminated jobs if supplied pid arguments... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6c703092759ace29263ea96374e18412c59acc7f;p=thirdparty%2Fbash.git job control cleanups; wait -n can return terminated jobs if supplied pid arguments; wait -n can wait for process substitutions if supplied pid arguments --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 4fc76260..66d52310 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -9810,3 +9810,51 @@ bashline.c completions that should be affected by FIGNORE; in other cases act as if it were set to 1 From a report and patch by Koichi Murase + + 7/15 + ---- +builtins/ulimit.def + - add help text saying that some of the units change in posix mode + + + 7/17 + ---- +subst.c + - remove some unused functions + +jobs.c,jobs.h + - procsub_search,procsub_delete: now take an extra parameter saying + whether or not to block SIGCHLD while they run; avoids overhead + when called when SIGCHLD is already blocked; changed callers and + extern declaration + - struct process now has a new FLAGS member; possible values defined + in jobs.h; initialized in alloc_process + - procsub_setflag,procsub_setflag: set one of the PROC_XXX flags on + one or all of the processes in the procsub list + - new value for the flags argument to make_child: FORK_PROCSUB + - add_process: now returns the PROCESS * it creates so the caller can + set flags or otherwise modify it + - get_job_by_pid: return NO_JOB for negative pids immediately + +subst.c + - process_substitute,command_substitute: call make_child with the + appropriate FORK_ flag + +jobs.c + - make_child: set PROC_ flags in the PROCESS * we create based on the + FORK_ flags the caller passes + - wait_for: only call set_procsub_status if the process has terminated + - wait_for_any_job: check for any terminated procsubs as well as any + terminated background jobs + +builtins/wait.def + - check_bgpids: rename to check_nonjobs + - wait_builtin: if the -n option is supplied and pid arguments are + supplied, check the bgpids list and procsubs for terminated processes + - check_nonjobs: if one of the pids in LIST corresponds to a terminated + procsub, return its status and move the procsub to the bgpids list + - set_waitlist: if we have a non-existent or invalid job, and a pid + argument that's >= 0, look for a procsub with that pid and set + PROC_WAITING if we have one + - unset_waitlist: unset PROC_WAITING in all procsubs + diff --git a/builtins/wait.def b/builtins/wait.def index efcfc0ff..98160bea 100644 --- a/builtins/wait.def +++ b/builtins/wait.def @@ -92,7 +92,7 @@ int wait_intr_flag; static int set_waitlist (WORD_LIST *); static void unset_waitlist (void); -static int check_bgpids (WORD_LIST *, struct procstat *); +static int check_nonjobs (WORD_LIST *, struct procstat *); /* Wait for the pid in LIST to stop or die. If no arguments are given, then wait for all of the active background processes of the shell and return @@ -211,12 +211,13 @@ wait_builtin (WORD_LIST *list) #if defined (JOB_CONTROL) if (nflag) { -#if 0 /* TAG:bash-5.4 stevenpelley@gmail.com 01/22/2024 */ +#if 1 /* TAG:bash-5.3 stevenpelley@gmail.com 01/22/2024 */ /* First let's see if there are any requested pids that have already - been removed from the jobs list and saved on bgpids. */ + been removed from the jobs list and saved on bgpids or are terminated + procsubs. */ if (list) { - status = check_bgpids (list, &pstat); + status = check_nonjobs (list, &pstat); if (status != -1) { if (vname) @@ -343,29 +344,47 @@ wait_builtin (WORD_LIST *list) #if defined (JOB_CONTROL) /* Take each valid pid or jobspec in LIST and mark the corresponding job as - J_WAITING, so wait -n knows which jobs to wait for. Return the number of - jobs we found. */ + J_WAITING, so wait -n knows which jobs to wait for. + If we have process substitutions, allow wait -n to wait for procsubs by + pid by marking them with PROC_WAITING. + Return the number of jobs or procsubs we found. */ static int set_waitlist (WORD_LIST *list) { sigset_t set, oset; int job, njob; - intmax_t pid; + intmax_t ipid; + pid_t pid; + PROCESS *child; WORD_LIST *l; BLOCK_CHILD (set, oset); njob = 0; for (l = list; l; l = l->next) { - job = NO_JOB; - job = (l && l->word && valid_number (l->word->word, &pid) && pid == (pid_t) pid) - ? get_job_by_pid ((pid_t) pid, 0, 0) - : get_job_spec (l); + child = NULL; + + pid = (l && l->word && valid_number (l->word->word, &ipid) && ipid == (pid_t)ipid) ? (pid_t)ipid : NO_PID; + job = (pid != NO_PID) ? get_job_by_pid (pid, 0, 0) : get_job_spec (l); + if (job == NO_JOB || jobs == 0 || INVALID_JOB (job)) { - sh_badjob (l->word->word); +#if defined (PROCESS_SUBSTITUTION) + child = (pid >= 0) ? procsub_search (pid, 0) : NULL; +#endif + if (child) + { + /* If we don't have a valid job, maybe we have a procsub */ + njob++; + child->flags |= PROC_WAITING; + } + else if (l->word->word[0] == '%') + sh_badjob (l->word->word); + else + sh_badpid (l->word->word); continue; } + /* We don't check yet to see if one of the desired jobs has already terminated, but we could. We wait until wait_for_any_job(). This has the advantage of validating all the arguments. */ @@ -390,17 +409,22 @@ unset_waitlist (void) for (i = 0; i < js.j_jobslots; i++) if (jobs[i] && (jobs[i]->flags & J_WAITING)) jobs[i]->flags &= ~J_WAITING; +#if defined (PROCESS_SUBSTITUTION) + procsub_unsetflag (ANY_PID, PROC_WAITING, 0); +#endif UNBLOCK_CHILD (oset); } -#if 0 /* TAG:bash-5.4 */ +/* For each pid in LIST, check the bgpids list and the procsub list, if we + have process substitutions. */ static int -check_bgpids (WORD_LIST *list, struct procstat *pstat) +check_nonjobs (WORD_LIST *list, struct procstat *pstat) { sigset_t set, oset; pid_t pid; intmax_t ipid; WORD_LIST *l; + PROCESS *child; int r, s; r = -1; @@ -408,11 +432,17 @@ check_bgpids (WORD_LIST *list, struct procstat *pstat) BLOCK_CHILD (set, oset); for (l = list; l; l = l->next) { - if (l && valid_number (l->word->word, &ipid) && ipid == (pid_t) ipid) + if (l && valid_number (l->word->word, &ipid) && ipid == (pid_t) ipid && ipid >= 0) pid = ipid; - else + else if (l && l->word->word[0] == '%') continue; /* skip job ids for now */ + else + { + r = -1; + continue; + } + /* check bgpids */ if ((s = retrieve_proc_status (pid, 0)) != -1) { pstat->pid = pid; @@ -423,10 +453,22 @@ check_bgpids (WORD_LIST *list, struct procstat *pstat) delete_proc_status (pid, 0); break; } + +#if defined (PROCESS_SUBSTITUTION) + /* If this corresponds to a terminated procsub, return it. */ + if ((child = procsub_search (pid, 0)) && child->running == PS_DONE) + { + pstat->pid = pid; + pstat->status = r = process_exit_status (child->status); + procsub_delete (pid, 0); /* moves to bgpids list */ + if (posixly_correct) + delete_proc_status (pid, 0); + break; + } +#endif } UNBLOCK_CHILD (oset); return r; } #endif -#endif diff --git a/jobs.c b/jobs.c index ab507158..7c32e6bc 100644 --- a/jobs.c +++ b/jobs.c @@ -282,7 +282,7 @@ static void cleanup_dead_jobs (void); static int processes_in_job (int); static void realloc_jobs_list (void); static int compact_jobs_list (int); -static void add_process (char *, pid_t); +static PROCESS *add_process (char *, pid_t); static void print_pipeline (PROCESS *, int, int, FILE *); static void pretty_print_job (int, int, FILE *); static void set_current_job (int); @@ -1051,27 +1051,30 @@ procsub_add (PROCESS *p) } PROCESS * -procsub_search (pid_t pid) +procsub_search (pid_t pid, int block) { PROCESS *p; sigset_t set, oset; - BLOCK_CHILD (set, oset); + if (block) + BLOCK_CHILD (set, oset); for (p = procsubs.head; p; p = p->next) if (p->pid == pid) break; - UNBLOCK_CHILD (oset); + if (block) + UNBLOCK_CHILD (oset); return p; } PROCESS * -procsub_delete (pid_t pid) +procsub_delete (pid_t pid, int block) { PROCESS *p, *prev; sigset_t set, oset; - BLOCK_CHILD (set, oset); + if (block) + BLOCK_CHILD (set, oset); for (p = prev = procsubs.head; p; prev = p, p = p->next) if (p->pid == pid) { @@ -1081,7 +1084,8 @@ procsub_delete (pid_t pid) if (p == 0) { - UNBLOCK_CHILD (oset); + if (block) + UNBLOCK_CHILD (oset); return p; } @@ -1098,7 +1102,9 @@ procsub_delete (pid_t pid) /* this can't be called anywhere in a signal handling path */ bgp_add (p->pid, process_exit_status (p->status)); - UNBLOCK_CHILD (oset); + + if (block) + UNBLOCK_CHILD (oset); return (p); } @@ -1108,7 +1114,7 @@ procsub_waitpid (pid_t pid) PROCESS *p; int r; - p = procsub_search (pid); + p = procsub_search (pid, 1); if (p == 0) return -1; if (p->running == PS_DONE) @@ -1138,7 +1144,6 @@ procsub_clear (void) sigset_t set, oset; BLOCK_CHILD (set, oset); - for (ps = procsubs.head; ps; ) { p = ps; @@ -1192,6 +1197,40 @@ procsub_reap (void) last_procsub_child = (PROCESS *)NULL; UNQUEUE_SIGCHLD (os); } + +void +procsub_setflag (pid_t pid, int flag, int block) +{ + sigset_t set, oset; + PROCESS *p; + + if (block) + BLOCK_CHILD (set, oset); + + for (p = procsubs.head; p; p = p->next) + if (p->pid == pid || pid == ANY_PID) + p->flags |= flag; + + if (block) + UNBLOCK_CHILD (oset); +} + +void +procsub_unsetflag (pid_t pid, int flag, int block) +{ + sigset_t set, oset; + PROCESS *p; + + if (block) + BLOCK_CHILD (set, oset); + + for (p = procsubs.head; p; p = p->next) + if (p->pid == pid || pid == ANY_PID) + p->flags &= ~flag; + + if (block) + UNBLOCK_CHILD (oset); +} #endif /* Reset the values of js.j_lastj and js.j_firstj after one or both have @@ -1505,6 +1544,7 @@ alloc_process (char *name, pid_t pid) t->pid = pid; WSTATUS (t->status) = 0; t->running = PS_RUNNING; /* default */ + t->flags = 0; t->command = name; t->next = (PROCESS *)0; @@ -1541,8 +1581,9 @@ discard_pipeline (PROCESS *chain) /* Add this process to the chain being built in the_pipeline. NAME is the command string that will be exec'ed later. - PID is the process id of the child. */ -static void + PID is the process id of the child. Returns the PROCESS * + we just created if the caller wants to use it or set flags. */ +static PROCESS * add_process (char *name, pid_t pid) { PROCESS *t, *p; @@ -1573,6 +1614,8 @@ add_process (char *name, pid_t pid) p = p->next; p->next = t; } + + return t; } /* Create a (dummy) PROCESS with NAME, PID, and STATUS, and make it the last @@ -1784,7 +1827,7 @@ find_pipeline (pid_t pid, int alive_only, int *jobp) return (p); #if defined (PROCESS_SUBSTITUTION) - if (procsubs.nproc > 0 && (p = procsub_search (pid)) && ((alive_only == 0 && PRECYCLED(p) == 0) || PALIVE(p))) + if (procsubs.nproc > 0 && (p = procsub_search (pid, 0)) && ((alive_only == 0 && PRECYCLED(p) == 0) || PALIVE(p))) return (p); #endif @@ -1854,6 +1897,9 @@ get_job_by_pid (pid_t pid, int block, PROCESS **procp) int job; sigset_t set, oset; + if (pid < 0) + return (NO_JOB); + if (block) BLOCK_CHILD (set, oset); @@ -2201,6 +2247,7 @@ make_child (char *command, int flags) unsigned int forksleep; sigset_t set, oset, oset_copy; pid_t pid; + PROCESS *child; SigHandler *oterm; sigemptyset (&oset_copy); @@ -2394,7 +2441,13 @@ make_child (char *command, int flags) /* Place all processes into the jobs array regardless of the state of job_control. */ - add_process (command, pid); + child = add_process (command, pid); + + /* Set up the flags based on what the caller provides. */ + if (flags & FORK_PROCSUB) + child->flags |= PROC_PROCSUB; + if (flags & FORK_COMSUB) + child->flags |= PROC_COMSUB; if (async_p) last_asynchronous_pid = pid; @@ -3358,8 +3411,9 @@ wait_for_job (int job, int flags, struct procstat *ps) return r; } -/* Wait for any background job started by this shell to finish. Very - similar to wait_for_background_pids(). Returns the exit status of +/* Wait for any background job started by this shell to finish, including + process substitutions. + Very similar to wait_for_background_pids(). Returns the exit status of the next exiting job, -1 if there are no background jobs. The caller is responsible for translating -1 into the right return value. RPID, if non-null, gets the pid of the job's process leader. */ @@ -3369,6 +3423,7 @@ wait_for_any_job (int flags, struct procstat *ps) pid_t pid; int i, r; sigset_t set, oset; + PROCESS *p, *child; /* Allow funsubs to run this, but don't remove jobs from the jobs table. */ if (jobs_list_frozen > 0) @@ -3406,6 +3461,33 @@ return_job: return r; } } + +#if defined (PROCESS_SUBSTITUTION) + /* We don't have any dead jobs, but let's see if we have a dead procsub. */ + for (p = procsubs.head; p; p = p->next) + { + /* If we're waiting for specific pids, skip over ones we're not interested in. */ + if ((flags & JWAIT_WAITING) && (p->flags & PROC_WAITING) == 0) + continue; + if (p->running == PS_DONE) + { +return_procsub: + r = process_exit_status (p->status); + pid = p->pid; + if (ps) + { + ps->pid = pid; + ps->status = r; + } + procsub_delete (pid, 0); /* XXX - procsub_reap? */ + if (posixly_correct) + bgp_delete (pid); + UNBLOCK_CHILD (oset); + return r; + } + } +#endif + UNBLOCK_CHILD (oset); /* At this point, we have no dead jobs in the jobs table. Wait until we @@ -3426,7 +3508,21 @@ return_job: goto return_job; } - if (i == js.j_jobslots) + p = NULL; +#if defined (PROCESS_SUBSTITUTION) + /* Do we have any procsubs that are still candidates? */ + for (p = procsubs.head; p; p = p->next) + { + if ((flags & JWAIT_WAITING) && (p->flags & PROC_WAITING) == 0) + continue; + else if (p->running == PS_DONE) + goto return_procsub; + else if (p->running == PS_RUNNING) /* still got one */ + break; + } +#endif + + if (i == js.j_jobslots && p == NULL) { UNBLOCK_CHILD (oset); return -1; @@ -3452,6 +3548,15 @@ return_job: if (jobs[i] && DEADJOB (i)) goto return_job; } +#if defined (PROCESS_SUBSTITUTION) + for (p = procsubs.head; p; p = p->next) + { + if ((flags & JWAIT_WAITING) && (p->flags & PROC_WAITING) == 0) + continue; + else if (p->running == PS_DONE) + goto return_procsub; + } +#endif UNBLOCK_CHILD (oset); } @@ -3994,7 +4099,7 @@ itrace("waitchld: waitpid returns %d block = %d children_exited = %d", pid, bloc or close file descriptors corresponding to terminated process substitutions. */ /* XXX - should combine this list with procsub_add, etc. */ - if ((ind = find_procsub_child (pid)) >= 0) + if ((ind = find_procsub_child (pid)) >= 0 && (WIFEXITED (status) || WIFSIGNALED (status))) set_procsub_status (ind, pid, WSTATUS (status)); #endif diff --git a/jobs.h b/jobs.h index 59b4ac58..33afdb59 100644 --- a/jobs.h +++ b/jobs.h @@ -65,6 +65,12 @@ #define PS_STOPPED 2 #define PS_RECYCLED 4 +/* Values for the `flags' field of a struct process. */ +#define PROC_WAITING 0x01 +#define PROC_PROCSUB 0x02 +#define PROC_COMSUB 0x04 +#define PROC_LASTPIPE 0x08 + /* Each child of the shell is remembered in a STRUCT PROCESS. A circular chain of such structures is a pipeline. */ typedef struct process { @@ -72,6 +78,7 @@ typedef struct process { pid_t pid; /* Process ID. */ WAIT status; /* The status of this command as returned by wait. */ int running; /* Non-zero if this process is running. */ + int flags; /* Placeholder for process-specific flag values. */ char *command; /* The particular program that is running. */ } PROCESS; @@ -206,11 +213,12 @@ struct procchain { #define ANY_PID (pid_t)-1 /* flags for make_child () */ -#define FORK_SYNC 0 /* normal synchronous process */ -#define FORK_ASYNC 1 /* background process */ -#define FORK_NOJOB 2 /* don't put process in separate pgrp */ -#define FORK_NOTERM 4 /* don't give terminal to any pgrp */ -#define FORK_COMSUB 8 /* command or process substitution */ +#define FORK_SYNC 0x0 /* normal synchronous process */ +#define FORK_ASYNC 0x1 /* background process */ +#define FORK_NOJOB 0x2 /* don't put process in separate pgrp */ +#define FORK_NOTERM 0x4 /* don't give terminal to any pgrp */ +#define FORK_COMSUB 0x8 /* command substitution */ +#define FORK_PROCSUB 0x10 /* process substitution */ /* System calls. */ #if !defined (HAVE_UNISTD_H) @@ -250,13 +258,15 @@ extern int retrieve_proc_status (pid_t, int); extern void delete_proc_status (pid_t, int); extern PROCESS *procsub_add (PROCESS *); -extern PROCESS *procsub_search (pid_t); -extern PROCESS *procsub_delete (pid_t); +extern PROCESS *procsub_search (pid_t, int); +extern PROCESS *procsub_delete (pid_t, int); extern int procsub_waitpid (pid_t); extern void procsub_waitall (void); extern void procsub_clear (void); extern void procsub_prune (void); extern void procsub_reap (void); +extern void procsub_setflag (pid_t, int, int); +extern void procsub_unsetflag (pid_t, int, int); extern void delete_job (int, int); extern void nohup_job (int); diff --git a/subst.c b/subst.c index cf38af7f..37e0bfa7 100644 --- a/subst.c +++ b/subst.c @@ -6094,25 +6094,6 @@ delete_procsubs (void) reap_some_procsubs (nfifo); } -#if 0 -/* UNUSED */ -void -wait_procsubs (void) -{ - int i, r; - - for (i = 0; i < nfifo; i++) - { - if (fifo_list[i].proc != (pid_t)-1 && fifo_list[i].proc > 0) - { - r = wait_for (fifo_list[i].proc, 0); - save_proc_status (fifo_list[i].proc, r); - fifo_list[i].proc = (pid_t)-1; - } - } -} -#endif - int fifos_pending (void) { @@ -6327,25 +6308,6 @@ delete_procsubs (void) reap_some_procsubs (totfds); } -#if 0 -/* UNUSED */ -void -wait_procsubs (void) -{ - int i, r; - - for (i = 0; nfds > 0 && i < totfds; i++) - { - if (dev_fd_list[i] != (pid_t)-1 && dev_fd_list[i] > 0) - { - r = wait_for (dev_fd_list[i], 0); - save_proc_status (dev_fd_list[i], r); - dev_fd_list[i] = (pid_t)-1; - } - } -} -#endif - #if defined (NOTDEF) print_dev_fd_list (void) { @@ -6443,7 +6405,7 @@ process_substitute (char *string, int open_for_read_in_child) save_pipeline (1); #endif /* JOB_CONTROL */ - pid = make_child ((char *)NULL, FORK_ASYNC); + pid = make_child ((char *)NULL, FORK_ASYNC|FORK_PROCSUB); if (pid == 0) { #if 0 @@ -7283,7 +7245,7 @@ command_substitute (char *string, int quoted, int flags) old_async_pid = last_asynchronous_pid; fork_flags = (subshell_environment&SUBSHELL_ASYNC) ? FORK_ASYNC : 0; - pid = make_child ((char *)NULL, fork_flags|FORK_NOTERM); + pid = make_child ((char *)NULL, fork_flags|FORK_NOTERM|FORK_COMSUB); last_asynchronous_pid = old_async_pid; if (pid == 0)