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 static void gettimeofday_ns(struct timespec *ts)
56 clock_gettime(CLOCK_REALTIME, ts);
59 static void gettimeofday_ns(struct timespec *ts)
61 BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
62 BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
64 gettimeofday((void*)ts, NULL);
69 /* Compare possibly overflowing unsigned counters */
70 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
91 struct timespec start;
102 struct fd_pair selfpipe;
103 struct fd_pair logpipe;
107 #define G (*(struct globals*)bb_common_bufsiz1)
108 #define haslog (G.haslog )
109 #define sigterm (G.sigterm )
110 #define pidchanged (G.pidchanged )
111 #define selfpipe (G.selfpipe )
112 #define logpipe (G.logpipe )
115 #define INIT_G() do { \
116 setup_common_bufsiz(); \
120 static void fatal2_cannot(const char *m1, const char *m2)
122 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
123 /* was exiting 111 */
125 static void fatal_cannot(const char *m)
127 fatal2_cannot(m, "");
128 /* was exiting 111 */
130 static void fatal2x_cannot(const char *m1, const char *m2)
132 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
133 /* was exiting 111 */
135 static void warn2_cannot(const char *m1, const char *m2)
137 bb_perror_msg("%s: warning: can't %s%s", dir, m1, m2);
139 static void warn_cannot(const char *m)
144 static void s_child(int sig_no UNUSED_PARAM)
146 write(selfpipe.wr, "", 1);
149 static void s_term(int sig_no UNUSED_PARAM)
152 write(selfpipe.wr, "", 1); /* XXX */
155 static int open_trunc_or_warn(const char *name)
158 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
160 bb_perror_msg("%s: warning: cannot open %s",
165 static void update_status(struct svdir *s)
170 const char *fstatus ="log/supervise/status";
171 const char *fstatusnew ="log/supervise/status.new";
172 const char *f_stat ="log/supervise/stat";
173 const char *fstatnew ="log/supervise/stat.new";
174 const char *fpid ="log/supervise/pid";
175 const char *fpidnew ="log/supervise/pid.new";
188 fd = open_trunc_or_warn(fpidnew);
192 char spid[sizeof(int)*3 + 2];
193 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
194 write(fd, spid, size);
197 if (rename_or_warn(fpidnew, fpid))
203 fd = open_trunc_or_warn(fstatnew);
208 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
212 p = stpcpy(p, "down");
215 p = stpcpy(p, "run");
218 p = stpcpy(p, "finish");
221 if (s->ctrl & C_PAUSE)
222 p = stpcpy(p, ", paused");
223 if (s->ctrl & C_TERM)
224 p = stpcpy(p, ", got TERM");
225 if (s->state != S_DOWN)
226 switch (s->sd_want) {
228 p = stpcpy(p, ", want down");
231 p = stpcpy(p, ", want exit");
235 write(fd, stat_buf, p - stat_buf);
239 rename_or_warn(fstatnew, f_stat);
241 /* supervise compatibility */
242 memset(&status, 0, sizeof(status));
243 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
244 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
245 status.pid_le32 = SWAP_LE32(s->pid);
246 if (s->ctrl & C_PAUSE)
248 if (s->sd_want == W_UP)
252 if (s->ctrl & C_TERM)
254 status.run_or_finish = s->state;
255 fd = open_trunc_or_warn(fstatusnew);
258 sz = write(fd, &status, sizeof(status));
260 if (sz != sizeof(status)) {
261 warn2_cannot("write ", fstatusnew);
265 rename_or_warn(fstatusnew, fstatus);
268 static unsigned custom(struct svdir *s, char c)
277 strcpy(a, "control/?");
278 a[8] = c; /* replace '?' */
279 if (stat(a, &st) == 0) {
280 if (st.st_mode & S_IXUSR) {
283 warn2_cannot("vfork for ", a);
288 if (haslog && dup2(logpipe.wr, 1) == -1)
289 warn2_cannot("setup stdout for ", a);
290 execl(a, a, (char *) NULL);
291 fatal2_cannot("run ", a);
294 if (safe_waitpid(pid, &w, 0) == -1) {
295 warn2_cannot("wait for child ", a);
298 return WEXITSTATUS(w) == 0;
302 warn2_cannot("stat ", a);
307 static void stopservice(struct svdir *s)
309 if (s->pid && !custom(s, 't')) {
310 kill(s->pid, SIGTERM);
314 if (s->sd_want == W_DOWN) {
315 kill(s->pid, SIGCONT);
319 if (s->sd_want == W_EXIT) {
320 kill(s->pid, SIGCONT);
325 static void startservice(struct svdir *s)
329 char exitcode[sizeof(int)*3 + 2];
331 if (s->state == S_FINISH) {
332 /* Two arguments are given to ./finish. The first one is ./run exit code,
333 * or -1 if ./run didnt exit normally. The second one is
334 * the least significant byte of the exit status as determined by waitpid;
335 * for instance it is 0 if ./run exited normally, and the signal number
336 * if ./run was terminated by a signal. If runsv cannot start ./run
337 * for some reason, the exit code is 111 and the status is 0.
341 if (WIFEXITED(s->wstat)) {
342 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
346 //if (WIFSIGNALED(s->wstat)) {
347 arg[2] = utoa(WTERMSIG(s->wstat));
357 stopservice(s); /* should never happen */
358 while ((p = vfork()) == -1) {
359 warn_cannot("vfork, sleeping");
365 /* NB: bug alert! right order is close, then dup2 */
369 xdup2(logpipe.rd, 0);
372 xdup2(logpipe.wr, 1);
375 /* Non-ignored signals revert to SIG_DFL on exec anyway */
380 sig_unblock(SIGCHLD);
381 sig_unblock(SIGTERM);
382 execv(arg[0], (char**) arg);
383 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
386 if (s->state != S_FINISH) {
387 gettimeofday_ns(&s->start);
396 static int ctrl(struct svdir *s, char c)
404 if (s->state == S_RUN)
410 if (s->state == S_DOWN)
419 case 't': /* sig term */
420 if (s->state == S_RUN)
423 case 'k': /* sig kill */
424 if ((s->state == S_RUN) && !custom(s, c))
425 kill(s->pid, SIGKILL);
428 case 'p': /* sig pause */
429 if ((s->state == S_RUN) && !custom(s, c))
430 kill(s->pid, SIGSTOP);
434 case 'c': /* sig cont */
435 if ((s->state == S_RUN) && !custom(s, c))
436 kill(s->pid, SIGCONT);
443 if (s->state == S_DOWN)
446 case 'a': /* sig alarm */
449 case 'h': /* sig hup */
452 case 'i': /* sig int */
455 case 'q': /* sig quit */
458 case '1': /* sig usr1 */
461 case '2': /* sig usr2 */
467 if ((s->state == S_RUN) && !custom(s, c))
472 static void open_control(const char *f, struct svdir *s)
476 if (stat(f, &st) == -1)
477 fatal2_cannot("stat ", f);
478 if (!S_ISFIFO(st.st_mode))
479 bb_error_msg_and_die("%s: fatal: %s exists but is not a fifo", dir, f);
480 s->fdcontrol = xopen(f, O_RDONLY|O_NDELAY);
481 close_on_exec_on(s->fdcontrol);
482 s->fdcontrolwrite = xopen(f, O_WRONLY|O_NDELAY);
483 close_on_exec_on(s->fdcontrolwrite);
487 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
488 int runsv_main(int argc UNUSED_PARAM, char **argv)
497 dir = single_argv(argv);
499 xpiped_pair(selfpipe);
500 close_on_exec_on(selfpipe.rd);
501 close_on_exec_on(selfpipe.wr);
502 ndelay_on(selfpipe.rd);
503 ndelay_on(selfpipe.wr);
506 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
508 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
511 /* bss: svd[0].pid = 0; */
512 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
513 if (C_NOOP) svd[0].ctrl = C_NOOP;
514 if (W_UP) svd[0].sd_want = W_UP;
515 /* bss: svd[0].islog = 0; */
516 /* bss: svd[1].pid = 0; */
517 gettimeofday_ns(&svd[0].start);
518 if (stat("down", &s) != -1)
519 svd[0].sd_want = W_DOWN;
521 if (stat("log", &s) == -1) {
523 warn_cannot("stat ./log");
525 if (!S_ISDIR(s.st_mode)) {
527 warn_cannot("stat log/down: log is not a directory");
530 svd[1].state = S_DOWN;
531 svd[1].ctrl = C_NOOP;
532 svd[1].sd_want = W_UP;
534 gettimeofday_ns(&svd[1].start);
535 if (stat("log/down", &s) != -1)
536 svd[1].sd_want = W_DOWN;
537 xpiped_pair(logpipe);
538 close_on_exec_on(logpipe.rd);
539 close_on_exec_on(logpipe.wr);
543 if (mkdir("supervise", 0700) == -1) {
544 r = readlink("supervise", buf, sizeof(buf));
546 if (r == sizeof(buf))
547 fatal2x_cannot("readlink ./supervise", ": name too long");
551 if ((errno != ENOENT) && (errno != EINVAL))
552 fatal_cannot("readlink ./supervise");
555 svd[0].fdlock = xopen3("log/supervise/lock"+4,
556 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
557 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
558 fatal_cannot("lock supervise/lock");
559 close_on_exec_on(svd[0].fdlock);
561 if (mkdir("log/supervise", 0700) == -1) {
562 r = readlink("log/supervise", buf, 256);
565 fatal2x_cannot("readlink ./log/supervise", ": name too long");
567 fd = xopen(".", O_RDONLY|O_NDELAY);
570 if (fchdir(fd) == -1)
571 fatal_cannot("change back to service directory");
575 if ((errno != ENOENT) && (errno != EINVAL))
576 fatal_cannot("readlink ./log/supervise");
579 svd[1].fdlock = xopen3("log/supervise/lock",
580 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
581 if (flock(svd[1].fdlock, LOCK_EX) == -1)
582 fatal_cannot("lock log/supervise/lock");
583 close_on_exec_on(svd[1].fdlock);
586 open_control("log/supervise/control"+4, &svd[0]);
588 open_control("log/supervise/control", &svd[1]);
590 mkfifo("log/supervise/ok"+4, 0600);
591 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
592 close_on_exec_on(fd);
594 mkfifo("log/supervise/ok", 0600);
595 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
596 close_on_exec_on(fd);
604 if (!svd[1].pid && svd[1].sd_want == W_UP)
605 startservice(&svd[1]);
607 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
608 startservice(&svd[0]);
610 x[0].fd = selfpipe.rd;
611 x[0].events = POLLIN;
612 x[1].fd = svd[0].fdcontrol;
613 x[1].events = POLLIN;
614 /* x[2] is used only if haslog == 1 */
615 x[2].fd = svd[1].fdcontrol;
616 x[2].events = POLLIN;
617 sig_unblock(SIGTERM);
618 sig_unblock(SIGCHLD);
619 poll(x, 2 + haslog, 3600*1000);
623 while (read(selfpipe.rd, &ch, 1) == 1)
630 child = wait_any_nohang(&wstat);
633 if ((child == -1) && (errno != EINTR))
635 if (child == svd[0].pid) {
636 svd[0].wstat = wstat;
639 svd[0].ctrl &= ~C_TERM;
640 if (svd[0].state != S_FINISH) {
641 fd = open("finish", O_RDONLY|O_NDELAY);
644 svd[0].state = S_FINISH;
645 update_status(&svd[0]);
649 svd[0].state = S_DOWN;
650 deadline = svd[0].start.tv_sec + 1;
651 gettimeofday_ns(&svd[0].start);
652 update_status(&svd[0]);
653 if (LESS(svd[0].start.tv_sec, deadline))
657 if (child == svd[1].pid) {
658 svd[0].wstat = wstat;
661 svd[1].state = S_DOWN;
662 svd[1].ctrl &= ~C_TERM;
663 deadline = svd[1].start.tv_sec + 1;
664 gettimeofday_ns(&svd[1].start);
665 update_status(&svd[1]);
666 if (LESS(svd[1].start.tv_sec, deadline))
671 if (read(svd[0].fdcontrol, &ch, 1) == 1)
674 if (read(svd[1].fdcontrol, &ch, 1) == 1)
682 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
685 if (svd[1].sd_want != W_EXIT) {
686 svd[1].sd_want = W_EXIT;
687 /* stopservice(&svd[1]); */
688 update_status(&svd[1]);