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 static int selfpipe[2];
62 static struct svdir svd[2];
64 static smallint sigterm;
65 static smallint haslog;
66 static smallint pidchanged = 1;
67 static int logpipe[2];
70 #define usage() bb_show_usage()
72 static void fatal2_cannot(const char *m1, const char *m2)
74 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
77 static void fatal_cannot(const char *m)
82 static void fatal2x_cannot(const char *m1, const char *m2)
84 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
87 static void warn_cannot(const char *m)
89 bb_perror_msg("%s: warning: cannot %s", dir, m);
91 static void warnx_cannot(const char *m)
93 bb_error_msg("%s: warning: cannot %s", dir, m);
96 static void s_child(int sig_no)
98 write(selfpipe[1], "", 1);
101 static void s_term(int sig_no)
104 write(selfpipe[1], "", 1); /* XXX */
107 static char *add_str(char *p, const char *to_add)
109 while ((*p = *to_add) != '\0') {
116 static int open_trunc_or_warn(const char *name)
118 int fd = open_trunc(name);
120 bb_perror_msg("%s: warning: cannot open %s",
125 static int rename_or_warn(const char *old, const char *new)
127 if (rename(old, new) == -1) {
128 bb_perror_msg("%s: warning: cannot rename %s to %s",
135 static void update_status(struct svdir *s)
143 fd = open_trunc_or_warn("supervise/pid.new");
147 char spid[sizeof(int)*3 + 2];
148 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
149 write(fd, spid, size);
152 if (rename_or_warn("supervise/pid.new",
153 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
159 fd = open_trunc_or_warn("supervise/stat.new");
164 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
168 p = add_str(p, "down");
171 p = add_str(p, "run");
174 p = add_str(p, "finish");
177 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
178 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
179 if (s->state != S_DOWN)
182 p = add_str(p, ", want down");
185 p = add_str(p, ", want exit");
189 write(fd, stat_buf, p - stat_buf);
193 rename_or_warn("supervise/stat.new",
194 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
196 /* supervise compatibility */
197 taia_pack(status, &s->start);
198 l = (unsigned long)s->pid;
199 status[12] = l; l >>=8;
200 status[13] = l; l >>=8;
201 status[14] = l; l >>=8;
203 if (s->ctrl & C_PAUSE)
211 if (s->ctrl & C_TERM)
215 status[19] = s->state;
216 fd = open_trunc_or_warn("supervise/status.new");
219 l = write(fd, status, sizeof(status));
221 warn_cannot("write supervise/status.new");
223 unlink("supervise/status.new");
227 if (l < sizeof(status)) {
228 warnx_cannot("write supervise/status.new: partial write");
231 rename_or_warn("supervise/status.new",
232 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
235 static unsigned custom(struct svdir *s, char c)
243 if (s->islog) return 0;
244 strcpy(a, "control/?");
246 if (stat(a, &st) == 0) {
247 if (st.st_mode & S_IXUSR) {
250 warn_cannot("fork for control/?");
254 if (haslog && dup2(logpipe[1], 1) == -1)
255 warn_cannot("setup stdout for control/?");
258 execve(a, prog, environ);
259 fatal_cannot("run control/?");
261 while (wait_pid(&w, pid) == -1) {
262 if (errno == EINTR) continue;
263 warn_cannot("wait for child control/?");
266 return !wait_exitcode(w);
270 warn_cannot("stat control/?");
275 static void stopservice(struct svdir *s)
277 if (s->pid && !custom(s, 't')) {
278 kill(s->pid, SIGTERM);
282 if (s->want == W_DOWN) {
283 kill(s->pid, SIGCONT);
287 if (s->want == W_EXIT) {
288 kill(s->pid, SIGCONT);
293 static void startservice(struct svdir *s)
298 if (s->state == S_FINISH)
299 run[0] = (char*)"./finish";
301 run[0] = (char*)"./run";
307 stopservice(s); /* should never happen */
308 while ((p = fork()) == -1) {
309 warn_cannot("fork, sleeping");
316 xdup2(logpipe[0], 0);
320 xdup2(logpipe[1], 1);
324 signal(SIGCHLD, SIG_DFL);
325 signal(SIGTERM, SIG_DFL);
326 sig_unblock(SIGCHLD);
327 sig_unblock(SIGTERM);
329 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
331 if (s->state != S_FINISH) {
341 static int ctrl(struct svdir *s, char c)
349 if (s->pid && s->state != S_FINISH) stopservice(s);
354 if (s->pid == 0) startservice(s);
361 case 't': /* sig term */
362 if (s->pid && s->state != S_FINISH) stopservice(s);
364 case 'k': /* sig kill */
365 if (s->pid && !custom(s, c)) kill(s->pid, SIGKILL);
368 case 'p': /* sig pause */
369 if (s->pid && !custom(s, c)) kill(s->pid, SIGSTOP);
373 case 'c': /* sig cont */
374 if (s->pid && !custom(s, c)) kill(s->pid, SIGCONT);
375 if (s->ctrl & C_PAUSE) s->ctrl &= ~C_PAUSE;
381 if (!s->pid) startservice(s);
383 case 'a': /* sig alarm */
386 case 'h': /* sig hup */
389 case 'i': /* sig int */
392 case 'q': /* sig quit */
395 case '1': /* sig usr1 */
398 case '2': /* sig usr2 */
404 if (s->pid && !custom(s, c))
409 int runsv_main(int argc, char **argv);
410 int runsv_main(int argc, char **argv)
417 if (!argv[1] || argv[2]) usage();
423 ndelay_on(selfpipe[0]);
424 ndelay_on(selfpipe[1]);
427 sig_catch(SIGCHLD, s_child);
429 sig_catch(SIGTERM, s_term);
432 /* bss: svd[0].pid = 0; */
433 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
434 if (C_NOOP) svd[0].ctrl = C_NOOP;
435 if (W_UP) svd[0].want = W_UP;
436 /* bss: svd[0].islog = 0; */
437 /* bss: svd[1].pid = 0; */
438 taia_now(&svd[0].start);
439 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
441 if (stat("log", &s) == -1) {
443 warn_cannot("stat ./log");
445 if (!S_ISDIR(s.st_mode))
446 warnx_cannot("stat log/down: log is not a directory");
449 svd[1].state = S_DOWN;
450 svd[1].ctrl = C_NOOP;
453 taia_now(&svd[1].start);
454 if (stat("log/down", &s) != -1)
455 svd[1].want = W_DOWN;
462 if (mkdir("supervise", 0700) == -1) {
463 r = readlink("supervise", buf, sizeof(buf));
465 if (r == sizeof(buf))
466 fatal2x_cannot("readlink ./supervise", ": name too long");
470 if ((errno != ENOENT) && (errno != EINVAL))
471 fatal_cannot("readlink ./supervise");
474 svd[0].fdlock = xopen3("log/supervise/lock"+4,
475 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
476 if (lock_exnb(svd[0].fdlock) == -1)
477 fatal_cannot("lock supervise/lock");
480 if (mkdir("log/supervise", 0700) == -1) {
481 r = readlink("log/supervise", buf, 256);
484 fatal2x_cannot("readlink ./log/supervise", ": name too long");
486 fd = xopen(".", O_RDONLY|O_NDELAY);
489 if (fchdir(fd) == -1)
490 fatal_cannot("change back to service directory");
494 if ((errno != ENOENT) && (errno != EINVAL))
495 fatal_cannot("readlink ./log/supervise");
498 svd[1].fdlock = xopen3("log/supervise/lock",
499 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
500 if (lock_ex(svd[1].fdlock) == -1)
501 fatal_cannot("lock log/supervise/lock");
505 mkfifo("log/supervise/control"+4, 0600);
506 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
507 coe(svd[0].fdcontrol);
508 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
509 coe(svd[0].fdcontrolwrite);
510 update_status(&svd[0]);
512 mkfifo("log/supervise/control", 0600);
513 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
514 coe(svd[1].fdcontrol);
515 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
516 coe(svd[1].fdcontrolwrite);
517 update_status(&svd[1]);
519 mkfifo("log/supervise/ok"+4, 0600);
520 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
523 mkfifo("log/supervise/ok", 0600);
524 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
529 struct taia deadline;
534 if (!svd[1].pid && svd[1].want == W_UP)
535 startservice(&svd[1]);
537 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
538 startservice(&svd[0]);
540 x[0].fd = selfpipe[0];
541 x[0].events = IOPAUSE_READ;
542 x[1].fd = svd[0].fdcontrol;
543 x[1].events = IOPAUSE_READ;
545 x[2].fd = svd[1].fdcontrol;
546 x[2].events = IOPAUSE_READ;
549 taia_uint(&deadline, 3600);
550 taia_add(&deadline, &now, &deadline);
552 sig_unblock(SIGTERM);
553 sig_unblock(SIGCHLD);
554 iopause(x, 2+haslog, &deadline, &now);
558 while (read(selfpipe[0], &ch, 1) == 1)
564 child = wait_nohang(&wstat);
566 if ((child == -1) && (errno != EINTR)) break;
567 if (child == svd[0].pid) {
570 svd[0].ctrl &=~ C_TERM;
571 if (svd[0].state != S_FINISH) {
572 fd = open_read("finish");
575 svd[0].state = S_FINISH;
576 update_status(&svd[0]);
580 svd[0].state = S_DOWN;
581 taia_uint(&deadline, 1);
582 taia_add(&deadline, &svd[0].start, &deadline);
583 taia_now(&svd[0].start);
584 update_status(&svd[0]);
585 if (taia_less(&svd[0].start, &deadline)) sleep(1);
588 if (child == svd[1].pid) {
591 svd[1].state = S_DOWN;
592 svd[1].ctrl &= ~C_TERM;
593 taia_uint(&deadline, 1);
594 taia_add(&deadline, &svd[1].start, &deadline);
595 taia_now(&svd[1].start);
596 update_status(&svd[1]);
597 if (taia_less(&svd[1].start, &deadline)) sleep(1);
601 if (read(svd[0].fdcontrol, &ch, 1) == 1)
604 if (read(svd[1].fdcontrol, &ch, 1) == 1)
612 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
615 if (svd[1].want != W_EXIT) {
616 svd[1].want = W_EXIT;
617 /* stopservice(&svd[1]); */
618 update_status(&svd[1]);
621 //if (close(logpipe[1]) == -1)
622 // warn_cannot("close logpipe[1]");
623 //if (close(logpipe[0]) == -1)
624 // warn_cannot("close logpipe[0]");