source: MondoRescue/branches/3.2/mindi-busybox/runit/runsv.c

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