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;
93 struct fd_pair selfpipe;
94 struct fd_pair logpipe;
98 #define G (*(struct globals*)&bb_common_bufsiz1)
99 #define haslog (G.haslog )
100 #define sigterm (G.sigterm )
101 #define pidchanged (G.pidchanged )
102 #define selfpipe (G.selfpipe )
103 #define logpipe (G.logpipe )
106 #define INIT_G() do { \
110 static void fatal2_cannot(const char *m1, const char *m2)
112 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
113 /* was exiting 111 */
115 static void fatal_cannot(const char *m)
117 fatal2_cannot(m, "");
118 /* was exiting 111 */
120 static void fatal2x_cannot(const char *m1, const char *m2)
122 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
123 /* was exiting 111 */
125 static void warn_cannot(const char *m)
127 bb_perror_msg("%s: warning: cannot %s", dir, m);
130 static void s_child(int sig_no UNUSED_PARAM)
132 write(selfpipe.wr, "", 1);
135 static void s_term(int sig_no UNUSED_PARAM)
138 write(selfpipe.wr, "", 1); /* XXX */
141 static char *add_str(char *p, const char *to_add)
143 while ((*p = *to_add) != '\0') {
150 static int open_trunc_or_warn(const char *name)
152 int fd = open_trunc(name);
154 bb_perror_msg("%s: warning: cannot open %s",
159 static void update_status(struct svdir *s)
167 fd = open_trunc_or_warn("supervise/pid.new");
171 char spid[sizeof(int)*3 + 2];
172 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
173 write(fd, spid, size);
176 if (rename_or_warn("supervise/pid.new",
177 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
183 fd = open_trunc_or_warn("supervise/stat.new");
188 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
192 p = add_str(p, "down");
195 p = add_str(p, "run");
198 p = add_str(p, "finish");
201 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
202 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
203 if (s->state != S_DOWN)
206 p = add_str(p, ", want down");
209 p = add_str(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)
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)
256 if (s->islog) return 0;
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/?");
273 fatal_cannot("run control/?");
276 while (safe_waitpid(pid, &w, 0) == -1) {
277 warn_cannot("wait for child control/?");
280 return !wait_exitcode(w);
284 warn_cannot("stat control/?");
289 static void stopservice(struct svdir *s)
291 if (s->pid && !custom(s, 't')) {
292 kill(s->pid, SIGTERM);
296 if (s->want == W_DOWN) {
297 kill(s->pid, SIGCONT);
301 if (s->want == W_EXIT) {
302 kill(s->pid, SIGCONT);
307 static void startservice(struct svdir *s)
312 if (s->state == S_FINISH)
313 run[0] = (char*)"./finish";
315 run[0] = (char*)"./run";
321 stopservice(s); /* should never happen */
322 while ((p = vfork()) == -1) {
323 warn_cannot("vfork, sleeping");
329 /* NB: bug alert! right order is close, then dup2 */
333 xdup2(logpipe.rd, 0);
336 xdup2(logpipe.wr, 1);
343 sig_unblock(SIGCHLD);
344 sig_unblock(SIGTERM);
346 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
349 if (s->state != S_FINISH) {
350 gettimeofday_ns(&s->start);
359 static int ctrl(struct svdir *s, char c)
367 if (s->pid && s->state != S_FINISH)
382 case 't': /* sig term */
383 if (s->pid && s->state != S_FINISH)
386 case 'k': /* sig kill */
387 if (s->pid && !custom(s, c))
388 kill(s->pid, SIGKILL);
391 case 'p': /* sig pause */
392 if (s->pid && !custom(s, c))
393 kill(s->pid, SIGSTOP);
397 case 'c': /* sig cont */
398 if (s->pid && !custom(s, c))
399 kill(s->pid, SIGCONT);
400 if (s->ctrl & C_PAUSE)
410 case 'a': /* sig alarm */
413 case 'h': /* sig hup */
416 case 'i': /* sig int */
419 case 'q': /* sig quit */
422 case '1': /* sig usr1 */
425 case '2': /* sig usr2 */
431 if (s->pid && !custom(s, c))
436 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
437 int runsv_main(int argc UNUSED_PARAM, char **argv)
446 if (!argv[1] || argv[2])
450 xpiped_pair(selfpipe);
451 close_on_exec_on(selfpipe.rd);
452 close_on_exec_on(selfpipe.wr);
453 ndelay_on(selfpipe.rd);
454 ndelay_on(selfpipe.wr);
457 bb_signals_recursive(1 << SIGCHLD, s_child);
459 bb_signals_recursive(1 << SIGTERM, s_term);
462 /* bss: svd[0].pid = 0; */
463 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
464 if (C_NOOP) svd[0].ctrl = C_NOOP;
465 if (W_UP) svd[0].want = W_UP;
466 /* bss: svd[0].islog = 0; */
467 /* bss: svd[1].pid = 0; */
468 gettimeofday_ns(&svd[0].start);
469 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
471 if (stat("log", &s) == -1) {
473 warn_cannot("stat ./log");
475 if (!S_ISDIR(s.st_mode)) {
477 warn_cannot("stat log/down: log is not a directory");
480 svd[1].state = S_DOWN;
481 svd[1].ctrl = C_NOOP;
484 gettimeofday_ns(&svd[1].start);
485 if (stat("log/down", &s) != -1)
486 svd[1].want = W_DOWN;
487 xpiped_pair(logpipe);
488 close_on_exec_on(logpipe.rd);
489 close_on_exec_on(logpipe.wr);
493 if (mkdir("supervise", 0700) == -1) {
494 r = readlink("supervise", buf, sizeof(buf));
496 if (r == sizeof(buf))
497 fatal2x_cannot("readlink ./supervise", ": name too long");
501 if ((errno != ENOENT) && (errno != EINVAL))
502 fatal_cannot("readlink ./supervise");
505 svd[0].fdlock = xopen3("log/supervise/lock"+4,
506 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
507 if (lock_exnb(svd[0].fdlock) == -1)
508 fatal_cannot("lock supervise/lock");
509 close_on_exec_on(svd[0].fdlock);
511 if (mkdir("log/supervise", 0700) == -1) {
512 r = readlink("log/supervise", buf, 256);
515 fatal2x_cannot("readlink ./log/supervise", ": name too long");
517 fd = xopen(".", O_RDONLY|O_NDELAY);
520 if (fchdir(fd) == -1)
521 fatal_cannot("change back to service directory");
525 if ((errno != ENOENT) && (errno != EINVAL))
526 fatal_cannot("readlink ./log/supervise");
529 svd[1].fdlock = xopen3("log/supervise/lock",
530 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
531 if (lock_ex(svd[1].fdlock) == -1)
532 fatal_cannot("lock log/supervise/lock");
533 close_on_exec_on(svd[1].fdlock);
536 mkfifo("log/supervise/control"+4, 0600);
537 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
538 close_on_exec_on(svd[0].fdcontrol);
539 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
540 close_on_exec_on(svd[0].fdcontrolwrite);
541 update_status(&svd[0]);
543 mkfifo("log/supervise/control", 0600);
544 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
545 close_on_exec_on(svd[1].fdcontrol);
546 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
547 close_on_exec_on(svd[1].fdcontrolwrite);
548 update_status(&svd[1]);
550 mkfifo("log/supervise/ok"+4, 0600);
551 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
552 close_on_exec_on(fd);
554 mkfifo("log/supervise/ok", 0600);
555 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
556 close_on_exec_on(fd);
564 if (!svd[1].pid && svd[1].want == W_UP)
565 startservice(&svd[1]);
567 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
568 startservice(&svd[0]);
570 x[0].fd = selfpipe.rd;
571 x[0].events = POLLIN;
572 x[1].fd = svd[0].fdcontrol;
573 x[1].events = POLLIN;
574 /* x[2] is used only if haslog == 1 */
575 x[2].fd = svd[1].fdcontrol;
576 x[2].events = POLLIN;
577 sig_unblock(SIGTERM);
578 sig_unblock(SIGCHLD);
579 poll(x, 2 + haslog, 3600*1000);
583 while (read(selfpipe.rd, &ch, 1) == 1)
590 child = wait_any_nohang(&wstat);
593 if ((child == -1) && (errno != EINTR))
595 if (child == svd[0].pid) {
598 svd[0].ctrl &=~ C_TERM;
599 if (svd[0].state != S_FINISH) {
600 fd = open_read("finish");
603 svd[0].state = S_FINISH;
604 update_status(&svd[0]);
608 svd[0].state = S_DOWN;
609 deadline = svd[0].start.tv_sec + 1;
610 gettimeofday_ns(&svd[0].start);
611 update_status(&svd[0]);
612 if (LESS(svd[0].start.tv_sec, deadline))
616 if (child == svd[1].pid) {
619 svd[1].state = S_DOWN;
620 svd[1].ctrl &= ~C_TERM;
621 deadline = svd[1].start.tv_sec + 1;
622 gettimeofday_ns(&svd[1].start);
623 update_status(&svd[1]);
624 if (LESS(svd[1].start.tv_sec, deadline))
629 if (read(svd[0].fdcontrol, &ch, 1) == 1)
632 if (read(svd[1].fdcontrol, &ch, 1) == 1)
640 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
643 if (svd[1].want != W_EXIT) {
644 svd[1].want = W_EXIT;
645 /* stopservice(&svd[1]); */
646 update_status(&svd[1]);