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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193#include <sys/file.h>
194#include "libbb.h"
195#include "common_bufsiz.h"
196#include "runit_lib.h"
197
198struct globals {
199 const char *acts;
200 char **service;
201 unsigned rc;
202
203 uint64_t tstart, tnow;
204 svstatus_t svstatus;
205 smallint islog;
206} FIX_ALIASING;
207#define G (*(struct globals*)bb_common_bufsiz1)
208#define acts (G.acts )
209#define service (G.service )
210#define rc (G.rc )
211#define tstart (G.tstart )
212#define tnow (G.tnow )
213#define svstatus (G.svstatus )
214#define islog (G.islog )
215#define INIT_G() do { \
216 setup_common_bufsiz(); \
217 \
218 memset(&G, 0, sizeof(G)); \
219} while (0)
220
221
222#define str_equal(s,t) (strcmp((s), (t)) == 0)
223
224
225#if ENABLE_SV || ENABLE_SVC
226static void fatal_cannot(const char *m1) NORETURN;
227static void fatal_cannot(const char *m1)
228{
229 bb_perror_msg("fatal: can't %s", m1);
230 _exit(151);
231}
232
233static void out(const char *p, const char *m1)
234{
235 printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
236 if (errno) {
237 printf(": "STRERROR_FMT STRERROR_ERRNO);
238 }
239 bb_putchar('\n');
240}
241
242#define WARN "warning: "
243#define OK "ok: "
244
245static void fail(const char *m1)
246{
247 ++rc;
248 out("fail: ", m1);
249}
250static void failx(const char *m1)
251{
252 errno = 0;
253 fail(m1);
254}
255static void warn(const char *m1)
256{
257 ++rc;
258
259 out("warning: ", m1);
260}
261static void ok(const char *m1)
262{
263 errno = 0;
264 out(OK, m1);
265}
266
267static int svstatus_get(void)
268{
269 int fd, r;
270
271 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
272 if (fd == -1) {
273 if (errno == ENODEV) {
274 *acts == 'x' ? ok("runsv not running")
275 : failx("runsv not running");
276 return 0;
277 }
278 warn("can't open supervise/ok");
279 return -1;
280 }
281 close(fd);
282 fd = open("supervise/status", O_RDONLY|O_NDELAY);
283 if (fd == -1) {
284 warn("can't open supervise/status");
285 return -1;
286 }
287 r = read(fd, &svstatus, 20);
288 close(fd);
289 switch (r) {
290 case 20:
291 break;
292 case -1:
293 warn("can't read supervise/status");
294 return -1;
295 default:
296 errno = 0;
297 warn("can't read supervise/status: bad format");
298 return -1;
299 }
300 return 1;
301}
302
303static unsigned svstatus_print(const char *m)
304{
305 int diff;
306 int pid;
307 int normallyup = 0;
308 struct stat s;
309 uint64_t timestamp;
310
311 if (stat("down", &s) == -1) {
312 if (errno != ENOENT) {
313 bb_perror_msg(WARN"can't stat %s/down", *service);
314 return 0;
315 }
316 normallyup = 1;
317 }
318 pid = SWAP_LE32(svstatus.pid_le32);
319 timestamp = SWAP_BE64(svstatus.time_be64);
320 switch (svstatus.run_or_finish) {
321 case 0: printf("down: "); break;
322 case 1: printf("run: "); break;
323 case 2: printf("finish: "); break;
324 }
325 printf("%s: ", m);
326 if (svstatus.run_or_finish)
327 printf("(pid %d) ", pid);
328 diff = tnow - timestamp;
329 printf("%us", (diff < 0 ? 0 : diff));
330 if (pid) {
331 if (!normallyup) printf(", normally down");
332 if (svstatus.paused) printf(", paused");
333 if (svstatus.want == 'd') printf(", want down");
334 if (svstatus.got_term) printf(", got TERM");
335 } else {
336 if (normallyup) printf(", normally up");
337 if (svstatus.want == 'u') printf(", want up");
338 }
339 return pid ? 1 : 2;
340}
341
342static int status(const char *unused UNUSED_PARAM)
343{
344 int r;
345
346 if (svstatus_get() <= 0)
347 return 0;
348
349 r = svstatus_print(*service);
350 islog = 1;
351 if (chdir("log") == -1) {
352 if (errno != ENOENT) {
353 printf("; ");
354 warn("can't change directory");
355 } else
356 bb_putchar('\n');
357 } else {
358 printf("; ");
359 if (svstatus_get()) {
360 r = svstatus_print("log");
361 bb_putchar('\n');
362 }
363 }
364 islog = 0;
365 return r;
366}
367
368static int checkscript(void)
369{
370 char *prog[2];
371 struct stat s;
372 int pid, w;
373
374 if (stat("check", &s) == -1) {
375 if (errno == ENOENT) return 1;
376 bb_perror_msg(WARN"can't stat %s/check", *service);
377 return 0;
378 }
379
380 prog[0] = (char*)"./check";
381 prog[1] = NULL;
382 pid = spawn(prog);
383 if (pid <= 0) {
384 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
385 return 0;
386 }
387 while (safe_waitpid(pid, &w, 0) == -1) {
388 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
389 return 0;
390 }
391 return WEXITSTATUS(w) == 0;
392}
393
394static int check(const char *a)
395{
396 int r;
397 unsigned pid_le32;
398 uint64_t timestamp;
399
400 r = svstatus_get();
401 if (r == -1)
402 return -1;
403 while (*a) {
404 if (r == 0) {
405 if (*a == 'x')
406 return 1;
407 return -1;
408 }
409 pid_le32 = svstatus.pid_le32;
410 switch (*a) {
411 case 'x':
412 return 0;
413 case 'u':
414 if (!pid_le32 || svstatus.run_or_finish != 1)
415 return 0;
416 if (!checkscript())
417 return 0;
418 break;
419 case 'd':
420 if (pid_le32 || svstatus.run_or_finish != 0)
421 return 0;
422 break;
423 case 'C':
424 if (pid_le32 && !checkscript())
425 return 0;
426 break;
427 case 't':
428 case 'k':
429 if (!pid_le32 && svstatus.want == 'd')
430 break;
431 timestamp = SWAP_BE64(svstatus.time_be64);
432 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
433 return 0;
434 break;
435 case 'o':
436 timestamp = SWAP_BE64(svstatus.time_be64);
437 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
438 return 0;
439 break;
440 case 'p':
441 if (pid_le32 && !svstatus.paused)
442 return 0;
443 break;
444 case 'c':
445 if (pid_le32 && svstatus.paused)
446 return 0;
447 break;
448 }
449 ++a;
450 }
451 printf(OK);
452 svstatus_print(*service);
453 bb_putchar('\n');
454 return 1;
455}
456
457static int control(const char *a)
458{
459 int fd, r, l;
460
461 if (svstatus_get() <= 0)
462 return -1;
463 if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
464 return 0;
465 fd = open("supervise/control", O_WRONLY|O_NDELAY);
466 if (fd == -1) {
467 if (errno != ENODEV)
468 warn("can't open supervise/control");
469 else
470 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
471 return -1;
472 }
473 l = strlen(a);
474 r = write(fd, a, l);
475 close(fd);
476 if (r != l) {
477 warn("can't write to supervise/control");
478 return -1;
479 }
480 return 1;
481}
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498static int sv(char **argv)
499{
500 char *x;
501 char *action;
502 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
503 unsigned waitsec = 7;
504 smallint kll = 0;
505 int verbose = 0;
506 int (*act)(const char*);
507 int (*cbk)(const char*);
508 int curdir;
509
510 INIT_G();
511
512 xfunc_error_retval = 100;
513
514 x = getenv("SVDIR");
515 if (x) varservice = x;
516 x = getenv("SVWAIT");
517 if (x) waitsec = xatou(x);
518
519 getopt32(argv, "^" "w:+v" "\0" "vv" ,
520 &waitsec, &verbose
521 );
522 argv += optind;
523 action = *argv++;
524 if (!action || !*argv) bb_show_usage();
525
526 tnow = time(NULL) + 0x400000000000000aULL;
527 tstart = tnow;
528 curdir = open(".", O_RDONLY|O_NDELAY);
529 if (curdir == -1)
530 fatal_cannot("open current directory");
531
532 act = &control;
533 acts = "s";
534 cbk = ✓
535
536 switch (*action) {
537 case 'x':
538 case 'e':
539 acts = "x";
540 if (!verbose) cbk = NULL;
541 break;
542 case 'X':
543 case 'E':
544 acts = "x";
545 kll = 1;
546 break;
547 case 'D':
548 acts = "d";
549 kll = 1;
550 break;
551 case 'T':
552 acts = "tc";
553 kll = 1;
554 break;
555 case 't':
556 if (str_equal(action, "try-restart")) {
557 acts = "tc";
558 break;
559 }
560 case 'c':
561 if (str_equal(action, "check")) {
562 act = NULL;
563 acts = "C";
564 break;
565 }
566 case 'u': case 'd': case 'o': case 'p': case 'h':
567 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
568 action[1] = '\0';
569 acts = action;
570 if (!verbose)
571 cbk = NULL;
572 break;
573 case 's':
574 if (str_equal(action, "shutdown")) {
575 acts = "x";
576 break;
577 }
578 if (str_equal(action, "start")) {
579 acts = "u";
580 break;
581 }
582 if (str_equal(action, "stop")) {
583 acts = "d";
584 break;
585 }
586
587 act = &status;
588 cbk = NULL;
589 break;
590 case 'r':
591 if (str_equal(action, "restart")) {
592 acts = "tcu";
593 break;
594 }
595 if (str_equal(action, "reload")) {
596 acts = "h";
597 break;
598 }
599 bb_show_usage();
600 case 'f':
601 if (str_equal(action, "force-reload")) {
602 acts = "tc";
603 kll = 1;
604 break;
605 }
606 if (str_equal(action, "force-restart")) {
607 acts = "tcu";
608 kll = 1;
609 break;
610 }
611 if (str_equal(action, "force-shutdown")) {
612 acts = "x";
613 kll = 1;
614 break;
615 }
616 if (str_equal(action, "force-stop")) {
617 acts = "d";
618 kll = 1;
619 break;
620 }
621 default:
622 bb_show_usage();
623 }
624
625 service = argv;
626 while ((x = *service) != NULL) {
627 if (x[0] != '/' && x[0] != '.'
628 && !last_char_is(x, '/')
629 ) {
630 if (chdir(varservice) == -1)
631 goto chdir_failed_0;
632 }
633 if (chdir(x) == -1) {
634 chdir_failed_0:
635 fail("can't change to service directory");
636 goto nullify_service_0;
637 }
638 if (act && (act(acts) == -1)) {
639 nullify_service_0:
640 *service = (char*) -1L;
641 }
642 if (fchdir(curdir) == -1)
643 fatal_cannot("change to original directory");
644 service++;
645 }
646
647 if (cbk) while (1) {
648 int want_exit;
649 int diff;
650
651 diff = tnow - tstart;
652 service = argv;
653 want_exit = 1;
654 while ((x = *service) != NULL) {
655 if (x == (char*) -1L)
656 goto next;
657 if (x[0] != '/' && x[0] != '.') {
658 if (chdir(varservice) == -1)
659 goto chdir_failed;
660 }
661 if (chdir(x) == -1) {
662 chdir_failed:
663 fail("can't change to service directory");
664 goto nullify_service;
665 }
666 if (cbk(acts) != 0)
667 goto nullify_service;
668 want_exit = 0;
669 if (diff >= waitsec) {
670 printf(kll ? "kill: " : "timeout: ");
671 if (svstatus_get() > 0) {
672 svstatus_print(x);
673 ++rc;
674 }
675 bb_putchar('\n');
676 if (kll)
677 control("k");
678 nullify_service:
679 *service = (char*) -1L;
680 }
681 if (fchdir(curdir) == -1)
682 fatal_cannot("change to original directory");
683 next:
684 service++;
685 }
686 if (want_exit) break;
687 usleep(420000);
688 tnow = time(NULL) + 0x400000000000000aULL;
689 }
690 return rc > 99 ? 99 : rc;
691}
692#endif
693
694#if ENABLE_SV
695int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
696int sv_main(int argc UNUSED_PARAM, char **argv)
697{
698 return sv(argv);
699}
700#endif
701
702
703
704
705
706
707
708
709
710
711
712#if ENABLE_SVC
713int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
714int svc_main(int argc UNUSED_PARAM, char **argv)
715{
716 char command[2];
717 const char *optstring;
718 unsigned opts;
719
720 optstring = "udopchaitkx";
721 opts = getopt32(argv, optstring);
722 argv += optind;
723 if (!argv[0] || !opts)
724 bb_show_usage();
725
726 argv -= 2;
727 if (optind > 2) {
728 argv--;
729 argv[2] = (char*)"--";
730 }
731 argv[0] = (char*)"sv";
732 argv[1] = command;
733 command[1] = '\0';
734
735 do {
736 if (opts & 1) {
737 int r;
738
739 command[0] = *optstring;
740
741
742
743
744 GETOPT_RESET();
745 r = sv(argv);
746 if (r)
747 return 1;
748 }
749 optstring++;
750 opts >>= 1;
751 } while (opts);
752
753 return 0;
754}
755#endif
756
757
758
759
760
761
762
763#if ENABLE_SVOK
764int svok_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
765int svok_main(int argc UNUSED_PARAM, char **argv)
766{
767 const char *dir = argv[1];
768
769 if (!dir)
770 bb_show_usage();
771
772 xfunc_error_retval = 111;
773
774
775
776
777
778 if (dir[0] != '/' && dir[0] != '.'
779 && !last_char_is(dir, '/')
780 ) {
781 xchdir(CONFIG_SV_DEFAULT_SERVICE_DIR);
782 }
783
784 xchdir(dir);
785 if (open("supervise/ok", O_WRONLY) < 0) {
786 if (errno == ENOENT || errno == ENXIO)
787 return 100;
788 bb_perror_msg_and_die("can't open '%s'", "supervise/ok");
789 }
790
791 return 0;
792}
793#endif
794