6d34dc133005de7a4b6022327d5d6deb6f6ad3fc
[platform/upstream/busybox.git] / runit / runsv.c
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, 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
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED 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 #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 */
41 static void gettimeofday_ns(struct timespec *ts)
42 {
43         syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
44 }
45 #else
46 static 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
64 /* state */
65 #define S_DOWN 0
66 #define S_RUN 1
67 #define S_FINISH 2
68 /* ctrl */
69 #define C_NOOP 0
70 #define C_TERM 1
71 #define C_PAUSE 2
72 /* want */
73 #define W_UP 0
74 #define W_DOWN 1
75 #define W_EXIT 2
76
77 struct 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
89 struct 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
110 static 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         /* was exiting 111 */
114 }
115 static void fatal_cannot(const char *m)
116 {
117         fatal2_cannot(m, "");
118         /* was exiting 111 */
119 }
120 static 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         /* was exiting 111 */
124 }
125 static void warn_cannot(const char *m)
126 {
127         bb_perror_msg("%s: warning: cannot %s", dir, m);
128 }
129
130 static void s_child(int sig_no UNUSED_PARAM)
131 {
132         write(selfpipe.wr, "", 1);
133 }
134
135 static void s_term(int sig_no UNUSED_PARAM)
136 {
137         sigterm = 1;
138         write(selfpipe.wr, "", 1); /* XXX */
139 }
140
141 static 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
150 static 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
159 static void update_status(struct svdir *s)
160 {
161         ssize_t sz;
162         int fd;
163         svstatus_t status;
164
165         /* pid */
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         /* stat */
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         /* 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->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
248 static 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; /* 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) {
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 !wait_exitcode(w);
278                 }
279         } else {
280                 if (errno != ENOENT)
281                         warn_cannot("stat control/?");
282         }
283         return 0;
284 }
285
286 static 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
304 static 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); /* should never happen */
318         while ((p = vfork()) == -1) {
319                 warn_cannot("vfork, sleeping");
320                 sleep(5);
321         }
322         if (p == 0) {
323                 /* child */
324                 if (haslog) {
325                         /* NB: bug alert! right order is close, then dup2 */
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                 /* Non-ignored signals revert to SIG_DFL on exec anyway */
336                 /*bb_signals(0
337                         + (1 << SIGCHLD)
338                         + (1 << SIGTERM)
339                         , SIG_DFL);*/
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         /* parent */
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
356 static int ctrl(struct svdir *s, char c)
357 {
358         int sig;
359
360         switch (c) {
361         case 'd': /* down */
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': /* up */
368                 s->want = W_UP;
369                 update_status(s);
370                 if (s->pid == 0)
371                         startservice(s);
372                 break;
373         case 'x': /* exit */
374                 if (s->islog)
375                         break;
376                 s->want = W_EXIT;
377                 update_status(s);
378                 /* FALLTHROUGH */
379         case 't': /* sig term */
380                 if (s->pid && s->state != S_FINISH)
381                         stopservice(s);
382                 break;
383         case 'k': /* sig kill */
384                 if (s->pid && !custom(s, c))
385                         kill(s->pid, SIGKILL);
386                 s->state = S_DOWN;
387                 break;
388         case 'p': /* sig pause */
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': /* sig cont */
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': /* once */
401                 s->want = W_DOWN;
402                 update_status(s);
403                 if (!s->pid)
404                         startservice(s);
405                 break;
406         case 'a': /* sig alarm */
407                 sig = SIGALRM;
408                 goto sendsig;
409         case 'h': /* sig hup */
410                 sig = SIGHUP;
411                 goto sendsig;
412         case 'i': /* sig int */
413                 sig = SIGINT;
414                 goto sendsig;
415         case 'q': /* sig quit */
416                 sig = SIGQUIT;
417                 goto sendsig;
418         case '1': /* sig usr1 */
419                 sig = SIGUSR1;
420                 goto sendsig;
421         case '2': /* sig usr2 */
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
432 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
433 int 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         /* bss: svd[0].pid = 0; */
459         if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
460         if (C_NOOP) svd[0].ctrl = C_NOOP;
461         if (W_UP) svd[0].want = W_UP;
462         /* bss: svd[0].islog = 0; */
463         /* bss: svd[1].pid = 0; */
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                 /* x[2] is used only if haslog == 1 */
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                 } /* for (;;) */
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                                 /* stopservice(&svd[1]); */
642                                 update_status(&svd[1]);
643                                 close(logpipe.wr);
644                                 close(logpipe.rd);
645                         }
646                 }
647         } /* for (;;) */
648         /* not reached */
649         return 0;
650 }