]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
job control cleanups; wait -n can return terminated jobs if supplied pid arguments...
authorChet Ramey <chet.ramey@case.edu>
Thu, 18 Jul 2024 20:48:17 +0000 (16:48 -0400)
committerChet Ramey <chet.ramey@case.edu>
Thu, 18 Jul 2024 20:48:17 +0000 (16:48 -0400)
CWRU/CWRU.chlog
builtins/wait.def
jobs.c
jobs.h
subst.c

index 4fc762600ea3edb764cf025f784c7216e519d0a7..66d523106c7bda7b6b8d3647601ff1c2a1b6f268 100644 (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
+
index efcfc0ffa45f99cd2aa543d3973a394c1fde3d7e..98160bea11d12454d3607b07767adf827565b3c3 100644 (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
diff --git a/jobs.c b/jobs.c
index ab5071587645d69b2371869a379546231cde5052..7c32e6bcae261e73ed561b1f4c99564961475892 100644 (file)
--- 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 59b4ac5863f527b656ecdc01333bd58e15cab984..33afdb59beb04ec6bff07263143e3eed543485c1 100644 (file)
--- a/jobs.h
+++ b/jobs.h
 #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 cf38af7f24dd94fcecad516a033f6f339fc07bc2..37e0bfa75d0fdfac5785e6e1702beca0886c63de 100644 (file)
--- 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)