blob: d941e897d0baf7917c3a8c084e16211f4590201f [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/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000037#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +000038#include "runit_lib.h"
39
Denis Vlasenko7bc53602007-08-31 21:45:52 +000040#if ENABLE_MONOTONIC_SYSCALL
41#include <sys/syscall.h>
42
43/* libc has incredibly messy way of doing this,
44 * typically requiring -lrt. We just skip all this mess */
45static void gettimeofday_ns(struct timespec *ts)
46{
47 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
48}
49#else
50static void gettimeofday_ns(struct timespec *ts)
51{
52 if (sizeof(struct timeval) == sizeof(struct timespec)
53 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
54 ) {
55 /* Cheat */
56 gettimeofday((void*)ts, NULL);
57 ts->tv_nsec *= 1000;
58 } else {
59 extern void BUG_need_to_implement_gettimeofday_ns(void);
60 BUG_need_to_implement_gettimeofday_ns();
61 }
62}
63#endif
64
65/* Compare possibly overflowing unsigned counters */
66#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
67
Denis Vlasenko04c63862006-11-17 18:58:49 +000068/* state */
69#define S_DOWN 0
70#define S_RUN 1
71#define S_FINISH 2
72/* ctrl */
73#define C_NOOP 0
74#define C_TERM 1
75#define C_PAUSE 2
76/* want */
77#define W_UP 0
78#define W_DOWN 1
79#define W_EXIT 2
80
81struct svdir {
82 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000083 smallint state;
84 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020085 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000086 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000087 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000088 int fdlock;
89 int fdcontrol;
90 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +020091 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +000092};
Denis Vlasenko04c63862006-11-17 18:58:49 +000093
Denis Vlasenkob9256052007-09-28 10:29:17 +000094struct globals {
95 smallint haslog;
96 smallint sigterm;
97 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +000098 struct fd_pair selfpipe;
99 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000100 char *dir;
101 struct svdir svd[2];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100102} FIX_ALIASING;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000103#define G (*(struct globals*)&bb_common_bufsiz1)
104#define haslog (G.haslog )
105#define sigterm (G.sigterm )
106#define pidchanged (G.pidchanged )
107#define selfpipe (G.selfpipe )
108#define logpipe (G.logpipe )
109#define dir (G.dir )
110#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000111#define INIT_G() do { \
112 pidchanged = 1; \
113} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000114
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000115static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000116{
117 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
118 /* was exiting 111 */
119}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000120static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000121{
122 fatal2_cannot(m, "");
123 /* was exiting 111 */
124}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000125static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000126{
127 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
128 /* was exiting 111 */
129}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000130static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000131{
132 bb_perror_msg("%s: warning: cannot %s", dir, m);
133}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000134
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000135static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000136{
Denis Vlasenko37188322008-02-16 13:20:56 +0000137 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000138}
139
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000140static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000141{
142 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000143 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000144}
145
Denis Vlasenko04c63862006-11-17 18:58:49 +0000146static int open_trunc_or_warn(const char *name)
147{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200148 /* Why O_NDELAY? */
149 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000150 if (fd < 0)
151 bb_perror_msg("%s: warning: cannot open %s",
152 dir, name);
153 return fd;
154}
155
Denis Vlasenko04c63862006-11-17 18:58:49 +0000156static void update_status(struct svdir *s)
157{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000158 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000159 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000160 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000161
162 /* pid */
163 if (pidchanged) {
164 fd = open_trunc_or_warn("supervise/pid.new");
165 if (fd < 0)
166 return;
167 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000168 char spid[sizeof(int)*3 + 2];
169 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000170 write(fd, spid, size);
171 }
172 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000173 if (rename_or_warn("supervise/pid.new",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100174 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000175 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000176 pidchanged = 0;
177 }
178
179 /* stat */
180 fd = open_trunc_or_warn("supervise/stat.new");
181 if (fd < -1)
182 return;
183
184 {
185 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
186 char *p = stat_buf;
187 switch (s->state) {
188 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800189 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000190 break;
191 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800192 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000193 break;
194 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800195 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000196 break;
197 }
Earl Chew80a34182009-08-02 02:23:27 +0200198 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800199 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200200 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800201 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000202 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200203 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000204 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800205 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000206 break;
207 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800208 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000209 break;
210 }
211 *p++ = '\n';
212 write(fd, stat_buf, p - stat_buf);
213 close(fd);
214 }
215
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000216 rename_or_warn("supervise/stat.new",
217 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000218
219 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000220 memset(&status, 0, sizeof(status));
221 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
222 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
223 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000224 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000225 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200226 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000227 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000229 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000230 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000231 status.got_term = 1;
232 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233 fd = open_trunc_or_warn("supervise/status.new");
234 if (fd < 0)
235 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000236 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000237 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000238 if (sz != sizeof(status)) {
239 warn_cannot("write supervise/status.new");
240 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000241 return;
242 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000243 rename_or_warn("supervise/status.new",
244 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000245}
246
247static unsigned custom(struct svdir *s, char c)
248{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000249 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000250 int w;
251 char a[10];
252 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000253
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200254 if (s->islog)
255 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000256 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000257 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258 if (stat(a, &st) == 0) {
259 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000260 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000261 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000262 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 return 0;
264 }
Earl Chew80a34182009-08-02 02:23:27 +0200265 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000266 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000267 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000269 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000270 fatal_cannot("run control/?");
271 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000272 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000273 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274 warn_cannot("wait for child control/?");
275 return 0;
276 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200277 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000278 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000279 } else {
280 if (errno != ENOENT)
281 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000282 }
283 return 0;
284}
285
286static void stopservice(struct svdir *s)
287{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000288 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000289 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000290 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000291 update_status(s);
292 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200293 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000295 custom(s, 'd');
296 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000297 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200298 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000299 kill(s->pid, SIGCONT);
300 custom(s, 'x');
301 }
302}
303
304static void startservice(struct svdir *s)
305{
306 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200307 const char *arg[4];
308 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000309
Earl Chew80a34182009-08-02 02:23:27 +0200310 if (s->state == S_FINISH) {
311/* Two arguments are given to ./finish. The first one is ./run exit code,
312 * or -1 if ./run didnt exit normally. The second one is
313 * the least significant byte of the exit status as determined by waitpid;
314 * for instance it is 0 if ./run exited normally, and the signal number
315 * if ./run was terminated by a signal. If runsv cannot start ./run
316 * for some reason, the exit code is 111 and the status is 0.
317 */
318 arg[0] = "./finish";
319 arg[1] = "-1";
320 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200321 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200322 arg[1] = exitcode;
323 }
324 //arg[2] = "0";
325 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200326 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200327 //}
328 arg[3] = NULL;
329 } else {
330 arg[0] = "./run";
331 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000332 custom(s, 'u');
333 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000334
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000335 if (s->pid != 0)
336 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000337 while ((p = vfork()) == -1) {
338 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000339 sleep(5);
340 }
341 if (p == 0) {
342 /* child */
343 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000344 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000345 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000346 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000347 close(logpipe.wr);
348 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000349 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000350 close(logpipe.rd);
351 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000352 }
353 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000354 /* Non-ignored signals revert to SIG_DFL on exec anyway */
355 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000356 + (1 << SIGCHLD)
357 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000358 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000359 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000360 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200361 execv(arg[0], (char**) arg);
362 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000363 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000364 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000365 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000366 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000367 s->state = S_RUN;
368 }
369 s->pid = p;
370 pidchanged = 1;
371 s->ctrl = C_NOOP;
372 update_status(s);
373}
374
375static int ctrl(struct svdir *s, char c)
376{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000377 int sig;
378
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000379 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000380 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200381 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000382 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000383 if (s->pid && s->state != S_FINISH)
384 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000385 break;
386 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200387 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000388 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000389 if (s->pid == 0)
390 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000391 break;
392 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000393 if (s->islog)
394 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200395 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000396 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000397 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000398 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000399 if (s->pid && s->state != S_FINISH)
400 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000401 break;
402 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000403 if (s->pid && !custom(s, c))
404 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000405 s->state = S_DOWN;
406 break;
407 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000408 if (s->pid && !custom(s, c))
409 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000410 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000411 update_status(s);
412 break;
413 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000414 if (s->pid && !custom(s, c))
415 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000416 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000417 update_status(s);
418 break;
419 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200420 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000421 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000422 if (!s->pid)
423 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000424 break;
425 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000426 sig = SIGALRM;
427 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000428 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000429 sig = SIGHUP;
430 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000431 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000432 sig = SIGINT;
433 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000435 sig = SIGQUIT;
436 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000437 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000438 sig = SIGUSR1;
439 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000441 sig = SIGUSR2;
442 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000443 }
444 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000445 sendsig:
446 if (s->pid && !custom(s, c))
447 kill(s->pid, sig);
448 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000449}
450
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000451int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000452int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000453{
454 struct stat s;
455 int fd;
456 int r;
457 char buf[256];
458
Denis Vlasenkob9256052007-09-28 10:29:17 +0000459 INIT_G();
460
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100461 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000462
Denis Vlasenko37188322008-02-16 13:20:56 +0000463 xpiped_pair(selfpipe);
464 close_on_exec_on(selfpipe.rd);
465 close_on_exec_on(selfpipe.wr);
466 ndelay_on(selfpipe.rd);
467 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000468
Denis Vlasenko8c783952007-01-27 22:21:52 +0000469 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000470 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000471 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000472 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000473
474 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000475 /* bss: svd[0].pid = 0; */
476 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
477 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200478 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000479 /* bss: svd[0].islog = 0; */
480 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000481 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200482 if (stat("down", &s) != -1)
483 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000484
485 if (stat("log", &s) == -1) {
486 if (errno != ENOENT)
487 warn_cannot("stat ./log");
488 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000489 if (!S_ISDIR(s.st_mode)) {
490 errno = 0;
491 warn_cannot("stat log/down: log is not a directory");
492 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000493 haslog = 1;
494 svd[1].state = S_DOWN;
495 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200496 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000497 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000498 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000499 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200500 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000501 xpiped_pair(logpipe);
502 close_on_exec_on(logpipe.rd);
503 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000504 }
505 }
506
507 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000508 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000509 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000510 if (r == sizeof(buf))
511 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000512 buf[r] = 0;
513 mkdir(buf, 0700);
514 } else {
515 if ((errno != ENOENT) && (errno != EINVAL))
516 fatal_cannot("readlink ./supervise");
517 }
518 }
519 svd[0].fdlock = xopen3("log/supervise/lock"+4,
520 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200521 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000522 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000523 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000524 if (haslog) {
525 if (mkdir("log/supervise", 0700) == -1) {
526 r = readlink("log/supervise", buf, 256);
527 if (r != -1) {
528 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000529 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000530 buf[r] = 0;
531 fd = xopen(".", O_RDONLY|O_NDELAY);
532 xchdir("./log");
533 mkdir(buf, 0700);
534 if (fchdir(fd) == -1)
535 fatal_cannot("change back to service directory");
536 close(fd);
537 }
538 else {
539 if ((errno != ENOENT) && (errno != EINVAL))
540 fatal_cannot("readlink ./log/supervise");
541 }
542 }
543 svd[1].fdlock = xopen3("log/supervise/lock",
544 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200545 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000546 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000547 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000548 }
549
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000550 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000552 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000554 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000555 update_status(&svd[0]);
556 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000557 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000559 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000561 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000562 update_status(&svd[1]);
563 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000564 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000565 fd = xopen("log/supervise/ok"+4, 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 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000568 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000569 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000570 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000571 }
572 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000573 struct pollfd x[3];
574 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000575 char ch;
576
577 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200578 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000579 startservice(&svd[1]);
580 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200581 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000582 startservice(&svd[0]);
583
Denis Vlasenko37188322008-02-16 13:20:56 +0000584 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000585 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000586 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000587 x[1].events = POLLIN;
588 /* x[2] is used only if haslog == 1 */
589 x[2].fd = svd[1].fdcontrol;
590 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000591 sig_unblock(SIGTERM);
592 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000593 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000594 sig_block(SIGTERM);
595 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000596
Denis Vlasenko37188322008-02-16 13:20:56 +0000597 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000598 continue;
599
Denis Vlasenko04c63862006-11-17 18:58:49 +0000600 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000601 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000602 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000603
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000604 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000605 if (!child)
606 break;
607 if ((child == -1) && (errno != EINTR))
608 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000609 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200610 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000611 svd[0].pid = 0;
612 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200613 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000614 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200615 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000616 if (fd != -1) {
617 close(fd);
618 svd[0].state = S_FINISH;
619 update_status(&svd[0]);
620 continue;
621 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000622 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000623 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000624 deadline = svd[0].start.tv_sec + 1;
625 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000626 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000627 if (LESS(svd[0].start.tv_sec, deadline))
628 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000629 }
630 if (haslog) {
631 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200632 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000633 svd[1].pid = 0;
634 pidchanged = 1;
635 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000636 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000637 deadline = svd[1].start.tv_sec + 1;
638 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000639 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000640 if (LESS(svd[1].start.tv_sec, deadline))
641 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000642 }
643 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000644 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000645 if (read(svd[0].fdcontrol, &ch, 1) == 1)
646 ctrl(&svd[0], ch);
647 if (haslog)
648 if (read(svd[1].fdcontrol, &ch, 1) == 1)
649 ctrl(&svd[1], ch);
650
651 if (sigterm) {
652 ctrl(&svd[0], 'x');
653 sigterm = 0;
654 }
655
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200656 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000657 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000658 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200659 if (svd[1].sd_want != W_EXIT) {
660 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000661 /* stopservice(&svd[1]); */
662 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000663 close(logpipe.wr);
664 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000665 }
666 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000667 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000668 /* not reached */
669 return 0;
670}