blob: ad8d84f74e142b7ea32dcfe3aba48640886417af [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
Pere Orga5bc8c002011-04-11 03:29:49 +020031//usage:#define runsv_trivial_usage
32//usage: "DIR"
33//usage:#define runsv_full_usage "\n\n"
34//usage: "Start and monitor a service and optionally an appendant log service"
35
Denis Vlasenko04c63862006-11-17 18:58:49 +000036#include <sys/poll.h>
37#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000038#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +000039#include "runit_lib.h"
40
Denis Vlasenko7bc53602007-08-31 21:45:52 +000041#if ENABLE_MONOTONIC_SYSCALL
42#include <sys/syscall.h>
43
44/* libc has incredibly messy way of doing this,
45 * typically requiring -lrt. We just skip all this mess */
46static void gettimeofday_ns(struct timespec *ts)
47{
48 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
49}
50#else
51static void gettimeofday_ns(struct timespec *ts)
52{
53 if (sizeof(struct timeval) == sizeof(struct timespec)
54 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
55 ) {
56 /* Cheat */
57 gettimeofday((void*)ts, NULL);
58 ts->tv_nsec *= 1000;
59 } else {
60 extern void BUG_need_to_implement_gettimeofday_ns(void);
61 BUG_need_to_implement_gettimeofday_ns();
62 }
63}
64#endif
65
66/* Compare possibly overflowing unsigned counters */
67#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
68
Denis Vlasenko04c63862006-11-17 18:58:49 +000069/* state */
70#define S_DOWN 0
71#define S_RUN 1
72#define S_FINISH 2
73/* ctrl */
74#define C_NOOP 0
75#define C_TERM 1
76#define C_PAUSE 2
77/* want */
78#define W_UP 0
79#define W_DOWN 1
80#define W_EXIT 2
81
82struct svdir {
83 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000084 smallint state;
85 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020086 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000087 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000088 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000089 int fdlock;
90 int fdcontrol;
91 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +020092 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +000093};
Denis Vlasenko04c63862006-11-17 18:58:49 +000094
Denis Vlasenkob9256052007-09-28 10:29:17 +000095struct globals {
96 smallint haslog;
97 smallint sigterm;
98 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +000099 struct fd_pair selfpipe;
100 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000101 char *dir;
102 struct svdir svd[2];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100103} FIX_ALIASING;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000104#define G (*(struct globals*)&bb_common_bufsiz1)
105#define haslog (G.haslog )
106#define sigterm (G.sigterm )
107#define pidchanged (G.pidchanged )
108#define selfpipe (G.selfpipe )
109#define logpipe (G.logpipe )
110#define dir (G.dir )
111#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000112#define INIT_G() do { \
113 pidchanged = 1; \
114} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000115
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000116static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000117{
118 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
119 /* was exiting 111 */
120}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000121static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000122{
123 fatal2_cannot(m, "");
124 /* was exiting 111 */
125}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000126static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000127{
128 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
129 /* was exiting 111 */
130}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000131static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000132{
133 bb_perror_msg("%s: warning: cannot %s", dir, m);
134}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000135
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000136static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000137{
Denis Vlasenko37188322008-02-16 13:20:56 +0000138 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000139}
140
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000141static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000142{
143 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000144 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000145}
146
Denis Vlasenko04c63862006-11-17 18:58:49 +0000147static int open_trunc_or_warn(const char *name)
148{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200149 /* Why O_NDELAY? */
150 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000151 if (fd < 0)
152 bb_perror_msg("%s: warning: cannot open %s",
153 dir, name);
154 return fd;
155}
156
Denis Vlasenko04c63862006-11-17 18:58:49 +0000157static void update_status(struct svdir *s)
158{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000159 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000160 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000161 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000162
163 /* pid */
164 if (pidchanged) {
165 fd = open_trunc_or_warn("supervise/pid.new");
166 if (fd < 0)
167 return;
168 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000169 char spid[sizeof(int)*3 + 2];
170 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000171 write(fd, spid, size);
172 }
173 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000174 if (rename_or_warn("supervise/pid.new",
175 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000176 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000177 pidchanged = 0;
178 }
179
180 /* stat */
181 fd = open_trunc_or_warn("supervise/stat.new");
182 if (fd < -1)
183 return;
184
185 {
186 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
187 char *p = stat_buf;
188 switch (s->state) {
189 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800190 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000191 break;
192 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800193 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000194 break;
195 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800196 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000197 break;
198 }
Earl Chew80a34182009-08-02 02:23:27 +0200199 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800200 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200201 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800202 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000203 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200204 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000205 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800206 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000207 break;
208 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800209 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000210 break;
211 }
212 *p++ = '\n';
213 write(fd, stat_buf, p - stat_buf);
214 close(fd);
215 }
216
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000217 rename_or_warn("supervise/stat.new",
218 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000219
220 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000221 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);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000225 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000226 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200227 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000228 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000230 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000231 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000232 status.got_term = 1;
233 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234 fd = open_trunc_or_warn("supervise/status.new");
235 if (fd < 0)
236 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000237 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000239 if (sz != sizeof(status)) {
240 warn_cannot("write supervise/status.new");
241 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000242 return;
243 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000244 rename_or_warn("supervise/status.new",
245 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000246}
247
248static unsigned custom(struct svdir *s, char c)
249{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000250 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000251 int w;
252 char a[10];
253 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000254
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200255 if (s->islog)
256 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000257 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000258 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 if (stat(a, &st) == 0) {
260 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000261 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000262 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000263 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000264 return 0;
265 }
Earl Chew80a34182009-08-02 02:23:27 +0200266 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000267 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000268 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000270 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000271 fatal_cannot("run control/?");
272 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000273 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000274 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275 warn_cannot("wait for child control/?");
276 return 0;
277 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200278 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000280 } else {
281 if (errno != ENOENT)
282 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000283 }
284 return 0;
285}
286
287static void stopservice(struct svdir *s)
288{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000289 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000290 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000291 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 update_status(s);
293 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200294 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000295 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000296 custom(s, 'd');
297 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000298 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200299 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000300 kill(s->pid, SIGCONT);
301 custom(s, 'x');
302 }
303}
304
305static void startservice(struct svdir *s)
306{
307 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200308 const char *arg[4];
309 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000310
Earl Chew80a34182009-08-02 02:23:27 +0200311 if (s->state == S_FINISH) {
312/* Two arguments are given to ./finish. The first one is ./run exit code,
313 * or -1 if ./run didnt exit normally. The second one is
314 * the least significant byte of the exit status as determined by waitpid;
315 * for instance it is 0 if ./run exited normally, and the signal number
316 * if ./run was terminated by a signal. If runsv cannot start ./run
317 * for some reason, the exit code is 111 and the status is 0.
318 */
319 arg[0] = "./finish";
320 arg[1] = "-1";
321 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200322 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200323 arg[1] = exitcode;
324 }
325 //arg[2] = "0";
326 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200327 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200328 //}
329 arg[3] = NULL;
330 } else {
331 arg[0] = "./run";
332 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000333 custom(s, 'u');
334 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000335
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000336 if (s->pid != 0)
337 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000338 while ((p = vfork()) == -1) {
339 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000340 sleep(5);
341 }
342 if (p == 0) {
343 /* child */
344 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000345 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000346 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000347 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000348 close(logpipe.wr);
349 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000350 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000351 close(logpipe.rd);
352 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000353 }
354 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000355 /* Non-ignored signals revert to SIG_DFL on exec anyway */
356 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000357 + (1 << SIGCHLD)
358 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000359 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000360 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000361 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200362 execv(arg[0], (char**) arg);
363 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000364 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000365 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000366 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000367 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000368 s->state = S_RUN;
369 }
370 s->pid = p;
371 pidchanged = 1;
372 s->ctrl = C_NOOP;
373 update_status(s);
374}
375
376static int ctrl(struct svdir *s, char c)
377{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000378 int sig;
379
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000380 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000381 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200382 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000383 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000384 if (s->pid && s->state != S_FINISH)
385 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000386 break;
387 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200388 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000389 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000390 if (s->pid == 0)
391 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000392 break;
393 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000394 if (s->islog)
395 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200396 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000397 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000398 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000399 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000400 if (s->pid && s->state != S_FINISH)
401 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000402 break;
403 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000404 if (s->pid && !custom(s, c))
405 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000406 s->state = S_DOWN;
407 break;
408 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000409 if (s->pid && !custom(s, c))
410 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000411 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000412 update_status(s);
413 break;
414 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000415 if (s->pid && !custom(s, c))
416 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000417 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000418 update_status(s);
419 break;
420 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200421 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000422 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000423 if (!s->pid)
424 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000425 break;
426 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000427 sig = SIGALRM;
428 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000429 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000430 sig = SIGHUP;
431 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000432 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000433 sig = SIGINT;
434 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000435 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000436 sig = SIGQUIT;
437 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000439 sig = SIGUSR1;
440 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000441 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000442 sig = SIGUSR2;
443 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000444 }
445 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000446 sendsig:
447 if (s->pid && !custom(s, c))
448 kill(s->pid, sig);
449 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000450}
451
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000452int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000453int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000454{
455 struct stat s;
456 int fd;
457 int r;
458 char buf[256];
459
Denis Vlasenkob9256052007-09-28 10:29:17 +0000460 INIT_G();
461
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100462 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000463
Denis Vlasenko37188322008-02-16 13:20:56 +0000464 xpiped_pair(selfpipe);
465 close_on_exec_on(selfpipe.rd);
466 close_on_exec_on(selfpipe.wr);
467 ndelay_on(selfpipe.rd);
468 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000469
Denis Vlasenko8c783952007-01-27 22:21:52 +0000470 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000471 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000472 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000473 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000474
475 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000476 /* bss: svd[0].pid = 0; */
477 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
478 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200479 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000480 /* bss: svd[0].islog = 0; */
481 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000482 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200483 if (stat("down", &s) != -1)
484 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000485
486 if (stat("log", &s) == -1) {
487 if (errno != ENOENT)
488 warn_cannot("stat ./log");
489 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000490 if (!S_ISDIR(s.st_mode)) {
491 errno = 0;
492 warn_cannot("stat log/down: log is not a directory");
493 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000494 haslog = 1;
495 svd[1].state = S_DOWN;
496 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200497 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000498 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000499 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000500 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200501 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000502 xpiped_pair(logpipe);
503 close_on_exec_on(logpipe.rd);
504 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000505 }
506 }
507
508 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000509 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000510 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000511 if (r == sizeof(buf))
512 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000513 buf[r] = 0;
514 mkdir(buf, 0700);
515 } else {
516 if ((errno != ENOENT) && (errno != EINVAL))
517 fatal_cannot("readlink ./supervise");
518 }
519 }
520 svd[0].fdlock = xopen3("log/supervise/lock"+4,
521 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200522 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000523 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000524 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000525 if (haslog) {
526 if (mkdir("log/supervise", 0700) == -1) {
527 r = readlink("log/supervise", buf, 256);
528 if (r != -1) {
529 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000530 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000531 buf[r] = 0;
532 fd = xopen(".", O_RDONLY|O_NDELAY);
533 xchdir("./log");
534 mkdir(buf, 0700);
535 if (fchdir(fd) == -1)
536 fatal_cannot("change back to service directory");
537 close(fd);
538 }
539 else {
540 if ((errno != ENOENT) && (errno != EINVAL))
541 fatal_cannot("readlink ./log/supervise");
542 }
543 }
544 svd[1].fdlock = xopen3("log/supervise/lock",
545 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200546 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000547 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000548 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000549 }
550
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000551 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000552 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000553 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000554 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000555 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000556 update_status(&svd[0]);
557 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000558 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000559 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000560 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000561 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000562 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000563 update_status(&svd[1]);
564 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000565 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000567 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000568 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000569 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000570 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000571 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000572 }
573 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000574 struct pollfd x[3];
575 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000576 char ch;
577
578 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200579 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000580 startservice(&svd[1]);
581 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200582 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000583 startservice(&svd[0]);
584
Denis Vlasenko37188322008-02-16 13:20:56 +0000585 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000586 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000587 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000588 x[1].events = POLLIN;
589 /* x[2] is used only if haslog == 1 */
590 x[2].fd = svd[1].fdcontrol;
591 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000592 sig_unblock(SIGTERM);
593 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000594 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000595 sig_block(SIGTERM);
596 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000597
Denis Vlasenko37188322008-02-16 13:20:56 +0000598 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000599 continue;
600
Denis Vlasenko04c63862006-11-17 18:58:49 +0000601 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000602 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000603 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000604
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000605 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000606 if (!child)
607 break;
608 if ((child == -1) && (errno != EINTR))
609 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000610 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200611 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000612 svd[0].pid = 0;
613 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200614 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000615 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200616 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000617 if (fd != -1) {
618 close(fd);
619 svd[0].state = S_FINISH;
620 update_status(&svd[0]);
621 continue;
622 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000623 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000624 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000625 deadline = svd[0].start.tv_sec + 1;
626 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000627 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000628 if (LESS(svd[0].start.tv_sec, deadline))
629 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000630 }
631 if (haslog) {
632 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200633 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000634 svd[1].pid = 0;
635 pidchanged = 1;
636 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000637 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000638 deadline = svd[1].start.tv_sec + 1;
639 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000640 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000641 if (LESS(svd[1].start.tv_sec, deadline))
642 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000643 }
644 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000645 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000646 if (read(svd[0].fdcontrol, &ch, 1) == 1)
647 ctrl(&svd[0], ch);
648 if (haslog)
649 if (read(svd[1].fdcontrol, &ch, 1) == 1)
650 ctrl(&svd[1], ch);
651
652 if (sigterm) {
653 ctrl(&svd[0], 'x');
654 sigterm = 0;
655 }
656
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200657 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000658 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000659 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200660 if (svd[1].sd_want != W_EXIT) {
661 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000662 /* stopservice(&svd[1]); */
663 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000664 close(logpipe.wr);
665 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000666 }
667 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000668 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000669 /* not reached */
670 return 0;
671}