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