1 /* ISC license. */
2 
3 #include <execline/config.h>
4 
5 #include <sys/wait.h>
6 #include <errno.h>
7 #ifdef EXECLINE_PEDANTIC_POSIX
8 #include <locale.h>
9 #endif
10 
11 #include <skalibs/types.h>
12 #include <skalibs/sgetopt.h>
13 #include <skalibs/strerr2.h>
14 #include <skalibs/tai.h>
15 #include <skalibs/djbunix.h>
16 #include <skalibs/selfpipe.h>
17 #include <skalibs/iopause.h>
18 #include <skalibs/exec.h>
19 
20 #include <execline/execline.h>
21 
22 #define USAGE "wait [ -I | -i ] [ -r | -t timeout ] { pids... }"
23 #define dieusage() strerr_dieusage(100, USAGE)
24 
25 typedef int actfunc_t (pid_t *, unsigned int *, int *) ;
26 typedef actfunc_t *actfunc_t_ref ;
27 
waitall(void)28 static inline int waitall (void)
29 {
30   int wstat = 0 ;
31   pid_t r = 1 ;
32   while (r > 0) r = wait(&wstat) ;
33   if (r < 0)
34   {
35     if (errno != ECHILD) strerr_diefu1sys(111, "wait") ;
36     else return 127 ;
37   }
38   return wait_estatus(wstat) ;
39 }
40 
waitany(pid_t * dummytab,unsigned int * dummyn,int * res)41 static int waitany (pid_t *dummytab, unsigned int *dummyn, int *res)
42 {
43   int wstat ;
44   pid_t r = 1 ;
45   while (r > 0) r = wait_nohang(&wstat) ;
46   if (!r) return (*res = wait_estatus(wstat), 1) ;
47   if (errno != ECHILD) strerr_diefu1sys(111, "wait") ;
48   *res = 127 ;
49   (void)dummytab ;
50   (void)dummyn ;
51   return 0 ;
52 }
53 
waitintab(pid_t * tab,unsigned int * n,int * res)54 static int waitintab (pid_t *tab, unsigned int *n, int *res)
55 {
56   unsigned int i = 0 ;
57   for (; i < *n ; i++)
58   {
59     int wstat ;
60     pid_t r = waitpid(tab[i], &wstat, WNOHANG) ;
61     if (r)
62     {
63       if (r < 0)
64       {
65         if (errno == ECHILD) *res = 127 ;
66         else strerr_diefu1sys(111, "waitpid") ;
67       }
68       else *res = wait_estatus(wstat) ;
69       tab[i--] = tab[--(*n)] ;
70     }
71   }
72   return !!*n ;
73 }
74 
handle_signals(void)75 static inline void handle_signals (void)
76 {
77   for (;;) switch (selfpipe_read())
78   {
79     case -1 : strerr_diefu1sys(111, "read selfpipe") ;
80     case 0 : return ;
81     case SIGCHLD : break ;
82     default: strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ;
83   }
84 }
85 
mainloop(tain_t * deadline,int insist,actfunc_t_ref f,pid_t * tab,unsigned int * n)86 static inline int mainloop (tain_t *deadline, int insist, actfunc_t_ref f, pid_t *tab, unsigned int *n)
87 {
88   iopause_fd x = { .events = IOPAUSE_READ } ;
89   int res = 0 ;
90   x.fd = selfpipe_init() ;
91   if (x.fd < 0) strerr_diefu1sys(111, "create selfpipe") ;
92   if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "trap SIGCHLD") ;
93   tain_now_set_stopwatch_g() ;
94   tain_add_g(deadline, deadline) ;
95   while ((*f)(tab, n, &res))
96   {
97     int r = iopause_g(&x, 1, deadline) ;
98     if (r < 0) strerr_diefu1sys(111, "iopause") ;
99     else if (!r)
100     {
101       if (!insist) break ;
102       errno = ETIMEDOUT ;
103       strerr_diefu1sys(1, "wait") ;
104     }
105     else handle_signals() ;
106   }
107   selfpipe_finish() ;
108   return res ;
109 }
110 
main(int argc,char const ** argv)111 int main (int argc, char const **argv)
112 {
113   tain_t tto ;
114   int argc1 ;
115   int hastimeout = 0 ;
116   int insist = 0 ;
117   int r ;
118   int hasblock ;
119   PROG = "wait" ;
120 #ifdef EXECLINE_PEDANTIC_POSIX
121   setlocale(LC_ALL, "") ;  /* but of course, dear POSIX */
122 #endif
123   {
124     subgetopt_t l = SUBGETOPT_ZERO ;
125     unsigned int t = 0 ;
126     for (;;)
127     {
128       int opt = subgetopt_r(argc, argv, "iIrt:", &l) ;
129       if (opt == -1) break ;
130       switch (opt)
131       {
132         case 'i' : insist = 1 ; break ;
133         case 'I' : insist = 0 ; break ;
134         case 'r' : t = 0 ; hastimeout = 1 ; break ;
135         case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; hastimeout = 1 ; break ;
136         default : dieusage() ;
137       }
138     }
139     argc -= l.ind ; argv += l.ind ;
140     if (hastimeout) tain_from_millisecs(&tto, t) ;
141     else tto = tain_infinite_relative ;
142   }
143   argc1 = el_semicolon(argv) ;
144   if (argc1 >= argc)
145   {
146     hasblock = 0 ;
147     argc1 = argc ;
148   }
149   else hasblock = 1 ;
150   if (!argc1 && !hastimeout) r = waitall() ;
151   else
152   {
153     actfunc_t_ref f = argc1 ? &waitintab : &waitany ;
154     unsigned int n = argc1 ? (unsigned int)argc1 : 1 ;
155     pid_t tab[n] ;
156     if (argc1)
157     {
158       unsigned int i = 0 ;
159       for (; i < n ; i++)
160         if (!pid0_scan(argv[i], tab+i)) strerr_dieusage(100, USAGE) ;
161     }
162     r = mainloop(&tto, insist, f, tab, &n) ;
163   }
164 
165   if (!hasblock) return r ;
166   xexec0(argv + argc1 + 1) ;
167 }
168