1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
40
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
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
62#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
63
64
65#define S_DOWN 0
66#define S_RUN 1
67#define S_FINISH 2
68
69#define C_NOOP 0
70#define C_TERM 1
71#define C_PAUSE 2
72
73#define W_UP 0
74#define W_DOWN 1
75#define W_EXIT 2
76
77struct svdir {
78 int pid;
79 smallint state;
80 smallint ctrl;
81 smallint sd_want;
82 smallint islog;
83 struct timespec start;
84 int fdlock;
85 int fdcontrol;
86 int fdcontrolwrite;
87 int wstat;
88};
89
90struct globals {
91 smallint haslog;
92 smallint sigterm;
93 smallint pidchanged;
94 struct fd_pair selfpipe;
95 struct fd_pair logpipe;
96 char *dir;
97 struct svdir svd[2];
98};
99#define G (*(struct globals*)&bb_common_bufsiz1)
100#define haslog (G.haslog )
101#define sigterm (G.sigterm )
102#define pidchanged (G.pidchanged )
103#define selfpipe (G.selfpipe )
104#define logpipe (G.logpipe )
105#define dir (G.dir )
106#define svd (G.svd )
107#define INIT_G() do { \
108 pidchanged = 1; \
109} while (0)
110
111static void fatal2_cannot(const char *m1, const char *m2)
112{
113 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
114
115}
116static void fatal_cannot(const char *m)
117{
118 fatal2_cannot(m, "");
119
120}
121static void fatal2x_cannot(const char *m1, const char *m2)
122{
123 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
124
125}
126static void warn_cannot(const char *m)
127{
128 bb_perror_msg("%s: warning: cannot %s", dir, m);
129}
130
131static void s_child(int sig_no UNUSED_PARAM)
132{
133 write(selfpipe.wr, "", 1);
134}
135
136static void s_term(int sig_no UNUSED_PARAM)
137{
138 sigterm = 1;
139 write(selfpipe.wr, "", 1);
140}
141
142
143static char *bb_stpcpy(char *p, const char *to_add)
144{
145 while ((*p = *to_add) != '\0') {
146 p++;
147 to_add++;
148 }
149 return p;
150}
151
152static int open_trunc_or_warn(const char *name)
153{
154 int fd = open_trunc(name);
155 if (fd < 0)
156 bb_perror_msg("%s: warning: cannot open %s",
157 dir, name);
158 return fd;
159}
160
161static void update_status(struct svdir *s)
162{
163 ssize_t sz;
164 int fd;
165 svstatus_t status;
166
167
168 if (pidchanged) {
169 fd = open_trunc_or_warn("supervise/pid.new");
170 if (fd < 0)
171 return;
172 if (s->pid) {
173 char spid[sizeof(int)*3 + 2];
174 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
175 write(fd, spid, size);
176 }
177 close(fd);
178 if (rename_or_warn("supervise/pid.new",
179 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
180 return;
181 pidchanged = 0;
182 }
183
184
185 fd = open_trunc_or_warn("supervise/stat.new");
186 if (fd < -1)
187 return;
188
189 {
190 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
191 char *p = stat_buf;
192 switch (s->state) {
193 case S_DOWN:
194 p = bb_stpcpy(p, "down");
195 break;
196 case S_RUN:
197 p = bb_stpcpy(p, "run");
198 break;
199 case S_FINISH:
200 p = bb_stpcpy(p, "finish");
201 break;
202 }
203 if (s->ctrl & C_PAUSE)
204 p = bb_stpcpy(p, ", paused");
205 if (s->ctrl & C_TERM)
206 p = bb_stpcpy(p, ", got TERM");
207 if (s->state != S_DOWN)
208 switch (s->sd_want) {
209 case W_DOWN:
210 p = bb_stpcpy(p, ", want down");
211 break;
212 case W_EXIT:
213 p = bb_stpcpy(p, ", want exit");
214 break;
215 }
216 *p++ = '\n';
217 write(fd, stat_buf, p - stat_buf);
218 close(fd);
219 }
220
221 rename_or_warn("supervise/stat.new",
222 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
223
224
225 memset(&status, 0, sizeof(status));
226 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
227 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
228 status.pid_le32 = SWAP_LE32(s->pid);
229 if (s->ctrl & C_PAUSE)
230 status.paused = 1;
231 if (s->sd_want == W_UP)
232 status.want = 'u';
233 else
234 status.want = 'd';
235 if (s->ctrl & C_TERM)
236 status.got_term = 1;
237 status.run_or_finish = s->state;
238 fd = open_trunc_or_warn("supervise/status.new");
239 if (fd < 0)
240 return;
241 sz = write(fd, &status, sizeof(status));
242 close(fd);
243 if (sz != sizeof(status)) {
244 warn_cannot("write supervise/status.new");
245 unlink("supervise/status.new");
246 return;
247 }
248 rename_or_warn("supervise/status.new",
249 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
250}
251
252static unsigned custom(struct svdir *s, char c)
253{
254 pid_t pid;
255 int w;
256 char a[10];
257 struct stat st;
258
259 if (s->islog)
260 return 0;
261 strcpy(a, "control/?");
262 a[8] = c;
263 if (stat(a, &st) == 0) {
264 if (st.st_mode & S_IXUSR) {
265 pid = vfork();
266 if (pid == -1) {
267 warn_cannot("vfork for control/?");
268 return 0;
269 }
270 if (pid == 0) {
271
272 if (haslog && dup2(logpipe.wr, 1) == -1)
273 warn_cannot("setup stdout for control/?");
274 execl(a, a, (char *) NULL);
275 fatal_cannot("run control/?");
276 }
277
278 if (safe_waitpid(pid, &w, 0) == -1) {
279 warn_cannot("wait for child control/?");
280 return 0;
281 }
282 return WEXITSTATUS(w) == 0;
283 }
284 } else {
285 if (errno != ENOENT)
286 warn_cannot("stat control/?");
287 }
288 return 0;
289}
290
291static void stopservice(struct svdir *s)
292{
293 if (s->pid && !custom(s, 't')) {
294 kill(s->pid, SIGTERM);
295 s->ctrl |= C_TERM;
296 update_status(s);
297 }
298 if (s->sd_want == W_DOWN) {
299 kill(s->pid, SIGCONT);
300 custom(s, 'd');
301 return;
302 }
303 if (s->sd_want == W_EXIT) {
304 kill(s->pid, SIGCONT);
305 custom(s, 'x');
306 }
307}
308
309static void startservice(struct svdir *s)
310{
311 int p;
312 const char *arg[4];
313 char exitcode[sizeof(int)*3 + 2];
314 char sigcode[sizeof(int)*3 + 2];
315
316 if (s->state == S_FINISH) {
317
318
319
320
321
322
323
324 arg[0] = "./finish";
325 arg[1] = "-1";
326 if (WIFEXITED(s->wstat)) {
327 sprintf(exitcode, "%u", (int) WEXITSTATUS(s->wstat));
328 arg[1] = exitcode;
329 }
330
331
332 sprintf(sigcode, "%u", (int) WTERMSIG(s->wstat));
333 arg[2] = sigcode;
334
335 arg[3] = NULL;
336 } else {
337 arg[0] = "./run";
338 arg[1] = NULL;
339 custom(s, 'u');
340 }
341
342 if (s->pid != 0)
343 stopservice(s);
344 while ((p = vfork()) == -1) {
345 warn_cannot("vfork, sleeping");
346 sleep(5);
347 }
348 if (p == 0) {
349
350 if (haslog) {
351
352 if (s->islog) {
353 xchdir("./log");
354 close(logpipe.wr);
355 xdup2(logpipe.rd, 0);
356 } else {
357 close(logpipe.rd);
358 xdup2(logpipe.wr, 1);
359 }
360 }
361
362
363
364
365
366 sig_unblock(SIGCHLD);
367 sig_unblock(SIGTERM);
368 execv(arg[0], (char**) arg);
369 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
370 }
371
372 if (s->state != S_FINISH) {
373 gettimeofday_ns(&s->start);
374 s->state = S_RUN;
375 }
376 s->pid = p;
377 pidchanged = 1;
378 s->ctrl = C_NOOP;
379 update_status(s);
380}
381
382static int ctrl(struct svdir *s, char c)
383{
384 int sig;
385
386 switch (c) {
387 case 'd':
388 s->sd_want = W_DOWN;
389 update_status(s);
390 if (s->pid && s->state != S_FINISH)
391 stopservice(s);
392 break;
393 case 'u':
394 s->sd_want = W_UP;
395 update_status(s);
396 if (s->pid == 0)
397 startservice(s);
398 break;
399 case 'x':
400 if (s->islog)
401 break;
402 s->sd_want = W_EXIT;
403 update_status(s);
404
405 case 't':
406 if (s->pid && s->state != S_FINISH)
407 stopservice(s);
408 break;
409 case 'k':
410 if (s->pid && !custom(s, c))
411 kill(s->pid, SIGKILL);
412 s->state = S_DOWN;
413 break;
414 case 'p':
415 if (s->pid && !custom(s, c))
416 kill(s->pid, SIGSTOP);
417 s->ctrl |= C_PAUSE;
418 update_status(s);
419 break;
420 case 'c':
421 if (s->pid && !custom(s, c))
422 kill(s->pid, SIGCONT);
423 s->ctrl &= ~C_PAUSE;
424 update_status(s);
425 break;
426 case 'o':
427 s->sd_want = W_DOWN;
428 update_status(s);
429 if (!s->pid)
430 startservice(s);
431 break;
432 case 'a':
433 sig = SIGALRM;
434 goto sendsig;
435 case 'h':
436 sig = SIGHUP;
437 goto sendsig;
438 case 'i':
439 sig = SIGINT;
440 goto sendsig;
441 case 'q':
442 sig = SIGQUIT;
443 goto sendsig;
444 case '1':
445 sig = SIGUSR1;
446 goto sendsig;
447 case '2':
448 sig = SIGUSR2;
449 goto sendsig;
450 }
451 return 1;
452 sendsig:
453 if (s->pid && !custom(s, c))
454 kill(s->pid, sig);
455 return 1;
456}
457
458int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
459int runsv_main(int argc UNUSED_PARAM, char **argv)
460{
461 struct stat s;
462 int fd;
463 int r;
464 char buf[256];
465
466 INIT_G();
467
468 if (!argv[1] || argv[2])
469 bb_show_usage();
470 dir = argv[1];
471
472 xpiped_pair(selfpipe);
473 close_on_exec_on(selfpipe.rd);
474 close_on_exec_on(selfpipe.wr);
475 ndelay_on(selfpipe.rd);
476 ndelay_on(selfpipe.wr);
477
478 sig_block(SIGCHLD);
479 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
480 sig_block(SIGTERM);
481 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
482
483 xchdir(dir);
484
485 if (S_DOWN) svd[0].state = S_DOWN;
486 if (C_NOOP) svd[0].ctrl = C_NOOP;
487 if (W_UP) svd[0].sd_want = W_UP;
488
489
490 gettimeofday_ns(&svd[0].start);
491 if (stat("down", &s) != -1)
492 svd[0].sd_want = W_DOWN;
493
494 if (stat("log", &s) == -1) {
495 if (errno != ENOENT)
496 warn_cannot("stat ./log");
497 } else {
498 if (!S_ISDIR(s.st_mode)) {
499 errno = 0;
500 warn_cannot("stat log/down: log is not a directory");
501 } else {
502 haslog = 1;
503 svd[1].state = S_DOWN;
504 svd[1].ctrl = C_NOOP;
505 svd[1].sd_want = W_UP;
506 svd[1].islog = 1;
507 gettimeofday_ns(&svd[1].start);
508 if (stat("log/down", &s) != -1)
509 svd[1].sd_want = W_DOWN;
510 xpiped_pair(logpipe);
511 close_on_exec_on(logpipe.rd);
512 close_on_exec_on(logpipe.wr);
513 }
514 }
515
516 if (mkdir("supervise", 0700) == -1) {
517 r = readlink("supervise", buf, sizeof(buf));
518 if (r != -1) {
519 if (r == sizeof(buf))
520 fatal2x_cannot("readlink ./supervise", ": name too long");
521 buf[r] = 0;
522 mkdir(buf, 0700);
523 } else {
524 if ((errno != ENOENT) && (errno != EINVAL))
525 fatal_cannot("readlink ./supervise");
526 }
527 }
528 svd[0].fdlock = xopen3("log/supervise/lock"+4,
529 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
530 if (lock_exnb(svd[0].fdlock) == -1)
531 fatal_cannot("lock supervise/lock");
532 close_on_exec_on(svd[0].fdlock);
533 if (haslog) {
534 if (mkdir("log/supervise", 0700) == -1) {
535 r = readlink("log/supervise", buf, 256);
536 if (r != -1) {
537 if (r == 256)
538 fatal2x_cannot("readlink ./log/supervise", ": name too long");
539 buf[r] = 0;
540 fd = xopen(".", O_RDONLY|O_NDELAY);
541 xchdir("./log");
542 mkdir(buf, 0700);
543 if (fchdir(fd) == -1)
544 fatal_cannot("change back to service directory");
545 close(fd);
546 }
547 else {
548 if ((errno != ENOENT) && (errno != EINVAL))
549 fatal_cannot("readlink ./log/supervise");
550 }
551 }
552 svd[1].fdlock = xopen3("log/supervise/lock",
553 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
554 if (lock_ex(svd[1].fdlock) == -1)
555 fatal_cannot("lock log/supervise/lock");
556 close_on_exec_on(svd[1].fdlock);
557 }
558
559 mkfifo("log/supervise/control"+4, 0600);
560 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
561 close_on_exec_on(svd[0].fdcontrol);
562 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
563 close_on_exec_on(svd[0].fdcontrolwrite);
564 update_status(&svd[0]);
565 if (haslog) {
566 mkfifo("log/supervise/control", 0600);
567 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
568 close_on_exec_on(svd[1].fdcontrol);
569 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
570 close_on_exec_on(svd[1].fdcontrolwrite);
571 update_status(&svd[1]);
572 }
573 mkfifo("log/supervise/ok"+4, 0600);
574 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
575 close_on_exec_on(fd);
576 if (haslog) {
577 mkfifo("log/supervise/ok", 0600);
578 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
579 close_on_exec_on(fd);
580 }
581 for (;;) {
582 struct pollfd x[3];
583 unsigned deadline;
584 char ch;
585
586 if (haslog)
587 if (!svd[1].pid && svd[1].sd_want == W_UP)
588 startservice(&svd[1]);
589 if (!svd[0].pid)
590 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
591 startservice(&svd[0]);
592
593 x[0].fd = selfpipe.rd;
594 x[0].events = POLLIN;
595 x[1].fd = svd[0].fdcontrol;
596 x[1].events = POLLIN;
597
598 x[2].fd = svd[1].fdcontrol;
599 x[2].events = POLLIN;
600 sig_unblock(SIGTERM);
601 sig_unblock(SIGCHLD);
602 poll(x, 2 + haslog, 3600*1000);
603 sig_block(SIGTERM);
604 sig_block(SIGCHLD);
605
606 while (read(selfpipe.rd, &ch, 1) == 1)
607 continue;
608
609 for (;;) {
610 pid_t child;
611 int wstat;
612
613 child = wait_any_nohang(&wstat);
614 if (!child)
615 break;
616 if ((child == -1) && (errno != EINTR))
617 break;
618 if (child == svd[0].pid) {
619 svd[0].wstat = wstat;
620 svd[0].pid = 0;
621 pidchanged = 1;
622 svd[0].ctrl &= ~C_TERM;
623 if (svd[0].state != S_FINISH) {
624 fd = open_read("finish");
625 if (fd != -1) {
626 close(fd);
627 svd[0].state = S_FINISH;
628 update_status(&svd[0]);
629 continue;
630 }
631 }
632 svd[0].state = S_DOWN;
633 deadline = svd[0].start.tv_sec + 1;
634 gettimeofday_ns(&svd[0].start);
635 update_status(&svd[0]);
636 if (LESS(svd[0].start.tv_sec, deadline))
637 sleep(1);
638 }
639 if (haslog) {
640 if (child == svd[1].pid) {
641 svd[0].wstat = wstat;
642 svd[1].pid = 0;
643 pidchanged = 1;
644 svd[1].state = S_DOWN;
645 svd[1].ctrl &= ~C_TERM;
646 deadline = svd[1].start.tv_sec + 1;
647 gettimeofday_ns(&svd[1].start);
648 update_status(&svd[1]);
649 if (LESS(svd[1].start.tv_sec, deadline))
650 sleep(1);
651 }
652 }
653 }
654 if (read(svd[0].fdcontrol, &ch, 1) == 1)
655 ctrl(&svd[0], ch);
656 if (haslog)
657 if (read(svd[1].fdcontrol, &ch, 1) == 1)
658 ctrl(&svd[1], ch);
659
660 if (sigterm) {
661 ctrl(&svd[0], 'x');
662 sigterm = 0;
663 }
664
665 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
666 if (svd[1].pid == 0)
667 _exit(EXIT_SUCCESS);
668 if (svd[1].sd_want != W_EXIT) {
669 svd[1].sd_want = W_EXIT;
670
671 update_status(&svd[1]);
672 close(logpipe.wr);
673 close(logpipe.rd);
674 }
675 }
676 }
677
678 return 0;
679}
680