job control cleanups; wait -n can return terminated jobs if supplied pid arguments; wait -n can wait for process substitutions if supplied pid arguments

This commit is contained in:
Chet Ramey
2024-07-18 16:48:17 -04:00
parent 5d53be184e
commit 6c70309275
5 changed files with 249 additions and 82 deletions
+48
View File
@@ -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 <myoga.murase@gmail.com>
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
+59 -17
View File
@@ -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
+123 -18
View File
@@ -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
+17 -7
View File
@@ -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);
+2 -40
View File
@@ -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)