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#include <sys/file.h>
47#include "libbb.h"
48#include "common_bufsiz.h"
49#include "runit_lib.h"
50
51#if ENABLE_MONOTONIC_SYSCALL
52#include <sys/syscall.h>
53
54
55
56static void gettimeofday_ns(struct timespec *ts)
57{
58 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
59}
60#else
61static void gettimeofday_ns(struct timespec *ts)
62{
63 BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
64 BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
65
66 gettimeofday((void*)ts, NULL);
67 ts->tv_nsec *= 1000;
68}
69#endif
70
71
72#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
73
74
75#define S_DOWN 0
76#define S_RUN 1
77#define S_FINISH 2
78
79#define C_NOOP 0
80#define C_TERM 1
81#define C_PAUSE 2
82
83#define W_UP 0
84#define W_DOWN 1
85#define W_EXIT 2
86
87struct svdir {
88 int pid;
89 smallint state;
90 smallint ctrl;
91 smallint sd_want;
92 smallint islog;
93 struct timespec start;
94 int fdlock;
95 int fdcontrol;
96 int fdcontrolwrite;
97 int wstat;
98};
99
100struct globals {
101 smallint haslog;
102 smallint sigterm;
103 smallint pidchanged;
104 struct fd_pair selfpipe;
105 struct fd_pair logpipe;
106 char *dir;
107 struct svdir svd[2];
108} FIX_ALIASING;
109#define G (*(struct globals*)bb_common_bufsiz1)
110#define haslog (G.haslog )
111#define sigterm (G.sigterm )
112#define pidchanged (G.pidchanged )
113#define selfpipe (G.selfpipe )
114#define logpipe (G.logpipe )
115#define dir (G.dir )
116#define svd (G.svd )
117#define INIT_G() do { \
118 setup_common_bufsiz(); \
119 pidchanged = 1; \
120} while (0)
121
122static void fatal2_cannot(const char *m1, const char *m2)
123{
124 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
125
126}
127static void fatal_cannot(const char *m)
128{
129 fatal2_cannot(m, "");
130
131}
132static void fatal2x_cannot(const char *m1, const char *m2)
133{
134 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
135
136}
137static void warn2_cannot(const char *m1, const char *m2)
138{
139 bb_perror_msg("%s: warning: can't %s%s", dir, m1, m2);
140}
141static void warn_cannot(const char *m)
142{
143 warn2_cannot(m, "");
144}
145
146static void s_child(int sig_no UNUSED_PARAM)
147{
148 write(selfpipe.wr, "", 1);
149}
150
151static void s_term(int sig_no UNUSED_PARAM)
152{
153 sigterm = 1;
154 write(selfpipe.wr, "", 1);
155}
156
157static int open_trunc_or_warn(const char *name)
158{
159
160 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
161 if (fd < 0)
162 bb_perror_msg("%s: warning: cannot open %s",
163 dir, name);
164 return fd;
165}
166
167static void update_status(struct svdir *s)
168{
169 ssize_t sz;
170 int fd;
171 svstatus_t status;
172 const char *fstatus ="log/supervise/status";
173 const char *fstatusnew ="log/supervise/status.new";
174 const char *f_stat ="log/supervise/stat";
175 const char *fstatnew ="log/supervise/stat.new";
176 const char *fpid ="log/supervise/pid";
177 const char *fpidnew ="log/supervise/pid.new";
178
179 if (!s->islog) {
180 fstatus += 4;
181 fstatusnew += 4;
182 f_stat += 4;
183 fstatnew += 4;
184 fpid += 4;
185 fpidnew += 4;
186 }
187
188
189 if (pidchanged) {
190 fd = open_trunc_or_warn(fpidnew);
191 if (fd < 0)
192 return;
193 if (s->pid) {
194 char spid[sizeof(int)*3 + 2];
195 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
196 write(fd, spid, size);
197 }
198 close(fd);
199 if (rename_or_warn(fpidnew, fpid))
200 return;
201 pidchanged = 0;
202 }
203
204
205 fd = open_trunc_or_warn(fstatnew);
206 if (fd < -1)
207 return;
208
209 {
210 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
211 char *p = stat_buf;
212 switch (s->state) {
213 case S_DOWN:
214 p = stpcpy(p, "down");
215 break;
216 case S_RUN:
217 p = stpcpy(p, "run");
218 break;
219 case S_FINISH:
220 p = stpcpy(p, "finish");
221 break;
222 }
223 if (s->ctrl & C_PAUSE)
224 p = stpcpy(p, ", paused");
225 if (s->ctrl & C_TERM)
226 p = stpcpy(p, ", got TERM");
227 if (s->state != S_DOWN)
228 switch (s->sd_want) {
229 case W_DOWN:
230 p = stpcpy(p, ", want down");
231 break;
232 case W_EXIT:
233 p = stpcpy(p, ", want exit");
234 break;
235 }
236 *p++ = '\n';
237 write(fd, stat_buf, p - stat_buf);
238 close(fd);
239 }
240
241 rename_or_warn(fstatnew, f_stat);
242
243
244 memset(&status, 0, sizeof(status));
245 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
246 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
247 status.pid_le32 = SWAP_LE32(s->pid);
248 if (s->ctrl & C_PAUSE)
249 status.paused = 1;
250 if (s->sd_want == W_UP)
251 status.want = 'u';
252 else
253 status.want = 'd';
254 if (s->ctrl & C_TERM)
255 status.got_term = 1;
256 status.run_or_finish = s->state;
257 fd = open_trunc_or_warn(fstatusnew);
258 if (fd < 0)
259 return;
260 sz = write(fd, &status, sizeof(status));
261 close(fd);
262 if (sz != sizeof(status)) {
263 warn2_cannot("write ", fstatusnew);
264 unlink(fstatusnew);
265 return;
266 }
267 rename_or_warn(fstatusnew, fstatus);
268}
269
270static unsigned custom(struct svdir *s, char c)
271{
272 pid_t pid;
273 int w;
274 char a[10];
275 struct stat st;
276
277 if (s->islog)
278 return 0;
279 strcpy(a, "control/?");
280 a[8] = c;
281 if (stat(a, &st) == 0) {
282 if (st.st_mode & S_IXUSR) {
283 pid = vfork();
284 if (pid == -1) {
285 warn2_cannot("vfork for ", a);
286 return 0;
287 }
288 if (pid == 0) {
289
290 if (haslog && dup2(logpipe.wr, 1) == -1)
291 warn2_cannot("setup stdout for ", a);
292 execl(a, a, (char *) NULL);
293 fatal2_cannot("run ", a);
294 }
295
296 if (safe_waitpid(pid, &w, 0) == -1) {
297 warn2_cannot("wait for child ", a);
298 return 0;
299 }
300 return WEXITSTATUS(w) == 0;
301 }
302 } else {
303 if (errno != ENOENT)
304 warn2_cannot("stat ", a);
305 }
306 return 0;
307}
308
309static void stopservice(struct svdir *s)
310{
311 if (s->pid && !custom(s, 't')) {
312 kill(s->pid, SIGTERM);
313 s->ctrl |= C_TERM;
314 update_status(s);
315 }
316 if (s->sd_want == W_DOWN) {
317 kill(s->pid, SIGCONT);
318 custom(s, 'd');
319 return;
320 }
321 if (s->sd_want == W_EXIT) {
322 kill(s->pid, SIGCONT);
323 custom(s, 'x');
324 }
325}
326
327static void startservice(struct svdir *s)
328{
329 int p;
330 const char *arg[4];
331 char exitcode[sizeof(int)*3 + 2];
332
333 if (s->state == S_FINISH) {
334
335
336
337
338
339
340
341 arg[0] = "./finish";
342 arg[1] = "-1";
343 if (WIFEXITED(s->wstat)) {
344 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
345 arg[1] = exitcode;
346 }
347
348
349 arg[2] = utoa(WTERMSIG(s->wstat));
350
351 arg[3] = NULL;
352 } else {
353 arg[0] = "./run";
354 arg[1] = NULL;
355 custom(s, 'u');
356 }
357
358 if (s->pid != 0)
359 stopservice(s);
360 while ((p = vfork()) == -1) {
361 warn_cannot("vfork, sleeping");
362 sleep(5);
363 }
364 if (p == 0) {
365
366 if (haslog) {
367
368 if (s->islog) {
369 xchdir("./log");
370 close(logpipe.wr);
371 xdup2(logpipe.rd, 0);
372 } else {
373 close(logpipe.rd);
374 xdup2(logpipe.wr, 1);
375 }
376 }
377
378
379
380
381
382 sig_unblock(SIGCHLD);
383 sig_unblock(SIGTERM);
384 execv(arg[0], (char**) arg);
385 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
386 }
387
388 if (s->state != S_FINISH) {
389 gettimeofday_ns(&s->start);
390 s->state = S_RUN;
391 }
392 s->pid = p;
393 pidchanged = 1;
394 s->ctrl = C_NOOP;
395 update_status(s);
396}
397
398static int ctrl(struct svdir *s, char c)
399{
400 int sig;
401
402 switch (c) {
403 case 'd':
404 s->sd_want = W_DOWN;
405 update_status(s);
406 if (s->state == S_RUN)
407 stopservice(s);
408 break;
409 case 'u':
410 s->sd_want = W_UP;
411 update_status(s);
412 if (s->state == S_DOWN)
413 startservice(s);
414 break;
415 case 'x':
416 if (s->islog)
417 break;
418 s->sd_want = W_EXIT;
419 update_status(s);
420
421 case 't':
422 if (s->state == S_RUN)
423 stopservice(s);
424 break;
425 case 'k':
426 if ((s->state == S_RUN) && !custom(s, c))
427 kill(s->pid, SIGKILL);
428 s->state = S_DOWN;
429 break;
430 case 'p':
431 if ((s->state == S_RUN) && !custom(s, c))
432 kill(s->pid, SIGSTOP);
433 s->ctrl |= C_PAUSE;
434 update_status(s);
435 break;
436 case 'c':
437 if ((s->state == S_RUN) && !custom(s, c))
438 kill(s->pid, SIGCONT);
439 s->ctrl &= ~C_PAUSE;
440 update_status(s);
441 break;
442 case 'o':
443 s->sd_want = W_DOWN;
444 update_status(s);
445 if (s->state == S_DOWN)
446 startservice(s);
447 break;
448 case 'a':
449 sig = SIGALRM;
450 goto sendsig;
451 case 'h':
452 sig = SIGHUP;
453 goto sendsig;
454 case 'i':
455 sig = SIGINT;
456 goto sendsig;
457 case 'q':
458 sig = SIGQUIT;
459 goto sendsig;
460 case '1':
461 sig = SIGUSR1;
462 goto sendsig;
463 case '2':
464 sig = SIGUSR2;
465 goto sendsig;
466 }
467 return 1;
468 sendsig:
469 if ((s->state == S_RUN) && !custom(s, c))
470 kill(s->pid, sig);
471 return 1;
472}
473
474static void open_control(const char *f, struct svdir *s)
475{
476 struct stat st;
477 mkfifo(f, 0600);
478 if (stat(f, &st) == -1)
479 fatal2_cannot("stat ", f);
480 if (!S_ISFIFO(st.st_mode))
481 bb_error_msg_and_die("%s: fatal: %s exists but is not a fifo", dir, f);
482 s->fdcontrol = xopen(f, O_RDONLY|O_NDELAY);
483 close_on_exec_on(s->fdcontrol);
484 s->fdcontrolwrite = xopen(f, O_WRONLY|O_NDELAY);
485 close_on_exec_on(s->fdcontrolwrite);
486 update_status(s);
487}
488
489int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
490int runsv_main(int argc UNUSED_PARAM, char **argv)
491{
492 struct stat s;
493 int fd;
494 int r;
495 char buf[256];
496
497 INIT_G();
498
499 dir = single_argv(argv);
500
501 xpiped_pair(selfpipe);
502 close_on_exec_on(selfpipe.rd);
503 close_on_exec_on(selfpipe.wr);
504 ndelay_on(selfpipe.rd);
505 ndelay_on(selfpipe.wr);
506
507 sig_block(SIGCHLD);
508 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
509 sig_block(SIGTERM);
510 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
511
512 xchdir(dir);
513
514 if (S_DOWN) svd[0].state = S_DOWN;
515 if (C_NOOP) svd[0].ctrl = C_NOOP;
516 if (W_UP) svd[0].sd_want = W_UP;
517
518
519 gettimeofday_ns(&svd[0].start);
520 if (stat("down", &s) != -1)
521 svd[0].sd_want = W_DOWN;
522
523 if (stat("log", &s) == -1) {
524 if (errno != ENOENT)
525 warn_cannot("stat ./log");
526 } else {
527 if (!S_ISDIR(s.st_mode)) {
528 errno = 0;
529 warn_cannot("stat log/down: log is not a directory");
530 } else {
531 haslog = 1;
532 svd[1].state = S_DOWN;
533 svd[1].ctrl = C_NOOP;
534 svd[1].sd_want = W_UP;
535 svd[1].islog = 1;
536 gettimeofday_ns(&svd[1].start);
537 if (stat("log/down", &s) != -1)
538 svd[1].sd_want = W_DOWN;
539 xpiped_pair(logpipe);
540 close_on_exec_on(logpipe.rd);
541 close_on_exec_on(logpipe.wr);
542 }
543 }
544
545 if (mkdir("supervise", 0700) == -1) {
546 r = readlink("supervise", buf, sizeof(buf));
547 if (r != -1) {
548 if (r == sizeof(buf))
549 fatal2x_cannot("readlink ./supervise", ": name too long");
550 buf[r] = 0;
551 mkdir(buf, 0700);
552 } else {
553 if ((errno != ENOENT) && (errno != EINVAL))
554 fatal_cannot("readlink ./supervise");
555 }
556 }
557 svd[0].fdlock = xopen3("log/supervise/lock"+4,
558 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
559 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
560 fatal_cannot("lock supervise/lock");
561 close_on_exec_on(svd[0].fdlock);
562 if (haslog) {
563 if (mkdir("log/supervise", 0700) == -1) {
564 r = readlink("log/supervise", buf, 256);
565 if (r != -1) {
566 if (r == 256)
567 fatal2x_cannot("readlink ./log/supervise", ": name too long");
568 buf[r] = 0;
569 fd = xopen(".", O_RDONLY|O_NDELAY);
570 xchdir("./log");
571 mkdir(buf, 0700);
572 if (fchdir(fd) == -1)
573 fatal_cannot("change back to service directory");
574 close(fd);
575 }
576 else {
577 if ((errno != ENOENT) && (errno != EINVAL))
578 fatal_cannot("readlink ./log/supervise");
579 }
580 }
581 svd[1].fdlock = xopen3("log/supervise/lock",
582 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
583 if (flock(svd[1].fdlock, LOCK_EX) == -1)
584 fatal_cannot("lock log/supervise/lock");
585 close_on_exec_on(svd[1].fdlock);
586 }
587
588 open_control("log/supervise/control"+4, &svd[0]);
589 if (haslog) {
590 open_control("log/supervise/control", &svd[1]);
591 }
592 mkfifo("log/supervise/ok"+4, 0600);
593 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
594 close_on_exec_on(fd);
595 if (haslog) {
596 mkfifo("log/supervise/ok", 0600);
597 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
598 close_on_exec_on(fd);
599 }
600 for (;;) {
601 struct pollfd x[3];
602 unsigned deadline;
603 char ch;
604
605 if (haslog)
606 if (!svd[1].pid && svd[1].sd_want == W_UP)
607 startservice(&svd[1]);
608 if (!svd[0].pid)
609 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
610 startservice(&svd[0]);
611
612 x[0].fd = selfpipe.rd;
613 x[0].events = POLLIN;
614 x[1].fd = svd[0].fdcontrol;
615 x[1].events = POLLIN;
616
617 x[2].fd = svd[1].fdcontrol;
618 x[2].events = POLLIN;
619 sig_unblock(SIGTERM);
620 sig_unblock(SIGCHLD);
621 poll(x, 2 + haslog, 3600*1000);
622 sig_block(SIGTERM);
623 sig_block(SIGCHLD);
624
625 while (read(selfpipe.rd, &ch, 1) == 1)
626 continue;
627
628 for (;;) {
629 pid_t child;
630 int wstat;
631
632 child = wait_any_nohang(&wstat);
633 if (!child)
634 break;
635 if ((child == -1) && (errno != EINTR))
636 break;
637 if (child == svd[0].pid) {
638 svd[0].wstat = wstat;
639 svd[0].pid = 0;
640 pidchanged = 1;
641 svd[0].ctrl &= ~C_TERM;
642 if (svd[0].state != S_FINISH) {
643 fd = open("finish", O_RDONLY|O_NDELAY);
644 if (fd != -1) {
645 close(fd);
646 svd[0].state = S_FINISH;
647 update_status(&svd[0]);
648 continue;
649 }
650 }
651 svd[0].state = S_DOWN;
652 deadline = svd[0].start.tv_sec + 1;
653 gettimeofday_ns(&svd[0].start);
654 update_status(&svd[0]);
655 if (LESS(svd[0].start.tv_sec, deadline))
656 sleep(1);
657 }
658 if (haslog) {
659 if (child == svd[1].pid) {
660 svd[0].wstat = wstat;
661 svd[1].pid = 0;
662 pidchanged = 1;
663 svd[1].state = S_DOWN;
664 svd[1].ctrl &= ~C_TERM;
665 deadline = svd[1].start.tv_sec + 1;
666 gettimeofday_ns(&svd[1].start);
667 update_status(&svd[1]);
668 if (LESS(svd[1].start.tv_sec, deadline))
669 sleep(1);
670 }
671 }
672 }
673 if (read(svd[0].fdcontrol, &ch, 1) == 1)
674 ctrl(&svd[0], ch);
675 if (haslog)
676 if (read(svd[1].fdcontrol, &ch, 1) == 1)
677 ctrl(&svd[1], ch);
678
679 if (sigterm) {
680 ctrl(&svd[0], 'x');
681 sigterm = 0;
682 }
683
684 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
685 if (svd[1].pid == 0)
686 _exit(EXIT_SUCCESS);
687 if (svd[1].sd_want != W_EXIT) {
688 svd[1].sd_want = W_EXIT;
689
690 update_status(&svd[1]);
691 close(logpipe.wr);
692 close(logpipe.rd);
693 }
694 }
695 }
696
697 return 0;
698}
699