blob: 4b18d12d5700d914fdd57e0b692e92d748ad31b8 [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> */
Denys Vlasenko0863e1a2015-10-19 00:41:28 +020029
30//config:config RUNSV
31//config: bool "runsv"
32//config: default y
33//config: help
34//config: runsv starts and monitors a service and optionally an appendant log
35//config: service.
36
37//applet:IF_RUNSV(APPLET(runsv, BB_DIR_USR_BIN, BB_SUID_DROP))
38
39//kbuild:lib-$(CONFIG_RUNSV) += runsv.o
Denis Vlasenko04c63862006-11-17 18:58:49 +000040
Pere Orga5bc8c002011-04-11 03:29:49 +020041//usage:#define runsv_trivial_usage
42//usage: "DIR"
43//usage:#define runsv_full_usage "\n\n"
44//usage: "Start and monitor a service and optionally an appendant log service"
45
Denis Vlasenko04c63862006-11-17 18:58:49 +000046#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000047#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +000048#include "runit_lib.h"
49
Denis Vlasenko7bc53602007-08-31 21:45:52 +000050#if ENABLE_MONOTONIC_SYSCALL
51#include <sys/syscall.h>
52
53/* libc has incredibly messy way of doing this,
54 * typically requiring -lrt. We just skip all this mess */
55static void gettimeofday_ns(struct timespec *ts)
56{
57 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
58}
59#else
60static void gettimeofday_ns(struct timespec *ts)
61{
Denys Vlasenkoab3964d2015-10-13 14:50:20 +020062 BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
63 BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
64 /* Cheat */
65 gettimeofday((void*)ts, NULL);
66 ts->tv_nsec *= 1000;
Denis Vlasenko7bc53602007-08-31 21:45:52 +000067}
68#endif
69
70/* Compare possibly overflowing unsigned counters */
71#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
72
Denis Vlasenko04c63862006-11-17 18:58:49 +000073/* state */
74#define S_DOWN 0
75#define S_RUN 1
76#define S_FINISH 2
77/* ctrl */
78#define C_NOOP 0
79#define C_TERM 1
80#define C_PAUSE 2
81/* want */
82#define W_UP 0
83#define W_DOWN 1
84#define W_EXIT 2
85
86struct svdir {
87 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000088 smallint state;
89 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020090 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000091 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000092 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000093 int fdlock;
94 int fdcontrol;
95 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +020096 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +000097};
Denis Vlasenko04c63862006-11-17 18:58:49 +000098
Denis Vlasenkob9256052007-09-28 10:29:17 +000099struct globals {
100 smallint haslog;
101 smallint sigterm;
102 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +0000103 struct fd_pair selfpipe;
104 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000105 char *dir;
106 struct svdir svd[2];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100107} FIX_ALIASING;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000108#define G (*(struct globals*)&bb_common_bufsiz1)
109#define haslog (G.haslog )
110#define sigterm (G.sigterm )
111#define pidchanged (G.pidchanged )
112#define selfpipe (G.selfpipe )
113#define logpipe (G.logpipe )
114#define dir (G.dir )
115#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000116#define INIT_G() do { \
117 pidchanged = 1; \
118} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000119
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000120static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000121{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200122 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000123 /* was exiting 111 */
124}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000125static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000126{
127 fatal2_cannot(m, "");
128 /* was exiting 111 */
129}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000130static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000131{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200132 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000133 /* was exiting 111 */
134}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000135static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000136{
137 bb_perror_msg("%s: warning: cannot %s", dir, m);
138}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000139
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000140static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000141{
Denis Vlasenko37188322008-02-16 13:20:56 +0000142 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000143}
144
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000145static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000146{
147 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000148 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000149}
150
Denis Vlasenko04c63862006-11-17 18:58:49 +0000151static int open_trunc_or_warn(const char *name)
152{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200153 /* Why O_NDELAY? */
154 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000155 if (fd < 0)
156 bb_perror_msg("%s: warning: cannot open %s",
157 dir, name);
158 return fd;
159}
160
Denis Vlasenko04c63862006-11-17 18:58:49 +0000161static void update_status(struct svdir *s)
162{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000163 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000164 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000165 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000166
167 /* pid */
168 if (pidchanged) {
169 fd = open_trunc_or_warn("supervise/pid.new");
170 if (fd < 0)
171 return;
172 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000173 char spid[sizeof(int)*3 + 2];
174 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000175 write(fd, spid, size);
176 }
177 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000178 if (rename_or_warn("supervise/pid.new",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100179 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000180 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000181 pidchanged = 0;
182 }
183
184 /* stat */
185 fd = open_trunc_or_warn("supervise/stat.new");
186 if (fd < -1)
187 return;
188
189 {
190 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
191 char *p = stat_buf;
192 switch (s->state) {
193 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800194 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000195 break;
196 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800197 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000198 break;
199 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800200 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000201 break;
202 }
Earl Chew80a34182009-08-02 02:23:27 +0200203 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800204 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200205 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800206 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000207 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200208 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000209 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800210 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211 break;
212 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800213 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000214 break;
215 }
216 *p++ = '\n';
217 write(fd, stat_buf, p - stat_buf);
218 close(fd);
219 }
220
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000221 rename_or_warn("supervise/stat.new",
222 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000223
224 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000225 memset(&status, 0, sizeof(status));
226 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
227 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
228 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000230 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200231 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000232 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000234 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000235 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000236 status.got_term = 1;
237 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 fd = open_trunc_or_warn("supervise/status.new");
239 if (fd < 0)
240 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000241 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000242 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000243 if (sz != sizeof(status)) {
244 warn_cannot("write supervise/status.new");
245 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000246 return;
247 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000248 rename_or_warn("supervise/status.new",
249 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000250}
251
252static unsigned custom(struct svdir *s, char c)
253{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000254 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000255 int w;
256 char a[10];
257 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200259 if (s->islog)
260 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000261 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000262 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 if (stat(a, &st) == 0) {
264 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000265 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000266 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000267 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268 return 0;
269 }
Earl Chew80a34182009-08-02 02:23:27 +0200270 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000271 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000272 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000273 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000274 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275 fatal_cannot("run control/?");
276 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000277 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000278 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 warn_cannot("wait for child control/?");
280 return 0;
281 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200282 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000283 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000284 } else {
285 if (errno != ENOENT)
286 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000287 }
288 return 0;
289}
290
291static void stopservice(struct svdir *s)
292{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000293 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000295 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000296 update_status(s);
297 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200298 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000299 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000300 custom(s, 'd');
301 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000302 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200303 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000304 kill(s->pid, SIGCONT);
305 custom(s, 'x');
306 }
307}
308
309static void startservice(struct svdir *s)
310{
311 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200312 const char *arg[4];
313 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314
Earl Chew80a34182009-08-02 02:23:27 +0200315 if (s->state == S_FINISH) {
316/* Two arguments are given to ./finish. The first one is ./run exit code,
317 * or -1 if ./run didnt exit normally. The second one is
318 * the least significant byte of the exit status as determined by waitpid;
319 * for instance it is 0 if ./run exited normally, and the signal number
320 * if ./run was terminated by a signal. If runsv cannot start ./run
321 * for some reason, the exit code is 111 and the status is 0.
322 */
323 arg[0] = "./finish";
324 arg[1] = "-1";
325 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200326 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200327 arg[1] = exitcode;
328 }
329 //arg[2] = "0";
330 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200331 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200332 //}
333 arg[3] = NULL;
334 } else {
335 arg[0] = "./run";
336 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000337 custom(s, 'u');
338 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000339
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000340 if (s->pid != 0)
341 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000342 while ((p = vfork()) == -1) {
343 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000344 sleep(5);
345 }
346 if (p == 0) {
347 /* child */
348 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000349 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000350 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000351 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000352 close(logpipe.wr);
353 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000354 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000355 close(logpipe.rd);
356 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000357 }
358 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000359 /* Non-ignored signals revert to SIG_DFL on exec anyway */
360 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000361 + (1 << SIGCHLD)
362 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000363 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000364 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000365 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200366 execv(arg[0], (char**) arg);
367 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000368 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000369 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000370 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000371 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000372 s->state = S_RUN;
373 }
374 s->pid = p;
375 pidchanged = 1;
376 s->ctrl = C_NOOP;
377 update_status(s);
378}
379
380static int ctrl(struct svdir *s, char c)
381{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000382 int sig;
383
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000384 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000385 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200386 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000387 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000388 if (s->pid && s->state != S_FINISH)
389 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000390 break;
391 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200392 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000393 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000394 if (s->pid == 0)
395 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000396 break;
397 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000398 if (s->islog)
399 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200400 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000401 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000402 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000403 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000404 if (s->pid && s->state != S_FINISH)
405 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000406 break;
407 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000408 if (s->pid && !custom(s, c))
409 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000410 s->state = S_DOWN;
411 break;
412 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000413 if (s->pid && !custom(s, c))
414 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000415 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000416 update_status(s);
417 break;
418 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000419 if (s->pid && !custom(s, c))
420 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000421 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000422 update_status(s);
423 break;
424 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200425 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000426 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000427 if (!s->pid)
428 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000429 break;
430 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000431 sig = SIGALRM;
432 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000433 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000434 sig = SIGHUP;
435 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000436 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000437 sig = SIGINT;
438 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000439 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000440 sig = SIGQUIT;
441 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000442 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000443 sig = SIGUSR1;
444 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000445 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000446 sig = SIGUSR2;
447 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000448 }
449 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000450 sendsig:
451 if (s->pid && !custom(s, c))
452 kill(s->pid, sig);
453 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000454}
455
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000456int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000457int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000458{
459 struct stat s;
460 int fd;
461 int r;
462 char buf[256];
463
Denis Vlasenkob9256052007-09-28 10:29:17 +0000464 INIT_G();
465
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100466 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000467
Denis Vlasenko37188322008-02-16 13:20:56 +0000468 xpiped_pair(selfpipe);
469 close_on_exec_on(selfpipe.rd);
470 close_on_exec_on(selfpipe.wr);
471 ndelay_on(selfpipe.rd);
472 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000473
Denis Vlasenko8c783952007-01-27 22:21:52 +0000474 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000475 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000476 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000477 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000478
479 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000480 /* bss: svd[0].pid = 0; */
481 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
482 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200483 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000484 /* bss: svd[0].islog = 0; */
485 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000486 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200487 if (stat("down", &s) != -1)
488 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000489
490 if (stat("log", &s) == -1) {
491 if (errno != ENOENT)
492 warn_cannot("stat ./log");
493 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000494 if (!S_ISDIR(s.st_mode)) {
495 errno = 0;
496 warn_cannot("stat log/down: log is not a directory");
497 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000498 haslog = 1;
499 svd[1].state = S_DOWN;
500 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200501 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000502 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000503 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000504 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200505 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000506 xpiped_pair(logpipe);
507 close_on_exec_on(logpipe.rd);
508 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000509 }
510 }
511
512 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000513 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000514 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000515 if (r == sizeof(buf))
516 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000517 buf[r] = 0;
518 mkdir(buf, 0700);
519 } else {
520 if ((errno != ENOENT) && (errno != EINVAL))
521 fatal_cannot("readlink ./supervise");
522 }
523 }
524 svd[0].fdlock = xopen3("log/supervise/lock"+4,
525 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200526 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000527 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000528 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000529 if (haslog) {
530 if (mkdir("log/supervise", 0700) == -1) {
531 r = readlink("log/supervise", buf, 256);
532 if (r != -1) {
533 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000534 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000535 buf[r] = 0;
536 fd = xopen(".", O_RDONLY|O_NDELAY);
537 xchdir("./log");
538 mkdir(buf, 0700);
539 if (fchdir(fd) == -1)
540 fatal_cannot("change back to service directory");
541 close(fd);
542 }
543 else {
544 if ((errno != ENOENT) && (errno != EINVAL))
545 fatal_cannot("readlink ./log/supervise");
546 }
547 }
548 svd[1].fdlock = xopen3("log/supervise/lock",
549 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200550 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000552 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 }
554
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000555 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000556 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000557 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000559 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 update_status(&svd[0]);
561 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000562 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000563 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000564 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000565 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000566 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000567 update_status(&svd[1]);
568 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000569 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000570 fd = xopen("log/supervise/ok"+4, 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 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000573 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000574 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000575 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000576 }
577 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000578 struct pollfd x[3];
579 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000580 char ch;
581
582 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200583 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 startservice(&svd[1]);
585 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200586 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000587 startservice(&svd[0]);
588
Denis Vlasenko37188322008-02-16 13:20:56 +0000589 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000590 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000591 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000592 x[1].events = POLLIN;
593 /* x[2] is used only if haslog == 1 */
594 x[2].fd = svd[1].fdcontrol;
595 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000596 sig_unblock(SIGTERM);
597 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000598 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000599 sig_block(SIGTERM);
600 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000601
Denis Vlasenko37188322008-02-16 13:20:56 +0000602 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000603 continue;
604
Denis Vlasenko04c63862006-11-17 18:58:49 +0000605 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000606 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000607 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000608
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000609 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000610 if (!child)
611 break;
612 if ((child == -1) && (errno != EINTR))
613 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000614 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200615 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000616 svd[0].pid = 0;
617 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200618 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000619 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200620 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000621 if (fd != -1) {
622 close(fd);
623 svd[0].state = S_FINISH;
624 update_status(&svd[0]);
625 continue;
626 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000627 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000628 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000629 deadline = svd[0].start.tv_sec + 1;
630 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000631 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000632 if (LESS(svd[0].start.tv_sec, deadline))
633 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000634 }
635 if (haslog) {
636 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200637 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000638 svd[1].pid = 0;
639 pidchanged = 1;
640 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000641 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000642 deadline = svd[1].start.tv_sec + 1;
643 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000644 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000645 if (LESS(svd[1].start.tv_sec, deadline))
646 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000647 }
648 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000649 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000650 if (read(svd[0].fdcontrol, &ch, 1) == 1)
651 ctrl(&svd[0], ch);
652 if (haslog)
653 if (read(svd[1].fdcontrol, &ch, 1) == 1)
654 ctrl(&svd[1], ch);
655
656 if (sigterm) {
657 ctrl(&svd[0], 'x');
658 sigterm = 0;
659 }
660
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200661 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000662 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000663 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200664 if (svd[1].sd_want != W_EXIT) {
665 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000666 /* stopservice(&svd[1]); */
667 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000668 close(logpipe.wr);
669 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000670 }
671 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000672 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000673 /* not reached */
674 return 0;
675}