bd4a81eeeafb646d36d3f21299fd33fbc5e61eed
[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 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 "busybox.h"
34 #include "runit_lib.h"
35
36 static int selfpipe[2];
37
38 /* state */
39 #define S_DOWN 0
40 #define S_RUN 1
41 #define S_FINISH 2
42 /* ctrl */
43 #define C_NOOP 0
44 #define C_TERM 1
45 #define C_PAUSE 2
46 /* want */
47 #define W_UP 0
48 #define W_DOWN 1
49 #define W_EXIT 2
50
51 struct svdir {
52         int pid;
53         smallint state;
54         smallint ctrl;
55         smallint want;
56         smallint islog;
57         struct taia start;
58         int fdlock;
59         int fdcontrol;
60         int fdcontrolwrite;
61 };
62 static struct svdir svd[2];
63
64 static smallint sigterm;
65 static smallint haslog;
66 static smallint pidchanged = 1;
67 static int logpipe[2];
68 static char *dir;
69
70 #define usage() bb_show_usage()
71
72 static void fatal2_cannot(const char *m1, const char *m2)
73 {
74         bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
75         /* was exiting 111 */
76 }
77 static void fatal_cannot(const char *m)
78 {
79         fatal2_cannot(m, "");
80         /* was exiting 111 */
81 }
82 static void fatal2x_cannot(const char *m1, const char *m2)
83 {
84         bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
85         /* was exiting 111 */
86 }
87 static void warn_cannot(const char *m)
88 {
89         bb_perror_msg("%s: warning: cannot %s", dir, m);
90 }
91 static void warnx_cannot(const char *m)
92 {
93         bb_error_msg("%s: warning: cannot %s", dir, m);
94 }
95
96 static void s_child(int sig_no)
97 {
98         write(selfpipe[1], "", 1);
99 }
100
101 static void s_term(int sig_no)
102 {
103         sigterm = 1;
104         write(selfpipe[1], "", 1); /* XXX */
105 }
106
107 static char *add_str(char *p, const char *to_add)
108 {
109         while ((*p = *to_add) != '\0') {
110                 p++;
111                 to_add++;
112         }
113         return p;
114 }
115
116 static int open_trunc_or_warn(const char *name)
117 {
118         int fd = open_trunc(name);
119         if (fd < 0)
120                 bb_perror_msg("%s: warning: cannot open %s",
121                                 dir, name);
122         return fd;
123 }
124
125 static int rename_or_warn(const char *old, const char *new)
126 {
127         if (rename(old, new) == -1) {
128                 bb_perror_msg("%s: warning: cannot rename %s to %s",
129                                 dir, old, new);
130                 return -1;
131         }
132         return 0;
133 }
134
135 static void update_status(struct svdir *s)
136 {
137         unsigned long l;
138         int fd;
139         char status[20];
140
141         /* pid */
142         if (pidchanged) {
143                 fd = open_trunc_or_warn("supervise/pid.new");
144                 if (fd < 0)
145                         return;
146                 if (s->pid) {
147                         char spid[sizeof(int)*3 + 2];
148                         int size = sprintf(spid, "%u\n", (unsigned)s->pid);
149                         write(fd, spid, size);
150                 }
151                 close(fd);
152                 if (rename_or_warn("supervise/pid.new",
153                     s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
154                         return;
155                 pidchanged = 0;
156         }
157
158         /* stat */
159         fd = open_trunc_or_warn("supervise/stat.new");
160         if (fd < -1)
161                 return;
162
163         {
164                 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
165                 char *p = stat_buf;
166                 switch (s->state) {
167                 case S_DOWN:
168                         p = add_str(p, "down");
169                         break;
170                 case S_RUN:
171                         p = add_str(p, "run");
172                         break;
173                 case S_FINISH:
174                         p = add_str(p, "finish");
175                         break;
176                 }
177                 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
178                 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
179                 if (s->state != S_DOWN)
180                         switch (s->want) {
181                         case W_DOWN:
182                                 p = add_str(p, ", want down");
183                                 break;
184                         case W_EXIT:
185                                 p = add_str(p, ", want exit");
186                                 break;
187                         }
188                 *p++ = '\n';
189                 write(fd, stat_buf, p - stat_buf);
190                 close(fd);
191         }
192
193         rename_or_warn("supervise/stat.new",
194                 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
195
196         /* supervise compatibility */
197         taia_pack(status, &s->start);
198         l = (unsigned long)s->pid;
199         status[12] = l; l >>=8;
200         status[13] = l; l >>=8;
201         status[14] = l; l >>=8;
202         status[15] = l;
203         if (s->ctrl & C_PAUSE)
204                 status[16] = 1;
205         else
206                 status[16] = 0;
207         if (s->want == W_UP)
208                 status[17] = 'u';
209         else
210                 status[17] = 'd';
211         if (s->ctrl & C_TERM)
212                 status[18] = 1;
213         else
214                 status[18] = 0;
215         status[19] = s->state;
216         fd = open_trunc_or_warn("supervise/status.new");
217         if (fd < 0)
218                 return;
219         l = write(fd, status, sizeof(status));
220         if (l < 0) {
221                 warn_cannot("write supervise/status.new");
222                 close(fd);
223                 unlink("supervise/status.new");
224                 return;
225         }
226         close(fd);
227         if (l < sizeof(status)) {
228                 warnx_cannot("write supervise/status.new: partial write");
229                 return;
230         }
231         rename_or_warn("supervise/status.new",
232                 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
233 }
234
235 static unsigned custom(struct svdir *s, char c)
236 {
237         int pid;
238         int w;
239         char a[10];
240         struct stat st;
241         char *prog[2];
242
243         if (s->islog) return 0;
244         strcpy(a, "control/?");
245         a[8] = c;
246         if (stat(a, &st) == 0) {
247                 if (st.st_mode & S_IXUSR) {
248                         pid = fork();
249                         if (pid == -1) {
250                                 warn_cannot("fork for control/?");
251                                 return 0;
252                         }
253                         if (!pid) {
254                                 if (haslog && dup2(logpipe[1], 1) == -1)
255                                         warn_cannot("setup stdout for control/?");
256                                 prog[0] = a;
257                                 prog[1] = NULL;
258                                 execve(a, prog, environ);
259                                 fatal_cannot("run control/?");
260                         }
261                         while (wait_pid(&w, pid) == -1) {
262                                 if (errno == EINTR) continue;
263                                 warn_cannot("wait for child control/?");
264                                 return 0;
265                         }
266                         return !wait_exitcode(w);
267                 }
268         } else {
269                 if (errno != ENOENT)
270                         warn_cannot("stat control/?");
271         }
272         return 0;
273 }
274
275 static void stopservice(struct svdir *s)
276 {
277         if (s->pid && !custom(s, 't')) {
278                 kill(s->pid, SIGTERM);
279                 s->ctrl |= C_TERM;
280                 update_status(s);
281         }
282         if (s->want == W_DOWN) {
283                 kill(s->pid, SIGCONT);
284                 custom(s, 'd');
285                 return;
286         }
287         if (s->want == W_EXIT) {
288                 kill(s->pid, SIGCONT);
289                 custom(s, 'x');
290         }
291 }
292
293 static void startservice(struct svdir *s)
294 {
295         int p;
296         char *run[2];
297
298         if (s->state == S_FINISH)
299                 run[0] = (char*)"./finish";
300         else {
301                 run[0] = (char*)"./run";
302                 custom(s, 'u');
303         }
304         run[1] = NULL;
305
306         if (s->pid != 0)
307                 stopservice(s); /* should never happen */
308         while ((p = fork()) == -1) {
309                 warn_cannot("fork, sleeping");
310                 sleep(5);
311         }
312         if (p == 0) {
313                 /* child */
314                 if (haslog) {
315                         if (s->islog) {
316                                 if (dup2(logpipe[0], 0) == -1)
317                                         fatal_cannot("setup filedescriptor for ./log/run");
318                                 close(logpipe[1]);
319                                 if (chdir("./log") == -1)
320                                         fatal_cannot("change directory to ./log");
321                         } else {
322                                 if (dup2(logpipe[1], 1) == -1)
323                                         fatal_cannot("setup filedescriptor for ./run");
324                                 close(logpipe[0]);
325                         }
326                 }
327                 signal(SIGCHLD, SIG_DFL);
328                 signal(SIGTERM, SIG_DFL);
329                 sig_unblock(SIGCHLD);
330                 sig_unblock(SIGTERM);
331                 execvp(*run, run);
332                 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
333         }
334         if (s->state != S_FINISH) {
335                 taia_now(&s->start);
336                 s->state = S_RUN;
337         }
338         s->pid = p;
339         pidchanged = 1;
340         s->ctrl = C_NOOP;
341         update_status(s);
342 }
343
344 static int ctrl(struct svdir *s, char c)
345 {
346         int sig;
347
348         switch (c) {
349         case 'd': /* down */
350                 s->want = W_DOWN;
351                 update_status(s);
352                 if (s->pid && s->state != S_FINISH) stopservice(s);
353                 break;
354         case 'u': /* up */
355                 s->want = W_UP;
356                 update_status(s);
357                 if (s->pid == 0) startservice(s);
358                 break;
359         case 'x': /* exit */
360                 if (s->islog) break;
361                 s->want = W_EXIT;
362                 update_status(s);
363                 /* FALLTHROUGH */
364         case 't': /* sig term */
365                 if (s->pid && s->state != S_FINISH) stopservice(s);
366                 break;
367         case 'k': /* sig kill */
368                 if (s->pid && !custom(s, c)) kill(s->pid, SIGKILL);
369                 s->state = S_DOWN;
370                 break;
371         case 'p': /* sig pause */
372                 if (s->pid && !custom(s, c)) kill(s->pid, SIGSTOP);
373                 s->ctrl |= C_PAUSE;
374                 update_status(s);
375                 break;
376         case 'c': /* sig cont */
377                 if (s->pid && !custom(s, c)) kill(s->pid, SIGCONT);
378                 if (s->ctrl & C_PAUSE) s->ctrl &= ~C_PAUSE;
379                 update_status(s);
380                 break;
381         case 'o': /* once */
382                 s->want = W_DOWN;
383                 update_status(s);
384                 if (!s->pid) startservice(s);
385                 break;
386         case 'a': /* sig alarm */
387                 sig = SIGALRM;
388                 goto sendsig;
389         case 'h': /* sig hup */
390                 sig = SIGHUP;
391                 goto sendsig;
392         case 'i': /* sig int */
393                 sig = SIGINT;
394                 goto sendsig;
395         case 'q': /* sig quit */
396                 sig = SIGQUIT;
397                 goto sendsig;
398         case '1': /* sig usr1 */
399                 sig = SIGUSR1;
400                 goto sendsig;
401         case '2': /* sig usr2 */
402                 sig = SIGUSR2;
403                 goto sendsig;
404         }
405         return 1;
406  sendsig:
407         if (s->pid && !custom(s, c))
408                 kill(s->pid, sig);
409         return 1;
410 }
411
412 int runsv_main(int argc, char **argv);
413 int runsv_main(int argc, char **argv)
414 {
415         struct stat s;
416         int fd;
417         int r;
418         char buf[256];
419
420         if (!argv[1] || argv[2]) usage();
421         dir = argv[1];
422
423         if (pipe(selfpipe) == -1) fatal_cannot("create selfpipe");
424         coe(selfpipe[0]);
425         coe(selfpipe[1]);
426         ndelay_on(selfpipe[0]);
427         ndelay_on(selfpipe[1]);
428
429         sig_block(SIGCHLD);
430         sig_catch(SIGCHLD, s_child);
431         sig_block(SIGTERM);
432         sig_catch(SIGTERM, s_term);
433
434         xchdir(dir);
435         /* bss: svd[0].pid = 0; */
436         if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
437         if (C_NOOP) svd[0].ctrl = C_NOOP;
438         if (W_UP) svd[0].want = W_UP;
439         /* bss: svd[0].islog = 0; */
440         /* bss: svd[1].pid = 0; */
441         taia_now(&svd[0].start);
442         if (stat("down", &s) != -1) svd[0].want = W_DOWN;
443
444         if (stat("log", &s) == -1) {
445                 if (errno != ENOENT)
446                         warn_cannot("stat ./log");
447         } else {
448                 if (!S_ISDIR(s.st_mode))
449                         warnx_cannot("stat log/down: log is not a directory");
450                 else {
451                         haslog = 1;
452                         svd[1].state = S_DOWN;
453                         svd[1].ctrl = C_NOOP;
454                         svd[1].want = W_UP;
455                         svd[1].islog = 1;
456                         taia_now(&svd[1].start);
457                         if (stat("log/down", &s) != -1)
458                                 svd[1].want = W_DOWN;
459                         if (pipe(logpipe) == -1)
460                                 fatal_cannot("create log pipe");
461                         coe(logpipe[0]);
462                         coe(logpipe[1]);
463                 }
464         }
465
466         if (mkdir("supervise", 0700) == -1) {
467                 r = readlink("supervise", buf, sizeof(buf));
468                 if (r != -1) {
469                         if (r == sizeof(buf))
470                                 fatal2x_cannot("readlink ./supervise", ": name too long");
471                         buf[r] = 0;
472                         mkdir(buf, 0700);
473                 } else {
474                         if ((errno != ENOENT) && (errno != EINVAL))
475                                 fatal_cannot("readlink ./supervise");
476                 }
477         }
478         svd[0].fdlock = xopen3("log/supervise/lock"+4,
479                         O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
480         if (lock_exnb(svd[0].fdlock) == -1)
481                 fatal_cannot("lock supervise/lock");
482         coe(svd[0].fdlock);
483         if (haslog) {
484                 if (mkdir("log/supervise", 0700) == -1) {
485                         r = readlink("log/supervise", buf, 256);
486                         if (r != -1) {
487                                 if (r == 256)
488                                         fatal2x_cannot("readlink ./log/supervise", ": name too long");
489                                 buf[r] = 0;
490                                 fd = xopen(".", O_RDONLY|O_NDELAY);
491                                 xchdir("./log");
492                                 mkdir(buf, 0700);
493                                 if (fchdir(fd) == -1)
494                                         fatal_cannot("change back to service directory");
495                                 close(fd);
496                         }
497                         else {
498                                 if ((errno != ENOENT) && (errno != EINVAL))
499                                         fatal_cannot("readlink ./log/supervise");
500                         }
501                 }
502                 svd[1].fdlock = xopen3("log/supervise/lock",
503                                 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
504                 if (lock_ex(svd[1].fdlock) == -1)
505                         fatal_cannot("lock log/supervise/lock");
506                 coe(svd[1].fdlock);
507         }
508
509         mkfifo("log/supervise/control"+4, 0600);
510         svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
511         coe(svd[0].fdcontrol);
512         svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
513         coe(svd[0].fdcontrolwrite);
514         update_status(&svd[0]);
515         if (haslog) {
516                 mkfifo("log/supervise/control", 0600);
517                 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
518                 coe(svd[1].fdcontrol);
519                 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
520                 coe(svd[1].fdcontrolwrite);
521                 update_status(&svd[1]);
522         }
523         mkfifo("log/supervise/ok"+4, 0600);
524         fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
525         coe(fd);
526         if (haslog) {
527                 mkfifo("log/supervise/ok", 0600);
528                 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
529                 coe(fd);
530         }
531         for (;;) {
532                 iopause_fd x[3];
533                 struct taia deadline;
534                 struct taia now;
535                 char ch;
536
537                 if (haslog)
538                         if (!svd[1].pid && svd[1].want == W_UP)
539                                 startservice(&svd[1]);
540                 if (!svd[0].pid)
541                         if (svd[0].want == W_UP || svd[0].state == S_FINISH)
542                                 startservice(&svd[0]);
543
544                 x[0].fd = selfpipe[0];
545                 x[0].events = IOPAUSE_READ;
546                 x[1].fd = svd[0].fdcontrol;
547                 x[1].events = IOPAUSE_READ;
548                 if (haslog) {
549                         x[2].fd = svd[1].fdcontrol;
550                         x[2].events = IOPAUSE_READ;
551                 }
552                 taia_now(&now);
553                 taia_uint(&deadline, 3600);
554                 taia_add(&deadline, &now, &deadline);
555
556                 sig_unblock(SIGTERM);
557                 sig_unblock(SIGCHLD);
558                 iopause(x, 2+haslog, &deadline, &now);
559                 sig_block(SIGTERM);
560                 sig_block(SIGCHLD);
561
562                 while (read(selfpipe[0], &ch, 1) == 1)
563                         ;
564                 for (;;) {
565                         int child;
566                         int wstat;
567
568                         child = wait_nohang(&wstat);
569                         if (!child) break;
570                         if ((child == -1) && (errno != EINTR)) break;
571                         if (child == svd[0].pid) {
572                                 svd[0].pid = 0;
573                                 pidchanged = 1;
574                                 svd[0].ctrl &=~ C_TERM;
575                                 if (svd[0].state != S_FINISH) {
576                                         fd = open_read("finish");
577                                         if (fd != -1) {
578                                                 close(fd);
579                                                 svd[0].state = S_FINISH;
580                                                 update_status(&svd[0]);
581                                                 continue;
582                                         }
583                                 }
584                                 svd[0].state = S_DOWN;
585                                 taia_uint(&deadline, 1);
586                                 taia_add(&deadline, &svd[0].start, &deadline);
587                                 taia_now(&svd[0].start);
588                                 update_status(&svd[0]);
589                                 if (taia_less(&svd[0].start, &deadline)) sleep(1);
590                         }
591                         if (haslog) {
592                                 if (child == svd[1].pid) {
593                                         svd[1].pid = 0;
594                                         pidchanged = 1;
595                                         svd[1].state = S_DOWN;
596                                         svd[1].ctrl &= ~C_TERM;
597                                         taia_uint(&deadline, 1);
598                                         taia_add(&deadline, &svd[1].start, &deadline);
599                                         taia_now(&svd[1].start);
600                                         update_status(&svd[1]);
601                                         if (taia_less(&svd[1].start, &deadline)) sleep(1);
602                                 }
603                         }
604                 }
605                 if (read(svd[0].fdcontrol, &ch, 1) == 1)
606                         ctrl(&svd[0], ch);
607                 if (haslog)
608                         if (read(svd[1].fdcontrol, &ch, 1) == 1)
609                                 ctrl(&svd[1], ch);
610
611                 if (sigterm) {
612                         ctrl(&svd[0], 'x');
613                         sigterm = 0;
614                 }
615
616                 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
617                         if (svd[1].pid == 0)
618                                 _exit(0);
619                         if (svd[1].want != W_EXIT) {
620                                 svd[1].want = W_EXIT;
621                                 /* stopservice(&svd[1]); */
622                                 update_status(&svd[1]);
623                                 close(logpipe[1]);
624                                 close(logpipe[0]);
625                                 //if (close(logpipe[1]) == -1)
626                                 //      warn_cannot("close logpipe[1]");
627                                 //if (close(logpipe[0]) == -1)
628                                 //      warn_cannot("close logpipe[0]");
629                         }
630                 }
631         }
632         /* not reached */
633         return 0;
634 }