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