cut: code shrink
[busybox-git.git] / runit / runsv.c
blob20a4453199348d68e6b24c6db6a44ac04c390705
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
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> */
30 //config:config RUNSV
31 //config: bool "runsv (8.2 kb)"
32 //config: default y
33 //config: help
34 //config: runsv starts and monitors a service and optionally an appendant log
35 //config: service.
37 //applet:IF_RUNSV(APPLET(runsv, BB_DIR_USR_BIN, BB_SUID_DROP))
39 //kbuild:lib-$(CONFIG_RUNSV) += runsv.o
41 //usage:#define runsv_trivial_usage
42 //usage: "DIR"
43 //usage:#define runsv_full_usage "\n\n"
44 //usage: "Start and monitor a service and optionally an appendant log service"
46 #include <sys/file.h>
47 #include "libbb.h"
48 #include "common_bufsiz.h"
49 #include "runit_lib.h"
51 #if ENABLE_MONOTONIC_SYSCALL
52 #include <sys/syscall.h>
54 static void gettimeofday_ns(struct timespec *ts)
56 clock_gettime(CLOCK_REALTIME, ts);
58 #else
59 static void gettimeofday_ns(struct timespec *ts)
61 if (sizeof(struct timeval) == sizeof(struct timespec)
62 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
63 ) {
64 /* Cheat */
65 xgettimeofday((void*)ts);
66 ts->tv_nsec *= 1000;
67 } else {
68 /* For example, musl has "incompatible" layouts */
69 struct timeval tv;
70 xgettimeofday(&tv);
71 ts->tv_sec = tv.tv_sec;
72 ts->tv_nsec = tv.tv_usec * 1000;
75 #endif
77 /* Compare possibly overflowing unsigned counters */
78 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
80 /* state */
81 #define S_DOWN 0
82 #define S_RUN 1
83 #define S_FINISH 2
84 /* ctrl */
85 #define C_NOOP 0
86 #define C_TERM 1
87 #define C_PAUSE 2
88 /* want */
89 #define W_UP 0
90 #define W_DOWN 1
91 #define W_EXIT 2
93 struct svdir {
94 int pid;
95 smallint state;
96 smallint ctrl;
97 smallint sd_want;
98 smallint islog;
99 struct timespec start;
100 int fdlock;
101 int fdcontrol;
102 int fdcontrolwrite;
103 int wstat;
106 struct globals {
107 smallint haslog;
108 smallint sigterm;
109 smallint pidchanged;
110 struct fd_pair selfpipe;
111 struct fd_pair logpipe;
112 char *dir;
113 struct svdir svd[2];
114 } FIX_ALIASING;
115 #define G (*(struct globals*)bb_common_bufsiz1)
116 #define haslog (G.haslog )
117 #define sigterm (G.sigterm )
118 #define pidchanged (G.pidchanged )
119 #define selfpipe (G.selfpipe )
120 #define logpipe (G.logpipe )
121 #define dir (G.dir )
122 #define svd (G.svd )
123 #define INIT_G() do { \
124 setup_common_bufsiz(); \
125 pidchanged = 1; \
126 } while (0)
128 static void fatal2_cannot(const char *m1, const char *m2)
130 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
131 /* was exiting 111 */
133 static void fatal_cannot(const char *m)
135 fatal2_cannot(m, "");
136 /* was exiting 111 */
138 static void fatal2x_cannot(const char *m1, const char *m2)
140 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
141 /* was exiting 111 */
143 static void warn2_cannot(const char *m1, const char *m2)
145 bb_perror_msg("%s: warning: can't %s%s", dir, m1, m2);
147 static void warn_cannot(const char *m)
149 warn2_cannot(m, "");
152 /* SIGCHLD/TERM handler is reentrancy-safe because they are unmasked
153 * only over poll() call, not over memory allocations
154 * or printouts. Do not need to save/restore errno either,
155 * as poll() error is not checked there.
157 static void s_chld_term(int sig_no)
159 if (sig_no == SIGTERM)
160 sigterm = 1;
161 write(selfpipe.wr, "", 1);
164 static int open_trunc_or_warn(const char *name)
166 /* Why O_NDELAY? */
167 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
168 if (fd < 0)
169 bb_perror_msg("%s: warning: cannot open %s",
170 dir, name);
171 return fd;
174 static void update_status(struct svdir *s)
176 ssize_t sz;
177 int fd;
178 svstatus_t status;
179 const char *fstatus ="log/supervise/status";
180 const char *fstatusnew ="log/supervise/status.new";
181 const char *f_stat ="log/supervise/stat";
182 const char *fstatnew ="log/supervise/stat.new";
183 const char *fpid ="log/supervise/pid";
184 const char *fpidnew ="log/supervise/pid.new";
186 if (!s->islog) {
187 fstatus += 4;
188 fstatusnew += 4;
189 f_stat += 4;
190 fstatnew += 4;
191 fpid += 4;
192 fpidnew += 4;
195 /* pid */
196 if (pidchanged) {
197 fd = open_trunc_or_warn(fpidnew);
198 if (fd < 0)
199 return;
200 if (s->pid) {
201 char spid[sizeof(int)*3 + 2];
202 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
203 write(fd, spid, size);
205 close(fd);
206 if (rename_or_warn(fpidnew, fpid))
207 return;
208 pidchanged = 0;
211 /* stat */
212 fd = open_trunc_or_warn(fstatnew);
213 if (fd < -1)
214 return;
217 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
218 char *p = stat_buf;
219 switch (s->state) {
220 case S_DOWN:
221 p = stpcpy(p, "down");
222 break;
223 case S_RUN:
224 p = stpcpy(p, "run");
225 break;
226 case S_FINISH:
227 p = stpcpy(p, "finish");
228 break;
230 if (s->ctrl & C_PAUSE)
231 p = stpcpy(p, ", paused");
232 if (s->ctrl & C_TERM)
233 p = stpcpy(p, ", got TERM");
234 if (s->state != S_DOWN)
235 switch (s->sd_want) {
236 case W_DOWN:
237 p = stpcpy(p, ", want down");
238 break;
239 case W_EXIT:
240 p = stpcpy(p, ", want exit");
241 break;
243 *p++ = '\n';
244 write(fd, stat_buf, p - stat_buf);
245 close(fd);
248 rename_or_warn(fstatnew, f_stat);
250 /* supervise compatibility */
251 memset(&status, 0, sizeof(status));
252 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
253 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
254 status.pid_le32 = SWAP_LE32(s->pid);
255 if (s->ctrl & C_PAUSE)
256 status.paused = 1;
257 if (s->sd_want == W_UP)
258 status.want = 'u';
259 else
260 status.want = 'd';
261 if (s->ctrl & C_TERM)
262 status.got_term = 1;
263 status.run_or_finish = s->state;
264 fd = open_trunc_or_warn(fstatusnew);
265 if (fd < 0)
266 return;
267 sz = write(fd, &status, sizeof(status));
268 close(fd);
269 if (sz != sizeof(status)) {
270 warn2_cannot("write ", fstatusnew);
271 unlink(fstatusnew);
272 return;
274 rename_or_warn(fstatusnew, fstatus);
277 static unsigned custom(struct svdir *s, char c)
279 pid_t pid;
280 int w;
281 char a[10];
282 struct stat st;
284 if (s->islog)
285 return 0;
286 strcpy(a, "control/?");
287 a[8] = c; /* replace '?' */
288 if (stat(a, &st) == 0) {
289 if (st.st_mode & S_IXUSR) {
290 pid = vfork();
291 if (pid == -1) {
292 warn2_cannot("vfork for ", a);
293 return 0;
295 if (pid == 0) {
296 /* child */
297 if (haslog && dup2(logpipe.wr, 1) == -1)
298 warn2_cannot("setup stdout for ", a);
299 execl(a, a, (char *) NULL);
300 fatal2_cannot("run ", a);
302 /* parent */
303 if (safe_waitpid(pid, &w, 0) == -1) {
304 warn2_cannot("wait for child ", a);
305 return 0;
307 return WEXITSTATUS(w) == 0;
309 } else {
310 if (errno != ENOENT)
311 warn2_cannot("stat ", a);
313 return 0;
316 static void stopservice(struct svdir *s)
318 if (s->pid && !custom(s, 't')) {
319 kill(s->pid, SIGTERM);
320 s->ctrl |= C_TERM;
321 update_status(s);
323 if (s->sd_want == W_DOWN) {
324 kill(s->pid, SIGCONT);
325 custom(s, 'd');
326 return;
328 if (s->sd_want == W_EXIT) {
329 kill(s->pid, SIGCONT);
330 custom(s, 'x');
334 static void startservice(struct svdir *s)
336 int p;
337 const char *arg[4];
338 char exitcode[sizeof(int)*3 + 2];
340 if (s->state == S_FINISH) {
341 /* Two arguments are given to ./finish. The first one is ./run exit code,
342 * or -1 if ./run didnt exit normally. The second one is
343 * the least significant byte of the exit status as determined by waitpid;
344 * for instance it is 0 if ./run exited normally, and the signal number
345 * if ./run was terminated by a signal. If runsv cannot start ./run
346 * for some reason, the exit code is 111 and the status is 0.
348 arg[0] = "./finish";
349 arg[1] = "-1";
350 if (WIFEXITED(s->wstat)) {
351 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
352 arg[1] = exitcode;
354 //arg[2] = "0";
355 //if (WIFSIGNALED(s->wstat)) {
356 arg[2] = utoa(WTERMSIG(s->wstat));
358 arg[3] = NULL;
359 } else {
360 arg[0] = "./run";
361 arg[1] = NULL;
362 custom(s, 'u');
365 if (s->pid != 0)
366 stopservice(s); /* should never happen */
367 while ((p = vfork()) == -1) {
368 warn_cannot("vfork, sleeping");
369 sleep(5);
371 if (p == 0) {
372 /* child */
373 if (haslog) {
374 /* NB: bug alert! right order is close, then dup2 */
375 if (s->islog) {
376 xchdir("./log");
377 close(logpipe.wr);
378 xdup2(logpipe.rd, 0);
379 } else {
380 close(logpipe.rd);
381 xdup2(logpipe.wr, 1);
384 /* Non-ignored signals revert to SIG_DFL on exec.
385 * But we can get signals BEFORE execl(), unlikely as that may be.
386 * SIGCHLD is safe (would merely write to selfpipe),
387 * but SIGTERM would set sigterm = 1 (with vfork, we affect parent).
388 * Avoid that.
390 /*signal(SIGCHLD, SIG_DFL);*/
391 signal(SIGTERM, SIG_DFL);
392 sig_unblock(SIGCHLD);
393 sig_unblock(SIGTERM);
394 execv(arg[0], (char**) arg);
395 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
397 /* parent */
398 if (s->state != S_FINISH) {
399 gettimeofday_ns(&s->start);
400 s->state = S_RUN;
402 s->pid = p;
403 pidchanged = 1;
404 s->ctrl = C_NOOP;
405 update_status(s);
408 static int ctrl(struct svdir *s, char c)
410 int sig;
412 switch (c) {
413 case 'd': /* down */
414 s->sd_want = W_DOWN;
415 update_status(s);
416 if (s->state == S_RUN)
417 stopservice(s);
418 break;
419 case 'u': /* up */
420 s->sd_want = W_UP;
421 update_status(s);
422 if (s->state == S_DOWN)
423 startservice(s);
424 break;
425 case 'x': /* exit */
426 if (s->islog)
427 break;
428 s->sd_want = W_EXIT;
429 update_status(s);
430 /* FALLTHROUGH */
431 case 't': /* sig term */
432 if (s->state == S_RUN)
433 stopservice(s);
434 break;
435 case 'k': /* sig kill */
436 if ((s->state == S_RUN) && !custom(s, c))
437 kill(s->pid, SIGKILL);
438 s->state = S_DOWN;
439 break;
440 case 'p': /* sig pause */
441 if ((s->state == S_RUN) && !custom(s, c))
442 kill(s->pid, SIGSTOP);
443 s->ctrl |= C_PAUSE;
444 update_status(s);
445 break;
446 case 'c': /* sig cont */
447 if ((s->state == S_RUN) && !custom(s, c))
448 kill(s->pid, SIGCONT);
449 s->ctrl &= ~C_PAUSE;
450 update_status(s);
451 break;
452 case 'o': /* once */
453 s->sd_want = W_DOWN;
454 update_status(s);
455 if (s->state == S_DOWN)
456 startservice(s);
457 break;
458 case 'a': /* sig alarm */
459 sig = SIGALRM;
460 goto sendsig;
461 case 'h': /* sig hup */
462 sig = SIGHUP;
463 goto sendsig;
464 case 'i': /* sig int */
465 sig = SIGINT;
466 goto sendsig;
467 case 'q': /* sig quit */
468 sig = SIGQUIT;
469 goto sendsig;
470 case '1': /* sig usr1 */
471 sig = SIGUSR1;
472 goto sendsig;
473 case '2': /* sig usr2 */
474 sig = SIGUSR2;
475 goto sendsig;
477 return 1;
478 sendsig:
479 if ((s->state == S_RUN) && !custom(s, c))
480 kill(s->pid, sig);
481 return 1;
484 static void open_control(const char *f, struct svdir *s)
486 struct stat st;
487 mkfifo(f, 0600);
488 if (stat(f, &st) == -1)
489 fatal2_cannot("stat ", f);
490 if (!S_ISFIFO(st.st_mode))
491 bb_error_msg_and_die("%s: fatal: %s exists but is not a fifo", dir, f);
492 s->fdcontrol = xopen(f, O_RDONLY|O_NDELAY);
493 close_on_exec_on(s->fdcontrol);
494 s->fdcontrolwrite = xopen(f, O_WRONLY|O_NDELAY);
495 close_on_exec_on(s->fdcontrolwrite);
496 update_status(s);
499 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
500 int runsv_main(int argc UNUSED_PARAM, char **argv)
502 struct stat s;
503 int fd;
504 int r;
505 char buf[256];
507 INIT_G();
509 dir = single_argv(argv);
511 xpiped_pair(selfpipe);
512 close_on_exec_on(selfpipe.rd);
513 close_on_exec_on(selfpipe.wr);
514 ndelay_on(selfpipe.rd);
515 ndelay_on(selfpipe.wr);
517 sig_block(SIGCHLD);
518 sig_block(SIGTERM);
519 /* No particular reason why we don't set SA_RESTART
520 * (poll() wouldn't restart regardless of that flag),
521 * we just follow what runit-2.1.2 does:
523 bb_signals_norestart(0
524 + (1 << SIGCHLD)
525 + (1 << SIGTERM)
526 , s_chld_term);
528 xchdir(dir);
529 /* bss: svd[0].pid = 0; */
530 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
531 if (C_NOOP) svd[0].ctrl = C_NOOP;
532 if (W_UP) svd[0].sd_want = W_UP;
533 /* bss: svd[0].islog = 0; */
534 /* bss: svd[1].pid = 0; */
535 gettimeofday_ns(&svd[0].start);
536 if (stat("down", &s) != -1)
537 svd[0].sd_want = W_DOWN;
539 if (stat("log", &s) == -1) {
540 if (errno != ENOENT)
541 warn_cannot("stat ./log");
542 } else {
543 if (!S_ISDIR(s.st_mode)) {
544 errno = 0;
545 warn_cannot("stat log/down: log is not a directory");
546 } else {
547 haslog = 1;
548 svd[1].state = S_DOWN;
549 svd[1].ctrl = C_NOOP;
550 svd[1].sd_want = W_UP;
551 svd[1].islog = 1;
552 gettimeofday_ns(&svd[1].start);
553 if (stat("log/down", &s) != -1)
554 svd[1].sd_want = W_DOWN;
555 xpiped_pair(logpipe);
556 close_on_exec_on(logpipe.rd);
557 close_on_exec_on(logpipe.wr);
561 if (mkdir("supervise", 0700) == -1) {
562 r = readlink("supervise", buf, sizeof(buf));
563 if (r != -1) {
564 if (r == sizeof(buf))
565 fatal2x_cannot("readlink ./supervise", ": name too long");
566 buf[r] = 0;
567 mkdir(buf, 0700);
568 } else {
569 if ((errno != ENOENT) && (errno != EINVAL))
570 fatal_cannot("readlink ./supervise");
573 svd[0].fdlock = xopen3("log/supervise/lock"+4,
574 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
575 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
576 fatal_cannot("lock supervise/lock");
577 close_on_exec_on(svd[0].fdlock);
578 if (haslog) {
579 if (mkdir("log/supervise", 0700) == -1) {
580 r = readlink("log/supervise", buf, 256);
581 if (r != -1) {
582 if (r == 256)
583 fatal2x_cannot("readlink ./log/supervise", ": name too long");
584 buf[r] = 0;
585 fd = xopen(".", O_RDONLY|O_NDELAY);
586 xchdir("./log");
587 mkdir(buf, 0700);
588 if (fchdir(fd) == -1)
589 fatal_cannot("change back to service directory");
590 close(fd);
592 else {
593 if ((errno != ENOENT) && (errno != EINVAL))
594 fatal_cannot("readlink ./log/supervise");
597 svd[1].fdlock = xopen3("log/supervise/lock",
598 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
599 if (flock(svd[1].fdlock, LOCK_EX) == -1)
600 fatal_cannot("lock log/supervise/lock");
601 close_on_exec_on(svd[1].fdlock);
604 open_control("log/supervise/control"+4, &svd[0]);
605 if (haslog) {
606 open_control("log/supervise/control", &svd[1]);
608 mkfifo("log/supervise/ok"+4, 0600);
609 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
610 close_on_exec_on(fd);
611 if (haslog) {
612 mkfifo("log/supervise/ok", 0600);
613 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
614 close_on_exec_on(fd);
616 for (;;) {
617 struct pollfd x[3];
618 unsigned deadline;
619 char ch;
621 if (haslog)
622 if (!svd[1].pid && svd[1].sd_want == W_UP)
623 startservice(&svd[1]);
624 if (!svd[0].pid)
625 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
626 startservice(&svd[0]);
628 x[0].fd = selfpipe.rd;
629 x[0].events = POLLIN;
630 x[1].fd = svd[0].fdcontrol;
631 x[1].events = POLLIN;
632 /* x[2] is used only if haslog == 1 */
633 x[2].fd = svd[1].fdcontrol;
634 x[2].events = POLLIN;
635 sig_unblock(SIGTERM);
636 sig_unblock(SIGCHLD);
637 poll(x, 2 + haslog, 3600*1000);
638 /* NB: signal handlers can trash errno of poll() */
639 sig_block(SIGTERM);
640 sig_block(SIGCHLD);
642 while (read(selfpipe.rd, &ch, 1) == 1)
643 continue;
645 for (;;) {
646 pid_t child;
647 int wstat;
649 child = wait_any_nohang(&wstat);
650 if (!child)
651 break;
652 if ((child == -1) && (errno != EINTR))
653 break;
654 if (child == svd[0].pid) {
655 svd[0].wstat = wstat;
656 svd[0].pid = 0;
657 pidchanged = 1;
658 svd[0].ctrl &= ~C_TERM;
659 if (svd[0].state != S_FINISH) {
660 fd = open("finish", O_RDONLY|O_NDELAY);
661 if (fd != -1) {
662 close(fd);
663 svd[0].state = S_FINISH;
664 update_status(&svd[0]);
665 continue;
668 svd[0].state = S_DOWN;
669 deadline = svd[0].start.tv_sec + 1;
670 gettimeofday_ns(&svd[0].start);
671 update_status(&svd[0]);
672 if (LESS(svd[0].start.tv_sec, deadline))
673 sleep1();
675 if (haslog) {
676 if (child == svd[1].pid) {
677 svd[0].wstat = wstat;
678 svd[1].pid = 0;
679 pidchanged = 1;
680 svd[1].state = S_DOWN;
681 svd[1].ctrl &= ~C_TERM;
682 deadline = svd[1].start.tv_sec + 1;
683 gettimeofday_ns(&svd[1].start);
684 update_status(&svd[1]);
685 if (LESS(svd[1].start.tv_sec, deadline))
686 sleep1();
689 } /* for (;;) */
690 if (read(svd[0].fdcontrol, &ch, 1) == 1)
691 ctrl(&svd[0], ch);
692 if (haslog)
693 if (read(svd[1].fdcontrol, &ch, 1) == 1)
694 ctrl(&svd[1], ch);
696 if (sigterm) {
697 ctrl(&svd[0], 'x');
698 sigterm = 0;
701 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
702 if (svd[1].pid == 0)
703 _exit_SUCCESS();
704 if (svd[1].sd_want != W_EXIT) {
705 svd[1].sd_want = W_EXIT;
706 /* stopservice(&svd[1]); */
707 update_status(&svd[1]);
708 close(logpipe.wr);
709 close(logpipe.rd);
712 } /* for (;;) */
713 /* not reached */
714 return 0;