runsvdir: conditionalize "log in argiment" (mis)feature. By Vladimir.
[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         int 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                 bb_signals(0
340                         + (1 << SIGCHLD)
341                         + (1 << SIGTERM)
342                         , SIG_DFL);
343                 sig_unblock(SIGCHLD);
344                 sig_unblock(SIGTERM);
345                 execvp(*run, run);
346                 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
347         }
348         /* parent */
349         if (s->state != S_FINISH) {
350                 gettimeofday_ns(&s->start);
351                 s->state = S_RUN;
352         }
353         s->pid = p;
354         pidchanged = 1;
355         s->ctrl = C_NOOP;
356         update_status(s);
357 }
358
359 static int ctrl(struct svdir *s, char c)
360 {
361         int sig;
362
363         switch (c) {
364         case 'd': /* down */
365                 s->want = W_DOWN;
366                 update_status(s);
367                 if (s->pid && s->state != S_FINISH)
368                         stopservice(s);
369                 break;
370         case 'u': /* up */
371                 s->want = W_UP;
372                 update_status(s);
373                 if (s->pid == 0)
374                         startservice(s);
375                 break;
376         case 'x': /* exit */
377                 if (s->islog)
378                         break;
379                 s->want = W_EXIT;
380                 update_status(s);
381                 /* FALLTHROUGH */
382         case 't': /* sig term */
383                 if (s->pid && s->state != S_FINISH)
384                         stopservice(s);
385                 break;
386         case 'k': /* sig kill */
387                 if (s->pid && !custom(s, c))
388                         kill(s->pid, SIGKILL);
389                 s->state = S_DOWN;
390                 break;
391         case 'p': /* sig pause */
392                 if (s->pid && !custom(s, c))
393                         kill(s->pid, SIGSTOP);
394                 s->ctrl |= C_PAUSE;
395                 update_status(s);
396                 break;
397         case 'c': /* sig cont */
398                 if (s->pid && !custom(s, c))
399                         kill(s->pid, SIGCONT);
400                 if (s->ctrl & C_PAUSE)
401                         s->ctrl &= ~C_PAUSE;
402                 update_status(s);
403                 break;
404         case 'o': /* once */
405                 s->want = W_DOWN;
406                 update_status(s);
407                 if (!s->pid)
408                         startservice(s);
409                 break;
410         case 'a': /* sig alarm */
411                 sig = SIGALRM;
412                 goto sendsig;
413         case 'h': /* sig hup */
414                 sig = SIGHUP;
415                 goto sendsig;
416         case 'i': /* sig int */
417                 sig = SIGINT;
418                 goto sendsig;
419         case 'q': /* sig quit */
420                 sig = SIGQUIT;
421                 goto sendsig;
422         case '1': /* sig usr1 */
423                 sig = SIGUSR1;
424                 goto sendsig;
425         case '2': /* sig usr2 */
426                 sig = SIGUSR2;
427                 goto sendsig;
428         }
429         return 1;
430  sendsig:
431         if (s->pid && !custom(s, c))
432                 kill(s->pid, sig);
433         return 1;
434 }
435
436 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
437 int runsv_main(int argc UNUSED_PARAM, char **argv)
438 {
439         struct stat s;
440         int fd;
441         int r;
442         char buf[256];
443
444         INIT_G();
445
446         if (!argv[1] || argv[2])
447                 bb_show_usage();
448         dir = argv[1];
449
450         xpiped_pair(selfpipe);
451         close_on_exec_on(selfpipe.rd);
452         close_on_exec_on(selfpipe.wr);
453         ndelay_on(selfpipe.rd);
454         ndelay_on(selfpipe.wr);
455
456         sig_block(SIGCHLD);
457         bb_signals_recursive(1 << SIGCHLD, s_child);
458         sig_block(SIGTERM);
459         bb_signals_recursive(1 << SIGTERM, s_term);
460
461         xchdir(dir);
462         /* bss: svd[0].pid = 0; */
463         if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
464         if (C_NOOP) svd[0].ctrl = C_NOOP;
465         if (W_UP) svd[0].want = W_UP;
466         /* bss: svd[0].islog = 0; */
467         /* bss: svd[1].pid = 0; */
468         gettimeofday_ns(&svd[0].start);
469         if (stat("down", &s) != -1) svd[0].want = W_DOWN;
470
471         if (stat("log", &s) == -1) {
472                 if (errno != ENOENT)
473                         warn_cannot("stat ./log");
474         } else {
475                 if (!S_ISDIR(s.st_mode)) {
476                         errno = 0;
477                         warn_cannot("stat log/down: log is not a directory");
478                 } else {
479                         haslog = 1;
480                         svd[1].state = S_DOWN;
481                         svd[1].ctrl = C_NOOP;
482                         svd[1].want = W_UP;
483                         svd[1].islog = 1;
484                         gettimeofday_ns(&svd[1].start);
485                         if (stat("log/down", &s) != -1)
486                                 svd[1].want = W_DOWN;
487                         xpiped_pair(logpipe);
488                         close_on_exec_on(logpipe.rd);
489                         close_on_exec_on(logpipe.wr);
490                 }
491         }
492
493         if (mkdir("supervise", 0700) == -1) {
494                 r = readlink("supervise", buf, sizeof(buf));
495                 if (r != -1) {
496                         if (r == sizeof(buf))
497                                 fatal2x_cannot("readlink ./supervise", ": name too long");
498                         buf[r] = 0;
499                         mkdir(buf, 0700);
500                 } else {
501                         if ((errno != ENOENT) && (errno != EINVAL))
502                                 fatal_cannot("readlink ./supervise");
503                 }
504         }
505         svd[0].fdlock = xopen3("log/supervise/lock"+4,
506                         O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
507         if (lock_exnb(svd[0].fdlock) == -1)
508                 fatal_cannot("lock supervise/lock");
509         close_on_exec_on(svd[0].fdlock);
510         if (haslog) {
511                 if (mkdir("log/supervise", 0700) == -1) {
512                         r = readlink("log/supervise", buf, 256);
513                         if (r != -1) {
514                                 if (r == 256)
515                                         fatal2x_cannot("readlink ./log/supervise", ": name too long");
516                                 buf[r] = 0;
517                                 fd = xopen(".", O_RDONLY|O_NDELAY);
518                                 xchdir("./log");
519                                 mkdir(buf, 0700);
520                                 if (fchdir(fd) == -1)
521                                         fatal_cannot("change back to service directory");
522                                 close(fd);
523                         }
524                         else {
525                                 if ((errno != ENOENT) && (errno != EINVAL))
526                                         fatal_cannot("readlink ./log/supervise");
527                         }
528                 }
529                 svd[1].fdlock = xopen3("log/supervise/lock",
530                                 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
531                 if (lock_ex(svd[1].fdlock) == -1)
532                         fatal_cannot("lock log/supervise/lock");
533                 close_on_exec_on(svd[1].fdlock);
534         }
535
536         mkfifo("log/supervise/control"+4, 0600);
537         svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
538         close_on_exec_on(svd[0].fdcontrol);
539         svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
540         close_on_exec_on(svd[0].fdcontrolwrite);
541         update_status(&svd[0]);
542         if (haslog) {
543                 mkfifo("log/supervise/control", 0600);
544                 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
545                 close_on_exec_on(svd[1].fdcontrol);
546                 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
547                 close_on_exec_on(svd[1].fdcontrolwrite);
548                 update_status(&svd[1]);
549         }
550         mkfifo("log/supervise/ok"+4, 0600);
551         fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
552         close_on_exec_on(fd);
553         if (haslog) {
554                 mkfifo("log/supervise/ok", 0600);
555                 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
556                 close_on_exec_on(fd);
557         }
558         for (;;) {
559                 struct pollfd x[3];
560                 unsigned deadline;
561                 char ch;
562
563                 if (haslog)
564                         if (!svd[1].pid && svd[1].want == W_UP)
565                                 startservice(&svd[1]);
566                 if (!svd[0].pid)
567                         if (svd[0].want == W_UP || svd[0].state == S_FINISH)
568                                 startservice(&svd[0]);
569
570                 x[0].fd = selfpipe.rd;
571                 x[0].events = POLLIN;
572                 x[1].fd = svd[0].fdcontrol;
573                 x[1].events = POLLIN;
574                 /* x[2] is used only if haslog == 1 */
575                 x[2].fd = svd[1].fdcontrol;
576                 x[2].events = POLLIN;
577                 sig_unblock(SIGTERM);
578                 sig_unblock(SIGCHLD);
579                 poll(x, 2 + haslog, 3600*1000);
580                 sig_block(SIGTERM);
581                 sig_block(SIGCHLD);
582
583                 while (read(selfpipe.rd, &ch, 1) == 1)
584                         continue;
585
586                 for (;;) {
587                         int child;
588                         int wstat;
589
590                         child = wait_any_nohang(&wstat);
591                         if (!child)
592                                 break;
593                         if ((child == -1) && (errno != EINTR))
594                                 break;
595                         if (child == svd[0].pid) {
596                                 svd[0].pid = 0;
597                                 pidchanged = 1;
598                                 svd[0].ctrl &=~ C_TERM;
599                                 if (svd[0].state != S_FINISH) {
600                                         fd = open_read("finish");
601                                         if (fd != -1) {
602                                                 close(fd);
603                                                 svd[0].state = S_FINISH;
604                                                 update_status(&svd[0]);
605                                                 continue;
606                                         }
607                                 }
608                                 svd[0].state = S_DOWN;
609                                 deadline = svd[0].start.tv_sec + 1;
610                                 gettimeofday_ns(&svd[0].start);
611                                 update_status(&svd[0]);
612                                 if (LESS(svd[0].start.tv_sec, deadline))
613                                         sleep(1);
614                         }
615                         if (haslog) {
616                                 if (child == svd[1].pid) {
617                                         svd[1].pid = 0;
618                                         pidchanged = 1;
619                                         svd[1].state = S_DOWN;
620                                         svd[1].ctrl &= ~C_TERM;
621                                         deadline = svd[1].start.tv_sec + 1;
622                                         gettimeofday_ns(&svd[1].start);
623                                         update_status(&svd[1]);
624                                         if (LESS(svd[1].start.tv_sec, deadline))
625                                                 sleep(1);
626                                 }
627                         }
628                 } /* for (;;) */
629                 if (read(svd[0].fdcontrol, &ch, 1) == 1)
630                         ctrl(&svd[0], ch);
631                 if (haslog)
632                         if (read(svd[1].fdcontrol, &ch, 1) == 1)
633                                 ctrl(&svd[1], ch);
634
635                 if (sigterm) {
636                         ctrl(&svd[0], 'x');
637                         sigterm = 0;
638                 }
639
640                 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
641                         if (svd[1].pid == 0)
642                                 _exit(EXIT_SUCCESS);
643                         if (svd[1].want != W_EXIT) {
644                                 svd[1].want = W_EXIT;
645                                 /* stopservice(&svd[1]); */
646                                 update_status(&svd[1]);
647                                 close(logpipe.wr);
648                                 close(logpipe.rd);
649                         }
650                 }
651         } /* for (;;) */
652         /* not reached */
653         return 0;
654 }