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];
315 if (s->state == S_FINISH) {
316 /* Two arguments are given to ./finish. The first one is ./run exit code,
317 * or -1 if ./run didnt exit normally. The second one is
318 * the least significant byte of the exit status as determined by waitpid;
319 * for instance it is 0 if ./run exited normally, and the signal number
320 * if ./run was terminated by a signal. If runsv cannot start ./run
321 * for some reason, the exit code is 111 and the status is 0.
325 if (WIFEXITED(s->wstat)) {
326 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
330 //if (WIFSIGNALED(s->wstat)) {
331 arg[2] = utoa(WTERMSIG(s->wstat));
341 stopservice(s); /* should never happen */
342 while ((p = vfork()) == -1) {
343 warn_cannot("vfork, sleeping");
349 /* NB: bug alert! right order is close, then dup2 */
353 xdup2(logpipe.rd, 0);
356 xdup2(logpipe.wr, 1);
359 /* Non-ignored signals revert to SIG_DFL on exec anyway */
364 sig_unblock(SIGCHLD);
365 sig_unblock(SIGTERM);
366 execv(arg[0], (char**) arg);
367 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
370 if (s->state != S_FINISH) {
371 gettimeofday_ns(&s->start);
380 static int ctrl(struct svdir *s, char c)
388 if (s->pid && s->state != S_FINISH)
403 case 't': /* sig term */
404 if (s->pid && s->state != S_FINISH)
407 case 'k': /* sig kill */
408 if (s->pid && !custom(s, c))
409 kill(s->pid, SIGKILL);
412 case 'p': /* sig pause */
413 if (s->pid && !custom(s, c))
414 kill(s->pid, SIGSTOP);
418 case 'c': /* sig cont */
419 if (s->pid && !custom(s, c))
420 kill(s->pid, SIGCONT);
430 case 'a': /* sig alarm */
433 case 'h': /* sig hup */
436 case 'i': /* sig int */
439 case 'q': /* sig quit */
442 case '1': /* sig usr1 */
445 case '2': /* sig usr2 */
451 if (s->pid && !custom(s, c))
456 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
457 int runsv_main(int argc UNUSED_PARAM, char **argv)
466 if (!argv[1] || argv[2])
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 (lock_exnb(svd[0].fdlock) == -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 (lock_ex(svd[1].fdlock) == -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_read("finish");
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]);