diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-17 18:58:49 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-17 18:58:49 +0000 |
commit | 04c6386c45cd795617dd754066ac3bd6a62757cb (patch) | |
tree | ca62c4d72fe5799be5ba36810e1c214e0cb8a49f /runit | |
parent | 8a00f44bf4675076adfe312b71a6ff3670feef7d (diff) | |
download | busybox-w32-04c6386c45cd795617dd754066ac3bd6a62757cb.tar.gz busybox-w32-04c6386c45cd795617dd754066ac3bd6a62757cb.tar.bz2 busybox-w32-04c6386c45cd795617dd754066ac3bd6a62757cb.zip |
forgot about avn add... :(
Diffstat (limited to 'runit')
-rw-r--r-- | runit/runsv.c | 613 | ||||
-rw-r--r-- | runit/runsvdir.c | 306 | ||||
-rw-r--r-- | runit/sv.c | 360 |
3 files changed, 1279 insertions, 0 deletions
diff --git a/runit/runsv.c b/runit/runsv.c new file mode 100644 index 000000000..e1b5459fb --- /dev/null +++ b/runit/runsv.c | |||
@@ -0,0 +1,613 @@ | |||
1 | /* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */ | ||
2 | /* TODO: depends on runit_lib.c - review and reduce/eliminate */ | ||
3 | |||
4 | #include <sys/poll.h> | ||
5 | #include <sys/file.h> | ||
6 | #include "busybox.h" | ||
7 | #include "runit_lib.h" | ||
8 | |||
9 | static int selfpipe[2]; | ||
10 | |||
11 | /* state */ | ||
12 | #define S_DOWN 0 | ||
13 | #define S_RUN 1 | ||
14 | #define S_FINISH 2 | ||
15 | /* ctrl */ | ||
16 | #define C_NOOP 0 | ||
17 | #define C_TERM 1 | ||
18 | #define C_PAUSE 2 | ||
19 | /* want */ | ||
20 | #define W_UP 0 | ||
21 | #define W_DOWN 1 | ||
22 | #define W_EXIT 2 | ||
23 | |||
24 | struct svdir { | ||
25 | int pid; | ||
26 | int state; | ||
27 | int ctrl; | ||
28 | int want; | ||
29 | struct taia start; | ||
30 | int fdlock; | ||
31 | int fdcontrol; | ||
32 | int fdcontrolwrite; | ||
33 | int islog; | ||
34 | }; | ||
35 | static struct svdir svd[2]; | ||
36 | |||
37 | static int sigterm = 0; | ||
38 | static int haslog = 0; | ||
39 | static int pidchanged = 1; | ||
40 | static int logpipe[2]; | ||
41 | static char *dir; | ||
42 | |||
43 | #define usage() bb_show_usage() | ||
44 | |||
45 | static void fatal2_cannot(char *m1, char *m2) | ||
46 | { | ||
47 | bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2); | ||
48 | /* was exiting 111 */ | ||
49 | } | ||
50 | static void fatal_cannot(char *m) | ||
51 | { | ||
52 | fatal2_cannot(m, ""); | ||
53 | /* was exiting 111 */ | ||
54 | } | ||
55 | static void fatal2x_cannot(char *m1, char *m2) | ||
56 | { | ||
57 | bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2); | ||
58 | /* was exiting 111 */ | ||
59 | } | ||
60 | static void warn_cannot(char *m) | ||
61 | { | ||
62 | bb_perror_msg("%s: warning: cannot %s", dir, m); | ||
63 | } | ||
64 | static void warnx_cannot(char *m) | ||
65 | { | ||
66 | bb_error_msg("%s: warning: cannot %s", dir, m); | ||
67 | } | ||
68 | |||
69 | static void stopservice(struct svdir *); | ||
70 | |||
71 | static void s_child(int sig_no) | ||
72 | { | ||
73 | write(selfpipe[1], "", 1); | ||
74 | } | ||
75 | |||
76 | static void s_term(int sig_no) | ||
77 | { | ||
78 | sigterm = 1; | ||
79 | write(selfpipe[1], "", 1); /* XXX */ | ||
80 | } | ||
81 | |||
82 | static char *add_str(char *p, const char *to_add) | ||
83 | { | ||
84 | while ((*p = *to_add) != '\0') { | ||
85 | p++; | ||
86 | to_add++; | ||
87 | } | ||
88 | return p; | ||
89 | } | ||
90 | |||
91 | static int open_trunc_or_warn(const char *name) | ||
92 | { | ||
93 | int fd = open_trunc(name); | ||
94 | if (fd < 0) | ||
95 | bb_perror_msg("%s: warning: cannot open %s", | ||
96 | dir, name); | ||
97 | return fd; | ||
98 | } | ||
99 | |||
100 | static int rename_or_warn(const char *old, const char *new) | ||
101 | { | ||
102 | if (rename(old, new) == -1) { | ||
103 | bb_perror_msg("%s: warning: cannot rename %s to %s", | ||
104 | dir, old, new); | ||
105 | return -1; | ||
106 | } | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static void update_status(struct svdir *s) | ||
111 | { | ||
112 | unsigned long l; | ||
113 | int fd; | ||
114 | char status[20]; | ||
115 | |||
116 | /* pid */ | ||
117 | if (pidchanged) { | ||
118 | fd = open_trunc_or_warn("supervise/pid.new"); | ||
119 | if (fd < 0) | ||
120 | return; | ||
121 | if (s->pid) { | ||
122 | char spid[sizeof(s->pid)*3 + 2]; | ||
123 | int size = sprintf(spid, "%d\n", s->pid); | ||
124 | write(fd, spid, size); | ||
125 | } | ||
126 | close(fd); | ||
127 | if (s->islog) { | ||
128 | if (rename_or_warn("supervise/pid.new", "log/supervise/pid")) | ||
129 | return; | ||
130 | } else if (rename_or_warn("supervise/pid.new", "supervise/pid")) { | ||
131 | return; | ||
132 | } | ||
133 | pidchanged = 0; | ||
134 | } | ||
135 | |||
136 | /* stat */ | ||
137 | fd = open_trunc_or_warn("supervise/stat.new"); | ||
138 | if (fd < -1) | ||
139 | return; | ||
140 | |||
141 | { | ||
142 | char stat_buf[sizeof("finish, paused, got TERM, want down\n")]; | ||
143 | char *p = stat_buf; | ||
144 | switch (s->state) { | ||
145 | case S_DOWN: | ||
146 | p = add_str(p, "down"); | ||
147 | break; | ||
148 | case S_RUN: | ||
149 | p = add_str(p, "run"); | ||
150 | break; | ||
151 | case S_FINISH: | ||
152 | p = add_str(p, "finish"); | ||
153 | break; | ||
154 | } | ||
155 | if (s->ctrl & C_PAUSE) p = add_str(p, ", paused"); | ||
156 | if (s->ctrl & C_TERM) p = add_str(p, ", got TERM"); | ||
157 | if (s->state != S_DOWN) | ||
158 | switch(s->want) { | ||
159 | case W_DOWN: | ||
160 | p = add_str(p, ", want down"); | ||
161 | break; | ||
162 | case W_EXIT: | ||
163 | p = add_str(p, ", want exit"); | ||
164 | break; | ||
165 | } | ||
166 | *p++ = '\n'; | ||
167 | write(fd, stat_buf, p - stat_buf); | ||
168 | close(fd); | ||
169 | } | ||
170 | |||
171 | if (s->islog) { | ||
172 | rename_or_warn("supervise/stat.new", "log/supervise/stat"); | ||
173 | } else { | ||
174 | rename_or_warn("supervise/stat.new", "log/supervise/stat"+4); | ||
175 | } | ||
176 | |||
177 | /* supervise compatibility */ | ||
178 | taia_pack(status, &s->start); | ||
179 | l = (unsigned long)s->pid; | ||
180 | status[12] = l; l >>=8; | ||
181 | status[13] = l; l >>=8; | ||
182 | status[14] = l; l >>=8; | ||
183 | status[15] = l; | ||
184 | if (s->ctrl & C_PAUSE) | ||
185 | status[16] = 1; | ||
186 | else | ||
187 | status[16] = 0; | ||
188 | if (s->want == W_UP) | ||
189 | status[17] = 'u'; | ||
190 | else | ||
191 | status[17] = 'd'; | ||
192 | if (s->ctrl & C_TERM) | ||
193 | status[18] = 1; | ||
194 | else | ||
195 | status[18] = 0; | ||
196 | status[19] = s->state; | ||
197 | fd = open_trunc_or_warn("supervise/status.new"); | ||
198 | if (fd < 0) | ||
199 | return; | ||
200 | l = write(fd, status, sizeof status); | ||
201 | if (l < 0) { | ||
202 | warn_cannot("write supervise/status.new"); | ||
203 | close(fd); | ||
204 | unlink("supervise/status.new"); | ||
205 | return; | ||
206 | } | ||
207 | close(fd); | ||
208 | if (l < sizeof status) { | ||
209 | warnx_cannot("write supervise/status.new: partial write"); | ||
210 | return; | ||
211 | } | ||
212 | if (s->islog) { | ||
213 | rename_or_warn("supervise/status.new", "log/supervise/status"); | ||
214 | } else { | ||
215 | rename_or_warn("supervise/status.new", "log/supervise/status"+4); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | static unsigned custom(struct svdir *s, char c) | ||
220 | { | ||
221 | int pid; | ||
222 | int w; | ||
223 | char a[10]; | ||
224 | struct stat st; | ||
225 | char *prog[2]; | ||
226 | |||
227 | if (s->islog) return 0; | ||
228 | memcpy(a, "control/?", 10); | ||
229 | a[8] = c; | ||
230 | if (stat(a, &st) == 0) { | ||
231 | if (st.st_mode & S_IXUSR) { | ||
232 | pid = fork(); | ||
233 | if (pid == -1) { | ||
234 | warn_cannot("fork for control/?"); | ||
235 | return 0; | ||
236 | } | ||
237 | if (!pid) { | ||
238 | if (haslog && fd_copy(1, logpipe[1]) == -1) | ||
239 | warn_cannot("setup stdout for control/?"); | ||
240 | prog[0] = a; | ||
241 | prog[1] = 0; | ||
242 | execve(a, prog, environ); | ||
243 | fatal_cannot("run control/?"); | ||
244 | } | ||
245 | while (wait_pid(&w, pid) == -1) { | ||
246 | if (errno == EINTR) continue; | ||
247 | warn_cannot("wait for child control/?"); | ||
248 | return 0; | ||
249 | } | ||
250 | return !wait_exitcode(w); | ||
251 | } | ||
252 | } | ||
253 | else { | ||
254 | if (errno == ENOENT) return 0; | ||
255 | warn_cannot("stat control/?"); | ||
256 | } | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static void stopservice(struct svdir *s) | ||
261 | { | ||
262 | if (s->pid && ! custom(s, 't')) { | ||
263 | kill(s->pid, SIGTERM); | ||
264 | s->ctrl |=C_TERM; | ||
265 | update_status(s); | ||
266 | } | ||
267 | if (s->want == W_DOWN) { | ||
268 | kill(s->pid, SIGCONT); | ||
269 | custom(s, 'd'); return; | ||
270 | } | ||
271 | if (s->want == W_EXIT) { | ||
272 | kill(s->pid, SIGCONT); | ||
273 | custom(s, 'x'); | ||
274 | } | ||
275 | } | ||
276 | |||
277 | static void startservice(struct svdir *s) | ||
278 | { | ||
279 | int p; | ||
280 | char *run[2]; | ||
281 | |||
282 | if (s->state == S_FINISH) | ||
283 | run[0] = "./finish"; | ||
284 | else { | ||
285 | run[0] = "./run"; | ||
286 | custom(s, 'u'); | ||
287 | } | ||
288 | run[1] = 0; | ||
289 | |||
290 | if (s->pid != 0) stopservice(s); /* should never happen */ | ||
291 | while ((p = fork()) == -1) { | ||
292 | warn_cannot("fork, sleeping"); | ||
293 | sleep(5); | ||
294 | } | ||
295 | if (p == 0) { | ||
296 | /* child */ | ||
297 | if (haslog) { | ||
298 | if (s->islog) { | ||
299 | if (fd_copy(0, logpipe[0]) == -1) | ||
300 | fatal_cannot("setup filedescriptor for ./log/run"); | ||
301 | close(logpipe[1]); | ||
302 | if (chdir("./log") == -1) | ||
303 | fatal_cannot("change directory to ./log"); | ||
304 | } else { | ||
305 | if (fd_copy(1, logpipe[1]) == -1) | ||
306 | fatal_cannot("setup filedescriptor for ./run"); | ||
307 | close(logpipe[0]); | ||
308 | } | ||
309 | } | ||
310 | sig_uncatch(sig_child); | ||
311 | sig_unblock(sig_child); | ||
312 | sig_uncatch(sig_term); | ||
313 | sig_unblock(sig_term); | ||
314 | execve(*run, run, environ); | ||
315 | if (s->islog) | ||
316 | fatal2_cannot("start log/", *run); | ||
317 | else | ||
318 | fatal2_cannot("start ", *run); | ||
319 | } | ||
320 | if (s->state != S_FINISH) { | ||
321 | taia_now(&s->start); | ||
322 | s->state = S_RUN; | ||
323 | } | ||
324 | s->pid = p; | ||
325 | pidchanged = 1; | ||
326 | s->ctrl = C_NOOP; | ||
327 | update_status(s); | ||
328 | } | ||
329 | |||
330 | static int ctrl(struct svdir *s, char c) | ||
331 | { | ||
332 | switch(c) { | ||
333 | case 'd': /* down */ | ||
334 | s->want = W_DOWN; | ||
335 | update_status(s); | ||
336 | if (s->pid && s->state != S_FINISH) stopservice(s); | ||
337 | break; | ||
338 | case 'u': /* up */ | ||
339 | s->want = W_UP; | ||
340 | update_status(s); | ||
341 | if (s->pid == 0) startservice(s); | ||
342 | break; | ||
343 | case 'x': /* exit */ | ||
344 | if (s->islog) break; | ||
345 | s->want = W_EXIT; | ||
346 | update_status(s); | ||
347 | if (s->pid && s->state != S_FINISH) stopservice(s); | ||
348 | break; | ||
349 | case 't': /* sig term */ | ||
350 | if (s->pid && s->state != S_FINISH) stopservice(s); | ||
351 | break; | ||
352 | case 'k': /* sig kill */ | ||
353 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGKILL); | ||
354 | s->state = S_DOWN; | ||
355 | break; | ||
356 | case 'p': /* sig pause */ | ||
357 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGSTOP); | ||
358 | s->ctrl |=C_PAUSE; | ||
359 | update_status(s); | ||
360 | break; | ||
361 | case 'c': /* sig cont */ | ||
362 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGCONT); | ||
363 | if (s->ctrl & C_PAUSE) s->ctrl &=~C_PAUSE; | ||
364 | update_status(s); | ||
365 | break; | ||
366 | case 'o': /* once */ | ||
367 | s->want = W_DOWN; | ||
368 | update_status(s); | ||
369 | if (!s->pid) startservice(s); | ||
370 | break; | ||
371 | case 'a': /* sig alarm */ | ||
372 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGALRM); | ||
373 | break; | ||
374 | case 'h': /* sig hup */ | ||
375 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGHUP); | ||
376 | break; | ||
377 | case 'i': /* sig int */ | ||
378 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGINT); | ||
379 | break; | ||
380 | case 'q': /* sig quit */ | ||
381 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGQUIT); | ||
382 | break; | ||
383 | case '1': /* sig usr1 */ | ||
384 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGUSR1); | ||
385 | break; | ||
386 | case '2': /* sig usr2 */ | ||
387 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGUSR2); | ||
388 | break; | ||
389 | } | ||
390 | return 1; | ||
391 | } | ||
392 | |||
393 | int runsv_main(int argc, char **argv) | ||
394 | { | ||
395 | struct stat s; | ||
396 | int fd; | ||
397 | int r; | ||
398 | char buf[256]; | ||
399 | |||
400 | if (!argv[1] || argv[2]) usage(); | ||
401 | dir = argv[1]; | ||
402 | |||
403 | if (pipe(selfpipe) == -1) fatal_cannot("create selfpipe"); | ||
404 | coe(selfpipe[0]); | ||
405 | coe(selfpipe[1]); | ||
406 | ndelay_on(selfpipe[0]); | ||
407 | ndelay_on(selfpipe[1]); | ||
408 | |||
409 | sig_block(sig_child); | ||
410 | sig_catch(sig_child, s_child); | ||
411 | sig_block(sig_term); | ||
412 | sig_catch(sig_term, s_term); | ||
413 | |||
414 | xchdir(dir); | ||
415 | svd[0].pid = 0; | ||
416 | svd[0].state = S_DOWN; | ||
417 | svd[0].ctrl = C_NOOP; | ||
418 | svd[0].want = W_UP; | ||
419 | svd[0].islog = 0; | ||
420 | svd[1].pid = 0; | ||
421 | taia_now(&svd[0].start); | ||
422 | if (stat("down", &s) != -1) svd[0].want = W_DOWN; | ||
423 | |||
424 | if (stat("log", &s) == -1) { | ||
425 | if (errno != ENOENT) | ||
426 | warn_cannot("stat ./log"); | ||
427 | } else { | ||
428 | if (!S_ISDIR(s.st_mode)) | ||
429 | warnx_cannot("stat log/down: log is not a directory"); | ||
430 | else { | ||
431 | haslog = 1; | ||
432 | svd[1].state = S_DOWN; | ||
433 | svd[1].ctrl = C_NOOP; | ||
434 | svd[1].want = W_UP; | ||
435 | svd[1].islog = 1; | ||
436 | taia_now(&svd[1].start); | ||
437 | if (stat("log/down", &s) != -1) | ||
438 | svd[1].want = W_DOWN; | ||
439 | if (pipe(logpipe) == -1) | ||
440 | fatal_cannot("create log pipe"); | ||
441 | coe(logpipe[0]); | ||
442 | coe(logpipe[1]); | ||
443 | } | ||
444 | } | ||
445 | |||
446 | if (mkdir("supervise", 0700) == -1) { | ||
447 | r = readlink("supervise", buf, 256); | ||
448 | if (r != -1) { | ||
449 | if (r == 256) | ||
450 | fatal2x_cannot("readlink ./supervise: ", "name too long"); | ||
451 | buf[r] = 0; | ||
452 | mkdir(buf, 0700); | ||
453 | } else { | ||
454 | if ((errno != ENOENT) && (errno != EINVAL)) | ||
455 | fatal_cannot("readlink ./supervise"); | ||
456 | } | ||
457 | } | ||
458 | svd[0].fdlock = xopen3("log/supervise/lock"+4, | ||
459 | O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); | ||
460 | if (lock_exnb(svd[0].fdlock) == -1) | ||
461 | fatal_cannot("lock supervise/lock"); | ||
462 | coe(svd[0].fdlock); | ||
463 | if (haslog) { | ||
464 | if (mkdir("log/supervise", 0700) == -1) { | ||
465 | r = readlink("log/supervise", buf, 256); | ||
466 | if (r != -1) { | ||
467 | if (r == 256) | ||
468 | fatal2x_cannot("readlink ./log/supervise: ", "name too long"); | ||
469 | buf[r] = 0; | ||
470 | fd = xopen(".", O_RDONLY|O_NDELAY); | ||
471 | xchdir("./log"); | ||
472 | mkdir(buf, 0700); | ||
473 | if (fchdir(fd) == -1) | ||
474 | fatal_cannot("change back to service directory"); | ||
475 | close(fd); | ||
476 | } | ||
477 | else { | ||
478 | if ((errno != ENOENT) && (errno != EINVAL)) | ||
479 | fatal_cannot("readlink ./log/supervise"); | ||
480 | } | ||
481 | } | ||
482 | svd[1].fdlock = xopen3("log/supervise/lock", | ||
483 | O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); | ||
484 | if (lock_ex(svd[1].fdlock) == -1) | ||
485 | fatal_cannot("lock log/supervise/lock"); | ||
486 | coe(svd[1].fdlock); | ||
487 | } | ||
488 | |||
489 | fifo_make("log/supervise/control"+4, 0600); | ||
490 | svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY); | ||
491 | coe(svd[0].fdcontrol); | ||
492 | svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY); | ||
493 | coe(svd[0].fdcontrolwrite); | ||
494 | update_status(&svd[0]); | ||
495 | if (haslog) { | ||
496 | fifo_make("log/supervise/control", 0600); | ||
497 | svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY); | ||
498 | coe(svd[1].fdcontrol); | ||
499 | svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY); | ||
500 | coe(svd[1].fdcontrolwrite); | ||
501 | update_status(&svd[1]); | ||
502 | } | ||
503 | fifo_make("log/supervise/ok"+4, 0600); | ||
504 | fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY); | ||
505 | coe(fd); | ||
506 | if (haslog) { | ||
507 | fifo_make("log/supervise/ok", 0600); | ||
508 | fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY); | ||
509 | coe(fd); | ||
510 | } | ||
511 | for (;;) { | ||
512 | iopause_fd x[3]; | ||
513 | struct taia deadline; | ||
514 | struct taia now; | ||
515 | char ch; | ||
516 | |||
517 | if (haslog) | ||
518 | if (!svd[1].pid && svd[1].want == W_UP) | ||
519 | startservice(&svd[1]); | ||
520 | if (!svd[0].pid) | ||
521 | if (svd[0].want == W_UP || svd[0].state == S_FINISH) | ||
522 | startservice(&svd[0]); | ||
523 | |||
524 | x[0].fd = selfpipe[0]; | ||
525 | x[0].events = IOPAUSE_READ; | ||
526 | x[1].fd = svd[0].fdcontrol; | ||
527 | x[1].events = IOPAUSE_READ; | ||
528 | if (haslog) { | ||
529 | x[2].fd = svd[1].fdcontrol; | ||
530 | x[2].events = IOPAUSE_READ; | ||
531 | } | ||
532 | taia_now(&now); | ||
533 | taia_uint(&deadline, 3600); | ||
534 | taia_add(&deadline, &now, &deadline); | ||
535 | |||
536 | sig_unblock(sig_term); | ||
537 | sig_unblock(sig_child); | ||
538 | iopause(x, 2+haslog, &deadline, &now); | ||
539 | sig_block(sig_term); | ||
540 | sig_block(sig_child); | ||
541 | |||
542 | while (read(selfpipe[0], &ch, 1) == 1) | ||
543 | ; | ||
544 | for (;;) { | ||
545 | int child; | ||
546 | int wstat; | ||
547 | |||
548 | child = wait_nohang(&wstat); | ||
549 | if (!child) break; | ||
550 | if ((child == -1) && (errno != EINTR)) break; | ||
551 | if (child == svd[0].pid) { | ||
552 | svd[0].pid = 0; | ||
553 | pidchanged = 1; | ||
554 | svd[0].ctrl &=~C_TERM; | ||
555 | if (svd[0].state != S_FINISH) | ||
556 | fd = open_read("finish"); | ||
557 | if (fd != -1) { | ||
558 | close(fd); | ||
559 | svd[0].state = S_FINISH; | ||
560 | update_status(&svd[0]); | ||
561 | continue; | ||
562 | } | ||
563 | svd[0].state = S_DOWN; | ||
564 | taia_uint(&deadline, 1); | ||
565 | taia_add(&deadline, &svd[0].start, &deadline); | ||
566 | taia_now(&svd[0].start); | ||
567 | update_status(&svd[0]); | ||
568 | if (taia_less(&svd[0].start, &deadline)) sleep(1); | ||
569 | } | ||
570 | if (haslog) { | ||
571 | if (child == svd[1].pid) { | ||
572 | svd[1].pid = 0; | ||
573 | pidchanged = 1; | ||
574 | svd[1].state = S_DOWN; | ||
575 | svd[1].ctrl &=~C_TERM; | ||
576 | taia_uint(&deadline, 1); | ||
577 | taia_add(&deadline, &svd[1].start, &deadline); | ||
578 | taia_now(&svd[1].start); | ||
579 | update_status(&svd[1]); | ||
580 | if (taia_less(&svd[1].start, &deadline)) sleep(1); | ||
581 | } | ||
582 | } | ||
583 | } | ||
584 | if (read(svd[0].fdcontrol, &ch, 1) == 1) | ||
585 | ctrl(&svd[0], ch); | ||
586 | if (haslog) | ||
587 | if (read(svd[1].fdcontrol, &ch, 1) == 1) | ||
588 | ctrl(&svd[1], ch); | ||
589 | |||
590 | if (sigterm) { | ||
591 | ctrl(&svd[0], 'x'); | ||
592 | sigterm = 0; | ||
593 | } | ||
594 | |||
595 | if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) { | ||
596 | if (svd[1].pid == 0) | ||
597 | _exit(0); | ||
598 | if (svd[1].want != W_EXIT) { | ||
599 | svd[1].want = W_EXIT; | ||
600 | /* stopservice(&svd[1]); */ | ||
601 | update_status(&svd[1]); | ||
602 | close(logpipe[1]); | ||
603 | close(logpipe[0]); | ||
604 | //if (close(logpipe[1]) == -1) | ||
605 | // warn_cannot("close logpipe[1]"); | ||
606 | //if (close(logpipe[0]) == -1) | ||
607 | // warn_cannot("close logpipe[0]"); | ||
608 | } | ||
609 | } | ||
610 | } | ||
611 | /* not reached */ | ||
612 | return 0; | ||
613 | } | ||
diff --git a/runit/runsvdir.c b/runit/runsvdir.c new file mode 100644 index 000000000..9238eec82 --- /dev/null +++ b/runit/runsvdir.c | |||
@@ -0,0 +1,306 @@ | |||
1 | /* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */ | ||
2 | /* TODO: depends on runit_lib.c - review and reduce/eliminate */ | ||
3 | |||
4 | #include <sys/poll.h> | ||
5 | #include <sys/file.h> | ||
6 | #include "busybox.h" | ||
7 | #include "runit_lib.h" | ||
8 | |||
9 | #define MAXSERVICES 1000 | ||
10 | |||
11 | static char *svdir; | ||
12 | static unsigned long dev; | ||
13 | static unsigned long ino; | ||
14 | static struct service { | ||
15 | unsigned long dev; | ||
16 | unsigned long ino; | ||
17 | int pid; | ||
18 | int isgone; | ||
19 | } *sv; | ||
20 | static int svnum; | ||
21 | static int check = 1; | ||
22 | static char *rplog; | ||
23 | static int rploglen; | ||
24 | static int logpipe[2]; | ||
25 | static iopause_fd io[1]; | ||
26 | static struct taia stamplog; | ||
27 | static int exitsoon; | ||
28 | static int pgrp; | ||
29 | |||
30 | #define usage() bb_show_usage() | ||
31 | static void fatal2_cannot(char *m1, char *m2) | ||
32 | { | ||
33 | bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2); | ||
34 | /* was exiting 100 */ | ||
35 | } | ||
36 | static void warn3x(char *m1, char *m2, char *m3) | ||
37 | { | ||
38 | bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3); | ||
39 | } | ||
40 | static void warn2_cannot(char *m1, char *m2) | ||
41 | { | ||
42 | warn3x("cannot ", m1, m2); | ||
43 | } | ||
44 | static void warnx(char *m1) | ||
45 | { | ||
46 | warn3x(m1, "", ""); | ||
47 | } | ||
48 | |||
49 | static void s_term(int sig_no) | ||
50 | { | ||
51 | exitsoon = 1; | ||
52 | } | ||
53 | static void s_hangup(int sig_no) | ||
54 | { | ||
55 | exitsoon = 2; | ||
56 | } | ||
57 | |||
58 | static void runsv(int no, char *name) | ||
59 | { | ||
60 | int pid = fork(); | ||
61 | |||
62 | if (pid == -1) { | ||
63 | warn2_cannot("fork for ", name); | ||
64 | return; | ||
65 | } | ||
66 | if (pid == 0) { | ||
67 | /* child */ | ||
68 | char *prog[3]; | ||
69 | |||
70 | prog[0] = "runsv"; | ||
71 | prog[1] = name; | ||
72 | prog[2] = 0; | ||
73 | sig_uncatch(sig_hangup); | ||
74 | sig_uncatch(sig_term); | ||
75 | if (pgrp) setsid(); | ||
76 | execvp(prog[0], prog); | ||
77 | //pathexec_run(*prog, prog, (char* const*)environ); | ||
78 | fatal2_cannot("start runsv ", name); | ||
79 | } | ||
80 | sv[no].pid = pid; | ||
81 | } | ||
82 | |||
83 | static void runsvdir(void) | ||
84 | { | ||
85 | DIR *dir; | ||
86 | direntry *d; | ||
87 | int i; | ||
88 | struct stat s; | ||
89 | |||
90 | dir = opendir("."); | ||
91 | if (!dir) { | ||
92 | warn2_cannot("open directory ", svdir); | ||
93 | return; | ||
94 | } | ||
95 | for (i = 0; i < svnum; i++) | ||
96 | sv[i].isgone = 1; | ||
97 | errno = 0; | ||
98 | while ((d = readdir(dir))) { | ||
99 | if (d->d_name[0] == '.') continue; | ||
100 | if (stat(d->d_name, &s) == -1) { | ||
101 | warn2_cannot("stat ", d->d_name); | ||
102 | errno = 0; | ||
103 | continue; | ||
104 | } | ||
105 | if (!S_ISDIR(s.st_mode)) continue; | ||
106 | for (i = 0; i < svnum; i++) { | ||
107 | if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) { | ||
108 | sv[i].isgone = 0; | ||
109 | if (!sv[i].pid) | ||
110 | runsv(i, d->d_name); | ||
111 | break; | ||
112 | } | ||
113 | } | ||
114 | if (i == svnum) { | ||
115 | /* new service */ | ||
116 | struct service *svnew = realloc(sv, (i+1) * sizeof(*sv)); | ||
117 | if (!svnew) { | ||
118 | warn3x("cannot start runsv ", d->d_name, | ||
119 | " too many services"); | ||
120 | continue; | ||
121 | } | ||
122 | sv = svnew; | ||
123 | svnum++; | ||
124 | memset(&sv[i], 0, sizeof(sv[i])); | ||
125 | sv[i].ino = s.st_ino; | ||
126 | sv[i].dev = s.st_dev; | ||
127 | //sv[i].pid = 0; | ||
128 | //sv[i].isgone = 0; | ||
129 | runsv(i, d->d_name); | ||
130 | check = 1; | ||
131 | } | ||
132 | } | ||
133 | if (errno) { | ||
134 | warn2_cannot("read directory ", svdir); | ||
135 | closedir(dir); | ||
136 | check = 1; | ||
137 | return; | ||
138 | } | ||
139 | closedir(dir); | ||
140 | |||
141 | /* SIGTERM removed runsv's */ | ||
142 | for (i = 0; i < svnum; i++) { | ||
143 | if (!sv[i].isgone) | ||
144 | continue; | ||
145 | if (sv[i].pid) | ||
146 | kill(sv[i].pid, SIGTERM); | ||
147 | sv[i] = sv[--svnum]; | ||
148 | check = 1; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | static int setup_log(void) | ||
153 | { | ||
154 | rploglen = strlen(rplog); | ||
155 | if (rploglen < 7) { | ||
156 | warnx("log must have at least seven characters"); | ||
157 | return 0; | ||
158 | } | ||
159 | if (pipe(logpipe) == -1) { | ||
160 | warnx("cannot create pipe for log"); | ||
161 | return -1; | ||
162 | } | ||
163 | coe(logpipe[1]); | ||
164 | coe(logpipe[0]); | ||
165 | ndelay_on(logpipe[0]); | ||
166 | ndelay_on(logpipe[1]); | ||
167 | if (fd_copy(2, logpipe[1]) == -1) { | ||
168 | warnx("cannot set filedescriptor for log"); | ||
169 | return -1; | ||
170 | } | ||
171 | io[0].fd = logpipe[0]; | ||
172 | io[0].events = IOPAUSE_READ; | ||
173 | taia_now(&stamplog); | ||
174 | return 1; | ||
175 | } | ||
176 | |||
177 | int runsvdir_main(int argc, char **argv) | ||
178 | { | ||
179 | struct stat s; | ||
180 | time_t mtime = 0; | ||
181 | int wstat; | ||
182 | int curdir; | ||
183 | int pid; | ||
184 | struct taia deadline; | ||
185 | struct taia now; | ||
186 | struct taia stampcheck; | ||
187 | char ch; | ||
188 | int i; | ||
189 | |||
190 | argv++; | ||
191 | if (!argv || !*argv) usage(); | ||
192 | if (**argv == '-') { | ||
193 | switch (*(*argv + 1)) { | ||
194 | case 'P': pgrp = 1; | ||
195 | case '-': ++argv; | ||
196 | } | ||
197 | if (!argv || !*argv) usage(); | ||
198 | } | ||
199 | |||
200 | sig_catch(sig_term, s_term); | ||
201 | sig_catch(sig_hangup, s_hangup); | ||
202 | svdir = *argv++; | ||
203 | if (argv && *argv) { | ||
204 | rplog = *argv; | ||
205 | if (setup_log() != 1) { | ||
206 | rplog = 0; | ||
207 | warnx("log service disabled"); | ||
208 | } | ||
209 | } | ||
210 | curdir = open_read("."); | ||
211 | if (curdir == -1) | ||
212 | fatal2_cannot("open current directory", ""); | ||
213 | coe(curdir); | ||
214 | |||
215 | taia_now(&stampcheck); | ||
216 | |||
217 | for (;;) { | ||
218 | /* collect children */ | ||
219 | for (;;) { | ||
220 | pid = wait_nohang(&wstat); | ||
221 | if (pid <= 0) break; | ||
222 | for (i = 0; i < svnum; i++) { | ||
223 | if (pid == sv[i].pid) { | ||
224 | /* runsv has gone */ | ||
225 | sv[i].pid = 0; | ||
226 | check = 1; | ||
227 | break; | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | |||
232 | taia_now(&now); | ||
233 | if (now.sec.x < (stampcheck.sec.x - 3)) { | ||
234 | /* time warp */ | ||
235 | warnx("time warp: resetting time stamp"); | ||
236 | taia_now(&stampcheck); | ||
237 | taia_now(&now); | ||
238 | if (rplog) taia_now(&stamplog); | ||
239 | } | ||
240 | if (taia_less(&now, &stampcheck) == 0) { | ||
241 | /* wait at least a second */ | ||
242 | taia_uint(&deadline, 1); | ||
243 | taia_add(&stampcheck, &now, &deadline); | ||
244 | |||
245 | if (stat(svdir, &s) != -1) { | ||
246 | if (check || s.st_mtime != mtime | ||
247 | || s.st_ino != ino || s.st_dev != dev | ||
248 | ) { | ||
249 | /* svdir modified */ | ||
250 | if (chdir(svdir) != -1) { | ||
251 | mtime = s.st_mtime; | ||
252 | dev = s.st_dev; | ||
253 | ino = s.st_ino; | ||
254 | check = 0; | ||
255 | if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime)) | ||
256 | sleep(1); | ||
257 | runsvdir(); | ||
258 | while (fchdir(curdir) == -1) { | ||
259 | warn2_cannot("change directory, pausing", ""); | ||
260 | sleep(5); | ||
261 | } | ||
262 | } else | ||
263 | warn2_cannot("change directory to ", svdir); | ||
264 | } | ||
265 | } else | ||
266 | warn2_cannot("stat ", svdir); | ||
267 | } | ||
268 | |||
269 | if (rplog) { | ||
270 | if (taia_less(&now, &stamplog) == 0) { | ||
271 | write(logpipe[1], ".", 1); | ||
272 | taia_uint(&deadline, 900); | ||
273 | taia_add(&stamplog, &now, &deadline); | ||
274 | } | ||
275 | } | ||
276 | taia_uint(&deadline, check ? 1 : 5); | ||
277 | taia_add(&deadline, &now, &deadline); | ||
278 | |||
279 | sig_block(sig_child); | ||
280 | if (rplog) | ||
281 | iopause(io, 1, &deadline, &now); | ||
282 | else | ||
283 | iopause(0, 0, &deadline, &now); | ||
284 | sig_unblock(sig_child); | ||
285 | |||
286 | if (rplog && (io[0].revents | IOPAUSE_READ)) | ||
287 | while (read(logpipe[0], &ch, 1) > 0) | ||
288 | if (ch) { | ||
289 | for (i = 6; i < rploglen; i++) | ||
290 | rplog[i-1] = rplog[i]; | ||
291 | rplog[rploglen-1] = ch; | ||
292 | } | ||
293 | |||
294 | switch (exitsoon) { | ||
295 | case 1: | ||
296 | _exit(0); | ||
297 | case 2: | ||
298 | for (i = 0; i < svnum; i++) | ||
299 | if (sv[i].pid) | ||
300 | kill(sv[i].pid, SIGTERM); | ||
301 | _exit(111); | ||
302 | } | ||
303 | } | ||
304 | /* not reached */ | ||
305 | return 0; | ||
306 | } | ||
diff --git a/runit/sv.c b/runit/sv.c new file mode 100644 index 000000000..819f31419 --- /dev/null +++ b/runit/sv.c | |||
@@ -0,0 +1,360 @@ | |||
1 | /* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */ | ||
2 | /* TODO: depends on runit_lib.c - review and reduce/eliminate */ | ||
3 | |||
4 | #include <sys/poll.h> | ||
5 | #include <sys/file.h> | ||
6 | #include "busybox.h" | ||
7 | #include "runit_lib.h" | ||
8 | |||
9 | static char *action; | ||
10 | static char *acts; | ||
11 | static char *varservice = "/var/service/"; | ||
12 | static char **service; | ||
13 | static char **servicex; | ||
14 | static unsigned services; | ||
15 | static unsigned rc = 0; | ||
16 | static unsigned verbose = 0; | ||
17 | static unsigned long waitsec = 7; | ||
18 | static unsigned kll = 0; | ||
19 | static struct taia tstart, tnow, tdiff; | ||
20 | static struct tai tstatus; | ||
21 | |||
22 | static int (*act)(char*) = 0; | ||
23 | static int (*cbk)(char*) = 0; | ||
24 | |||
25 | static int curdir, fd, r; | ||
26 | static char svstatus[20]; | ||
27 | |||
28 | #define usage() bb_show_usage() | ||
29 | |||
30 | static void fatal_cannot(char *m1) | ||
31 | { | ||
32 | bb_perror_msg("fatal: cannot %s", m1); | ||
33 | _exit(151); | ||
34 | } | ||
35 | |||
36 | static void out(char *p, char *m1) | ||
37 | { | ||
38 | printf("%s%s: %s", p, *service, m1); | ||
39 | if (errno) { | ||
40 | printf(": %s", strerror(errno)); | ||
41 | } | ||
42 | puts(""); /* will also flush the output */ | ||
43 | } | ||
44 | |||
45 | #define FAIL "fail: " | ||
46 | #define WARN "warning: " | ||
47 | #define OK "ok: " | ||
48 | #define RUN "run: " | ||
49 | #define FINISH "finish: " | ||
50 | #define DOWN "down: " | ||
51 | #define TIMEOUT "timeout: " | ||
52 | #define KILL "kill: " | ||
53 | |||
54 | static void fail(char *m1) { ++rc; out(FAIL, m1); } | ||
55 | static void failx(char *m1) { errno = 0; fail(m1); } | ||
56 | static void warn_cannot(char *m1) { ++rc; out("warning: cannot ", m1); } | ||
57 | static void warnx_cannot(char *m1) { errno = 0; warn_cannot(m1); } | ||
58 | static void ok(char *m1) { errno = 0; out(OK, m1); } | ||
59 | |||
60 | static int svstatus_get(void) | ||
61 | { | ||
62 | if ((fd = open_write("supervise/ok")) == -1) { | ||
63 | if (errno == ENODEV) { | ||
64 | *acts == 'x' ? ok("runsv not running") | ||
65 | : failx("runsv not running"); | ||
66 | return 0; | ||
67 | } | ||
68 | warn_cannot("open supervise/ok"); | ||
69 | return -1; | ||
70 | } | ||
71 | close(fd); | ||
72 | if ((fd = open_read("supervise/status")) == -1) { | ||
73 | warn_cannot("open supervise/status"); | ||
74 | return -1; | ||
75 | } | ||
76 | r = read(fd, svstatus, 20); | ||
77 | close(fd); | ||
78 | switch(r) { | ||
79 | case 20: break; | ||
80 | case -1: warn_cannot("read supervise/status"); return -1; | ||
81 | default: warnx_cannot("read supervise/status: bad format"); return -1; | ||
82 | } | ||
83 | return 1; | ||
84 | } | ||
85 | |||
86 | static unsigned svstatus_print(char *m) | ||
87 | { | ||
88 | int pid; | ||
89 | int normallyup = 0; | ||
90 | struct stat s; | ||
91 | |||
92 | if (stat("down", &s) == -1) { | ||
93 | if (errno != ENOENT) { | ||
94 | bb_perror_msg(WARN"cannot stat %s/down", *service); | ||
95 | return 0; | ||
96 | } | ||
97 | normallyup = 1; | ||
98 | } | ||
99 | pid = (unsigned char) svstatus[15]; | ||
100 | pid <<= 8; pid += (unsigned char)svstatus[14]; | ||
101 | pid <<= 8; pid += (unsigned char)svstatus[13]; | ||
102 | pid <<= 8; pid += (unsigned char)svstatus[12]; | ||
103 | tai_unpack(svstatus, &tstatus); | ||
104 | if (pid) { | ||
105 | switch (svstatus[19]) { | ||
106 | case 1: printf(RUN); break; | ||
107 | case 2: printf(FINISH); break; | ||
108 | } | ||
109 | printf("%s: (pid %d) ", m, pid); | ||
110 | } | ||
111 | else { | ||
112 | printf(DOWN"%s: ", m); | ||
113 | } | ||
114 | printf("%lus", (unsigned long)(tnow.sec.x < tstatus.x ? 0 : tnow.sec.x-tstatus.x)); | ||
115 | if (pid && !normallyup) printf(", normally down"); | ||
116 | if (!pid && normallyup) printf(", normally up"); | ||
117 | if (pid && svstatus[16]) printf(", paused"); | ||
118 | if (!pid && (svstatus[17] == 'u')) printf(", want up"); | ||
119 | if (pid && (svstatus[17] == 'd')) printf(", want down"); | ||
120 | if (pid && svstatus[18]) printf(", got TERM"); | ||
121 | return pid ? 1 : 2; | ||
122 | } | ||
123 | |||
124 | static int status(char *unused) | ||
125 | { | ||
126 | r = svstatus_get(); | ||
127 | switch(r) { case -1: case 0: return 0; } | ||
128 | r = svstatus_print(*service); | ||
129 | if (chdir("log") == -1) { | ||
130 | if (errno != ENOENT) { | ||
131 | printf("; log: "WARN"cannot change to log service directory: %s", | ||
132 | strerror(errno)); | ||
133 | } | ||
134 | } else if (svstatus_get()) { | ||
135 | printf("; "); | ||
136 | svstatus_print("log"); | ||
137 | } | ||
138 | puts(""); /* will also flush the output */ | ||
139 | return r; | ||
140 | } | ||
141 | |||
142 | static int checkscript(void) | ||
143 | { | ||
144 | char *prog[2]; | ||
145 | struct stat s; | ||
146 | int pid, w; | ||
147 | |||
148 | if (stat("check", &s) == -1) { | ||
149 | if (errno == ENOENT) return 1; | ||
150 | bb_perror_msg(WARN"cannot stat %s/check", *service); | ||
151 | return 0; | ||
152 | } | ||
153 | /* if (!(s.st_mode & S_IXUSR)) return 1; */ | ||
154 | if ((pid = fork()) == -1) { | ||
155 | bb_perror_msg(WARN"cannot fork for %s/check", *service); | ||
156 | return 0; | ||
157 | } | ||
158 | if (!pid) { | ||
159 | prog[0] = "./check"; | ||
160 | prog[1] = 0; | ||
161 | close(1); | ||
162 | execve("check", prog, environ); | ||
163 | bb_perror_msg(WARN"cannot run %s/check", *service); | ||
164 | _exit(0); | ||
165 | } | ||
166 | while (wait_pid(&w, pid) == -1) { | ||
167 | if (errno == EINTR) continue; | ||
168 | bb_perror_msg(WARN"cannot wait for child %s/check", *service); | ||
169 | return 0; | ||
170 | } | ||
171 | return !wait_exitcode(w); | ||
172 | } | ||
173 | |||
174 | static int check(char *a) | ||
175 | { | ||
176 | unsigned pid; | ||
177 | |||
178 | if ((r = svstatus_get()) == -1) return -1; | ||
179 | if (r == 0) { if (*a == 'x') return 1; return -1; } | ||
180 | pid = (unsigned char)svstatus[15]; | ||
181 | pid <<= 8; pid += (unsigned char)svstatus[14]; | ||
182 | pid <<= 8; pid += (unsigned char)svstatus[13]; | ||
183 | pid <<= 8; pid += (unsigned char)svstatus[12]; | ||
184 | switch (*a) { | ||
185 | case 'x': return 0; | ||
186 | case 'u': | ||
187 | if (!pid || svstatus[19] != 1) return 0; | ||
188 | if (!checkscript()) return 0; | ||
189 | break; | ||
190 | case 'd': if (pid) return 0; break; | ||
191 | case 'c': if (pid) if (!checkscript()) return 0; break; | ||
192 | case 't': | ||
193 | if (!pid && svstatus[17] == 'd') break; | ||
194 | tai_unpack(svstatus, &tstatus); | ||
195 | if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript()) | ||
196 | return 0; | ||
197 | break; | ||
198 | case 'o': | ||
199 | tai_unpack(svstatus, &tstatus); | ||
200 | if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd')) | ||
201 | return 0; | ||
202 | } | ||
203 | printf(OK); svstatus_print(*service); puts(""); /* will also flush the output */ | ||
204 | return 1; | ||
205 | } | ||
206 | |||
207 | static int control(char *a) | ||
208 | { | ||
209 | if (svstatus_get() <= 0) return -1; | ||
210 | if (svstatus[17] == *a) return 0; | ||
211 | if ((fd = open_write("supervise/control")) == -1) { | ||
212 | if (errno != ENODEV) | ||
213 | warn_cannot("open supervise/control"); | ||
214 | else | ||
215 | *a == 'x' ? ok("runsv not running") : failx("runsv not running"); | ||
216 | return -1; | ||
217 | } | ||
218 | r = write(fd, a, strlen(a)); | ||
219 | close(fd); | ||
220 | if (r != strlen(a)) { | ||
221 | warn_cannot("write to supervise/control"); | ||
222 | return -1; | ||
223 | } | ||
224 | return 1; | ||
225 | } | ||
226 | |||
227 | int sv_main(int argc, char **argv) | ||
228 | { | ||
229 | unsigned opt; | ||
230 | unsigned i, want_exit; | ||
231 | char *x; | ||
232 | |||
233 | for (i = strlen(*argv); i; --i) | ||
234 | if ((*argv)[i-1] == '/') | ||
235 | break; | ||
236 | *argv += i; | ||
237 | service = argv; | ||
238 | services = 1; | ||
239 | if ((x = getenv("SVDIR"))) varservice = x; | ||
240 | if ((x = getenv("SVWAIT"))) waitsec = xatoul(x); | ||
241 | /* TODO: V can be handled internally by getopt_ulflags */ | ||
242 | opt = getopt32(argc, argv, "w:vV", &x); | ||
243 | if (opt & 1) waitsec = xatoul(x); | ||
244 | if (opt & 2) verbose = 1; | ||
245 | if (opt & 4) usage(); | ||
246 | if (!(action = *argv++)) usage(); | ||
247 | --argc; | ||
248 | service = argv; services = argc; | ||
249 | if (!*service) usage(); | ||
250 | |||
251 | taia_now(&tnow); tstart = tnow; | ||
252 | if ((curdir = open_read(".")) == -1) | ||
253 | fatal_cannot("open current directory"); | ||
254 | |||
255 | act = &control; acts = "s"; | ||
256 | if (verbose) cbk = ✓ | ||
257 | switch (*action) { | ||
258 | case 'x': case 'e': | ||
259 | acts = "x"; break; | ||
260 | case 'X': case 'E': | ||
261 | acts = "x"; kll = 1; cbk = ✓ break; | ||
262 | case 'D': | ||
263 | acts = "d"; kll = 1; cbk = ✓ break; | ||
264 | case 'T': | ||
265 | acts = "tc"; kll = 1; cbk = ✓ break; | ||
266 | case 'c': | ||
267 | if (!str_diff(action, "check")) { | ||
268 | act = 0; | ||
269 | acts = "c"; | ||
270 | cbk = ✓ | ||
271 | break; | ||
272 | } | ||
273 | case 'u': case 'd': case 'o': case 't': case 'p': case 'h': | ||
274 | case 'a': case 'i': case 'k': case 'q': case '1': case '2': | ||
275 | action[1] = 0; acts = action; break; | ||
276 | case 's': | ||
277 | if (!str_diff(action, "shutdown")) { | ||
278 | acts = "x"; | ||
279 | cbk = ✓ | ||
280 | break; | ||
281 | } | ||
282 | if (!str_diff(action, "start")) { | ||
283 | acts = "u"; | ||
284 | cbk = ✓ | ||
285 | break; | ||
286 | } | ||
287 | if (!str_diff(action, "stop")) { | ||
288 | acts = "d"; | ||
289 | cbk = ✓ | ||
290 | break; | ||
291 | } | ||
292 | act = &status; cbk = 0; break; | ||
293 | case 'r': | ||
294 | if (!str_diff(action, "restart")) { | ||
295 | acts = "tcu"; | ||
296 | cbk = ✓ | ||
297 | break; | ||
298 | } | ||
299 | usage(); | ||
300 | case 'f': | ||
301 | if (!str_diff(action, "force-reload")) | ||
302 | { acts = "tc"; kll = 1; cbk = ✓ break; } | ||
303 | if (!str_diff(action, "force-restart")) | ||
304 | { acts = "tcu"; kll = 1; cbk = ✓ break; } | ||
305 | if (!str_diff(action, "force-shutdown")) | ||
306 | { acts = "x"; kll = 1; cbk = ✓ break; } | ||
307 | if (!str_diff(action, "force-stop")) | ||
308 | { acts = "d"; kll = 1; cbk = ✓ break; } | ||
309 | default: | ||
310 | usage(); | ||
311 | } | ||
312 | |||
313 | servicex = service; | ||
314 | for (i = 0; i < services; ++i) { | ||
315 | if ((**service != '/') && (**service != '.')) { | ||
316 | if ((chdir(varservice) == -1) || (chdir(*service) == -1)) { | ||
317 | fail("cannot change to service directory"); | ||
318 | *service = 0; | ||
319 | } | ||
320 | } else if (chdir(*service) == -1) { | ||
321 | fail("cannot change to service directory"); | ||
322 | *service = 0; | ||
323 | } | ||
324 | if (*service) if (act && (act(acts) == -1)) *service = 0; | ||
325 | if (fchdir(curdir) == -1) fatal_cannot("change to original directory"); | ||
326 | service++; | ||
327 | } | ||
328 | |||
329 | if (*cbk) | ||
330 | for (;;) { | ||
331 | taia_sub(&tdiff, &tnow, &tstart); | ||
332 | service = servicex; want_exit = 1; | ||
333 | for (i = 0; i < services; ++i, ++service) { | ||
334 | if (!*service) continue; | ||
335 | if ((**service != '/') && (**service != '.')) { | ||
336 | if ((chdir(varservice) == -1) || (chdir(*service) == -1)) { | ||
337 | fail("cannot change to service directory"); | ||
338 | *service = 0; | ||
339 | } | ||
340 | } else if (chdir(*service) == -1) { | ||
341 | fail("cannot change to service directory"); | ||
342 | *service = 0; | ||
343 | } | ||
344 | if (*service) { if (cbk(acts) != 0) *service = 0; else want_exit = 0; } | ||
345 | if (*service && taia_approx(&tdiff) > waitsec) { | ||
346 | kll ? printf(KILL) : printf(TIMEOUT); | ||
347 | if (svstatus_get() > 0) { svstatus_print(*service); ++rc; } | ||
348 | puts(""); /* will also flush the output */ | ||
349 | if (kll) control("k"); | ||
350 | *service = 0; | ||
351 | } | ||
352 | if (fchdir(curdir) == -1) | ||
353 | fatal_cannot("change to original directory"); | ||
354 | } | ||
355 | if (want_exit) break; | ||
356 | usleep(420000); | ||
357 | taia_now(&tnow); | ||
358 | } | ||
359 | return rc > 99 ? 99 : rc; | ||
360 | } | ||