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
+
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
#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)
#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. */
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;
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;
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
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);
}
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)
{
if (p == 0)
{
- UNBLOCK_CHILD (oset);
+ if (block)
+ UNBLOCK_CHILD (oset);
return p;
}
/* 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);
}
PROCESS *p;
int r;
- p = procsub_search (pid);
+ p = procsub_search (pid, 1);
if (p == 0)
return -1;
if (p->running == PS_DONE)
sigset_t set, oset;
BLOCK_CHILD (set, oset);
-
for (ps = procsubs.head; ps; )
{
p = ps;
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
t->pid = pid;
WSTATUS (t->status) = 0;
t->running = PS_RUNNING; /* default */
+ t->flags = 0;
t->command = name;
t->next = (PROCESS *)0;
/* 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;
p = p->next;
p->next = t;
}
+
+ return t;
}
/* Create a (dummy) PROCESS with NAME, PID, and STATUS, and make it the last
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
int job;
sigset_t set, oset;
+ if (pid < 0)
+ return (NO_JOB);
+
if (block)
BLOCK_CHILD (set, oset);
unsigned int forksleep;
sigset_t set, oset, oset_copy;
pid_t pid;
+ PROCESS *child;
SigHandler *oterm;
sigemptyset (&oset_copy);
/* 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;
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. */
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)
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
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;
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);
}
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
#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 {
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;
#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)
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);
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)
{
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)
{
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
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)