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> */
31 //config: bool "runsv (7.8 kb)"
34 //config: runsv starts and monitors a service and optionally an appendant log
37 //applet:IF_RUNSV(APPLET(runsv, BB_DIR_USR_BIN, BB_SUID_DROP))
39 //kbuild:lib-$(CONFIG_RUNSV) += runsv.o
41 //usage:#define runsv_trivial_usage
43 //usage:#define runsv_full_usage "\n\n"
44 //usage: "Start and monitor a service and optionally an appendant log service"
48 #include "common_bufsiz.h"
49 #include "runit_lib.h"
51 #if ENABLE_MONOTONIC_SYSCALL
52 #include <sys/syscall.h>
54 /* libc has incredibly messy way of doing this,
55 * typically requiring -lrt. We just skip all this mess */
56 static void gettimeofday_ns(struct timespec *ts)
58 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
61 static void gettimeofday_ns(struct timespec *ts)
63 BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
64 BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
66 gettimeofday((void*)ts, NULL);
71 /* Compare possibly overflowing unsigned counters */
72 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
93 struct timespec start;
104 struct fd_pair selfpipe;
105 struct fd_pair logpipe;
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 )
117 #define INIT_G() do { \
118 setup_common_bufsiz(); \
122 static void fatal2_cannot(const char *m1, const char *m2)
124 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
125 /* was exiting 111 */
127 static void fatal_cannot(const char *m)
129 fatal2_cannot(m, "");
130 /* was exiting 111 */
132 static void fatal2x_cannot(const char *m1, const char *m2)
134 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
135 /* was exiting 111 */
137 static void warn2_cannot(const char *m1, const char *m2)
139 bb_perror_msg("%s: warning: can't %s%s", dir, m1, m2);
141 static void warn_cannot(const char *m)
146 static void s_child(int sig_no UNUSED_PARAM)
148 write(selfpipe.wr, "", 1);
151 static void s_term(int sig_no UNUSED_PARAM)
154 write(selfpipe.wr, "", 1); /* XXX */
157 static int open_trunc_or_warn(const char *name)
160 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
162 bb_perror_msg("%s: warning: cannot open %s",
167 static void update_status(struct svdir *s)
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";
190 fd = open_trunc_or_warn(fpidnew);
194 char spid[sizeof(int)*3 + 2];
195 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
196 write(fd, spid, size);
199 if (rename_or_warn(fpidnew, fpid))
205 fd = open_trunc_or_warn(fstatnew);
210 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
214 p = stpcpy(p, "down");
217 p = stpcpy(p, "run");
220 p = stpcpy(p, "finish");
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) {
230 p = stpcpy(p, ", want down");
233 p = stpcpy(p, ", want exit");
237 write(fd, stat_buf, p - stat_buf);
241 rename_or_warn(fstatnew, f_stat);
243 /* supervise compatibility */
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)
250 if (s->sd_want == W_UP)
254 if (s->ctrl & C_TERM)
256 status.run_or_finish = s->state;
257 fd = open_trunc_or_warn(fstatusnew);
260 sz = write(fd, &status, sizeof(status));
262 if (sz != sizeof(status)) {
263 warn2_cannot("write ", fstatusnew);
267 rename_or_warn(fstatusnew, fstatus);
270 static unsigned custom(struct svdir *s, char c)
279 strcpy(a, "control/?");
280 a[8] = c; /* replace '?' */
281 if (stat(a, &st) == 0) {
282 if (st.st_mode & S_IXUSR) {
285 warn2_cannot("vfork for ", a);
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);
296 if (safe_waitpid(pid, &w, 0) == -1) {
297 warn2_cannot("wait for child ", a);
300 return WEXITSTATUS(w) == 0;
304 warn2_cannot("stat ", a);
309 static void stopservice(struct svdir *s)
311 if (s->pid && !custom(s, 't')) {
312 kill(s->pid, SIGTERM);
316 if (s->sd_want == W_DOWN) {
317 kill(s->pid, SIGCONT);
321 if (s->sd_want == W_EXIT) {
322 kill(s->pid, SIGCONT);
327 static void startservice(struct svdir *s)
331 char exitcode[sizeof(int)*3 + 2];
333 if (s->state == S_FINISH) {
334 /* Two arguments are given to ./finish. The first one is ./run exit code,
335 * or -1 if ./run didnt exit normally. The second one is
336 * the least significant byte of the exit status as determined by waitpid;
337 * for instance it is 0 if ./run exited normally, and the signal number
338 * if ./run was terminated by a signal. If runsv cannot start ./run
339 * for some reason, the exit code is 111 and the status is 0.
343 if (WIFEXITED(s->wstat)) {
344 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
348 //if (WIFSIGNALED(s->wstat)) {
349 arg[2] = utoa(WTERMSIG(s->wstat));
359 stopservice(s); /* should never happen */
360 while ((p = vfork()) == -1) {
361 warn_cannot("vfork, sleeping");
367 /* NB: bug alert! right order is close, then dup2 */
371 xdup2(logpipe.rd, 0);
374 xdup2(logpipe.wr, 1);
377 /* Non-ignored signals revert to SIG_DFL on exec anyway */
382 sig_unblock(SIGCHLD);
383 sig_unblock(SIGTERM);
384 execv(arg[0], (char**) arg);
385 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
388 if (s->state != S_FINISH) {
389 gettimeofday_ns(&s->start);
398 static int ctrl(struct svdir *s, char c)
406 if (s->state == S_RUN)
412 if (s->state == S_DOWN)
421 case 't': /* sig term */
422 if (s->state == S_RUN)
425 case 'k': /* sig kill */
426 if ((s->state == S_RUN) && !custom(s, c))
427 kill(s->pid, SIGKILL);
430 case 'p': /* sig pause */
431 if ((s->state == S_RUN) && !custom(s, c))
432 kill(s->pid, SIGSTOP);
436 case 'c': /* sig cont */
437 if ((s->state == S_RUN) && !custom(s, c))
438 kill(s->pid, SIGCONT);
445 if (s->state == S_DOWN)
448 case 'a': /* sig alarm */
451 case 'h': /* sig hup */
454 case 'i': /* sig int */
457 case 'q': /* sig quit */
460 case '1': /* sig usr1 */
463 case '2': /* sig usr2 */
469 if ((s->state == S_RUN) && !custom(s, c))
474 static void open_control(const char *f, struct svdir *s)
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);
489 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
490 int runsv_main(int argc UNUSED_PARAM, char **argv)
499 dir = single_argv(argv);
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);
508 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
510 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
513 /* bss: svd[0].pid = 0; */
514 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
515 if (C_NOOP) svd[0].ctrl = C_NOOP;
516 if (W_UP) svd[0].sd_want = W_UP;
517 /* bss: svd[0].islog = 0; */
518 /* bss: svd[1].pid = 0; */
519 gettimeofday_ns(&svd[0].start);
520 if (stat("down", &s) != -1)
521 svd[0].sd_want = W_DOWN;
523 if (stat("log", &s) == -1) {
525 warn_cannot("stat ./log");
527 if (!S_ISDIR(s.st_mode)) {
529 warn_cannot("stat log/down: log is not a directory");
532 svd[1].state = S_DOWN;
533 svd[1].ctrl = C_NOOP;
534 svd[1].sd_want = W_UP;
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);
545 if (mkdir("supervise", 0700) == -1) {
546 r = readlink("supervise", buf, sizeof(buf));
548 if (r == sizeof(buf))
549 fatal2x_cannot("readlink ./supervise", ": name too long");
553 if ((errno != ENOENT) && (errno != EINVAL))
554 fatal_cannot("readlink ./supervise");
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);
563 if (mkdir("log/supervise", 0700) == -1) {
564 r = readlink("log/supervise", buf, 256);
567 fatal2x_cannot("readlink ./log/supervise", ": name too long");
569 fd = xopen(".", O_RDONLY|O_NDELAY);
572 if (fchdir(fd) == -1)
573 fatal_cannot("change back to service directory");
577 if ((errno != ENOENT) && (errno != EINVAL))
578 fatal_cannot("readlink ./log/supervise");
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);
588 open_control("log/supervise/control"+4, &svd[0]);
590 open_control("log/supervise/control", &svd[1]);
592 mkfifo("log/supervise/ok"+4, 0600);
593 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
594 close_on_exec_on(fd);
596 mkfifo("log/supervise/ok", 0600);
597 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
598 close_on_exec_on(fd);
606 if (!svd[1].pid && svd[1].sd_want == W_UP)
607 startservice(&svd[1]);
609 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
610 startservice(&svd[0]);
612 x[0].fd = selfpipe.rd;
613 x[0].events = POLLIN;
614 x[1].fd = svd[0].fdcontrol;
615 x[1].events = POLLIN;
616 /* x[2] is used only if haslog == 1 */
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);
625 while (read(selfpipe.rd, &ch, 1) == 1)
632 child = wait_any_nohang(&wstat);
635 if ((child == -1) && (errno != EINTR))
637 if (child == svd[0].pid) {
638 svd[0].wstat = wstat;
641 svd[0].ctrl &= ~C_TERM;
642 if (svd[0].state != S_FINISH) {
643 fd = open("finish", O_RDONLY|O_NDELAY);
646 svd[0].state = S_FINISH;
647 update_status(&svd[0]);
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))
659 if (child == svd[1].pid) {
660 svd[0].wstat = wstat;
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))
673 if (read(svd[0].fdcontrol, &ch, 1) == 1)
676 if (read(svd[1].fdcontrol, &ch, 1) == 1)
684 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
687 if (svd[1].sd_want != W_EXIT) {
688 svd[1].sd_want = W_EXIT;
689 /* stopservice(&svd[1]); */
690 update_status(&svd[1]);