1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172#include <sys/file.h>
173#include "libbb.h"
174#include "runit_lib.h"
175
176struct globals {
177 const char *acts;
178 char **service;
179 unsigned rc;
180
181 uint64_t tstart, tnow;
182 svstatus_t svstatus;
183} FIX_ALIASING;
184#define G (*(struct globals*)&bb_common_bufsiz1)
185#define acts (G.acts )
186#define service (G.service )
187#define rc (G.rc )
188#define tstart (G.tstart )
189#define tnow (G.tnow )
190#define svstatus (G.svstatus )
191#define INIT_G() do { } while (0)
192
193
194#define str_equal(s,t) (!strcmp((s), (t)))
195
196
197static void fatal_cannot(const char *m1) NORETURN;
198static void fatal_cannot(const char *m1)
199{
200 bb_perror_msg("fatal: can't %s", m1);
201 _exit(151);
202}
203
204static void out(const char *p, const char *m1)
205{
206 printf("%s%s: %s", p, *service, m1);
207 if (errno) {
208 printf(": %s", strerror(errno));
209 }
210 bb_putchar('\n');
211}
212
213#define WARN "warning: "
214#define OK "ok: "
215
216static void fail(const char *m1)
217{
218 ++rc;
219 out("fail: ", m1);
220}
221static void failx(const char *m1)
222{
223 errno = 0;
224 fail(m1);
225}
226static void warn(const char *m1)
227{
228 ++rc;
229
230 out("warning: ", m1);
231}
232static void ok(const char *m1)
233{
234 errno = 0;
235 out(OK, m1);
236}
237
238static int svstatus_get(void)
239{
240 int fd, r;
241
242 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
243 if (fd == -1) {
244 if (errno == ENODEV) {
245 *acts == 'x' ? ok("runsv not running")
246 : failx("runsv not running");
247 return 0;
248 }
249 warn("can't open supervise/ok");
250 return -1;
251 }
252 close(fd);
253 fd = open("supervise/status", O_RDONLY|O_NDELAY);
254 if (fd == -1) {
255 warn("can't open supervise/status");
256 return -1;
257 }
258 r = read(fd, &svstatus, 20);
259 close(fd);
260 switch (r) {
261 case 20:
262 break;
263 case -1:
264 warn("can't read supervise/status");
265 return -1;
266 default:
267 errno = 0;
268 warn("can't read supervise/status: bad format");
269 return -1;
270 }
271 return 1;
272}
273
274static unsigned svstatus_print(const char *m)
275{
276 int diff;
277 int pid;
278 int normallyup = 0;
279 struct stat s;
280 uint64_t timestamp;
281
282 if (stat("down", &s) == -1) {
283 if (errno != ENOENT) {
284 bb_perror_msg(WARN"can't stat %s/down", *service);
285 return 0;
286 }
287 normallyup = 1;
288 }
289 pid = SWAP_LE32(svstatus.pid_le32);
290 timestamp = SWAP_BE64(svstatus.time_be64);
291 if (pid) {
292 switch (svstatus.run_or_finish) {
293 case 1: printf("run: "); break;
294 case 2: printf("finish: "); break;
295 }
296 printf("%s: (pid %d) ", m, pid);
297 } else {
298 printf("down: %s: ", m);
299 }
300 diff = tnow - timestamp;
301 printf("%us", (diff < 0 ? 0 : diff));
302 if (pid) {
303 if (!normallyup) printf(", normally down");
304 if (svstatus.paused) printf(", paused");
305 if (svstatus.want == 'd') printf(", want down");
306 if (svstatus.got_term) printf(", got TERM");
307 } else {
308 if (normallyup) printf(", normally up");
309 if (svstatus.want == 'u') printf(", want up");
310 }
311 return pid ? 1 : 2;
312}
313
314static int status(const char *unused UNUSED_PARAM)
315{
316 int r;
317
318 if (svstatus_get() <= 0)
319 return 0;
320
321 r = svstatus_print(*service);
322 if (chdir("log") == -1) {
323 if (errno != ENOENT) {
324 printf("; log: "WARN"can't change to log service directory: %s",
325 strerror(errno));
326 }
327 } else if (svstatus_get()) {
328 printf("; ");
329 svstatus_print("log");
330 }
331 bb_putchar('\n');
332 return r;
333}
334
335static int checkscript(void)
336{
337 char *prog[2];
338 struct stat s;
339 int pid, w;
340
341 if (stat("check", &s) == -1) {
342 if (errno == ENOENT) return 1;
343 bb_perror_msg(WARN"can't stat %s/check", *service);
344 return 0;
345 }
346
347 prog[0] = (char*)"./check";
348 prog[1] = NULL;
349 pid = spawn(prog);
350 if (pid <= 0) {
351 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
352 return 0;
353 }
354 while (safe_waitpid(pid, &w, 0) == -1) {
355 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
356 return 0;
357 }
358 return WEXITSTATUS(w) == 0;
359}
360
361static int check(const char *a)
362{
363 int r;
364 unsigned pid_le32;
365 uint64_t timestamp;
366
367 r = svstatus_get();
368 if (r == -1)
369 return -1;
370 if (r == 0) {
371 if (*a == 'x')
372 return 1;
373 return -1;
374 }
375 pid_le32 = svstatus.pid_le32;
376 switch (*a) {
377 case 'x':
378 return 0;
379 case 'u':
380 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
381 if (!checkscript()) return 0;
382 break;
383 case 'd':
384 if (pid_le32) return 0;
385 break;
386 case 'c':
387 if (pid_le32 && !checkscript()) return 0;
388 break;
389 case 't':
390 if (!pid_le32 && svstatus.want == 'd') break;
391 timestamp = SWAP_BE64(svstatus.time_be64);
392 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
393 return 0;
394 break;
395 case 'o':
396 timestamp = SWAP_BE64(svstatus.time_be64);
397 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
398 return 0;
399 }
400 printf(OK);
401 svstatus_print(*service);
402 bb_putchar('\n');
403 return 1;
404}
405
406static int control(const char *a)
407{
408 int fd, r, l;
409
410
411
412
413
414
415
416
417
418 fd = open("supervise/control", O_WRONLY|O_NDELAY);
419 if (fd == -1) {
420 if (errno != ENODEV)
421 warn("can't open supervise/control");
422 else
423 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
424 return -1;
425 }
426 l = strlen(a);
427 r = write(fd, a, l);
428 close(fd);
429 if (r != l) {
430 warn("can't write to supervise/control");
431 return -1;
432 }
433 return 1;
434}
435
436int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
437int sv_main(int argc UNUSED_PARAM, char **argv)
438{
439 char *x;
440 char *action;
441 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
442 unsigned waitsec = 7;
443 smallint kll = 0;
444 int verbose = 0;
445 int (*act)(const char*);
446 int (*cbk)(const char*);
447 int curdir;
448
449 INIT_G();
450
451 xfunc_error_retval = 100;
452
453 x = getenv("SVDIR");
454 if (x) varservice = x;
455 x = getenv("SVWAIT");
456 if (x) waitsec = xatou(x);
457
458 opt_complementary = "w+:vv";
459 getopt32(argv, "w:v", &waitsec, &verbose);
460 argv += optind;
461 action = *argv++;
462 if (!action || !*argv) bb_show_usage();
463
464 tnow = time(NULL) + 0x400000000000000aULL;
465 tstart = tnow;
466 curdir = open(".", O_RDONLY|O_NDELAY);
467 if (curdir == -1)
468 fatal_cannot("open current directory");
469
470 act = &control;
471 acts = "s";
472 cbk = ✓
473
474 switch (*action) {
475 case 'x':
476 case 'e':
477 acts = "x";
478 if (!verbose) cbk = NULL;
479 break;
480 case 'X':
481 case 'E':
482 acts = "x";
483 kll = 1;
484 break;
485 case 'D':
486 acts = "d";
487 kll = 1;
488 break;
489 case 'T':
490 acts = "tc";
491 kll = 1;
492 break;
493 case 'c':
494 if (str_equal(action, "check")) {
495 act = NULL;
496 acts = "c";
497 break;
498 }
499 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
500 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
501 action[1] = '\0';
502 acts = action;
503 if (!verbose) cbk = NULL;
504 break;
505 case 's':
506 if (str_equal(action, "shutdown")) {
507 acts = "x";
508 break;
509 }
510 if (str_equal(action, "start")) {
511 acts = "u";
512 break;
513 }
514 if (str_equal(action, "stop")) {
515 acts = "d";
516 break;
517 }
518
519 act = &status;
520 cbk = NULL;
521 break;
522 case 'r':
523 if (str_equal(action, "restart")) {
524 acts = "tcu";
525 break;
526 }
527 bb_show_usage();
528 case 'f':
529 if (str_equal(action, "force-reload")) {
530 acts = "tc";
531 kll = 1;
532 break;
533 }
534 if (str_equal(action, "force-restart")) {
535 acts = "tcu";
536 kll = 1;
537 break;
538 }
539 if (str_equal(action, "force-shutdown")) {
540 acts = "x";
541 kll = 1;
542 break;
543 }
544 if (str_equal(action, "force-stop")) {
545 acts = "d";
546 kll = 1;
547 break;
548 }
549 default:
550 bb_show_usage();
551 }
552
553 service = argv;
554 while ((x = *service) != NULL) {
555 if (x[0] != '/' && x[0] != '.') {
556 if (chdir(varservice) == -1)
557 goto chdir_failed_0;
558 }
559 if (chdir(x) == -1) {
560 chdir_failed_0:
561 fail("can't change to service directory");
562 goto nullify_service_0;
563 }
564 if (act && (act(acts) == -1)) {
565 nullify_service_0:
566 *service = (char*) -1L;
567 }
568 if (fchdir(curdir) == -1)
569 fatal_cannot("change to original directory");
570 service++;
571 }
572
573 if (cbk) while (1) {
574 int want_exit;
575 int diff;
576
577 diff = tnow - tstart;
578 service = argv;
579 want_exit = 1;
580 while ((x = *service) != NULL) {
581 if (x == (char*) -1L)
582 goto next;
583 if (x[0] != '/' && x[0] != '.') {
584 if (chdir(varservice) == -1)
585 goto chdir_failed;
586 }
587 if (chdir(x) == -1) {
588 chdir_failed:
589 fail("can't change to service directory");
590 goto nullify_service;
591 }
592 if (cbk(acts) != 0)
593 goto nullify_service;
594 want_exit = 0;
595 if (diff >= waitsec) {
596 printf(kll ? "kill: " : "timeout: ");
597 if (svstatus_get() > 0) {
598 svstatus_print(x);
599 ++rc;
600 }
601 bb_putchar('\n');
602 if (kll)
603 control("k");
604 nullify_service:
605 *service = (char*) -1L;
606 }
607 if (fchdir(curdir) == -1)
608 fatal_cannot("change to original directory");
609 next:
610 service++;
611 }
612 if (want_exit) break;
613 usleep(420000);
614 tnow = time(NULL) + 0x400000000000000aULL;
615 }
616 return rc > 99 ? 99 : rc;
617}
618