summaryrefslogtreecommitdiff
path: root/runit/runsv.c (plain)
blob: d941e897d0baf7917c3a8c084e16211f4590201f
1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
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.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31//usage:#define runsv_trivial_usage
32//usage: "DIR"
33//usage:#define runsv_full_usage "\n\n"
34//usage: "Start and monitor a service and optionally an appendant log service"
35
36#include <sys/file.h>
37#include "libbb.h"
38#include "runit_lib.h"
39
40#if ENABLE_MONOTONIC_SYSCALL
41#include <sys/syscall.h>
42
43/* libc has incredibly messy way of doing this,
44 * typically requiring -lrt. We just skip all this mess */
45static void gettimeofday_ns(struct timespec *ts)
46{
47 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
48}
49#else
50static void gettimeofday_ns(struct timespec *ts)
51{
52 if (sizeof(struct timeval) == sizeof(struct timespec)
53 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
54 ) {
55 /* Cheat */
56 gettimeofday((void*)ts, NULL);
57 ts->tv_nsec *= 1000;
58 } else {
59 extern void BUG_need_to_implement_gettimeofday_ns(void);
60 BUG_need_to_implement_gettimeofday_ns();
61 }
62}
63#endif
64
65/* Compare possibly overflowing unsigned counters */
66#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
67
68/* state */
69#define S_DOWN 0
70#define S_RUN 1
71#define S_FINISH 2
72/* ctrl */
73#define C_NOOP 0
74#define C_TERM 1
75#define C_PAUSE 2
76/* want */
77#define W_UP 0
78#define W_DOWN 1
79#define W_EXIT 2
80
81struct svdir {
82 int pid;
83 smallint state;
84 smallint ctrl;
85 smallint sd_want;
86 smallint islog;
87 struct timespec start;
88 int fdlock;
89 int fdcontrol;
90 int fdcontrolwrite;
91 int wstat;
92};
93
94struct globals {
95 smallint haslog;
96 smallint sigterm;
97 smallint pidchanged;
98 struct fd_pair selfpipe;
99 struct fd_pair logpipe;
100 char *dir;
101 struct svdir svd[2];
102} FIX_ALIASING;
103#define G (*(struct globals*)&bb_common_bufsiz1)
104#define haslog (G.haslog )
105#define sigterm (G.sigterm )
106#define pidchanged (G.pidchanged )
107#define selfpipe (G.selfpipe )
108#define logpipe (G.logpipe )
109#define dir (G.dir )
110#define svd (G.svd )
111#define INIT_G() do { \
112 pidchanged = 1; \
113} while (0)
114
115static void fatal2_cannot(const char *m1, const char *m2)
116{
117 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
118 /* was exiting 111 */
119}
120static void fatal_cannot(const char *m)
121{
122 fatal2_cannot(m, "");
123 /* was exiting 111 */
124}
125static void fatal2x_cannot(const char *m1, const char *m2)
126{
127 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
128 /* was exiting 111 */
129}
130static void warn_cannot(const char *m)
131{
132 bb_perror_msg("%s: warning: cannot %s", dir, m);
133}
134
135static void s_child(int sig_no UNUSED_PARAM)
136{
137 write(selfpipe.wr, "", 1);
138}
139
140static void s_term(int sig_no UNUSED_PARAM)
141{
142 sigterm = 1;
143 write(selfpipe.wr, "", 1); /* XXX */
144}
145
146static int open_trunc_or_warn(const char *name)
147{
148 /* Why O_NDELAY? */
149 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
150 if (fd < 0)
151 bb_perror_msg("%s: warning: cannot open %s",
152 dir, name);
153 return fd;
154}
155
156static void update_status(struct svdir *s)
157{
158 ssize_t sz;
159 int fd;
160 svstatus_t status;
161
162 /* pid */
163 if (pidchanged) {
164 fd = open_trunc_or_warn("supervise/pid.new");
165 if (fd < 0)
166 return;
167 if (s->pid) {
168 char spid[sizeof(int)*3 + 2];
169 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
170 write(fd, spid, size);
171 }
172 close(fd);
173 if (rename_or_warn("supervise/pid.new",
174 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
175 return;
176 pidchanged = 0;
177 }
178
179 /* stat */
180 fd = open_trunc_or_warn("supervise/stat.new");
181 if (fd < -1)
182 return;
183
184 {
185 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
186 char *p = stat_buf;
187 switch (s->state) {
188 case S_DOWN:
189 p = stpcpy(p, "down");
190 break;
191 case S_RUN:
192 p = stpcpy(p, "run");
193 break;
194 case S_FINISH:
195 p = stpcpy(p, "finish");
196 break;
197 }
198 if (s->ctrl & C_PAUSE)
199 p = stpcpy(p, ", paused");
200 if (s->ctrl & C_TERM)
201 p = stpcpy(p, ", got TERM");
202 if (s->state != S_DOWN)
203 switch (s->sd_want) {
204 case W_DOWN:
205 p = stpcpy(p, ", want down");
206 break;
207 case W_EXIT:
208 p = stpcpy(p, ", want exit");
209 break;
210 }
211 *p++ = '\n';
212 write(fd, stat_buf, p - stat_buf);
213 close(fd);
214 }
215
216 rename_or_warn("supervise/stat.new",
217 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
218
219 /* supervise compatibility */
220 memset(&status, 0, sizeof(status));
221 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
222 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
223 status.pid_le32 = SWAP_LE32(s->pid);
224 if (s->ctrl & C_PAUSE)
225 status.paused = 1;
226 if (s->sd_want == W_UP)
227 status.want = 'u';
228 else
229 status.want = 'd';
230 if (s->ctrl & C_TERM)
231 status.got_term = 1;
232 status.run_or_finish = s->state;
233 fd = open_trunc_or_warn("supervise/status.new");
234 if (fd < 0)
235 return;
236 sz = write(fd, &status, sizeof(status));
237 close(fd);
238 if (sz != sizeof(status)) {
239 warn_cannot("write supervise/status.new");
240 unlink("supervise/status.new");
241 return;
242 }
243 rename_or_warn("supervise/status.new",
244 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
245}
246
247static unsigned custom(struct svdir *s, char c)
248{
249 pid_t pid;
250 int w;
251 char a[10];
252 struct stat st;
253
254 if (s->islog)
255 return 0;
256 strcpy(a, "control/?");
257 a[8] = c; /* replace '?' */
258 if (stat(a, &st) == 0) {
259 if (st.st_mode & S_IXUSR) {
260 pid = vfork();
261 if (pid == -1) {
262 warn_cannot("vfork for control/?");
263 return 0;
264 }
265 if (pid == 0) {
266 /* child */
267 if (haslog && dup2(logpipe.wr, 1) == -1)
268 warn_cannot("setup stdout for control/?");
269 execl(a, a, (char *) NULL);
270 fatal_cannot("run control/?");
271 }
272 /* parent */
273 if (safe_waitpid(pid, &w, 0) == -1) {
274 warn_cannot("wait for child control/?");
275 return 0;
276 }
277 return WEXITSTATUS(w) == 0;
278 }
279 } else {
280 if (errno != ENOENT)
281 warn_cannot("stat control/?");
282 }
283 return 0;
284}
285
286static void stopservice(struct svdir *s)
287{
288 if (s->pid && !custom(s, 't')) {
289 kill(s->pid, SIGTERM);
290 s->ctrl |= C_TERM;
291 update_status(s);
292 }
293 if (s->sd_want == W_DOWN) {
294 kill(s->pid, SIGCONT);
295 custom(s, 'd');
296 return;
297 }
298 if (s->sd_want == W_EXIT) {
299 kill(s->pid, SIGCONT);
300 custom(s, 'x');
301 }
302}
303
304static void startservice(struct svdir *s)
305{
306 int p;
307 const char *arg[4];
308 char exitcode[sizeof(int)*3 + 2];
309
310 if (s->state == S_FINISH) {
311/* Two arguments are given to ./finish. The first one is ./run exit code,
312 * or -1 if ./run didnt exit normally. The second one is
313 * the least significant byte of the exit status as determined by waitpid;
314 * for instance it is 0 if ./run exited normally, and the signal number
315 * if ./run was terminated by a signal. If runsv cannot start ./run
316 * for some reason, the exit code is 111 and the status is 0.
317 */
318 arg[0] = "./finish";
319 arg[1] = "-1";
320 if (WIFEXITED(s->wstat)) {
321 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
322 arg[1] = exitcode;
323 }
324 //arg[2] = "0";
325 //if (WIFSIGNALED(s->wstat)) {
326 arg[2] = utoa(WTERMSIG(s->wstat));
327 //}
328 arg[3] = NULL;
329 } else {
330 arg[0] = "./run";
331 arg[1] = NULL;
332 custom(s, 'u');
333 }
334
335 if (s->pid != 0)
336 stopservice(s); /* should never happen */
337 while ((p = vfork()) == -1) {
338 warn_cannot("vfork, sleeping");
339 sleep(5);
340 }
341 if (p == 0) {
342 /* child */
343 if (haslog) {
344 /* NB: bug alert! right order is close, then dup2 */
345 if (s->islog) {
346 xchdir("./log");
347 close(logpipe.wr);
348 xdup2(logpipe.rd, 0);
349 } else {
350 close(logpipe.rd);
351 xdup2(logpipe.wr, 1);
352 }
353 }
354 /* Non-ignored signals revert to SIG_DFL on exec anyway */
355 /*bb_signals(0
356 + (1 << SIGCHLD)
357 + (1 << SIGTERM)
358 , SIG_DFL);*/
359 sig_unblock(SIGCHLD);
360 sig_unblock(SIGTERM);
361 execv(arg[0], (char**) arg);
362 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
363 }
364 /* parent */
365 if (s->state != S_FINISH) {
366 gettimeofday_ns(&s->start);
367 s->state = S_RUN;
368 }
369 s->pid = p;
370 pidchanged = 1;
371 s->ctrl = C_NOOP;
372 update_status(s);
373}
374
375static int ctrl(struct svdir *s, char c)
376{
377 int sig;
378
379 switch (c) {
380 case 'd': /* down */
381 s->sd_want = W_DOWN;
382 update_status(s);
383 if (s->pid && s->state != S_FINISH)
384 stopservice(s);
385 break;
386 case 'u': /* up */
387 s->sd_want = W_UP;
388 update_status(s);
389 if (s->pid == 0)
390 startservice(s);
391 break;
392 case 'x': /* exit */
393 if (s->islog)
394 break;
395 s->sd_want = W_EXIT;
396 update_status(s);
397 /* FALLTHROUGH */
398 case 't': /* sig term */
399 if (s->pid && s->state != S_FINISH)
400 stopservice(s);
401 break;
402 case 'k': /* sig kill */
403 if (s->pid && !custom(s, c))
404 kill(s->pid, SIGKILL);
405 s->state = S_DOWN;
406 break;
407 case 'p': /* sig pause */
408 if (s->pid && !custom(s, c))
409 kill(s->pid, SIGSTOP);
410 s->ctrl |= C_PAUSE;
411 update_status(s);
412 break;
413 case 'c': /* sig cont */
414 if (s->pid && !custom(s, c))
415 kill(s->pid, SIGCONT);
416 s->ctrl &= ~C_PAUSE;
417 update_status(s);
418 break;
419 case 'o': /* once */
420 s->sd_want = W_DOWN;
421 update_status(s);
422 if (!s->pid)
423 startservice(s);
424 break;
425 case 'a': /* sig alarm */
426 sig = SIGALRM;
427 goto sendsig;
428 case 'h': /* sig hup */
429 sig = SIGHUP;
430 goto sendsig;
431 case 'i': /* sig int */
432 sig = SIGINT;
433 goto sendsig;
434 case 'q': /* sig quit */
435 sig = SIGQUIT;
436 goto sendsig;
437 case '1': /* sig usr1 */
438 sig = SIGUSR1;
439 goto sendsig;
440 case '2': /* sig usr2 */
441 sig = SIGUSR2;
442 goto sendsig;
443 }
444 return 1;
445 sendsig:
446 if (s->pid && !custom(s, c))
447 kill(s->pid, sig);
448 return 1;
449}
450
451int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
452int runsv_main(int argc UNUSED_PARAM, char **argv)
453{
454 struct stat s;
455 int fd;
456 int r;
457 char buf[256];
458
459 INIT_G();
460
461 dir = single_argv(argv);
462
463 xpiped_pair(selfpipe);
464 close_on_exec_on(selfpipe.rd);
465 close_on_exec_on(selfpipe.wr);
466 ndelay_on(selfpipe.rd);
467 ndelay_on(selfpipe.wr);
468
469 sig_block(SIGCHLD);
470 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
471 sig_block(SIGTERM);
472 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
473
474 xchdir(dir);
475 /* bss: svd[0].pid = 0; */
476 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
477 if (C_NOOP) svd[0].ctrl = C_NOOP;
478 if (W_UP) svd[0].sd_want = W_UP;
479 /* bss: svd[0].islog = 0; */
480 /* bss: svd[1].pid = 0; */
481 gettimeofday_ns(&svd[0].start);
482 if (stat("down", &s) != -1)
483 svd[0].sd_want = W_DOWN;
484
485 if (stat("log", &s) == -1) {
486 if (errno != ENOENT)
487 warn_cannot("stat ./log");
488 } else {
489 if (!S_ISDIR(s.st_mode)) {
490 errno = 0;
491 warn_cannot("stat log/down: log is not a directory");
492 } else {
493 haslog = 1;
494 svd[1].state = S_DOWN;
495 svd[1].ctrl = C_NOOP;
496 svd[1].sd_want = W_UP;
497 svd[1].islog = 1;
498 gettimeofday_ns(&svd[1].start);
499 if (stat("log/down", &s) != -1)
500 svd[1].sd_want = W_DOWN;
501 xpiped_pair(logpipe);
502 close_on_exec_on(logpipe.rd);
503 close_on_exec_on(logpipe.wr);
504 }
505 }
506
507 if (mkdir("supervise", 0700) == -1) {
508 r = readlink("supervise", buf, sizeof(buf));
509 if (r != -1) {
510 if (r == sizeof(buf))
511 fatal2x_cannot("readlink ./supervise", ": name too long");
512 buf[r] = 0;
513 mkdir(buf, 0700);
514 } else {
515 if ((errno != ENOENT) && (errno != EINVAL))
516 fatal_cannot("readlink ./supervise");
517 }
518 }
519 svd[0].fdlock = xopen3("log/supervise/lock"+4,
520 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
521 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
522 fatal_cannot("lock supervise/lock");
523 close_on_exec_on(svd[0].fdlock);
524 if (haslog) {
525 if (mkdir("log/supervise", 0700) == -1) {
526 r = readlink("log/supervise", buf, 256);
527 if (r != -1) {
528 if (r == 256)
529 fatal2x_cannot("readlink ./log/supervise", ": name too long");
530 buf[r] = 0;
531 fd = xopen(".", O_RDONLY|O_NDELAY);
532 xchdir("./log");
533 mkdir(buf, 0700);
534 if (fchdir(fd) == -1)
535 fatal_cannot("change back to service directory");
536 close(fd);
537 }
538 else {
539 if ((errno != ENOENT) && (errno != EINVAL))
540 fatal_cannot("readlink ./log/supervise");
541 }
542 }
543 svd[1].fdlock = xopen3("log/supervise/lock",
544 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
545 if (flock(svd[1].fdlock, LOCK_EX) == -1)
546 fatal_cannot("lock log/supervise/lock");
547 close_on_exec_on(svd[1].fdlock);
548 }
549
550 mkfifo("log/supervise/control"+4, 0600);
551 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
552 close_on_exec_on(svd[0].fdcontrol);
553 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
554 close_on_exec_on(svd[0].fdcontrolwrite);
555 update_status(&svd[0]);
556 if (haslog) {
557 mkfifo("log/supervise/control", 0600);
558 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
559 close_on_exec_on(svd[1].fdcontrol);
560 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
561 close_on_exec_on(svd[1].fdcontrolwrite);
562 update_status(&svd[1]);
563 }
564 mkfifo("log/supervise/ok"+4, 0600);
565 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
566 close_on_exec_on(fd);
567 if (haslog) {
568 mkfifo("log/supervise/ok", 0600);
569 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
570 close_on_exec_on(fd);
571 }
572 for (;;) {
573 struct pollfd x[3];
574 unsigned deadline;
575 char ch;
576
577 if (haslog)
578 if (!svd[1].pid && svd[1].sd_want == W_UP)
579 startservice(&svd[1]);
580 if (!svd[0].pid)
581 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
582 startservice(&svd[0]);
583
584 x[0].fd = selfpipe.rd;
585 x[0].events = POLLIN;
586 x[1].fd = svd[0].fdcontrol;
587 x[1].events = POLLIN;
588 /* x[2] is used only if haslog == 1 */
589 x[2].fd = svd[1].fdcontrol;
590 x[2].events = POLLIN;
591 sig_unblock(SIGTERM);
592 sig_unblock(SIGCHLD);
593 poll(x, 2 + haslog, 3600*1000);
594 sig_block(SIGTERM);
595 sig_block(SIGCHLD);
596
597 while (read(selfpipe.rd, &ch, 1) == 1)
598 continue;
599
600 for (;;) {
601 pid_t child;
602 int wstat;
603
604 child = wait_any_nohang(&wstat);
605 if (!child)
606 break;
607 if ((child == -1) && (errno != EINTR))
608 break;
609 if (child == svd[0].pid) {
610 svd[0].wstat = wstat;
611 svd[0].pid = 0;
612 pidchanged = 1;
613 svd[0].ctrl &= ~C_TERM;
614 if (svd[0].state != S_FINISH) {
615 fd = open("finish", O_RDONLY|O_NDELAY);
616 if (fd != -1) {
617 close(fd);
618 svd[0].state = S_FINISH;
619 update_status(&svd[0]);
620 continue;
621 }
622 }
623 svd[0].state = S_DOWN;
624 deadline = svd[0].start.tv_sec + 1;
625 gettimeofday_ns(&svd[0].start);
626 update_status(&svd[0]);
627 if (LESS(svd[0].start.tv_sec, deadline))
628 sleep(1);
629 }
630 if (haslog) {
631 if (child == svd[1].pid) {
632 svd[0].wstat = wstat;
633 svd[1].pid = 0;
634 pidchanged = 1;
635 svd[1].state = S_DOWN;
636 svd[1].ctrl &= ~C_TERM;
637 deadline = svd[1].start.tv_sec + 1;
638 gettimeofday_ns(&svd[1].start);
639 update_status(&svd[1]);
640 if (LESS(svd[1].start.tv_sec, deadline))
641 sleep(1);
642 }
643 }
644 } /* for (;;) */
645 if (read(svd[0].fdcontrol, &ch, 1) == 1)
646 ctrl(&svd[0], ch);
647 if (haslog)
648 if (read(svd[1].fdcontrol, &ch, 1) == 1)
649 ctrl(&svd[1], ch);
650
651 if (sigterm) {
652 ctrl(&svd[0], 'x');
653 sigterm = 0;
654 }
655
656 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
657 if (svd[1].pid == 0)
658 _exit(EXIT_SUCCESS);
659 if (svd[1].sd_want != W_EXIT) {
660 svd[1].sd_want = W_EXIT;
661 /* stopservice(&svd[1]); */
662 update_status(&svd[1]);
663 close(logpipe.wr);
664 close(logpipe.rd);
665 }
666 }
667 } /* for (;;) */
668 /* not reached */
669 return 0;
670}
671