source: MondoRescue/branches/stable/mindi-busybox/runit/runsv.c@ 1770

Last change on this file since 1770 was 1765, checked in by Bruno Cornec, 16 years ago

Update to busybox 1.7.2

  • Property svn:eol-style set to native
File size: 14.6 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 Denis Vlasenko <vda.linux@googlemail.com> */
29/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31#include <sys/poll.h>
32#include <sys/file.h>
33#include "libbb.h"
34#include "runit_lib.h"
35
36#if ENABLE_MONOTONIC_SYSCALL
37#include <sys/syscall.h>
38
39/* libc has incredibly messy way of doing this,
40 * typically requiring -lrt. We just skip all this mess */
41static void gettimeofday_ns(struct timespec *ts)
42{
43 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
44}
45#else
46static void gettimeofday_ns(struct timespec *ts)
47{
48 if (sizeof(struct timeval) == sizeof(struct timespec)
49 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
50 ) {
51 /* Cheat */
52 gettimeofday((void*)ts, NULL);
53 ts->tv_nsec *= 1000;
54 } else {
55 extern void BUG_need_to_implement_gettimeofday_ns(void);
56 BUG_need_to_implement_gettimeofday_ns();
57 }
58}
59#endif
60
61/* Compare possibly overflowing unsigned counters */
62#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
63
64static int selfpipe[2];
65
66/* state */
67#define S_DOWN 0
68#define S_RUN 1
69#define S_FINISH 2
70/* ctrl */
71#define C_NOOP 0
72#define C_TERM 1
73#define C_PAUSE 2
74/* want */
75#define W_UP 0
76#define W_DOWN 1
77#define W_EXIT 2
78
79struct svdir {
80 int pid;
81 smallint state;
82 smallint ctrl;
83 smallint want;
84 smallint islog;
85 struct timespec start;
86 int fdlock;
87 int fdcontrol;
88 int fdcontrolwrite;
89};
90
91static struct svdir svd[2];
92static smallint sigterm;
93static smallint haslog;
94static smallint pidchanged = 1;
95static int logpipe[2];
96static char *dir;
97
98static void fatal2_cannot(const char *m1, const char *m2)
99{
100 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
101 /* was exiting 111 */
102}
103static void fatal_cannot(const char *m)
104{
105 fatal2_cannot(m, "");
106 /* was exiting 111 */
107}
108static void fatal2x_cannot(const char *m1, const char *m2)
109{
110 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
111 /* was exiting 111 */
112}
113static void warn_cannot(const char *m)
114{
115 bb_perror_msg("%s: warning: cannot %s", dir, m);
116}
117
118static void s_child(int sig_no)
119{
120 write(selfpipe[1], "", 1);
121}
122
123static void s_term(int sig_no)
124{
125 sigterm = 1;
126 write(selfpipe[1], "", 1); /* XXX */
127}
128
129static char *add_str(char *p, const char *to_add)
130{
131 while ((*p = *to_add) != '\0') {
132 p++;
133 to_add++;
134 }
135 return p;
136}
137
138static int open_trunc_or_warn(const char *name)
139{
140 int fd = open_trunc(name);
141 if (fd < 0)
142 bb_perror_msg("%s: warning: cannot open %s",
143 dir, name);
144 return fd;
145}
146
147static int rename_or_warn(const char *old, const char *new)
148{
149 if (rename(old, new) == -1) {
150 bb_perror_msg("%s: warning: cannot rename %s to %s",
151 dir, old, new);
152 return -1;
153 }
154 return 0;
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 = add_str(p, "down");
191 break;
192 case S_RUN:
193 p = add_str(p, "run");
194 break;
195 case S_FINISH:
196 p = add_str(p, "finish");
197 break;
198 }
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)
202 switch (s->want) {
203 case W_DOWN:
204 p = add_str(p, ", want down");
205 break;
206 case W_EXIT:
207 p = add_str(p, ", want exit");
208 break;
209 }
210 *p++ = '\n';
211 write(fd, stat_buf, p - stat_buf);
212 close(fd);
213 }
214
215 rename_or_warn("supervise/stat.new",
216 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
217
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)
224 status.paused = 1;
225 if (s->want == W_UP)
226 status.want = 'u';
227 else
228 status.want = 'd';
229 if (s->ctrl & C_TERM)
230 status.got_term = 1;
231 status.run_or_finish = s->state;
232 fd = open_trunc_or_warn("supervise/status.new");
233 if (fd < 0)
234 return;
235 sz = write(fd, &status, sizeof(status));
236 close(fd);
237 if (sz != sizeof(status)) {
238 warn_cannot("write supervise/status.new");
239 unlink("supervise/status.new");
240 return;
241 }
242 rename_or_warn("supervise/status.new",
243 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
244}
245
246static unsigned custom(struct svdir *s, char c)
247{
248 int pid;
249 int w;
250 char a[10];
251 struct stat st;
252 char *prog[2];
253
254 if (s->islog) return 0;
255 strcpy(a, "control/?");
256 a[8] = c;
257 if (stat(a, &st) == 0) {
258 if (st.st_mode & S_IXUSR) {
259 pid = fork();
260 if (pid == -1) {
261 warn_cannot("fork for control/?");
262 return 0;
263 }
264 if (!pid) {
265 if (haslog && dup2(logpipe[1], 1) == -1)
266 warn_cannot("setup stdout for control/?");
267 prog[0] = a;
268 prog[1] = NULL;
269 execve(a, prog, environ);
270 fatal_cannot("run control/?");
271 }
272 while (wait_pid(&w, pid) == -1) {
273 if (errno == EINTR) continue;
274 warn_cannot("wait for child control/?");
275 return 0;
276 }
277 return !wait_exitcode(w);
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->want == W_DOWN) {
294 kill(s->pid, SIGCONT);
295 custom(s, 'd');
296 return;
297 }
298 if (s->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 char *run[2];
308
309 if (s->state == S_FINISH)
310 run[0] = (char*)"./finish";
311 else {
312 run[0] = (char*)"./run";
313 custom(s, 'u');
314 }
315 run[1] = NULL;
316
317 if (s->pid != 0)
318 stopservice(s); /* should never happen */
319 while ((p = fork()) == -1) {
320 warn_cannot("fork, sleeping");
321 sleep(5);
322 }
323 if (p == 0) {
324 /* child */
325 if (haslog) {
326 if (s->islog) {
327 xdup2(logpipe[0], 0);
328 close(logpipe[1]);
329 xchdir("./log");
330 } else {
331 xdup2(logpipe[1], 1);
332 close(logpipe[0]);
333 }
334 }
335 signal(SIGCHLD, SIG_DFL);
336 signal(SIGTERM, SIG_DFL);
337 sig_unblock(SIGCHLD);
338 sig_unblock(SIGTERM);
339 execvp(*run, run);
340 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
341 }
342 if (s->state != S_FINISH) {
343 gettimeofday_ns(&s->start);
344 s->state = S_RUN;
345 }
346 s->pid = p;
347 pidchanged = 1;
348 s->ctrl = C_NOOP;
349 update_status(s);
350}
351
352static int ctrl(struct svdir *s, char c)
353{
354 int sig;
355
356 switch (c) {
357 case 'd': /* down */
358 s->want = W_DOWN;
359 update_status(s);
360 if (s->pid && s->state != S_FINISH)
361 stopservice(s);
362 break;
363 case 'u': /* up */
364 s->want = W_UP;
365 update_status(s);
366 if (s->pid == 0)
367 startservice(s);
368 break;
369 case 'x': /* exit */
370 if (s->islog)
371 break;
372 s->want = W_EXIT;
373 update_status(s);
374 /* FALLTHROUGH */
375 case 't': /* sig term */
376 if (s->pid && s->state != S_FINISH)
377 stopservice(s);
378 break;
379 case 'k': /* sig kill */
380 if (s->pid && !custom(s, c))
381 kill(s->pid, SIGKILL);
382 s->state = S_DOWN;
383 break;
384 case 'p': /* sig pause */
385 if (s->pid && !custom(s, c))
386 kill(s->pid, SIGSTOP);
387 s->ctrl |= C_PAUSE;
388 update_status(s);
389 break;
390 case 'c': /* sig cont */
391 if (s->pid && !custom(s, c))
392 kill(s->pid, SIGCONT);
393 if (s->ctrl & C_PAUSE)
394 s->ctrl &= ~C_PAUSE;
395 update_status(s);
396 break;
397 case 'o': /* once */
398 s->want = W_DOWN;
399 update_status(s);
400 if (!s->pid)
401 startservice(s);
402 break;
403 case 'a': /* sig alarm */
404 sig = SIGALRM;
405 goto sendsig;
406 case 'h': /* sig hup */
407 sig = SIGHUP;
408 goto sendsig;
409 case 'i': /* sig int */
410 sig = SIGINT;
411 goto sendsig;
412 case 'q': /* sig quit */
413 sig = SIGQUIT;
414 goto sendsig;
415 case '1': /* sig usr1 */
416 sig = SIGUSR1;
417 goto sendsig;
418 case '2': /* sig usr2 */
419 sig = SIGUSR2;
420 goto sendsig;
421 }
422 return 1;
423 sendsig:
424 if (s->pid && !custom(s, c))
425 kill(s->pid, sig);
426 return 1;
427}
428
429int runsv_main(int argc, char **argv);
430int runsv_main(int argc, char **argv)
431{
432 struct stat s;
433 int fd;
434 int r;
435 char buf[256];
436
437 if (!argv[1] || argv[2])
438 bb_show_usage();
439 dir = argv[1];
440
441 xpipe(selfpipe);
442 coe(selfpipe[0]);
443 coe(selfpipe[1]);
444 ndelay_on(selfpipe[0]);
445 ndelay_on(selfpipe[1]);
446
447 sig_block(SIGCHLD);
448 sig_catch(SIGCHLD, s_child);
449 sig_block(SIGTERM);
450 sig_catch(SIGTERM, s_term);
451
452 xchdir(dir);
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;
461
462 if (stat("log", &s) == -1) {
463 if (errno != ENOENT)
464 warn_cannot("stat ./log");
465 } else {
466 if (!S_ISDIR(s.st_mode)) {
467 errno = 0;
468 warn_cannot("stat log/down: log is not a directory");
469 } else {
470 haslog = 1;
471 svd[1].state = S_DOWN;
472 svd[1].ctrl = C_NOOP;
473 svd[1].want = W_UP;
474 svd[1].islog = 1;
475 gettimeofday_ns(&svd[1].start);
476 if (stat("log/down", &s) != -1)
477 svd[1].want = W_DOWN;
478 xpipe(logpipe);
479 coe(logpipe[0]);
480 coe(logpipe[1]);
481 }
482 }
483
484 if (mkdir("supervise", 0700) == -1) {
485 r = readlink("supervise", buf, sizeof(buf));
486 if (r != -1) {
487 if (r == sizeof(buf))
488 fatal2x_cannot("readlink ./supervise", ": name too long");
489 buf[r] = 0;
490 mkdir(buf, 0700);
491 } else {
492 if ((errno != ENOENT) && (errno != EINVAL))
493 fatal_cannot("readlink ./supervise");
494 }
495 }
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");
500 coe(svd[0].fdlock);
501 if (haslog) {
502 if (mkdir("log/supervise", 0700) == -1) {
503 r = readlink("log/supervise", buf, 256);
504 if (r != -1) {
505 if (r == 256)
506 fatal2x_cannot("readlink ./log/supervise", ": name too long");
507 buf[r] = 0;
508 fd = xopen(".", O_RDONLY|O_NDELAY);
509 xchdir("./log");
510 mkdir(buf, 0700);
511 if (fchdir(fd) == -1)
512 fatal_cannot("change back to service directory");
513 close(fd);
514 }
515 else {
516 if ((errno != ENOENT) && (errno != EINVAL))
517 fatal_cannot("readlink ./log/supervise");
518 }
519 }
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");
524 coe(svd[1].fdlock);
525 }
526
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]);
533 if (haslog) {
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]);
540 }
541 mkfifo("log/supervise/ok"+4, 0600);
542 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
543 coe(fd);
544 if (haslog) {
545 mkfifo("log/supervise/ok", 0600);
546 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
547 coe(fd);
548 }
549 for (;;) {
550 struct pollfd x[3];
551 unsigned deadline;
552 char ch;
553
554 if (haslog)
555 if (!svd[1].pid && svd[1].want == W_UP)
556 startservice(&svd[1]);
557 if (!svd[0].pid)
558 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
559 startservice(&svd[0]);
560
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);
571 sig_block(SIGTERM);
572 sig_block(SIGCHLD);
573
574 while (read(selfpipe[0], &ch, 1) == 1)
575 continue;
576
577 for (;;) {
578 int child;
579 int wstat;
580
581 child = wait_nohang(&wstat);
582 if (!child)
583 break;
584 if ((child == -1) && (errno != EINTR))
585 break;
586 if (child == svd[0].pid) {
587 svd[0].pid = 0;
588 pidchanged = 1;
589 svd[0].ctrl &=~ C_TERM;
590 if (svd[0].state != S_FINISH) {
591 fd = open_read("finish");
592 if (fd != -1) {
593 close(fd);
594 svd[0].state = S_FINISH;
595 update_status(&svd[0]);
596 continue;
597 }
598 }
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))
604 sleep(1);
605 }
606 if (haslog) {
607 if (child == svd[1].pid) {
608 svd[1].pid = 0;
609 pidchanged = 1;
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))
616 sleep(1);
617 }
618 }
619 }
620 if (read(svd[0].fdcontrol, &ch, 1) == 1)
621 ctrl(&svd[0], ch);
622 if (haslog)
623 if (read(svd[1].fdcontrol, &ch, 1) == 1)
624 ctrl(&svd[1], ch);
625
626 if (sigterm) {
627 ctrl(&svd[0], 'x');
628 sigterm = 0;
629 }
630
631 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
632 if (svd[1].pid == 0)
633 _exit(0);
634 if (svd[1].want != W_EXIT) {
635 svd[1].want = W_EXIT;
636 /* stopservice(&svd[1]); */
637 update_status(&svd[1]);
638 close(logpipe[1]);
639 close(logpipe[0]);
640 }
641 }
642 }
643 /* not reached */
644 return 0;
645}
Note: See TracBrowser for help on using the repository browser.