2 Copyright (c) 2001-2006, Gerrit Pape
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
31 //usage:#define runsv_trivial_usage
33 //usage:#define runsv_full_usage "\n\n"
34 //usage: "Start and monitor a service and optionally an appendant log service"
38 #include "runit_lib.h"
40 #if ENABLE_MONOTONIC_SYSCALL
41 #include <sys/syscall.h>
43 /* libc has incredibly messy way of doing this,
44 * typically requiring -lrt. We just skip all this mess */
45 static void gettimeofday_ns(struct timespec *ts)
47 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
50 static void gettimeofday_ns(struct timespec *ts)
52 if (sizeof(struct timeval) == sizeof(struct timespec)
53 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
56 gettimeofday((void*)ts, NULL);
59 extern void BUG_need_to_implement_gettimeofday_ns(void);
60 BUG_need_to_implement_gettimeofday_ns();
65 /* Compare possibly overflowing unsigned counters */
66 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
87 struct timespec start;
98 struct fd_pair selfpipe;
99 struct fd_pair logpipe;
103 #define G (*(struct globals*)&bb_common_bufsiz1)
104 #define haslog (G.haslog )
105 #define sigterm (G.sigterm )
106 #define pidchanged (G.pidchanged )
107 #define selfpipe (G.selfpipe )
108 #define logpipe (G.logpipe )
111 #define INIT_G() do { \
115 static void fatal2_cannot(const char *m1, const char *m2)
117 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
118 /* was exiting 111 */
120 static void fatal_cannot(const char *m)
122 fatal2_cannot(m, "");
123 /* was exiting 111 */
125 static void fatal2x_cannot(const char *m1, const char *m2)
127 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
128 /* was exiting 111 */
130 static void warn_cannot(const char *m)
132 bb_perror_msg("%s: warning: cannot %s", dir, m);
135 static void s_child(int sig_no UNUSED_PARAM)
137 write(selfpipe.wr, "", 1);
140 static void s_term(int sig_no UNUSED_PARAM)
143 write(selfpipe.wr, "", 1); /* XXX */
146 static int open_trunc_or_warn(const char *name)
149 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
151 bb_perror_msg("%s: warning: cannot open %s",
156 static void update_status(struct svdir *s)
164 fd = open_trunc_or_warn("supervise/pid.new");
168 char spid[sizeof(int)*3 + 2];
169 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
170 write(fd, spid, size);
173 if (rename_or_warn("supervise/pid.new",
174 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
180 fd = open_trunc_or_warn("supervise/stat.new");
185 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
189 p = stpcpy(p, "down");
192 p = stpcpy(p, "run");
195 p = stpcpy(p, "finish");
198 if (s->ctrl & C_PAUSE)
199 p = stpcpy(p, ", paused");
200 if (s->ctrl & C_TERM)
201 p = stpcpy(p, ", got TERM");
202 if (s->state != S_DOWN)
203 switch (s->sd_want) {
205 p = stpcpy(p, ", want down");
208 p = stpcpy(p, ", want exit");
212 write(fd, stat_buf, p - stat_buf);
216 rename_or_warn("supervise/stat.new",
217 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
219 /* supervise compatibility */
220 memset(&status, 0, sizeof(status));
221 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
222 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
223 status.pid_le32 = SWAP_LE32(s->pid);
224 if (s->ctrl & C_PAUSE)
226 if (s->sd_want == W_UP)
230 if (s->ctrl & C_TERM)
232 status.run_or_finish = s->state;
233 fd = open_trunc_or_warn("supervise/status.new");
236 sz = write(fd, &status, sizeof(status));
238 if (sz != sizeof(status)) {
239 warn_cannot("write supervise/status.new");
240 unlink("supervise/status.new");
243 rename_or_warn("supervise/status.new",
244 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
247 static unsigned custom(struct svdir *s, char c)
256 strcpy(a, "control/?");
257 a[8] = c; /* replace '?' */
258 if (stat(a, &st) == 0) {
259 if (st.st_mode & S_IXUSR) {
262 warn_cannot("vfork for control/?");
267 if (haslog && dup2(logpipe.wr, 1) == -1)
268 warn_cannot("setup stdout for control/?");
269 execl(a, a, (char *) NULL);
270 fatal_cannot("run control/?");
273 if (safe_waitpid(pid, &w, 0) == -1) {
274 warn_cannot("wait for child control/?");
277 return WEXITSTATUS(w) == 0;
281 warn_cannot("stat control/?");
286 static void stopservice(struct svdir *s)
288 if (s->pid && !custom(s, 't')) {
289 kill(s->pid, SIGTERM);
293 if (s->sd_want == W_DOWN) {
294 kill(s->pid, SIGCONT);
298 if (s->sd_want == W_EXIT) {
299 kill(s->pid, SIGCONT);
304 static void startservice(struct svdir *s)
308 char exitcode[sizeof(int)*3 + 2];
310 if (s->state == S_FINISH) {
311 /* Two arguments are given to ./finish. The first one is ./run exit code,
312 * or -1 if ./run didnt exit normally. The second one is
313 * the least significant byte of the exit status as determined by waitpid;
314 * for instance it is 0 if ./run exited normally, and the signal number
315 * if ./run was terminated by a signal. If runsv cannot start ./run
316 * for some reason, the exit code is 111 and the status is 0.
320 if (WIFEXITED(s->wstat)) {
321 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
325 //if (WIFSIGNALED(s->wstat)) {
326 arg[2] = utoa(WTERMSIG(s->wstat));
336 stopservice(s); /* should never happen */
337 while ((p = vfork()) == -1) {
338 warn_cannot("vfork, sleeping");
344 /* NB: bug alert! right order is close, then dup2 */
348 xdup2(logpipe.rd, 0);
351 xdup2(logpipe.wr, 1);
354 /* Non-ignored signals revert to SIG_DFL on exec anyway */
359 sig_unblock(SIGCHLD);
360 sig_unblock(SIGTERM);
361 execv(arg[0], (char**) arg);
362 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
365 if (s->state != S_FINISH) {
366 gettimeofday_ns(&s->start);
375 static int ctrl(struct svdir *s, char c)
383 if (s->pid && s->state != S_FINISH)
398 case 't': /* sig term */
399 if (s->pid && s->state != S_FINISH)
402 case 'k': /* sig kill */
403 if (s->pid && !custom(s, c))
404 kill(s->pid, SIGKILL);
407 case 'p': /* sig pause */
408 if (s->pid && !custom(s, c))
409 kill(s->pid, SIGSTOP);
413 case 'c': /* sig cont */
414 if (s->pid && !custom(s, c))
415 kill(s->pid, SIGCONT);
425 case 'a': /* sig alarm */
428 case 'h': /* sig hup */
431 case 'i': /* sig int */
434 case 'q': /* sig quit */
437 case '1': /* sig usr1 */
440 case '2': /* sig usr2 */
446 if (s->pid && !custom(s, c))
451 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
452 int runsv_main(int argc UNUSED_PARAM, char **argv)
461 dir = single_argv(argv);
463 xpiped_pair(selfpipe);
464 close_on_exec_on(selfpipe.rd);
465 close_on_exec_on(selfpipe.wr);
466 ndelay_on(selfpipe.rd);
467 ndelay_on(selfpipe.wr);
470 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
472 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
475 /* bss: svd[0].pid = 0; */
476 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
477 if (C_NOOP) svd[0].ctrl = C_NOOP;
478 if (W_UP) svd[0].sd_want = W_UP;
479 /* bss: svd[0].islog = 0; */
480 /* bss: svd[1].pid = 0; */
481 gettimeofday_ns(&svd[0].start);
482 if (stat("down", &s) != -1)
483 svd[0].sd_want = W_DOWN;
485 if (stat("log", &s) == -1) {
487 warn_cannot("stat ./log");
489 if (!S_ISDIR(s.st_mode)) {
491 warn_cannot("stat log/down: log is not a directory");
494 svd[1].state = S_DOWN;
495 svd[1].ctrl = C_NOOP;
496 svd[1].sd_want = W_UP;
498 gettimeofday_ns(&svd[1].start);
499 if (stat("log/down", &s) != -1)
500 svd[1].sd_want = W_DOWN;
501 xpiped_pair(logpipe);
502 close_on_exec_on(logpipe.rd);
503 close_on_exec_on(logpipe.wr);
507 if (mkdir("supervise", 0700) == -1) {
508 r = readlink("supervise", buf, sizeof(buf));
510 if (r == sizeof(buf))
511 fatal2x_cannot("readlink ./supervise", ": name too long");
515 if ((errno != ENOENT) && (errno != EINVAL))
516 fatal_cannot("readlink ./supervise");
519 svd[0].fdlock = xopen3("log/supervise/lock"+4,
520 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
521 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
522 fatal_cannot("lock supervise/lock");
523 close_on_exec_on(svd[0].fdlock);
525 if (mkdir("log/supervise", 0700) == -1) {
526 r = readlink("log/supervise", buf, 256);
529 fatal2x_cannot("readlink ./log/supervise", ": name too long");
531 fd = xopen(".", O_RDONLY|O_NDELAY);
534 if (fchdir(fd) == -1)
535 fatal_cannot("change back to service directory");
539 if ((errno != ENOENT) && (errno != EINVAL))
540 fatal_cannot("readlink ./log/supervise");
543 svd[1].fdlock = xopen3("log/supervise/lock",
544 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
545 if (flock(svd[1].fdlock, LOCK_EX) == -1)
546 fatal_cannot("lock log/supervise/lock");
547 close_on_exec_on(svd[1].fdlock);
550 mkfifo("log/supervise/control"+4, 0600);
551 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
552 close_on_exec_on(svd[0].fdcontrol);
553 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
554 close_on_exec_on(svd[0].fdcontrolwrite);
555 update_status(&svd[0]);
557 mkfifo("log/supervise/control", 0600);
558 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
559 close_on_exec_on(svd[1].fdcontrol);
560 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
561 close_on_exec_on(svd[1].fdcontrolwrite);
562 update_status(&svd[1]);
564 mkfifo("log/supervise/ok"+4, 0600);
565 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
566 close_on_exec_on(fd);
568 mkfifo("log/supervise/ok", 0600);
569 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
570 close_on_exec_on(fd);
578 if (!svd[1].pid && svd[1].sd_want == W_UP)
579 startservice(&svd[1]);
581 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
582 startservice(&svd[0]);
584 x[0].fd = selfpipe.rd;
585 x[0].events = POLLIN;
586 x[1].fd = svd[0].fdcontrol;
587 x[1].events = POLLIN;
588 /* x[2] is used only if haslog == 1 */
589 x[2].fd = svd[1].fdcontrol;
590 x[2].events = POLLIN;
591 sig_unblock(SIGTERM);
592 sig_unblock(SIGCHLD);
593 poll(x, 2 + haslog, 3600*1000);
597 while (read(selfpipe.rd, &ch, 1) == 1)
604 child = wait_any_nohang(&wstat);
607 if ((child == -1) && (errno != EINTR))
609 if (child == svd[0].pid) {
610 svd[0].wstat = wstat;
613 svd[0].ctrl &= ~C_TERM;
614 if (svd[0].state != S_FINISH) {
615 fd = open("finish", O_RDONLY|O_NDELAY);
618 svd[0].state = S_FINISH;
619 update_status(&svd[0]);
623 svd[0].state = S_DOWN;
624 deadline = svd[0].start.tv_sec + 1;
625 gettimeofday_ns(&svd[0].start);
626 update_status(&svd[0]);
627 if (LESS(svd[0].start.tv_sec, deadline))
631 if (child == svd[1].pid) {
632 svd[0].wstat = wstat;
635 svd[1].state = S_DOWN;
636 svd[1].ctrl &= ~C_TERM;
637 deadline = svd[1].start.tv_sec + 1;
638 gettimeofday_ns(&svd[1].start);
639 update_status(&svd[1]);
640 if (LESS(svd[1].start.tv_sec, deadline))
645 if (read(svd[0].fdcontrol, &ch, 1) == 1)
648 if (read(svd[1].fdcontrol, &ch, 1) == 1)
656 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
659 if (svd[1].sd_want != W_EXIT) {
660 svd[1].sd_want = W_EXIT;
661 /* stopservice(&svd[1]); */
662 update_status(&svd[1]);