init: preparatory patch, no code changes
[oweals/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         char *prog[2];
255
256         if (s->islog) return 0;
257         strcpy(a, "control/?");
258         a[8] = c; /* replace '?' */
259         if (stat(a, &st) == 0) {
260                 if (st.st_mode & S_IXUSR) {
261                         pid = vfork();
262                         if (pid == -1) {
263                                 warn_cannot("vfork for control/?");
264                                 return 0;
265                         }
266                         if (!pid) {
267                                 /* child */
268                                 if (haslog && dup2(logpipe.wr, 1) == -1)
269                                         warn_cannot("setup stdout for control/?");
270                                 prog[0] = a;
271                                 prog[1] = NULL;
272                                 execv(a, prog);
273                                 fatal_cannot("run control/?");
274                         }
275                         /* parent */
276                         while (safe_waitpid(pid, &w, 0) == -1) {
277                                 warn_cannot("wait for child control/?");
278                                 return 0;
279                         }
280                         return !wait_exitcode(w);
281                 }
282         } else {
283                 if (errno != ENOENT)
284                         warn_cannot("stat control/?");
285         }
286         return 0;
287 }
288
289 static void stopservice(struct svdir *s)
290 {
291         if (s->pid && !custom(s, 't')) {
292                 kill(s->pid, SIGTERM);
293                 s->ctrl |= C_TERM;
294                 update_status(s);
295         }
296         if (s->want == W_DOWN) {
297                 kill(s->pid, SIGCONT);
298                 custom(s, 'd');
299                 return;
300         }
301         if (s->want == W_EXIT) {
302                 kill(s->pid, SIGCONT);
303                 custom(s, 'x');
304         }
305 }
306
307 static void startservice(struct svdir *s)
308 {
309         int p;
310         char *run[2];
311
312         if (s->state == S_FINISH)
313                 run[0] = (char*)"./finish";
314         else {
315                 run[0] = (char*)"./run";
316                 custom(s, 'u');
317         }
318         run[1] = NULL;
319
320         if (s->pid != 0)
321                 stopservice(s); /* should never happen */
322         while ((p = vfork()) == -1) {
323                 warn_cannot("vfork, sleeping");
324                 sleep(5);
325         }
326         if (p == 0) {
327                 /* child */
328                 if (haslog) {
329                         /* NB: bug alert! right order is close, then dup2 */
330                         if (s->islog) {
331                                 xchdir("./log");
332                                 close(logpipe.wr);
333                                 xdup2(logpipe.rd, 0);
334                         } else {
335                                 close(logpipe.rd);
336                                 xdup2(logpipe.wr, 1);
337                         }
338                 }
339                 /* Non-ignored signals revert to SIG_DFL on exec anyway */
340                 /*bb_signals(0
341                         + (1 << SIGCHLD)
342                         + (1 << SIGTERM)
343                         , SIG_DFL);*/
344                 sig_unblock(SIGCHLD);
345                 sig_unblock(SIGTERM);
346                 execvp(*run, run);
347                 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
348         }
349         /* parent */
350         if (s->state != S_FINISH) {
351                 gettimeofday_ns(&s->start);
352                 s->state = S_RUN;
353         }
354         s->pid = p;
355         pidchanged = 1;
356         s->ctrl = C_NOOP;
357         update_status(s);
358 }
359
360 static int ctrl(struct svdir *s, char c)
361 {
362         int sig;
363
364         switch (c) {
365         case 'd': /* down */
366                 s->want = W_DOWN;
367                 update_status(s);
368                 if (s->pid && s->state != S_FINISH)
369                         stopservice(s);
370                 break;
371         case 'u': /* up */
372                 s->want = W_UP;
373                 update_status(s);
374                 if (s->pid == 0)
375                         startservice(s);
376                 break;
377         case 'x': /* exit */
378                 if (s->islog)
379                         break;
380                 s->want = W_EXIT;
381                 update_status(s);
382                 /* FALLTHROUGH */
383         case 't': /* sig term */
384                 if (s->pid && s->state != S_FINISH)
385                         stopservice(s);
386                 break;
387         case 'k': /* sig kill */
388                 if (s->pid && !custom(s, c))
389                         kill(s->pid, SIGKILL);
390                 s->state = S_DOWN;
391                 break;
392         case 'p': /* sig pause */
393                 if (s->pid && !custom(s, c))
394                         kill(s->pid, SIGSTOP);
395                 s->ctrl |= C_PAUSE;
396                 update_status(s);
397                 break;
398         case 'c': /* sig cont */
399                 if (s->pid && !custom(s, c))
400                         kill(s->pid, SIGCONT);
401                 if (s->ctrl & C_PAUSE)
402                         s->ctrl &= ~C_PAUSE;
403                 update_status(s);
404                 break;
405         case 'o': /* once */
406                 s->want = W_DOWN;
407                 update_status(s);
408                 if (!s->pid)
409                         startservice(s);
410                 break;
411         case 'a': /* sig alarm */
412                 sig = SIGALRM;
413                 goto sendsig;
414         case 'h': /* sig hup */
415                 sig = SIGHUP;
416                 goto sendsig;
417         case 'i': /* sig int */
418                 sig = SIGINT;
419                 goto sendsig;
420         case 'q': /* sig quit */
421                 sig = SIGQUIT;
422                 goto sendsig;
423         case '1': /* sig usr1 */
424                 sig = SIGUSR1;
425                 goto sendsig;
426         case '2': /* sig usr2 */
427                 sig = SIGUSR2;
428                 goto sendsig;
429         }
430         return 1;
431  sendsig:
432         if (s->pid && !custom(s, c))
433                 kill(s->pid, sig);
434         return 1;
435 }
436
437 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
438 int runsv_main(int argc UNUSED_PARAM, char **argv)
439 {
440         struct stat s;
441         int fd;
442         int r;
443         char buf[256];
444
445         INIT_G();
446
447         if (!argv[1] || argv[2])
448                 bb_show_usage();
449         dir = argv[1];
450
451         xpiped_pair(selfpipe);
452         close_on_exec_on(selfpipe.rd);
453         close_on_exec_on(selfpipe.wr);
454         ndelay_on(selfpipe.rd);
455         ndelay_on(selfpipe.wr);
456
457         sig_block(SIGCHLD);
458         bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
459         sig_block(SIGTERM);
460         bb_signals_recursive_norestart(1 << SIGTERM, s_term);
461
462         xchdir(dir);
463         /* bss: svd[0].pid = 0; */
464         if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
465         if (C_NOOP) svd[0].ctrl = C_NOOP;
466         if (W_UP) svd[0].want = W_UP;
467         /* bss: svd[0].islog = 0; */
468         /* bss: svd[1].pid = 0; */
469         gettimeofday_ns(&svd[0].start);
470         if (stat("down", &s) != -1) svd[0].want = W_DOWN;
471
472         if (stat("log", &s) == -1) {
473                 if (errno != ENOENT)
474                         warn_cannot("stat ./log");
475         } else {
476                 if (!S_ISDIR(s.st_mode)) {
477                         errno = 0;
478                         warn_cannot("stat log/down: log is not a directory");
479                 } else {
480                         haslog = 1;
481                         svd[1].state = S_DOWN;
482                         svd[1].ctrl = C_NOOP;
483                         svd[1].want = W_UP;
484                         svd[1].islog = 1;
485                         gettimeofday_ns(&svd[1].start);
486                         if (stat("log/down", &s) != -1)
487                                 svd[1].want = W_DOWN;
488                         xpiped_pair(logpipe);
489                         close_on_exec_on(logpipe.rd);
490                         close_on_exec_on(logpipe.wr);
491                 }
492         }
493
494         if (mkdir("supervise", 0700) == -1) {
495                 r = readlink("supervise", buf, sizeof(buf));
496                 if (r != -1) {
497                         if (r == sizeof(buf))
498                                 fatal2x_cannot("readlink ./supervise", ": name too long");
499                         buf[r] = 0;
500                         mkdir(buf, 0700);
501                 } else {
502                         if ((errno != ENOENT) && (errno != EINVAL))
503                                 fatal_cannot("readlink ./supervise");
504                 }
505         }
506         svd[0].fdlock = xopen3("log/supervise/lock"+4,
507                         O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
508         if (lock_exnb(svd[0].fdlock) == -1)
509                 fatal_cannot("lock supervise/lock");
510         close_on_exec_on(svd[0].fdlock);
511         if (haslog) {
512                 if (mkdir("log/supervise", 0700) == -1) {
513                         r = readlink("log/supervise", buf, 256);
514                         if (r != -1) {
515                                 if (r == 256)
516                                         fatal2x_cannot("readlink ./log/supervise", ": name too long");
517                                 buf[r] = 0;
518                                 fd = xopen(".", O_RDONLY|O_NDELAY);
519                                 xchdir("./log");
520                                 mkdir(buf, 0700);
521                                 if (fchdir(fd) == -1)
522                                         fatal_cannot("change back to service directory");
523                                 close(fd);
524                         }
525                         else {
526                                 if ((errno != ENOENT) && (errno != EINVAL))
527                                         fatal_cannot("readlink ./log/supervise");
528                         }
529                 }
530                 svd[1].fdlock = xopen3("log/supervise/lock",
531                                 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
532                 if (lock_ex(svd[1].fdlock) == -1)
533                         fatal_cannot("lock log/supervise/lock");
534                 close_on_exec_on(svd[1].fdlock);
535         }
536
537         mkfifo("log/supervise/control"+4, 0600);
538         svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
539         close_on_exec_on(svd[0].fdcontrol);
540         svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
541         close_on_exec_on(svd[0].fdcontrolwrite);
542         update_status(&svd[0]);
543         if (haslog) {
544                 mkfifo("log/supervise/control", 0600);
545                 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
546                 close_on_exec_on(svd[1].fdcontrol);
547                 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
548                 close_on_exec_on(svd[1].fdcontrolwrite);
549                 update_status(&svd[1]);
550         }
551         mkfifo("log/supervise/ok"+4, 0600);
552         fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
553         close_on_exec_on(fd);
554         if (haslog) {
555                 mkfifo("log/supervise/ok", 0600);
556                 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
557                 close_on_exec_on(fd);
558         }
559         for (;;) {
560                 struct pollfd x[3];
561                 unsigned deadline;
562                 char ch;
563
564                 if (haslog)
565                         if (!svd[1].pid && svd[1].want == W_UP)
566                                 startservice(&svd[1]);
567                 if (!svd[0].pid)
568                         if (svd[0].want == W_UP || svd[0].state == S_FINISH)
569                                 startservice(&svd[0]);
570
571                 x[0].fd = selfpipe.rd;
572                 x[0].events = POLLIN;
573                 x[1].fd = svd[0].fdcontrol;
574                 x[1].events = POLLIN;
575                 /* x[2] is used only if haslog == 1 */
576                 x[2].fd = svd[1].fdcontrol;
577                 x[2].events = POLLIN;
578                 sig_unblock(SIGTERM);
579                 sig_unblock(SIGCHLD);
580                 poll(x, 2 + haslog, 3600*1000);
581                 sig_block(SIGTERM);
582                 sig_block(SIGCHLD);
583
584                 while (read(selfpipe.rd, &ch, 1) == 1)
585                         continue;
586
587                 for (;;) {
588                         pid_t child;
589                         int wstat;
590
591                         child = wait_any_nohang(&wstat);
592                         if (!child)
593                                 break;
594                         if ((child == -1) && (errno != EINTR))
595                                 break;
596                         if (child == svd[0].pid) {
597                                 svd[0].pid = 0;
598                                 pidchanged = 1;
599                                 svd[0].ctrl &=~ C_TERM;
600                                 if (svd[0].state != S_FINISH) {
601                                         fd = open_read("finish");
602                                         if (fd != -1) {
603                                                 close(fd);
604                                                 svd[0].state = S_FINISH;
605                                                 update_status(&svd[0]);
606                                                 continue;
607                                         }
608                                 }
609                                 svd[0].state = S_DOWN;
610                                 deadline = svd[0].start.tv_sec + 1;
611                                 gettimeofday_ns(&svd[0].start);
612                                 update_status(&svd[0]);
613                                 if (LESS(svd[0].start.tv_sec, deadline))
614                                         sleep(1);
615                         }
616                         if (haslog) {
617                                 if (child == svd[1].pid) {
618                                         svd[1].pid = 0;
619                                         pidchanged = 1;
620                                         svd[1].state = S_DOWN;
621                                         svd[1].ctrl &= ~C_TERM;
622                                         deadline = svd[1].start.tv_sec + 1;
623                                         gettimeofday_ns(&svd[1].start);
624                                         update_status(&svd[1]);
625                                         if (LESS(svd[1].start.tv_sec, deadline))
626                                                 sleep(1);
627                                 }
628                         }
629                 } /* for (;;) */
630                 if (read(svd[0].fdcontrol, &ch, 1) == 1)
631                         ctrl(&svd[0], ch);
632                 if (haslog)
633                         if (read(svd[1].fdcontrol, &ch, 1) == 1)
634                                 ctrl(&svd[1], ch);
635
636                 if (sigterm) {
637                         ctrl(&svd[0], 'x');
638                         sigterm = 0;
639                 }
640
641                 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
642                         if (svd[1].pid == 0)
643                                 _exit(EXIT_SUCCESS);
644                         if (svd[1].want != W_EXIT) {
645                                 svd[1].want = W_EXIT;
646                                 /* stopservice(&svd[1]); */
647                                 update_status(&svd[1]);
648                                 close(logpipe.wr);
649                                 close(logpipe.rd);
650                         }
651                 }
652         } /* for (;;) */
653         /* not reached */
654         return 0;
655 }