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 Denis 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)
64 static int selfpipe[2];
85 struct timespec start;
91 static struct svdir svd[2];
92 static smallint sigterm;
93 static smallint haslog;
94 static smallint pidchanged = 1;
95 static int logpipe[2];
98 static void fatal2_cannot(const char *m1, const char *m2)
100 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
101 /* was exiting 111 */
103 static void fatal_cannot(const char *m)
105 fatal2_cannot(m, "");
106 /* was exiting 111 */
108 static void fatal2x_cannot(const char *m1, const char *m2)
110 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
111 /* was exiting 111 */
113 static void warn_cannot(const char *m)
115 bb_perror_msg("%s: warning: cannot %s", dir, m);
118 static void s_child(int sig_no)
120 write(selfpipe[1], "", 1);
123 static void s_term(int sig_no)
126 write(selfpipe[1], "", 1); /* XXX */
129 static char *add_str(char *p, const char *to_add)
131 while ((*p = *to_add) != '\0') {
138 static int open_trunc_or_warn(const char *name)
140 int fd = open_trunc(name);
142 bb_perror_msg("%s: warning: cannot open %s",
147 static int rename_or_warn(const char *old, const char *new)
149 if (rename(old, new) == -1) {
150 bb_perror_msg("%s: warning: cannot rename %s to %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 = add_str(p, "down");
193 p = add_str(p, "run");
196 p = add_str(p, "finish");
199 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
200 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
201 if (s->state != S_DOWN)
204 p = add_str(p, ", want down");
207 p = add_str(p, ", want exit");
211 write(fd, stat_buf, p - stat_buf);
215 rename_or_warn("supervise/stat.new",
216 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
218 /* supervise compatibility */
219 memset(&status, 0, sizeof(status));
220 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
221 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
222 status.pid_le32 = SWAP_LE32(s->pid);
223 if (s->ctrl & C_PAUSE)
229 if (s->ctrl & C_TERM)
231 status.run_or_finish = s->state;
232 fd = open_trunc_or_warn("supervise/status.new");
235 sz = write(fd, &status, sizeof(status));
237 if (sz != sizeof(status)) {
238 warn_cannot("write supervise/status.new");
239 unlink("supervise/status.new");
242 rename_or_warn("supervise/status.new",
243 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
246 static unsigned custom(struct svdir *s, char c)
254 if (s->islog) return 0;
255 strcpy(a, "control/?");
257 if (stat(a, &st) == 0) {
258 if (st.st_mode & S_IXUSR) {
261 warn_cannot("fork for control/?");
265 if (haslog && dup2(logpipe[1], 1) == -1)
266 warn_cannot("setup stdout for control/?");
269 execve(a, prog, environ);
270 fatal_cannot("run control/?");
272 while (wait_pid(&w, pid) == -1) {
273 if (errno == EINTR) continue;
274 warn_cannot("wait for child control/?");
277 return !wait_exitcode(w);
281 warn_cannot("stat control/?");
286 static void stopservice(struct svdir *s)
288 if (s->pid && !custom(s, 't')) {
289 kill(s->pid, SIGTERM);
293 if (s->want == W_DOWN) {
294 kill(s->pid, SIGCONT);
298 if (s->want == W_EXIT) {
299 kill(s->pid, SIGCONT);
304 static void startservice(struct svdir *s)
309 if (s->state == S_FINISH)
310 run[0] = (char*)"./finish";
312 run[0] = (char*)"./run";
318 stopservice(s); /* should never happen */
319 while ((p = fork()) == -1) {
320 warn_cannot("fork, sleeping");
327 xdup2(logpipe[0], 0);
331 xdup2(logpipe[1], 1);
335 signal(SIGCHLD, SIG_DFL);
336 signal(SIGTERM, SIG_DFL);
337 sig_unblock(SIGCHLD);
338 sig_unblock(SIGTERM);
340 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
342 if (s->state != S_FINISH) {
343 gettimeofday_ns(&s->start);
352 static int ctrl(struct svdir *s, char c)
360 if (s->pid && s->state != S_FINISH)
375 case 't': /* sig term */
376 if (s->pid && s->state != S_FINISH)
379 case 'k': /* sig kill */
380 if (s->pid && !custom(s, c))
381 kill(s->pid, SIGKILL);
384 case 'p': /* sig pause */
385 if (s->pid && !custom(s, c))
386 kill(s->pid, SIGSTOP);
390 case 'c': /* sig cont */
391 if (s->pid && !custom(s, c))
392 kill(s->pid, SIGCONT);
393 if (s->ctrl & C_PAUSE)
403 case 'a': /* sig alarm */
406 case 'h': /* sig hup */
409 case 'i': /* sig int */
412 case 'q': /* sig quit */
415 case '1': /* sig usr1 */
418 case '2': /* sig usr2 */
424 if (s->pid && !custom(s, c))
429 int runsv_main(int argc, char **argv);
430 int runsv_main(int argc, char **argv)
437 if (!argv[1] || argv[2])
444 ndelay_on(selfpipe[0]);
445 ndelay_on(selfpipe[1]);
448 sig_catch(SIGCHLD, s_child);
450 sig_catch(SIGTERM, s_term);
453 /* bss: svd[0].pid = 0; */
454 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
455 if (C_NOOP) svd[0].ctrl = C_NOOP;
456 if (W_UP) svd[0].want = W_UP;
457 /* bss: svd[0].islog = 0; */
458 /* bss: svd[1].pid = 0; */
459 gettimeofday_ns(&svd[0].start);
460 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
462 if (stat("log", &s) == -1) {
464 warn_cannot("stat ./log");
466 if (!S_ISDIR(s.st_mode)) {
468 warn_cannot("stat log/down: log is not a directory");
471 svd[1].state = S_DOWN;
472 svd[1].ctrl = C_NOOP;
475 gettimeofday_ns(&svd[1].start);
476 if (stat("log/down", &s) != -1)
477 svd[1].want = W_DOWN;
484 if (mkdir("supervise", 0700) == -1) {
485 r = readlink("supervise", buf, sizeof(buf));
487 if (r == sizeof(buf))
488 fatal2x_cannot("readlink ./supervise", ": name too long");
492 if ((errno != ENOENT) && (errno != EINVAL))
493 fatal_cannot("readlink ./supervise");
496 svd[0].fdlock = xopen3("log/supervise/lock"+4,
497 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
498 if (lock_exnb(svd[0].fdlock) == -1)
499 fatal_cannot("lock supervise/lock");
502 if (mkdir("log/supervise", 0700) == -1) {
503 r = readlink("log/supervise", buf, 256);
506 fatal2x_cannot("readlink ./log/supervise", ": name too long");
508 fd = xopen(".", O_RDONLY|O_NDELAY);
511 if (fchdir(fd) == -1)
512 fatal_cannot("change back to service directory");
516 if ((errno != ENOENT) && (errno != EINVAL))
517 fatal_cannot("readlink ./log/supervise");
520 svd[1].fdlock = xopen3("log/supervise/lock",
521 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
522 if (lock_ex(svd[1].fdlock) == -1)
523 fatal_cannot("lock log/supervise/lock");
527 mkfifo("log/supervise/control"+4, 0600);
528 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
529 coe(svd[0].fdcontrol);
530 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
531 coe(svd[0].fdcontrolwrite);
532 update_status(&svd[0]);
534 mkfifo("log/supervise/control", 0600);
535 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
536 coe(svd[1].fdcontrol);
537 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
538 coe(svd[1].fdcontrolwrite);
539 update_status(&svd[1]);
541 mkfifo("log/supervise/ok"+4, 0600);
542 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
545 mkfifo("log/supervise/ok", 0600);
546 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
555 if (!svd[1].pid && svd[1].want == W_UP)
556 startservice(&svd[1]);
558 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
559 startservice(&svd[0]);
561 x[0].fd = selfpipe[0];
562 x[0].events = POLLIN;
563 x[1].fd = svd[0].fdcontrol;
564 x[1].events = POLLIN;
565 /* x[2] is used only if haslog == 1 */
566 x[2].fd = svd[1].fdcontrol;
567 x[2].events = POLLIN;
568 sig_unblock(SIGTERM);
569 sig_unblock(SIGCHLD);
570 poll(x, 2 + haslog, 3600*1000);
574 while (read(selfpipe[0], &ch, 1) == 1)
581 child = wait_nohang(&wstat);
584 if ((child == -1) && (errno != EINTR))
586 if (child == svd[0].pid) {
589 svd[0].ctrl &=~ C_TERM;
590 if (svd[0].state != S_FINISH) {
591 fd = open_read("finish");
594 svd[0].state = S_FINISH;
595 update_status(&svd[0]);
599 svd[0].state = S_DOWN;
600 deadline = svd[0].start.tv_sec + 1;
601 gettimeofday_ns(&svd[0].start);
602 update_status(&svd[0]);
603 if (LESS(svd[0].start.tv_sec, deadline))
607 if (child == svd[1].pid) {
610 svd[1].state = S_DOWN;
611 svd[1].ctrl &= ~C_TERM;
612 deadline = svd[1].start.tv_sec + 1;
613 gettimeofday_ns(&svd[1].start);
614 update_status(&svd[1]);
615 if (LESS(svd[1].start.tv_sec, deadline))
620 if (read(svd[0].fdcontrol, &ch, 1) == 1)
623 if (read(svd[1].fdcontrol, &ch, 1) == 1)
631 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
634 if (svd[1].want != W_EXIT) {
635 svd[1].want = W_EXIT;
636 /* stopservice(&svd[1]); */
637 update_status(&svd[1]);