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#include <sys/poll.h>
157#include <sys/file.h>
158#include "libbb.h"
159#include "runit_lib.h"
160
161struct globals {
162 const char *acts;
163 char **service;
164 unsigned rc;
165
166 uint64_t tstart, tnow;
167 svstatus_t svstatus;
168};
169#define G (*(struct globals*)&bb_common_bufsiz1)
170#define acts (G.acts )
171#define service (G.service )
172#define rc (G.rc )
173#define tstart (G.tstart )
174#define tnow (G.tnow )
175#define svstatus (G.svstatus )
176#define INIT_G() do { } while (0)
177
178
179static void fatal_cannot(const char *m1) NORETURN;
180static void fatal_cannot(const char *m1)
181{
182 bb_perror_msg("fatal: can't %s", m1);
183 _exit(151);
184}
185
186static void out(const char *p, const char *m1)
187{
188 printf("%s%s: %s", p, *service, m1);
189 if (errno) {
190 printf(": %s", strerror(errno));
191 }
192 bb_putchar('\n');
193}
194
195#define WARN "warning: "
196#define OK "ok: "
197
198static void fail(const char *m1)
199{
200 ++rc;
201 out("fail: ", m1);
202}
203static void failx(const char *m1)
204{
205 errno = 0;
206 fail(m1);
207}
208static void warn(const char *m1)
209{
210 ++rc;
211
212 out("warning: ", m1);
213}
214static void ok(const char *m1)
215{
216 errno = 0;
217 out(OK, m1);
218}
219
220static int svstatus_get(void)
221{
222 int fd, r;
223
224 fd = open_write("supervise/ok");
225 if (fd == -1) {
226 if (errno == ENODEV) {
227 *acts == 'x' ? ok("runsv not running")
228 : failx("runsv not running");
229 return 0;
230 }
231 warn("cannot open supervise/ok");
232 return -1;
233 }
234 close(fd);
235 fd = open_read("supervise/status");
236 if (fd == -1) {
237 warn("cannot open supervise/status");
238 return -1;
239 }
240 r = read(fd, &svstatus, 20);
241 close(fd);
242 switch (r) {
243 case 20:
244 break;
245 case -1:
246 warn("cannot read supervise/status");
247 return -1;
248 default:
249 errno = 0;
250 warn("cannot read supervise/status: bad format");
251 return -1;
252 }
253 return 1;
254}
255
256static unsigned svstatus_print(const char *m)
257{
258 int diff;
259 int pid;
260 int normallyup = 0;
261 struct stat s;
262 uint64_t timestamp;
263
264 if (stat("down", &s) == -1) {
265 if (errno != ENOENT) {
266 bb_perror_msg(WARN"cannot stat %s/down", *service);
267 return 0;
268 }
269 normallyup = 1;
270 }
271 pid = SWAP_LE32(svstatus.pid_le32);
272 timestamp = SWAP_BE64(svstatus.time_be64);
273 if (pid) {
274 switch (svstatus.run_or_finish) {
275 case 1: printf("run: "); break;
276 case 2: printf("finish: "); break;
277 }
278 printf("%s: (pid %d) ", m, pid);
279 } else {
280 printf("down: %s: ", m);
281 }
282 diff = tnow - timestamp;
283 printf("%us", (diff < 0 ? 0 : diff));
284 if (pid) {
285 if (!normallyup) printf(", normally down");
286 if (svstatus.paused) printf(", paused");
287 if (svstatus.want == 'd') printf(", want down");
288 if (svstatus.got_term) printf(", got TERM");
289 } else {
290 if (normallyup) printf(", normally up");
291 if (svstatus.want == 'u') printf(", want up");
292 }
293 return pid ? 1 : 2;
294}
295
296static int status(const char *unused UNUSED_PARAM)
297{
298 int r;
299
300 r = svstatus_get();
301 switch (r) { case -1: case 0: return 0; }
302
303 r = svstatus_print(*service);
304 if (chdir("log") == -1) {
305 if (errno != ENOENT) {
306 printf("; log: "WARN"cannot change to log service directory: %s",
307 strerror(errno));
308 }
309 } else if (svstatus_get()) {
310 printf("; ");
311 svstatus_print("log");
312 }
313 bb_putchar('\n');
314 return r;
315}
316
317static int checkscript(void)
318{
319 char *prog[2];
320 struct stat s;
321 int pid, w;
322
323 if (stat("check", &s) == -1) {
324 if (errno == ENOENT) return 1;
325 bb_perror_msg(WARN"cannot stat %s/check", *service);
326 return 0;
327 }
328
329 prog[0] = (char*)"./check";
330 prog[1] = NULL;
331 pid = spawn(prog);
332 if (pid <= 0) {
333 bb_perror_msg(WARN"cannot %s child %s/check", "run", *service);
334 return 0;
335 }
336 while (safe_waitpid(pid, &w, 0) == -1) {
337 bb_perror_msg(WARN"cannot %s child %s/check", "wait for", *service);
338 return 0;
339 }
340 return !wait_exitcode(w);
341}
342
343static int check(const char *a)
344{
345 int r;
346 unsigned pid;
347 uint64_t timestamp;
348
349 r = svstatus_get();
350 if (r == -1)
351 return -1;
352 if (r == 0) {
353 if (*a == 'x')
354 return 1;
355 return -1;
356 }
357 pid = SWAP_LE32(svstatus.pid_le32);
358 switch (*a) {
359 case 'x':
360 return 0;
361 case 'u':
362 if (!pid || svstatus.run_or_finish != 1) return 0;
363 if (!checkscript()) return 0;
364 break;
365 case 'd':
366 if (pid) return 0;
367 break;
368 case 'c':
369 if (pid && !checkscript()) return 0;
370 break;
371 case 't':
372 if (!pid && svstatus.want == 'd') break;
373 timestamp = SWAP_BE64(svstatus.time_be64);
374 if ((tstart > timestamp) || !pid || svstatus.got_term || !checkscript())
375 return 0;
376 break;
377 case 'o':
378 timestamp = SWAP_BE64(svstatus.time_be64);
379 if ((!pid && tstart > timestamp) || (pid && svstatus.want != 'd'))
380 return 0;
381 }
382 printf(OK);
383 svstatus_print(*service);
384 bb_putchar('\n');
385 return 1;
386}
387
388static int control(const char *a)
389{
390 int fd, r;
391
392 if (svstatus_get() <= 0)
393 return -1;
394 if (svstatus.want == *a)
395 return 0;
396 fd = open_write("supervise/control");
397 if (fd == -1) {
398 if (errno != ENODEV)
399 warn("cannot open supervise/control");
400 else
401 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
402 return -1;
403 }
404 r = write(fd, a, strlen(a));
405 close(fd);
406 if (r != strlen(a)) {
407 warn("cannot write to supervise/control");
408 return -1;
409 }
410 return 1;
411}
412
413int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
414int sv_main(int argc, char **argv)
415{
416 unsigned opt;
417 unsigned i, want_exit;
418 char *x;
419 char *action;
420 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
421 unsigned services;
422 char **servicex;
423 unsigned waitsec = 7;
424 smallint kll = 0;
425 int verbose = 0;
426 int (*act)(const char*);
427 int (*cbk)(const char*);
428 int curdir;
429
430 INIT_G();
431
432 xfunc_error_retval = 100;
433
434 x = getenv("SVDIR");
435 if (x) varservice = x;
436 x = getenv("SVWAIT");
437 if (x) waitsec = xatou(x);
438
439 opt_complementary = "w+:vv";
440 opt = getopt32(argv, "w:v", &waitsec, &verbose);
441 argc -= optind;
442 argv += optind;
443 action = *argv++;
444 if (!action || !*argv) bb_show_usage();
445 service = argv;
446 services = argc - 1;
447
448 tnow = time(0) + 0x400000000000000aULL;
449 tstart = tnow;
450 curdir = open_read(".");
451 if (curdir == -1)
452 fatal_cannot("open current directory");
453
454 act = &control;
455 acts = "s";
456 cbk = ✓
457
458 switch (*action) {
459 case 'x':
460 case 'e':
461 acts = "x";
462 if (!verbose) cbk = NULL;
463 break;
464 case 'X':
465 case 'E':
466 acts = "x";
467 kll = 1;
468 break;
469 case 'D':
470 acts = "d";
471 kll = 1;
472 break;
473 case 'T':
474 acts = "tc";
475 kll = 1;
476 break;
477 case 'c':
478 if (str_equal(action, "check")) {
479 act = NULL;
480 acts = "c";
481 break;
482 }
483 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
484 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
485 action[1] = '\0';
486 acts = action;
487 if (!verbose) cbk = NULL;
488 break;
489 case 's':
490 if (str_equal(action, "shutdown")) {
491 acts = "x";
492 break;
493 }
494 if (str_equal(action, "start")) {
495 acts = "u";
496 break;
497 }
498 if (str_equal(action, "stop")) {
499 acts = "d";
500 break;
501 }
502
503 act = &status;
504 cbk = NULL;
505 break;
506 case 'r':
507 if (str_equal(action, "restart")) {
508 acts = "tcu";
509 break;
510 }
511 bb_show_usage();
512 case 'f':
513 if (str_equal(action, "force-reload")) {
514 acts = "tc";
515 kll = 1;
516 break;
517 }
518 if (str_equal(action, "force-restart")) {
519 acts = "tcu";
520 kll = 1;
521 break;
522 }
523 if (str_equal(action, "force-shutdown")) {
524 acts = "x";
525 kll = 1;
526 break;
527 }
528 if (str_equal(action, "force-stop")) {
529 acts = "d";
530 kll = 1;
531 break;
532 }
533 default:
534 bb_show_usage();
535 }
536
537 servicex = service;
538 for (i = 0; i < services; ++i) {
539 if ((**service != '/') && (**service != '.')) {
540 if (chdir(varservice) == -1)
541 goto chdir_failed_0;
542 }
543 if (chdir(*service) == -1) {
544 chdir_failed_0:
545 fail("cannot change to service directory");
546 goto nullify_service_0;
547 }
548 if (act && (act(acts) == -1)) {
549 nullify_service_0:
550 *service = NULL;
551 }
552 if (fchdir(curdir) == -1)
553 fatal_cannot("change to original directory");
554 service++;
555 }
556
557 if (cbk) while (1) {
558 int diff;
559
560 diff = tnow - tstart;
561 service = servicex;
562 want_exit = 1;
563 for (i = 0; i < services; ++i, ++service) {
564 if (!*service)
565 continue;
566 if ((**service != '/') && (**service != '.')) {
567 if (chdir(varservice) == -1)
568 goto chdir_failed;
569 }
570 if (chdir(*service) == -1) {
571 chdir_failed:
572 fail("cannot change to service directory");
573 goto nullify_service;
574 }
575 if (cbk(acts) != 0)
576 goto nullify_service;
577 want_exit = 0;
578 if (diff >= waitsec) {
579 printf(kll ? "kill: " : "timeout: ");
580 if (svstatus_get() > 0) {
581 svstatus_print(*service);
582 ++rc;
583 }
584 bb_putchar('\n');
585 if (kll)
586 control("k");
587 nullify_service:
588 *service = NULL;
589 }
590 if (fchdir(curdir) == -1)
591 fatal_cannot("change to original directory");
592 }
593 if (want_exit) break;
594 usleep(420000);
595 tnow = time(0) + 0x400000000000000aULL;
596 }
597 return rc > 99 ? 99 : rc;
598}
599