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 */
34 #include "runit_lib.h"
36 #if ENABLE_MONOTONIC_SYSCALL
37 #include <sys/syscall.h>
39 /* libc has incredibly messy way of doing this,
40 * typically requiring -lrt. We just skip all this mess */
41 static void gettimeofday_ns(struct timespec *ts)
43 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
46 static void gettimeofday_ns(struct timespec *ts)
48 if (sizeof(struct timeval) == sizeof(struct timespec)
49 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
52 gettimeofday((void*)ts, NULL);
55 extern void BUG_need_to_implement_gettimeofday_ns(void);
56 BUG_need_to_implement_gettimeofday_ns();
61 /* Compare possibly overflowing unsigned counters */
62 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
83 struct timespec start;
94 struct fd_pair selfpipe;
95 struct fd_pair logpipe;
99 #define G (*(struct globals*)&bb_common_bufsiz1)
100 #define haslog (G.haslog )
101 #define sigterm (G.sigterm )
102 #define pidchanged (G.pidchanged )
103 #define selfpipe (G.selfpipe )
104 #define logpipe (G.logpipe )
107 #define INIT_G() do { \
111 static void fatal2_cannot(const char *m1, const char *m2)
113 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
114 /* was exiting 111 */
116 static void fatal_cannot(const char *m)
118 fatal2_cannot(m, "");
119 /* was exiting 111 */
121 static void fatal2x_cannot(const char *m1, const char *m2)
123 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
124 /* was exiting 111 */
126 static void warn_cannot(const char *m)
128 bb_perror_msg("%s: warning: cannot %s", dir, m);
131 static void s_child(int sig_no UNUSED_PARAM)
133 write(selfpipe.wr, "", 1);
136 static void s_term(int sig_no UNUSED_PARAM)
139 write(selfpipe.wr, "", 1); /* XXX */
142 /* libbb candidate */
143 static char *bb_stpcpy(char *p, const char *to_add)
145 while ((*p = *to_add) != '\0') {
152 static int open_trunc_or_warn(const char *name)
155 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
157 bb_perror_msg("%s: warning: cannot open %s",
162 static void update_status(struct svdir *s)
170 fd = open_trunc_or_warn("supervise/pid.new");
174 char spid[sizeof(int)*3 + 2];
175 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
176 write(fd, spid, size);
179 if (rename_or_warn("supervise/pid.new",
180 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
186 fd = open_trunc_or_warn("supervise/stat.new");
191 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
195 p = bb_stpcpy(p, "down");
198 p = bb_stpcpy(p, "run");
201 p = bb_stpcpy(p, "finish");
204 if (s->ctrl & C_PAUSE)
205 p = bb_stpcpy(p, ", paused");
206 if (s->ctrl & C_TERM)
207 p = bb_stpcpy(p, ", got TERM");
208 if (s->state != S_DOWN)
209 switch (s->sd_want) {
211 p = bb_stpcpy(p, ", want down");
214 p = bb_stpcpy(p, ", want exit");
218 write(fd, stat_buf, p - stat_buf);
222 rename_or_warn("supervise/stat.new",
223 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
225 /* supervise compatibility */
226 memset(&status, 0, sizeof(status));
227 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
228 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
229 status.pid_le32 = SWAP_LE32(s->pid);
230 if (s->ctrl & C_PAUSE)
232 if (s->sd_want == W_UP)
236 if (s->ctrl & C_TERM)
238 status.run_or_finish = s->state;
239 fd = open_trunc_or_warn("supervise/status.new");
242 sz = write(fd, &status, sizeof(status));
244 if (sz != sizeof(status)) {
245 warn_cannot("write supervise/status.new");
246 unlink("supervise/status.new");
249 rename_or_warn("supervise/status.new",
250 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
253 static unsigned custom(struct svdir *s, char c)
262 strcpy(a, "control/?");
263 a[8] = c; /* replace '?' */
264 if (stat(a, &st) == 0) {
265 if (st.st_mode & S_IXUSR) {
268 warn_cannot("vfork for control/?");
273 if (haslog && dup2(logpipe.wr, 1) == -1)
274 warn_cannot("setup stdout for control/?");
275 execl(a, a, (char *) NULL);
276 fatal_cannot("run control/?");
279 if (safe_waitpid(pid, &w, 0) == -1) {
280 warn_cannot("wait for child control/?");
283 return WEXITSTATUS(w) == 0;
287 warn_cannot("stat control/?");
292 static void stopservice(struct svdir *s)
294 if (s->pid && !custom(s, 't')) {
295 kill(s->pid, SIGTERM);
299 if (s->sd_want == W_DOWN) {
300 kill(s->pid, SIGCONT);
304 if (s->sd_want == W_EXIT) {
305 kill(s->pid, SIGCONT);
310 static void startservice(struct svdir *s)
314 char exitcode[sizeof(int)*3 + 2];
316 if (s->state == S_FINISH) {
317 /* Two arguments are given to ./finish. The first one is ./run exit code,
318 * or -1 if ./run didnt exit normally. The second one is
319 * the least significant byte of the exit status as determined by waitpid;
320 * for instance it is 0 if ./run exited normally, and the signal number
321 * if ./run was terminated by a signal. If runsv cannot start ./run
322 * for some reason, the exit code is 111 and the status is 0.
326 if (WIFEXITED(s->wstat)) {
327 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
331 //if (WIFSIGNALED(s->wstat)) {
332 arg[2] = utoa(WTERMSIG(s->wstat));
342 stopservice(s); /* should never happen */
343 while ((p = vfork()) == -1) {
344 warn_cannot("vfork, sleeping");
350 /* NB: bug alert! right order is close, then dup2 */
354 xdup2(logpipe.rd, 0);
357 xdup2(logpipe.wr, 1);
360 /* Non-ignored signals revert to SIG_DFL on exec anyway */
365 sig_unblock(SIGCHLD);
366 sig_unblock(SIGTERM);
367 execv(arg[0], (char**) arg);
368 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
371 if (s->state != S_FINISH) {
372 gettimeofday_ns(&s->start);
381 static int ctrl(struct svdir *s, char c)
389 if (s->pid && s->state != S_FINISH)
404 case 't': /* sig term */
405 if (s->pid && s->state != S_FINISH)
408 case 'k': /* sig kill */
409 if (s->pid && !custom(s, c))
410 kill(s->pid, SIGKILL);
413 case 'p': /* sig pause */
414 if (s->pid && !custom(s, c))
415 kill(s->pid, SIGSTOP);
419 case 'c': /* sig cont */
420 if (s->pid && !custom(s, c))
421 kill(s->pid, SIGCONT);
431 case 'a': /* sig alarm */
434 case 'h': /* sig hup */
437 case 'i': /* sig int */
440 case 'q': /* sig quit */
443 case '1': /* sig usr1 */
446 case '2': /* sig usr2 */
452 if (s->pid && !custom(s, c))
457 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
458 int runsv_main(int argc UNUSED_PARAM, char **argv)
467 dir = single_argv(argv);
469 xpiped_pair(selfpipe);
470 close_on_exec_on(selfpipe.rd);
471 close_on_exec_on(selfpipe.wr);
472 ndelay_on(selfpipe.rd);
473 ndelay_on(selfpipe.wr);
476 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
478 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
481 /* bss: svd[0].pid = 0; */
482 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
483 if (C_NOOP) svd[0].ctrl = C_NOOP;
484 if (W_UP) svd[0].sd_want = W_UP;
485 /* bss: svd[0].islog = 0; */
486 /* bss: svd[1].pid = 0; */
487 gettimeofday_ns(&svd[0].start);
488 if (stat("down", &s) != -1)
489 svd[0].sd_want = W_DOWN;
491 if (stat("log", &s) == -1) {
493 warn_cannot("stat ./log");
495 if (!S_ISDIR(s.st_mode)) {
497 warn_cannot("stat log/down: log is not a directory");
500 svd[1].state = S_DOWN;
501 svd[1].ctrl = C_NOOP;
502 svd[1].sd_want = W_UP;
504 gettimeofday_ns(&svd[1].start);
505 if (stat("log/down", &s) != -1)
506 svd[1].sd_want = W_DOWN;
507 xpiped_pair(logpipe);
508 close_on_exec_on(logpipe.rd);
509 close_on_exec_on(logpipe.wr);
513 if (mkdir("supervise", 0700) == -1) {
514 r = readlink("supervise", buf, sizeof(buf));
516 if (r == sizeof(buf))
517 fatal2x_cannot("readlink ./supervise", ": name too long");
521 if ((errno != ENOENT) && (errno != EINVAL))
522 fatal_cannot("readlink ./supervise");
525 svd[0].fdlock = xopen3("log/supervise/lock"+4,
526 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
527 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
528 fatal_cannot("lock supervise/lock");
529 close_on_exec_on(svd[0].fdlock);
531 if (mkdir("log/supervise", 0700) == -1) {
532 r = readlink("log/supervise", buf, 256);
535 fatal2x_cannot("readlink ./log/supervise", ": name too long");
537 fd = xopen(".", O_RDONLY|O_NDELAY);
540 if (fchdir(fd) == -1)
541 fatal_cannot("change back to service directory");
545 if ((errno != ENOENT) && (errno != EINVAL))
546 fatal_cannot("readlink ./log/supervise");
549 svd[1].fdlock = xopen3("log/supervise/lock",
550 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
551 if (flock(svd[1].fdlock, LOCK_EX) == -1)
552 fatal_cannot("lock log/supervise/lock");
553 close_on_exec_on(svd[1].fdlock);
556 mkfifo("log/supervise/control"+4, 0600);
557 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
558 close_on_exec_on(svd[0].fdcontrol);
559 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
560 close_on_exec_on(svd[0].fdcontrolwrite);
561 update_status(&svd[0]);
563 mkfifo("log/supervise/control", 0600);
564 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
565 close_on_exec_on(svd[1].fdcontrol);
566 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
567 close_on_exec_on(svd[1].fdcontrolwrite);
568 update_status(&svd[1]);
570 mkfifo("log/supervise/ok"+4, 0600);
571 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
572 close_on_exec_on(fd);
574 mkfifo("log/supervise/ok", 0600);
575 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
576 close_on_exec_on(fd);
584 if (!svd[1].pid && svd[1].sd_want == W_UP)
585 startservice(&svd[1]);
587 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
588 startservice(&svd[0]);
590 x[0].fd = selfpipe.rd;
591 x[0].events = POLLIN;
592 x[1].fd = svd[0].fdcontrol;
593 x[1].events = POLLIN;
594 /* x[2] is used only if haslog == 1 */
595 x[2].fd = svd[1].fdcontrol;
596 x[2].events = POLLIN;
597 sig_unblock(SIGTERM);
598 sig_unblock(SIGCHLD);
599 poll(x, 2 + haslog, 3600*1000);
603 while (read(selfpipe.rd, &ch, 1) == 1)
610 child = wait_any_nohang(&wstat);
613 if ((child == -1) && (errno != EINTR))
615 if (child == svd[0].pid) {
616 svd[0].wstat = wstat;
619 svd[0].ctrl &= ~C_TERM;
620 if (svd[0].state != S_FINISH) {
621 fd = open("finish", O_RDONLY|O_NDELAY);
624 svd[0].state = S_FINISH;
625 update_status(&svd[0]);
629 svd[0].state = S_DOWN;
630 deadline = svd[0].start.tv_sec + 1;
631 gettimeofday_ns(&svd[0].start);
632 update_status(&svd[0]);
633 if (LESS(svd[0].start.tv_sec, deadline))
637 if (child == svd[1].pid) {
638 svd[0].wstat = wstat;
641 svd[1].state = S_DOWN;
642 svd[1].ctrl &= ~C_TERM;
643 deadline = svd[1].start.tv_sec + 1;
644 gettimeofday_ns(&svd[1].start);
645 update_status(&svd[1]);
646 if (LESS(svd[1].start.tv_sec, deadline))
651 if (read(svd[0].fdcontrol, &ch, 1) == 1)
654 if (read(svd[1].fdcontrol, &ch, 1) == 1)
662 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
665 if (svd[1].sd_want != W_EXIT) {
666 svd[1].sd_want = W_EXIT;
667 /* stopservice(&svd[1]); */
668 update_status(&svd[1]);