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"
39 #include "runit_lib.h"
41 #if ENABLE_MONOTONIC_SYSCALL
42 #include <sys/syscall.h>
44 /* libc has incredibly messy way of doing this,
45 * typically requiring -lrt. We just skip all this mess */
46 static void gettimeofday_ns(struct timespec *ts)
48 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
51 static void gettimeofday_ns(struct timespec *ts)
53 if (sizeof(struct timeval) == sizeof(struct timespec)
54 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
57 gettimeofday((void*)ts, NULL);
60 extern void BUG_need_to_implement_gettimeofday_ns(void);
61 BUG_need_to_implement_gettimeofday_ns();
66 /* Compare possibly overflowing unsigned counters */
67 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
88 struct timespec start;
99 struct fd_pair selfpipe;
100 struct fd_pair logpipe;
104 #define G (*(struct globals*)&bb_common_bufsiz1)
105 #define haslog (G.haslog )
106 #define sigterm (G.sigterm )
107 #define pidchanged (G.pidchanged )
108 #define selfpipe (G.selfpipe )
109 #define logpipe (G.logpipe )
112 #define INIT_G() do { \
116 static void fatal2_cannot(const char *m1, const char *m2)
118 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
119 /* was exiting 111 */
121 static void fatal_cannot(const char *m)
123 fatal2_cannot(m, "");
124 /* was exiting 111 */
126 static void fatal2x_cannot(const char *m1, const char *m2)
128 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
129 /* was exiting 111 */
131 static void warn_cannot(const char *m)
133 bb_perror_msg("%s: warning: cannot %s", dir, m);
136 static void s_child(int sig_no UNUSED_PARAM)
138 write(selfpipe.wr, "", 1);
141 static void s_term(int sig_no UNUSED_PARAM)
144 write(selfpipe.wr, "", 1); /* XXX */
147 static int open_trunc_or_warn(const char *name)
150 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
152 bb_perror_msg("%s: warning: cannot open %s",
157 static void update_status(struct svdir *s)
165 fd = open_trunc_or_warn("supervise/pid.new");
169 char spid[sizeof(int)*3 + 2];
170 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
171 write(fd, spid, size);
174 if (rename_or_warn("supervise/pid.new",
175 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
181 fd = open_trunc_or_warn("supervise/stat.new");
186 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
190 p = stpcpy(p, "down");
193 p = stpcpy(p, "run");
196 p = stpcpy(p, "finish");
199 if (s->ctrl & C_PAUSE)
200 p = stpcpy(p, ", paused");
201 if (s->ctrl & C_TERM)
202 p = stpcpy(p, ", got TERM");
203 if (s->state != S_DOWN)
204 switch (s->sd_want) {
206 p = stpcpy(p, ", want down");
209 p = stpcpy(p, ", want exit");
213 write(fd, stat_buf, p - stat_buf);
217 rename_or_warn("supervise/stat.new",
218 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
220 /* supervise compatibility */
221 memset(&status, 0, sizeof(status));
222 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
223 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
224 status.pid_le32 = SWAP_LE32(s->pid);
225 if (s->ctrl & C_PAUSE)
227 if (s->sd_want == W_UP)
231 if (s->ctrl & C_TERM)
233 status.run_or_finish = s->state;
234 fd = open_trunc_or_warn("supervise/status.new");
237 sz = write(fd, &status, sizeof(status));
239 if (sz != sizeof(status)) {
240 warn_cannot("write supervise/status.new");
241 unlink("supervise/status.new");
244 rename_or_warn("supervise/status.new",
245 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
248 static unsigned custom(struct svdir *s, char c)
257 strcpy(a, "control/?");
258 a[8] = c; /* replace '?' */
259 if (stat(a, &st) == 0) {
260 if (st.st_mode & S_IXUSR) {
263 warn_cannot("vfork for control/?");
268 if (haslog && dup2(logpipe.wr, 1) == -1)
269 warn_cannot("setup stdout for control/?");
270 execl(a, a, (char *) NULL);
271 fatal_cannot("run control/?");
274 if (safe_waitpid(pid, &w, 0) == -1) {
275 warn_cannot("wait for child control/?");
278 return WEXITSTATUS(w) == 0;
282 warn_cannot("stat control/?");
287 static void stopservice(struct svdir *s)
289 if (s->pid && !custom(s, 't')) {
290 kill(s->pid, SIGTERM);
294 if (s->sd_want == W_DOWN) {
295 kill(s->pid, SIGCONT);
299 if (s->sd_want == W_EXIT) {
300 kill(s->pid, SIGCONT);
305 static void startservice(struct svdir *s)
309 char exitcode[sizeof(int)*3 + 2];
311 if (s->state == S_FINISH) {
312 /* Two arguments are given to ./finish. The first one is ./run exit code,
313 * or -1 if ./run didnt exit normally. The second one is
314 * the least significant byte of the exit status as determined by waitpid;
315 * for instance it is 0 if ./run exited normally, and the signal number
316 * if ./run was terminated by a signal. If runsv cannot start ./run
317 * for some reason, the exit code is 111 and the status is 0.
321 if (WIFEXITED(s->wstat)) {
322 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
326 //if (WIFSIGNALED(s->wstat)) {
327 arg[2] = utoa(WTERMSIG(s->wstat));
337 stopservice(s); /* should never happen */
338 while ((p = vfork()) == -1) {
339 warn_cannot("vfork, sleeping");
345 /* NB: bug alert! right order is close, then dup2 */
349 xdup2(logpipe.rd, 0);
352 xdup2(logpipe.wr, 1);
355 /* Non-ignored signals revert to SIG_DFL on exec anyway */
360 sig_unblock(SIGCHLD);
361 sig_unblock(SIGTERM);
362 execv(arg[0], (char**) arg);
363 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
366 if (s->state != S_FINISH) {
367 gettimeofday_ns(&s->start);
376 static int ctrl(struct svdir *s, char c)
384 if (s->pid && s->state != S_FINISH)
399 case 't': /* sig term */
400 if (s->pid && s->state != S_FINISH)
403 case 'k': /* sig kill */
404 if (s->pid && !custom(s, c))
405 kill(s->pid, SIGKILL);
408 case 'p': /* sig pause */
409 if (s->pid && !custom(s, c))
410 kill(s->pid, SIGSTOP);
414 case 'c': /* sig cont */
415 if (s->pid && !custom(s, c))
416 kill(s->pid, SIGCONT);
426 case 'a': /* sig alarm */
429 case 'h': /* sig hup */
432 case 'i': /* sig int */
435 case 'q': /* sig quit */
438 case '1': /* sig usr1 */
441 case '2': /* sig usr2 */
447 if (s->pid && !custom(s, c))
452 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
453 int runsv_main(int argc UNUSED_PARAM, char **argv)
462 dir = single_argv(argv);
464 xpiped_pair(selfpipe);
465 close_on_exec_on(selfpipe.rd);
466 close_on_exec_on(selfpipe.wr);
467 ndelay_on(selfpipe.rd);
468 ndelay_on(selfpipe.wr);
471 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
473 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
476 /* bss: svd[0].pid = 0; */
477 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
478 if (C_NOOP) svd[0].ctrl = C_NOOP;
479 if (W_UP) svd[0].sd_want = W_UP;
480 /* bss: svd[0].islog = 0; */
481 /* bss: svd[1].pid = 0; */
482 gettimeofday_ns(&svd[0].start);
483 if (stat("down", &s) != -1)
484 svd[0].sd_want = W_DOWN;
486 if (stat("log", &s) == -1) {
488 warn_cannot("stat ./log");
490 if (!S_ISDIR(s.st_mode)) {
492 warn_cannot("stat log/down: log is not a directory");
495 svd[1].state = S_DOWN;
496 svd[1].ctrl = C_NOOP;
497 svd[1].sd_want = W_UP;
499 gettimeofday_ns(&svd[1].start);
500 if (stat("log/down", &s) != -1)
501 svd[1].sd_want = W_DOWN;
502 xpiped_pair(logpipe);
503 close_on_exec_on(logpipe.rd);
504 close_on_exec_on(logpipe.wr);
508 if (mkdir("supervise", 0700) == -1) {
509 r = readlink("supervise", buf, sizeof(buf));
511 if (r == sizeof(buf))
512 fatal2x_cannot("readlink ./supervise", ": name too long");
516 if ((errno != ENOENT) && (errno != EINVAL))
517 fatal_cannot("readlink ./supervise");
520 svd[0].fdlock = xopen3("log/supervise/lock"+4,
521 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
522 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
523 fatal_cannot("lock supervise/lock");
524 close_on_exec_on(svd[0].fdlock);
526 if (mkdir("log/supervise", 0700) == -1) {
527 r = readlink("log/supervise", buf, 256);
530 fatal2x_cannot("readlink ./log/supervise", ": name too long");
532 fd = xopen(".", O_RDONLY|O_NDELAY);
535 if (fchdir(fd) == -1)
536 fatal_cannot("change back to service directory");
540 if ((errno != ENOENT) && (errno != EINVAL))
541 fatal_cannot("readlink ./log/supervise");
544 svd[1].fdlock = xopen3("log/supervise/lock",
545 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
546 if (flock(svd[1].fdlock, LOCK_EX) == -1)
547 fatal_cannot("lock log/supervise/lock");
548 close_on_exec_on(svd[1].fdlock);
551 mkfifo("log/supervise/control"+4, 0600);
552 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
553 close_on_exec_on(svd[0].fdcontrol);
554 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
555 close_on_exec_on(svd[0].fdcontrolwrite);
556 update_status(&svd[0]);
558 mkfifo("log/supervise/control", 0600);
559 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
560 close_on_exec_on(svd[1].fdcontrol);
561 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
562 close_on_exec_on(svd[1].fdcontrolwrite);
563 update_status(&svd[1]);
565 mkfifo("log/supervise/ok"+4, 0600);
566 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
567 close_on_exec_on(fd);
569 mkfifo("log/supervise/ok", 0600);
570 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
571 close_on_exec_on(fd);
579 if (!svd[1].pid && svd[1].sd_want == W_UP)
580 startservice(&svd[1]);
582 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
583 startservice(&svd[0]);
585 x[0].fd = selfpipe.rd;
586 x[0].events = POLLIN;
587 x[1].fd = svd[0].fdcontrol;
588 x[1].events = POLLIN;
589 /* x[2] is used only if haslog == 1 */
590 x[2].fd = svd[1].fdcontrol;
591 x[2].events = POLLIN;
592 sig_unblock(SIGTERM);
593 sig_unblock(SIGCHLD);
594 poll(x, 2 + haslog, 3600*1000);
598 while (read(selfpipe.rd, &ch, 1) == 1)
605 child = wait_any_nohang(&wstat);
608 if ((child == -1) && (errno != EINTR))
610 if (child == svd[0].pid) {
611 svd[0].wstat = wstat;
614 svd[0].ctrl &= ~C_TERM;
615 if (svd[0].state != S_FINISH) {
616 fd = open("finish", O_RDONLY|O_NDELAY);
619 svd[0].state = S_FINISH;
620 update_status(&svd[0]);
624 svd[0].state = S_DOWN;
625 deadline = svd[0].start.tv_sec + 1;
626 gettimeofday_ns(&svd[0].start);
627 update_status(&svd[0]);
628 if (LESS(svd[0].start.tv_sec, deadline))
632 if (child == svd[1].pid) {
633 svd[0].wstat = wstat;
636 svd[1].state = S_DOWN;
637 svd[1].ctrl &= ~C_TERM;
638 deadline = svd[1].start.tv_sec + 1;
639 gettimeofday_ns(&svd[1].start);
640 update_status(&svd[1]);
641 if (LESS(svd[1].start.tv_sec, deadline))
646 if (read(svd[0].fdcontrol, &ch, 1) == 1)
649 if (read(svd[1].fdcontrol, &ch, 1) == 1)
657 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
660 if (svd[1].sd_want != W_EXIT) {
661 svd[1].sd_want = W_EXIT;
662 /* stopservice(&svd[1]); */
663 update_status(&svd[1]);