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 want;
82 smallint islog;
83 struct timespec start;
84 int fdlock;
85 int fdcontrol;
86 int fdcontrolwrite;
87};
88
89struct globals {
90 smallint haslog;
91 smallint sigterm;
92 smallint pidchanged;
93 struct fd_pair selfpipe;
94 struct fd_pair logpipe;
95 char *dir;
96 struct svdir svd[2];
97};
98#define G (*(struct globals*)&bb_common_bufsiz1)
99#define haslog (G.haslog )
100#define sigterm (G.sigterm )
101#define pidchanged (G.pidchanged )
102#define selfpipe (G.selfpipe )
103#define logpipe (G.logpipe )
104#define dir (G.dir )
105#define svd (G.svd )
106#define INIT_G() do { \
107 pidchanged = 1; \
108} while (0)
109
110static void fatal2_cannot(const char *m1, const char *m2)
111{
112 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
113
114}
115static void fatal_cannot(const char *m)
116{
117 fatal2_cannot(m, "");
118
119}
120static void fatal2x_cannot(const char *m1, const char *m2)
121{
122 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
123
124}
125static void warn_cannot(const char *m)
126{
127 bb_perror_msg("%s: warning: cannot %s", dir, m);
128}
129
130static void s_child(int sig_no UNUSED_PARAM)
131{
132 write(selfpipe.wr, "", 1);
133}
134
135static void s_term(int sig_no UNUSED_PARAM)
136{
137 sigterm = 1;
138 write(selfpipe.wr, "", 1);
139}
140
141static char *add_str(char *p, const char *to_add)
142{
143 while ((*p = *to_add) != '\0') {
144 p++;
145 to_add++;
146 }
147 return p;
148}
149
150static int open_trunc_or_warn(const char *name)
151{
152 int fd = open_trunc(name);
153 if (fd < 0)
154 bb_perror_msg("%s: warning: cannot open %s",
155 dir, name);
156 return fd;
157}
158
159static void update_status(struct svdir *s)
160{
161 ssize_t sz;
162 int fd;
163 svstatus_t status;
164
165
166 if (pidchanged) {
167 fd = open_trunc_or_warn("supervise/pid.new");
168 if (fd < 0)
169 return;
170 if (s->pid) {
171 char spid[sizeof(int)*3 + 2];
172 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
173 write(fd, spid, size);
174 }
175 close(fd);
176 if (rename_or_warn("supervise/pid.new",
177 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
178 return;
179 pidchanged = 0;
180 }
181
182
183 fd = open_trunc_or_warn("supervise/stat.new");
184 if (fd < -1)
185 return;
186
187 {
188 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
189 char *p = stat_buf;
190 switch (s->state) {
191 case S_DOWN:
192 p = add_str(p, "down");
193 break;
194 case S_RUN:
195 p = add_str(p, "run");
196 break;
197 case S_FINISH:
198 p = add_str(p, "finish");
199 break;
200 }
201 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
202 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
203 if (s->state != S_DOWN)
204 switch (s->want) {
205 case W_DOWN:
206 p = add_str(p, ", want down");
207 break;
208 case W_EXIT:
209 p = add_str(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
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->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) return 0;
256 strcpy(a, "control/?");
257 a[8] = c;
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) {
266
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
273 if (safe_waitpid(pid, &w, 0) == -1) {
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 const char *run;
308
309 if (s->state == S_FINISH)
310 run = "./finish";
311 else {
312 run = "./run";
313 custom(s, 'u');
314 }
315
316 if (s->pid != 0)
317 stopservice(s);
318 while ((p = vfork()) == -1) {
319 warn_cannot("vfork, sleeping");
320 sleep(5);
321 }
322 if (p == 0) {
323
324 if (haslog) {
325
326 if (s->islog) {
327 xchdir("./log");
328 close(logpipe.wr);
329 xdup2(logpipe.rd, 0);
330 } else {
331 close(logpipe.rd);
332 xdup2(logpipe.wr, 1);
333 }
334 }
335
336
337
338
339
340 sig_unblock(SIGCHLD);
341 sig_unblock(SIGTERM);
342 execl(run, run, (char *) NULL);
343 fatal2_cannot(s->islog ? "start log/" : "start ", run);
344 }
345
346 if (s->state != S_FINISH) {
347 gettimeofday_ns(&s->start);
348 s->state = S_RUN;
349 }
350 s->pid = p;
351 pidchanged = 1;
352 s->ctrl = C_NOOP;
353 update_status(s);
354}
355
356static int ctrl(struct svdir *s, char c)
357{
358 int sig;
359
360 switch (c) {
361 case 'd':
362 s->want = W_DOWN;
363 update_status(s);
364 if (s->pid && s->state != S_FINISH)
365 stopservice(s);
366 break;
367 case 'u':
368 s->want = W_UP;
369 update_status(s);
370 if (s->pid == 0)
371 startservice(s);
372 break;
373 case 'x':
374 if (s->islog)
375 break;
376 s->want = W_EXIT;
377 update_status(s);
378
379 case 't':
380 if (s->pid && s->state != S_FINISH)
381 stopservice(s);
382 break;
383 case 'k':
384 if (s->pid && !custom(s, c))
385 kill(s->pid, SIGKILL);
386 s->state = S_DOWN;
387 break;
388 case 'p':
389 if (s->pid && !custom(s, c))
390 kill(s->pid, SIGSTOP);
391 s->ctrl |= C_PAUSE;
392 update_status(s);
393 break;
394 case 'c':
395 if (s->pid && !custom(s, c))
396 kill(s->pid, SIGCONT);
397 s->ctrl &= ~C_PAUSE;
398 update_status(s);
399 break;
400 case 'o':
401 s->want = W_DOWN;
402 update_status(s);
403 if (!s->pid)
404 startservice(s);
405 break;
406 case 'a':
407 sig = SIGALRM;
408 goto sendsig;
409 case 'h':
410 sig = SIGHUP;
411 goto sendsig;
412 case 'i':
413 sig = SIGINT;
414 goto sendsig;
415 case 'q':
416 sig = SIGQUIT;
417 goto sendsig;
418 case '1':
419 sig = SIGUSR1;
420 goto sendsig;
421 case '2':
422 sig = SIGUSR2;
423 goto sendsig;
424 }
425 return 1;
426 sendsig:
427 if (s->pid && !custom(s, c))
428 kill(s->pid, sig);
429 return 1;
430}
431
432int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
433int runsv_main(int argc UNUSED_PARAM, char **argv)
434{
435 struct stat s;
436 int fd;
437 int r;
438 char buf[256];
439
440 INIT_G();
441
442 if (!argv[1] || argv[2])
443 bb_show_usage();
444 dir = argv[1];
445
446 xpiped_pair(selfpipe);
447 close_on_exec_on(selfpipe.rd);
448 close_on_exec_on(selfpipe.wr);
449 ndelay_on(selfpipe.rd);
450 ndelay_on(selfpipe.wr);
451
452 sig_block(SIGCHLD);
453 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
454 sig_block(SIGTERM);
455 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
456
457 xchdir(dir);
458
459 if (S_DOWN) svd[0].state = S_DOWN;
460 if (C_NOOP) svd[0].ctrl = C_NOOP;
461 if (W_UP) svd[0].want = W_UP;
462
463
464 gettimeofday_ns(&svd[0].start);
465 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
466
467 if (stat("log", &s) == -1) {
468 if (errno != ENOENT)
469 warn_cannot("stat ./log");
470 } else {
471 if (!S_ISDIR(s.st_mode)) {
472 errno = 0;
473 warn_cannot("stat log/down: log is not a directory");
474 } else {
475 haslog = 1;
476 svd[1].state = S_DOWN;
477 svd[1].ctrl = C_NOOP;
478 svd[1].want = W_UP;
479 svd[1].islog = 1;
480 gettimeofday_ns(&svd[1].start);
481 if (stat("log/down", &s) != -1)
482 svd[1].want = W_DOWN;
483 xpiped_pair(logpipe);
484 close_on_exec_on(logpipe.rd);
485 close_on_exec_on(logpipe.wr);
486 }
487 }
488
489 if (mkdir("supervise", 0700) == -1) {
490 r = readlink("supervise", buf, sizeof(buf));
491 if (r != -1) {
492 if (r == sizeof(buf))
493 fatal2x_cannot("readlink ./supervise", ": name too long");
494 buf[r] = 0;
495 mkdir(buf, 0700);
496 } else {
497 if ((errno != ENOENT) && (errno != EINVAL))
498 fatal_cannot("readlink ./supervise");
499 }
500 }
501 svd[0].fdlock = xopen3("log/supervise/lock"+4,
502 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
503 if (lock_exnb(svd[0].fdlock) == -1)
504 fatal_cannot("lock supervise/lock");
505 close_on_exec_on(svd[0].fdlock);
506 if (haslog) {
507 if (mkdir("log/supervise", 0700) == -1) {
508 r = readlink("log/supervise", buf, 256);
509 if (r != -1) {
510 if (r == 256)
511 fatal2x_cannot("readlink ./log/supervise", ": name too long");
512 buf[r] = 0;
513 fd = xopen(".", O_RDONLY|O_NDELAY);
514 xchdir("./log");
515 mkdir(buf, 0700);
516 if (fchdir(fd) == -1)
517 fatal_cannot("change back to service directory");
518 close(fd);
519 }
520 else {
521 if ((errno != ENOENT) && (errno != EINVAL))
522 fatal_cannot("readlink ./log/supervise");
523 }
524 }
525 svd[1].fdlock = xopen3("log/supervise/lock",
526 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
527 if (lock_ex(svd[1].fdlock) == -1)
528 fatal_cannot("lock log/supervise/lock");
529 close_on_exec_on(svd[1].fdlock);
530 }
531
532 mkfifo("log/supervise/control"+4, 0600);
533 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
534 close_on_exec_on(svd[0].fdcontrol);
535 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
536 close_on_exec_on(svd[0].fdcontrolwrite);
537 update_status(&svd[0]);
538 if (haslog) {
539 mkfifo("log/supervise/control", 0600);
540 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
541 close_on_exec_on(svd[1].fdcontrol);
542 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
543 close_on_exec_on(svd[1].fdcontrolwrite);
544 update_status(&svd[1]);
545 }
546 mkfifo("log/supervise/ok"+4, 0600);
547 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
548 close_on_exec_on(fd);
549 if (haslog) {
550 mkfifo("log/supervise/ok", 0600);
551 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
552 close_on_exec_on(fd);
553 }
554 for (;;) {
555 struct pollfd x[3];
556 unsigned deadline;
557 char ch;
558
559 if (haslog)
560 if (!svd[1].pid && svd[1].want == W_UP)
561 startservice(&svd[1]);
562 if (!svd[0].pid)
563 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
564 startservice(&svd[0]);
565
566 x[0].fd = selfpipe.rd;
567 x[0].events = POLLIN;
568 x[1].fd = svd[0].fdcontrol;
569 x[1].events = POLLIN;
570
571 x[2].fd = svd[1].fdcontrol;
572 x[2].events = POLLIN;
573 sig_unblock(SIGTERM);
574 sig_unblock(SIGCHLD);
575 poll(x, 2 + haslog, 3600*1000);
576 sig_block(SIGTERM);
577 sig_block(SIGCHLD);
578
579 while (read(selfpipe.rd, &ch, 1) == 1)
580 continue;
581
582 for (;;) {
583 pid_t child;
584 int wstat;
585
586 child = wait_any_nohang(&wstat);
587 if (!child)
588 break;
589 if ((child == -1) && (errno != EINTR))
590 break;
591 if (child == svd[0].pid) {
592 svd[0].pid = 0;
593 pidchanged = 1;
594 svd[0].ctrl &=~ C_TERM;
595 if (svd[0].state != S_FINISH) {
596 fd = open_read("finish");
597 if (fd != -1) {
598 close(fd);
599 svd[0].state = S_FINISH;
600 update_status(&svd[0]);
601 continue;
602 }
603 }
604 svd[0].state = S_DOWN;
605 deadline = svd[0].start.tv_sec + 1;
606 gettimeofday_ns(&svd[0].start);
607 update_status(&svd[0]);
608 if (LESS(svd[0].start.tv_sec, deadline))
609 sleep(1);
610 }
611 if (haslog) {
612 if (child == svd[1].pid) {
613 svd[1].pid = 0;
614 pidchanged = 1;
615 svd[1].state = S_DOWN;
616 svd[1].ctrl &= ~C_TERM;
617 deadline = svd[1].start.tv_sec + 1;
618 gettimeofday_ns(&svd[1].start);
619 update_status(&svd[1]);
620 if (LESS(svd[1].start.tv_sec, deadline))
621 sleep(1);
622 }
623 }
624 }
625 if (read(svd[0].fdcontrol, &ch, 1) == 1)
626 ctrl(&svd[0], ch);
627 if (haslog)
628 if (read(svd[1].fdcontrol, &ch, 1) == 1)
629 ctrl(&svd[1], ch);
630
631 if (sigterm) {
632 ctrl(&svd[0], 'x');
633 sigterm = 0;
634 }
635
636 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
637 if (svd[1].pid == 0)
638 _exit(EXIT_SUCCESS);
639 if (svd[1].want != W_EXIT) {
640 svd[1].want = W_EXIT;
641
642 update_status(&svd[1]);
643 close(logpipe.wr);
644 close(logpipe.rd);
645 }
646 }
647 }
648
649 return 0;
650}
651