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"
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 warn_cannot(const char *m)
139 bb_perror_msg("%s: warning: cannot %s", dir, m);
142 static void s_child(int sig_no UNUSED_PARAM)
144 write(selfpipe.wr, "", 1);
147 static void s_term(int sig_no UNUSED_PARAM)
150 write(selfpipe.wr, "", 1); /* XXX */
153 static int open_trunc_or_warn(const char *name)
156 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
158 bb_perror_msg("%s: warning: cannot open %s",
163 static void update_status(struct svdir *s)
171 fd = open_trunc_or_warn("supervise/pid.new");
175 char spid[sizeof(int)*3 + 2];
176 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
177 write(fd, spid, size);
180 if (rename_or_warn("supervise/pid.new",
181 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
187 fd = open_trunc_or_warn("supervise/stat.new");
192 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
196 p = stpcpy(p, "down");
199 p = stpcpy(p, "run");
202 p = stpcpy(p, "finish");
205 if (s->ctrl & C_PAUSE)
206 p = stpcpy(p, ", paused");
207 if (s->ctrl & C_TERM)
208 p = stpcpy(p, ", got TERM");
209 if (s->state != S_DOWN)
210 switch (s->sd_want) {
212 p = stpcpy(p, ", want down");
215 p = stpcpy(p, ", want exit");
219 write(fd, stat_buf, p - stat_buf);
223 rename_or_warn("supervise/stat.new",
224 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
226 /* supervise compatibility */
227 memset(&status, 0, sizeof(status));
228 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
229 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
230 status.pid_le32 = SWAP_LE32(s->pid);
231 if (s->ctrl & C_PAUSE)
233 if (s->sd_want == W_UP)
237 if (s->ctrl & C_TERM)
239 status.run_or_finish = s->state;
240 fd = open_trunc_or_warn("supervise/status.new");
243 sz = write(fd, &status, sizeof(status));
245 if (sz != sizeof(status)) {
246 warn_cannot("write supervise/status.new");
247 unlink("supervise/status.new");
250 rename_or_warn("supervise/status.new",
251 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
254 static unsigned custom(struct svdir *s, char c)
263 strcpy(a, "control/?");
264 a[8] = c; /* replace '?' */
265 if (stat(a, &st) == 0) {
266 if (st.st_mode & S_IXUSR) {
269 warn_cannot("vfork for control/?");
274 if (haslog && dup2(logpipe.wr, 1) == -1)
275 warn_cannot("setup stdout for control/?");
276 execl(a, a, (char *) NULL);
277 fatal_cannot("run control/?");
280 if (safe_waitpid(pid, &w, 0) == -1) {
281 warn_cannot("wait for child control/?");
284 return WEXITSTATUS(w) == 0;
288 warn_cannot("stat control/?");
293 static void stopservice(struct svdir *s)
295 if (s->pid && !custom(s, 't')) {
296 kill(s->pid, SIGTERM);
300 if (s->sd_want == W_DOWN) {
301 kill(s->pid, SIGCONT);
305 if (s->sd_want == W_EXIT) {
306 kill(s->pid, SIGCONT);
311 static void startservice(struct svdir *s)
315 char exitcode[sizeof(int)*3 + 2];
317 if (s->state == S_FINISH) {
318 /* Two arguments are given to ./finish. The first one is ./run exit code,
319 * or -1 if ./run didnt exit normally. The second one is
320 * the least significant byte of the exit status as determined by waitpid;
321 * for instance it is 0 if ./run exited normally, and the signal number
322 * if ./run was terminated by a signal. If runsv cannot start ./run
323 * for some reason, the exit code is 111 and the status is 0.
327 if (WIFEXITED(s->wstat)) {
328 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
332 //if (WIFSIGNALED(s->wstat)) {
333 arg[2] = utoa(WTERMSIG(s->wstat));
343 stopservice(s); /* should never happen */
344 while ((p = vfork()) == -1) {
345 warn_cannot("vfork, sleeping");
351 /* NB: bug alert! right order is close, then dup2 */
355 xdup2(logpipe.rd, 0);
358 xdup2(logpipe.wr, 1);
361 /* Non-ignored signals revert to SIG_DFL on exec anyway */
366 sig_unblock(SIGCHLD);
367 sig_unblock(SIGTERM);
368 execv(arg[0], (char**) arg);
369 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
372 if (s->state != S_FINISH) {
373 gettimeofday_ns(&s->start);
382 static int ctrl(struct svdir *s, char c)
390 if (s->pid && s->state != S_FINISH)
405 case 't': /* sig term */
406 if (s->pid && s->state != S_FINISH)
409 case 'k': /* sig kill */
410 if (s->pid && !custom(s, c))
411 kill(s->pid, SIGKILL);
414 case 'p': /* sig pause */
415 if (s->pid && !custom(s, c))
416 kill(s->pid, SIGSTOP);
420 case 'c': /* sig cont */
421 if (s->pid && !custom(s, c))
422 kill(s->pid, SIGCONT);
432 case 'a': /* sig alarm */
435 case 'h': /* sig hup */
438 case 'i': /* sig int */
441 case 'q': /* sig quit */
444 case '1': /* sig usr1 */
447 case '2': /* sig usr2 */
453 if (s->pid && !custom(s, c))
458 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
459 int runsv_main(int argc UNUSED_PARAM, char **argv)
468 dir = single_argv(argv);
470 xpiped_pair(selfpipe);
471 close_on_exec_on(selfpipe.rd);
472 close_on_exec_on(selfpipe.wr);
473 ndelay_on(selfpipe.rd);
474 ndelay_on(selfpipe.wr);
477 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
479 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
482 /* bss: svd[0].pid = 0; */
483 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
484 if (C_NOOP) svd[0].ctrl = C_NOOP;
485 if (W_UP) svd[0].sd_want = W_UP;
486 /* bss: svd[0].islog = 0; */
487 /* bss: svd[1].pid = 0; */
488 gettimeofday_ns(&svd[0].start);
489 if (stat("down", &s) != -1)
490 svd[0].sd_want = W_DOWN;
492 if (stat("log", &s) == -1) {
494 warn_cannot("stat ./log");
496 if (!S_ISDIR(s.st_mode)) {
498 warn_cannot("stat log/down: log is not a directory");
501 svd[1].state = S_DOWN;
502 svd[1].ctrl = C_NOOP;
503 svd[1].sd_want = W_UP;
505 gettimeofday_ns(&svd[1].start);
506 if (stat("log/down", &s) != -1)
507 svd[1].sd_want = W_DOWN;
508 xpiped_pair(logpipe);
509 close_on_exec_on(logpipe.rd);
510 close_on_exec_on(logpipe.wr);
514 if (mkdir("supervise", 0700) == -1) {
515 r = readlink("supervise", buf, sizeof(buf));
517 if (r == sizeof(buf))
518 fatal2x_cannot("readlink ./supervise", ": name too long");
522 if ((errno != ENOENT) && (errno != EINVAL))
523 fatal_cannot("readlink ./supervise");
526 svd[0].fdlock = xopen3("log/supervise/lock"+4,
527 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
528 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
529 fatal_cannot("lock supervise/lock");
530 close_on_exec_on(svd[0].fdlock);
532 if (mkdir("log/supervise", 0700) == -1) {
533 r = readlink("log/supervise", buf, 256);
536 fatal2x_cannot("readlink ./log/supervise", ": name too long");
538 fd = xopen(".", O_RDONLY|O_NDELAY);
541 if (fchdir(fd) == -1)
542 fatal_cannot("change back to service directory");
546 if ((errno != ENOENT) && (errno != EINVAL))
547 fatal_cannot("readlink ./log/supervise");
550 svd[1].fdlock = xopen3("log/supervise/lock",
551 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
552 if (flock(svd[1].fdlock, LOCK_EX) == -1)
553 fatal_cannot("lock log/supervise/lock");
554 close_on_exec_on(svd[1].fdlock);
557 mkfifo("log/supervise/control"+4, 0600);
558 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
559 close_on_exec_on(svd[0].fdcontrol);
560 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
561 close_on_exec_on(svd[0].fdcontrolwrite);
562 update_status(&svd[0]);
564 mkfifo("log/supervise/control", 0600);
565 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
566 close_on_exec_on(svd[1].fdcontrol);
567 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
568 close_on_exec_on(svd[1].fdcontrolwrite);
569 update_status(&svd[1]);
571 mkfifo("log/supervise/ok"+4, 0600);
572 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
573 close_on_exec_on(fd);
575 mkfifo("log/supervise/ok", 0600);
576 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
577 close_on_exec_on(fd);
585 if (!svd[1].pid && svd[1].sd_want == W_UP)
586 startservice(&svd[1]);
588 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
589 startservice(&svd[0]);
591 x[0].fd = selfpipe.rd;
592 x[0].events = POLLIN;
593 x[1].fd = svd[0].fdcontrol;
594 x[1].events = POLLIN;
595 /* x[2] is used only if haslog == 1 */
596 x[2].fd = svd[1].fdcontrol;
597 x[2].events = POLLIN;
598 sig_unblock(SIGTERM);
599 sig_unblock(SIGCHLD);
600 poll(x, 2 + haslog, 3600*1000);
604 while (read(selfpipe.rd, &ch, 1) == 1)
611 child = wait_any_nohang(&wstat);
614 if ((child == -1) && (errno != EINTR))
616 if (child == svd[0].pid) {
617 svd[0].wstat = wstat;
620 svd[0].ctrl &= ~C_TERM;
621 if (svd[0].state != S_FINISH) {
622 fd = open("finish", O_RDONLY|O_NDELAY);
625 svd[0].state = S_FINISH;
626 update_status(&svd[0]);
630 svd[0].state = S_DOWN;
631 deadline = svd[0].start.tv_sec + 1;
632 gettimeofday_ns(&svd[0].start);
633 update_status(&svd[0]);
634 if (LESS(svd[0].start.tv_sec, deadline))
638 if (child == svd[1].pid) {
639 svd[0].wstat = wstat;
642 svd[1].state = S_DOWN;
643 svd[1].ctrl &= ~C_TERM;
644 deadline = svd[1].start.tv_sec + 1;
645 gettimeofday_ns(&svd[1].start);
646 update_status(&svd[1]);
647 if (LESS(svd[1].start.tv_sec, deadline))
652 if (read(svd[0].fdcontrol, &ch, 1) == 1)
655 if (read(svd[1].fdcontrol, &ch, 1) == 1)
663 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
666 if (svd[1].sd_want != W_EXIT) {
667 svd[1].sd_want = W_EXIT;
668 /* stopservice(&svd[1]); */
669 update_status(&svd[1]);