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)
154 int fd = open_trunc(name);
156 bb_perror_msg("%s: warning: cannot open %s",
161 static void update_status(struct svdir *s)
169 fd = open_trunc_or_warn("supervise/pid.new");
173 char spid[sizeof(int)*3 + 2];
174 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
175 write(fd, spid, size);
178 if (rename_or_warn("supervise/pid.new",
179 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
185 fd = open_trunc_or_warn("supervise/stat.new");
190 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
194 p = bb_stpcpy(p, "down");
197 p = bb_stpcpy(p, "run");
200 p = bb_stpcpy(p, "finish");
203 if (s->ctrl & C_PAUSE)
204 p = bb_stpcpy(p, ", paused");
205 if (s->ctrl & C_TERM)
206 p = bb_stpcpy(p, ", got TERM");
207 if (s->state != S_DOWN)
208 switch (s->sd_want) {
210 p = bb_stpcpy(p, ", want down");
213 p = bb_stpcpy(p, ", want exit");
217 write(fd, stat_buf, p - stat_buf);
221 rename_or_warn("supervise/stat.new",
222 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
224 /* supervise compatibility */
225 memset(&status, 0, sizeof(status));
226 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
227 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
228 status.pid_le32 = SWAP_LE32(s->pid);
229 if (s->ctrl & C_PAUSE)
231 if (s->sd_want == W_UP)
235 if (s->ctrl & C_TERM)
237 status.run_or_finish = s->state;
238 fd = open_trunc_or_warn("supervise/status.new");
241 sz = write(fd, &status, sizeof(status));
243 if (sz != sizeof(status)) {
244 warn_cannot("write supervise/status.new");
245 unlink("supervise/status.new");
248 rename_or_warn("supervise/status.new",
249 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
252 static unsigned custom(struct svdir *s, char c)
261 strcpy(a, "control/?");
262 a[8] = c; /* replace '?' */
263 if (stat(a, &st) == 0) {
264 if (st.st_mode & S_IXUSR) {
267 warn_cannot("vfork for control/?");
272 if (haslog && dup2(logpipe.wr, 1) == -1)
273 warn_cannot("setup stdout for control/?");
274 execl(a, a, (char *) NULL);
275 fatal_cannot("run control/?");
278 if (safe_waitpid(pid, &w, 0) == -1) {
279 warn_cannot("wait for child control/?");
282 return WEXITSTATUS(w) == 0;
286 warn_cannot("stat control/?");
291 static void stopservice(struct svdir *s)
293 if (s->pid && !custom(s, 't')) {
294 kill(s->pid, SIGTERM);
298 if (s->sd_want == W_DOWN) {
299 kill(s->pid, SIGCONT);
303 if (s->sd_want == W_EXIT) {
304 kill(s->pid, SIGCONT);
309 static void startservice(struct svdir *s)
313 char exitcode[sizeof(int)*3 + 2];
314 char sigcode[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 sprintf(exitcode, "%u", (int) WEXITSTATUS(s->wstat));
331 //if (WIFSIGNALED(s->wstat)) {
332 sprintf(sigcode, "%u", (int) 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 if (!argv[1] || argv[2])
472 xpiped_pair(selfpipe);
473 close_on_exec_on(selfpipe.rd);
474 close_on_exec_on(selfpipe.wr);
475 ndelay_on(selfpipe.rd);
476 ndelay_on(selfpipe.wr);
479 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
481 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
484 /* bss: svd[0].pid = 0; */
485 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
486 if (C_NOOP) svd[0].ctrl = C_NOOP;
487 if (W_UP) svd[0].sd_want = W_UP;
488 /* bss: svd[0].islog = 0; */
489 /* bss: svd[1].pid = 0; */
490 gettimeofday_ns(&svd[0].start);
491 if (stat("down", &s) != -1)
492 svd[0].sd_want = W_DOWN;
494 if (stat("log", &s) == -1) {
496 warn_cannot("stat ./log");
498 if (!S_ISDIR(s.st_mode)) {
500 warn_cannot("stat log/down: log is not a directory");
503 svd[1].state = S_DOWN;
504 svd[1].ctrl = C_NOOP;
505 svd[1].sd_want = W_UP;
507 gettimeofday_ns(&svd[1].start);
508 if (stat("log/down", &s) != -1)
509 svd[1].sd_want = W_DOWN;
510 xpiped_pair(logpipe);
511 close_on_exec_on(logpipe.rd);
512 close_on_exec_on(logpipe.wr);
516 if (mkdir("supervise", 0700) == -1) {
517 r = readlink("supervise", buf, sizeof(buf));
519 if (r == sizeof(buf))
520 fatal2x_cannot("readlink ./supervise", ": name too long");
524 if ((errno != ENOENT) && (errno != EINVAL))
525 fatal_cannot("readlink ./supervise");
528 svd[0].fdlock = xopen3("log/supervise/lock"+4,
529 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
530 if (lock_exnb(svd[0].fdlock) == -1)
531 fatal_cannot("lock supervise/lock");
532 close_on_exec_on(svd[0].fdlock);
534 if (mkdir("log/supervise", 0700) == -1) {
535 r = readlink("log/supervise", buf, 256);
538 fatal2x_cannot("readlink ./log/supervise", ": name too long");
540 fd = xopen(".", O_RDONLY|O_NDELAY);
543 if (fchdir(fd) == -1)
544 fatal_cannot("change back to service directory");
548 if ((errno != ENOENT) && (errno != EINVAL))
549 fatal_cannot("readlink ./log/supervise");
552 svd[1].fdlock = xopen3("log/supervise/lock",
553 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
554 if (lock_ex(svd[1].fdlock) == -1)
555 fatal_cannot("lock log/supervise/lock");
556 close_on_exec_on(svd[1].fdlock);
559 mkfifo("log/supervise/control"+4, 0600);
560 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
561 close_on_exec_on(svd[0].fdcontrol);
562 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
563 close_on_exec_on(svd[0].fdcontrolwrite);
564 update_status(&svd[0]);
566 mkfifo("log/supervise/control", 0600);
567 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
568 close_on_exec_on(svd[1].fdcontrol);
569 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
570 close_on_exec_on(svd[1].fdcontrolwrite);
571 update_status(&svd[1]);
573 mkfifo("log/supervise/ok"+4, 0600);
574 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
575 close_on_exec_on(fd);
577 mkfifo("log/supervise/ok", 0600);
578 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
579 close_on_exec_on(fd);
587 if (!svd[1].pid && svd[1].sd_want == W_UP)
588 startservice(&svd[1]);
590 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
591 startservice(&svd[0]);
593 x[0].fd = selfpipe.rd;
594 x[0].events = POLLIN;
595 x[1].fd = svd[0].fdcontrol;
596 x[1].events = POLLIN;
597 /* x[2] is used only if haslog == 1 */
598 x[2].fd = svd[1].fdcontrol;
599 x[2].events = POLLIN;
600 sig_unblock(SIGTERM);
601 sig_unblock(SIGCHLD);
602 poll(x, 2 + haslog, 3600*1000);
606 while (read(selfpipe.rd, &ch, 1) == 1)
613 child = wait_any_nohang(&wstat);
616 if ((child == -1) && (errno != EINTR))
618 if (child == svd[0].pid) {
619 svd[0].wstat = wstat;
622 svd[0].ctrl &= ~C_TERM;
623 if (svd[0].state != S_FINISH) {
624 fd = open_read("finish");
627 svd[0].state = S_FINISH;
628 update_status(&svd[0]);
632 svd[0].state = S_DOWN;
633 deadline = svd[0].start.tv_sec + 1;
634 gettimeofday_ns(&svd[0].start);
635 update_status(&svd[0]);
636 if (LESS(svd[0].start.tv_sec, deadline))
640 if (child == svd[1].pid) {
641 svd[0].wstat = wstat;
644 svd[1].state = S_DOWN;
645 svd[1].ctrl &= ~C_TERM;
646 deadline = svd[1].start.tv_sec + 1;
647 gettimeofday_ns(&svd[1].start);
648 update_status(&svd[1]);
649 if (LESS(svd[1].start.tv_sec, deadline))
654 if (read(svd[0].fdcontrol, &ch, 1) == 1)
657 if (read(svd[1].fdcontrol, &ch, 1) == 1)
665 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
668 if (svd[1].sd_want != W_EXIT) {
669 svd[1].sd_want = W_EXIT;
670 /* stopservice(&svd[1]); */
671 update_status(&svd[1]);