blob: e76572daa4c6641ebae51ea175a787abb180e973 [file] [log] [blame]
Denis Vlasenko8a164052007-03-12 23:34:52 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000028/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denis Vlasenko04c63862006-11-17 18:58:49 +000029/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31#include <sys/poll.h>
32#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000033#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +000034#include "runit_lib.h"
35
Denis Vlasenko7bc53602007-08-31 21:45:52 +000036#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 */
41static void gettimeofday_ns(struct timespec *ts)
42{
43 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
44}
45#else
46static 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
Denis Vlasenko04c63862006-11-17 18:58:49 +000064/* 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
77struct svdir {
78 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000079 smallint state;
80 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020081 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000082 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000083 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000084 int fdlock;
85 int fdcontrol;
86 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +020087 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +000088};
Denis Vlasenko04c63862006-11-17 18:58:49 +000089
Denis Vlasenkob9256052007-09-28 10:29:17 +000090struct globals {
91 smallint haslog;
92 smallint sigterm;
93 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +000094 struct fd_pair selfpipe;
95 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +000096 char *dir;
97 struct svdir svd[2];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010098} FIX_ALIASING;
Denis Vlasenkob9256052007-09-28 10:29:17 +000099#define G (*(struct globals*)&bb_common_bufsiz1)
100#define haslog (G.haslog )
101#define sigterm (G.sigterm )
102#define pidchanged (G.pidchanged )
103#define selfpipe (G.selfpipe )
104#define logpipe (G.logpipe )
105#define dir (G.dir )
106#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000107#define INIT_G() do { \
108 pidchanged = 1; \
109} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000110
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000111static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000112{
113 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
114 /* was exiting 111 */
115}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000116static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000117{
118 fatal2_cannot(m, "");
119 /* was exiting 111 */
120}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000121static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000122{
123 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
124 /* was exiting 111 */
125}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000126static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000127{
128 bb_perror_msg("%s: warning: cannot %s", dir, m);
129}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000130
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000131static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000132{
Denis Vlasenko37188322008-02-16 13:20:56 +0000133 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000134}
135
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000136static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000137{
138 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000139 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000140}
141
Denis Vlasenko04c63862006-11-17 18:58:49 +0000142static int open_trunc_or_warn(const char *name)
143{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200144 /* Why O_NDELAY? */
145 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000146 if (fd < 0)
147 bb_perror_msg("%s: warning: cannot open %s",
148 dir, name);
149 return fd;
150}
151
Denis Vlasenko04c63862006-11-17 18:58:49 +0000152static void update_status(struct svdir *s)
153{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000154 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000155 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000156 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000157
158 /* pid */
159 if (pidchanged) {
160 fd = open_trunc_or_warn("supervise/pid.new");
161 if (fd < 0)
162 return;
163 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000164 char spid[sizeof(int)*3 + 2];
165 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000166 write(fd, spid, size);
167 }
168 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000169 if (rename_or_warn("supervise/pid.new",
170 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000171 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000172 pidchanged = 0;
173 }
174
175 /* stat */
176 fd = open_trunc_or_warn("supervise/stat.new");
177 if (fd < -1)
178 return;
179
180 {
181 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
182 char *p = stat_buf;
183 switch (s->state) {
184 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800185 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000186 break;
187 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800188 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000189 break;
190 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800191 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000192 break;
193 }
Earl Chew80a34182009-08-02 02:23:27 +0200194 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800195 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200196 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800197 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000198 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200199 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000200 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800201 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000202 break;
203 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800204 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000205 break;
206 }
207 *p++ = '\n';
208 write(fd, stat_buf, p - stat_buf);
209 close(fd);
210 }
211
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000212 rename_or_warn("supervise/stat.new",
213 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000214
215 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000216 memset(&status, 0, sizeof(status));
217 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
218 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
219 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000220 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000221 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200222 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000223 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000224 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000225 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000226 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000227 status.got_term = 1;
228 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 fd = open_trunc_or_warn("supervise/status.new");
230 if (fd < 0)
231 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000232 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000234 if (sz != sizeof(status)) {
235 warn_cannot("write supervise/status.new");
236 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000237 return;
238 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000239 rename_or_warn("supervise/status.new",
240 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000241}
242
243static unsigned custom(struct svdir *s, char c)
244{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000245 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000246 int w;
247 char a[10];
248 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000249
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200250 if (s->islog)
251 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000252 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000253 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000254 if (stat(a, &st) == 0) {
255 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000256 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000257 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000258 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 return 0;
260 }
Earl Chew80a34182009-08-02 02:23:27 +0200261 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000262 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000263 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000264 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000265 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000266 fatal_cannot("run control/?");
267 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000268 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000269 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000270 warn_cannot("wait for child control/?");
271 return 0;
272 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200273 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000275 } else {
276 if (errno != ENOENT)
277 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000278 }
279 return 0;
280}
281
282static void stopservice(struct svdir *s)
283{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000284 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000285 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000286 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000287 update_status(s);
288 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200289 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000290 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000291 custom(s, 'd');
292 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000293 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200294 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000295 kill(s->pid, SIGCONT);
296 custom(s, 'x');
297 }
298}
299
300static void startservice(struct svdir *s)
301{
302 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200303 const char *arg[4];
304 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000305
Earl Chew80a34182009-08-02 02:23:27 +0200306 if (s->state == S_FINISH) {
307/* Two arguments are given to ./finish. The first one is ./run exit code,
308 * or -1 if ./run didnt exit normally. The second one is
309 * the least significant byte of the exit status as determined by waitpid;
310 * for instance it is 0 if ./run exited normally, and the signal number
311 * if ./run was terminated by a signal. If runsv cannot start ./run
312 * for some reason, the exit code is 111 and the status is 0.
313 */
314 arg[0] = "./finish";
315 arg[1] = "-1";
316 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200317 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200318 arg[1] = exitcode;
319 }
320 //arg[2] = "0";
321 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200322 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200323 //}
324 arg[3] = NULL;
325 } else {
326 arg[0] = "./run";
327 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000328 custom(s, 'u');
329 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000330
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000331 if (s->pid != 0)
332 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000333 while ((p = vfork()) == -1) {
334 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000335 sleep(5);
336 }
337 if (p == 0) {
338 /* child */
339 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000340 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000341 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000342 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000343 close(logpipe.wr);
344 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000345 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000346 close(logpipe.rd);
347 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000348 }
349 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000350 /* Non-ignored signals revert to SIG_DFL on exec anyway */
351 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000352 + (1 << SIGCHLD)
353 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000354 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000355 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000356 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200357 execv(arg[0], (char**) arg);
358 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000359 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000360 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000361 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000362 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000363 s->state = S_RUN;
364 }
365 s->pid = p;
366 pidchanged = 1;
367 s->ctrl = C_NOOP;
368 update_status(s);
369}
370
371static int ctrl(struct svdir *s, char c)
372{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000373 int sig;
374
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000375 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000376 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200377 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000378 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000379 if (s->pid && s->state != S_FINISH)
380 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000381 break;
382 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200383 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000384 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000385 if (s->pid == 0)
386 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000387 break;
388 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000389 if (s->islog)
390 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200391 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000392 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000393 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000394 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000395 if (s->pid && s->state != S_FINISH)
396 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000397 break;
398 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000399 if (s->pid && !custom(s, c))
400 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000401 s->state = S_DOWN;
402 break;
403 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000404 if (s->pid && !custom(s, c))
405 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000406 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000407 update_status(s);
408 break;
409 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000410 if (s->pid && !custom(s, c))
411 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000412 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000413 update_status(s);
414 break;
415 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200416 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000417 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000418 if (!s->pid)
419 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000420 break;
421 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000422 sig = SIGALRM;
423 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000424 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000425 sig = SIGHUP;
426 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000427 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000428 sig = SIGINT;
429 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000430 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000431 sig = SIGQUIT;
432 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000433 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000434 sig = SIGUSR1;
435 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000436 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000437 sig = SIGUSR2;
438 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000439 }
440 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000441 sendsig:
442 if (s->pid && !custom(s, c))
443 kill(s->pid, sig);
444 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000445}
446
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000447int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000448int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000449{
450 struct stat s;
451 int fd;
452 int r;
453 char buf[256];
454
Denis Vlasenkob9256052007-09-28 10:29:17 +0000455 INIT_G();
456
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100457 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000458
Denis Vlasenko37188322008-02-16 13:20:56 +0000459 xpiped_pair(selfpipe);
460 close_on_exec_on(selfpipe.rd);
461 close_on_exec_on(selfpipe.wr);
462 ndelay_on(selfpipe.rd);
463 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000464
Denis Vlasenko8c783952007-01-27 22:21:52 +0000465 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000466 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000467 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000468 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000469
470 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000471 /* bss: svd[0].pid = 0; */
472 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
473 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200474 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000475 /* bss: svd[0].islog = 0; */
476 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000477 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200478 if (stat("down", &s) != -1)
479 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000480
481 if (stat("log", &s) == -1) {
482 if (errno != ENOENT)
483 warn_cannot("stat ./log");
484 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000485 if (!S_ISDIR(s.st_mode)) {
486 errno = 0;
487 warn_cannot("stat log/down: log is not a directory");
488 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000489 haslog = 1;
490 svd[1].state = S_DOWN;
491 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200492 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000493 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000494 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000495 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200496 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000497 xpiped_pair(logpipe);
498 close_on_exec_on(logpipe.rd);
499 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000500 }
501 }
502
503 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000504 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000505 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000506 if (r == sizeof(buf))
507 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000508 buf[r] = 0;
509 mkdir(buf, 0700);
510 } else {
511 if ((errno != ENOENT) && (errno != EINVAL))
512 fatal_cannot("readlink ./supervise");
513 }
514 }
515 svd[0].fdlock = xopen3("log/supervise/lock"+4,
516 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200517 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000518 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000519 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000520 if (haslog) {
521 if (mkdir("log/supervise", 0700) == -1) {
522 r = readlink("log/supervise", buf, 256);
523 if (r != -1) {
524 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000525 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000526 buf[r] = 0;
527 fd = xopen(".", O_RDONLY|O_NDELAY);
528 xchdir("./log");
529 mkdir(buf, 0700);
530 if (fchdir(fd) == -1)
531 fatal_cannot("change back to service directory");
532 close(fd);
533 }
534 else {
535 if ((errno != ENOENT) && (errno != EINVAL))
536 fatal_cannot("readlink ./log/supervise");
537 }
538 }
539 svd[1].fdlock = xopen3("log/supervise/lock",
540 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200541 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000542 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000543 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000544 }
545
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000546 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000547 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000548 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000549 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000550 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 update_status(&svd[0]);
552 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000553 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000554 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000555 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000556 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000557 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 update_status(&svd[1]);
559 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000560 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000561 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000562 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000563 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000564 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000565 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000566 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000567 }
568 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000569 struct pollfd x[3];
570 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000571 char ch;
572
573 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200574 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000575 startservice(&svd[1]);
576 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200577 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000578 startservice(&svd[0]);
579
Denis Vlasenko37188322008-02-16 13:20:56 +0000580 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000581 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000582 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000583 x[1].events = POLLIN;
584 /* x[2] is used only if haslog == 1 */
585 x[2].fd = svd[1].fdcontrol;
586 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000587 sig_unblock(SIGTERM);
588 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000589 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000590 sig_block(SIGTERM);
591 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000592
Denis Vlasenko37188322008-02-16 13:20:56 +0000593 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000594 continue;
595
Denis Vlasenko04c63862006-11-17 18:58:49 +0000596 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000597 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000598 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000599
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000600 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000601 if (!child)
602 break;
603 if ((child == -1) && (errno != EINTR))
604 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000605 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200606 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000607 svd[0].pid = 0;
608 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200609 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000610 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200611 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000612 if (fd != -1) {
613 close(fd);
614 svd[0].state = S_FINISH;
615 update_status(&svd[0]);
616 continue;
617 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000618 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000619 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000620 deadline = svd[0].start.tv_sec + 1;
621 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000622 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000623 if (LESS(svd[0].start.tv_sec, deadline))
624 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000625 }
626 if (haslog) {
627 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200628 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000629 svd[1].pid = 0;
630 pidchanged = 1;
631 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000632 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000633 deadline = svd[1].start.tv_sec + 1;
634 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000635 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000636 if (LESS(svd[1].start.tv_sec, deadline))
637 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000638 }
639 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000640 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000641 if (read(svd[0].fdcontrol, &ch, 1) == 1)
642 ctrl(&svd[0], ch);
643 if (haslog)
644 if (read(svd[1].fdcontrol, &ch, 1) == 1)
645 ctrl(&svd[1], ch);
646
647 if (sigterm) {
648 ctrl(&svd[0], 'x');
649 sigterm = 0;
650 }
651
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200652 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000653 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000654 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200655 if (svd[1].sd_want != W_EXIT) {
656 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000657 /* stopservice(&svd[1]); */
658 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000659 close(logpipe.wr);
660 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000661 }
662 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000663 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000664 /* not reached */
665 return 0;
666}