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