blob: e0e31508a486175e6bf1f7dc0461c995cfa9da3c [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"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020048#include "common_bufsiz.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +000049#include "runit_lib.h"
50
Denis Vlasenko7bc53602007-08-31 21:45:52 +000051#if ENABLE_MONOTONIC_SYSCALL
52#include <sys/syscall.h>
53
54/* libc has incredibly messy way of doing this,
55 * typically requiring -lrt. We just skip all this mess */
56static void gettimeofday_ns(struct timespec *ts)
57{
58 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
59}
60#else
61static void gettimeofday_ns(struct timespec *ts)
62{
Denys Vlasenkoab3964d2015-10-13 14:50:20 +020063 BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
64 BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
65 /* Cheat */
66 gettimeofday((void*)ts, NULL);
67 ts->tv_nsec *= 1000;
Denis Vlasenko7bc53602007-08-31 21:45:52 +000068}
69#endif
70
71/* Compare possibly overflowing unsigned counters */
72#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
73
Denis Vlasenko04c63862006-11-17 18:58:49 +000074/* state */
75#define S_DOWN 0
76#define S_RUN 1
77#define S_FINISH 2
78/* ctrl */
79#define C_NOOP 0
80#define C_TERM 1
81#define C_PAUSE 2
82/* want */
83#define W_UP 0
84#define W_DOWN 1
85#define W_EXIT 2
86
87struct svdir {
88 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000089 smallint state;
90 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020091 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000092 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000093 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000094 int fdlock;
95 int fdcontrol;
96 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +020097 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +000098};
Denis Vlasenko04c63862006-11-17 18:58:49 +000099
Denis Vlasenkob9256052007-09-28 10:29:17 +0000100struct globals {
101 smallint haslog;
102 smallint sigterm;
103 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +0000104 struct fd_pair selfpipe;
105 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000106 char *dir;
107 struct svdir svd[2];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100108} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200109#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkob9256052007-09-28 10:29:17 +0000110#define haslog (G.haslog )
111#define sigterm (G.sigterm )
112#define pidchanged (G.pidchanged )
113#define selfpipe (G.selfpipe )
114#define logpipe (G.logpipe )
115#define dir (G.dir )
116#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000117#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200118 setup_common_bufsiz(); \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000119 pidchanged = 1; \
120} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000121
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000122static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000123{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200124 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000125 /* was exiting 111 */
126}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000127static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000128{
129 fatal2_cannot(m, "");
130 /* was exiting 111 */
131}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000132static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000133{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200134 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000135 /* was exiting 111 */
136}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000137static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000138{
139 bb_perror_msg("%s: warning: cannot %s", dir, m);
140}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000141
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000142static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000143{
Denis Vlasenko37188322008-02-16 13:20:56 +0000144 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000145}
146
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000147static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000148{
149 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000150 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000151}
152
Denis Vlasenko04c63862006-11-17 18:58:49 +0000153static int open_trunc_or_warn(const char *name)
154{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200155 /* Why O_NDELAY? */
156 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000157 if (fd < 0)
158 bb_perror_msg("%s: warning: cannot open %s",
159 dir, name);
160 return fd;
161}
162
Denis Vlasenko04c63862006-11-17 18:58:49 +0000163static void update_status(struct svdir *s)
164{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000165 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000166 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000167 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000168
169 /* pid */
170 if (pidchanged) {
171 fd = open_trunc_or_warn("supervise/pid.new");
172 if (fd < 0)
173 return;
174 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000175 char spid[sizeof(int)*3 + 2];
176 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000177 write(fd, spid, size);
178 }
179 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000180 if (rename_or_warn("supervise/pid.new",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100181 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000182 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000183 pidchanged = 0;
184 }
185
186 /* stat */
187 fd = open_trunc_or_warn("supervise/stat.new");
188 if (fd < -1)
189 return;
190
191 {
192 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
193 char *p = stat_buf;
194 switch (s->state) {
195 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800196 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000197 break;
198 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800199 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000200 break;
201 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800202 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000203 break;
204 }
Earl Chew80a34182009-08-02 02:23:27 +0200205 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800206 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200207 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800208 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000209 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200210 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800212 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000213 break;
214 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800215 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000216 break;
217 }
218 *p++ = '\n';
219 write(fd, stat_buf, p - stat_buf);
220 close(fd);
221 }
222
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000223 rename_or_warn("supervise/stat.new",
224 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000225
226 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000227 memset(&status, 0, sizeof(status));
228 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
229 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
230 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000231 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000232 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200233 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000234 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000235 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000236 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000237 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000238 status.got_term = 1;
239 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000240 fd = open_trunc_or_warn("supervise/status.new");
241 if (fd < 0)
242 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000243 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000244 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000245 if (sz != sizeof(status)) {
246 warn_cannot("write supervise/status.new");
247 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000248 return;
249 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000250 rename_or_warn("supervise/status.new",
251 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000252}
253
254static unsigned custom(struct svdir *s, char c)
255{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000256 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000257 int w;
258 char a[10];
259 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000260
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200261 if (s->islog)
262 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000263 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000264 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000265 if (stat(a, &st) == 0) {
266 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000267 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000269 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000270 return 0;
271 }
Earl Chew80a34182009-08-02 02:23:27 +0200272 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000273 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000274 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000276 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000277 fatal_cannot("run control/?");
278 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000279 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000280 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000281 warn_cannot("wait for child control/?");
282 return 0;
283 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200284 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000285 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000286 } else {
287 if (errno != ENOENT)
288 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000289 }
290 return 0;
291}
292
293static void stopservice(struct svdir *s)
294{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000295 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000296 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000297 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000298 update_status(s);
299 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200300 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000301 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000302 custom(s, 'd');
303 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000304 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200305 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000306 kill(s->pid, SIGCONT);
307 custom(s, 'x');
308 }
309}
310
311static void startservice(struct svdir *s)
312{
313 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200314 const char *arg[4];
315 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000316
Earl Chew80a34182009-08-02 02:23:27 +0200317 if (s->state == S_FINISH) {
318/* Two arguments are given to ./finish. The first one is ./run exit code,
319 * or -1 if ./run didnt exit normally. The second one is
320 * the least significant byte of the exit status as determined by waitpid;
321 * for instance it is 0 if ./run exited normally, and the signal number
322 * if ./run was terminated by a signal. If runsv cannot start ./run
323 * for some reason, the exit code is 111 and the status is 0.
324 */
325 arg[0] = "./finish";
326 arg[1] = "-1";
327 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200328 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200329 arg[1] = exitcode;
330 }
331 //arg[2] = "0";
332 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200333 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200334 //}
335 arg[3] = NULL;
336 } else {
337 arg[0] = "./run";
338 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000339 custom(s, 'u');
340 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000341
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000342 if (s->pid != 0)
343 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000344 while ((p = vfork()) == -1) {
345 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000346 sleep(5);
347 }
348 if (p == 0) {
349 /* child */
350 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000351 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000352 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000353 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000354 close(logpipe.wr);
355 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000356 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000357 close(logpipe.rd);
358 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000359 }
360 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000361 /* Non-ignored signals revert to SIG_DFL on exec anyway */
362 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000363 + (1 << SIGCHLD)
364 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000365 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000366 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000367 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200368 execv(arg[0], (char**) arg);
369 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000370 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000371 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000372 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000373 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000374 s->state = S_RUN;
375 }
376 s->pid = p;
377 pidchanged = 1;
378 s->ctrl = C_NOOP;
379 update_status(s);
380}
381
382static int ctrl(struct svdir *s, char c)
383{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000384 int sig;
385
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000386 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000387 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200388 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000389 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000390 if (s->pid && s->state != S_FINISH)
391 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000392 break;
393 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200394 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000395 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000396 if (s->pid == 0)
397 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000398 break;
399 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000400 if (s->islog)
401 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200402 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000403 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000404 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000405 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000406 if (s->pid && s->state != S_FINISH)
407 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000408 break;
409 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000410 if (s->pid && !custom(s, c))
411 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000412 s->state = S_DOWN;
413 break;
414 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000415 if (s->pid && !custom(s, c))
416 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000417 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000418 update_status(s);
419 break;
420 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000421 if (s->pid && !custom(s, c))
422 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000423 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000424 update_status(s);
425 break;
426 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200427 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000428 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000429 if (!s->pid)
430 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000431 break;
432 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000433 sig = SIGALRM;
434 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000435 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000436 sig = SIGHUP;
437 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000439 sig = SIGINT;
440 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000441 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000442 sig = SIGQUIT;
443 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000444 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000445 sig = SIGUSR1;
446 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000447 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000448 sig = SIGUSR2;
449 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000450 }
451 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000452 sendsig:
453 if (s->pid && !custom(s, c))
454 kill(s->pid, sig);
455 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000456}
457
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000458int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000459int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000460{
461 struct stat s;
462 int fd;
463 int r;
464 char buf[256];
465
Denis Vlasenkob9256052007-09-28 10:29:17 +0000466 INIT_G();
467
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100468 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000469
Denis Vlasenko37188322008-02-16 13:20:56 +0000470 xpiped_pair(selfpipe);
471 close_on_exec_on(selfpipe.rd);
472 close_on_exec_on(selfpipe.wr);
473 ndelay_on(selfpipe.rd);
474 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000475
Denis Vlasenko8c783952007-01-27 22:21:52 +0000476 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000477 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000478 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000479 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000480
481 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000482 /* bss: svd[0].pid = 0; */
483 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
484 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200485 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000486 /* bss: svd[0].islog = 0; */
487 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000488 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200489 if (stat("down", &s) != -1)
490 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000491
492 if (stat("log", &s) == -1) {
493 if (errno != ENOENT)
494 warn_cannot("stat ./log");
495 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000496 if (!S_ISDIR(s.st_mode)) {
497 errno = 0;
498 warn_cannot("stat log/down: log is not a directory");
499 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000500 haslog = 1;
501 svd[1].state = S_DOWN;
502 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200503 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000504 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000505 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000506 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200507 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000508 xpiped_pair(logpipe);
509 close_on_exec_on(logpipe.rd);
510 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000511 }
512 }
513
514 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000515 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000516 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000517 if (r == sizeof(buf))
518 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000519 buf[r] = 0;
520 mkdir(buf, 0700);
521 } else {
522 if ((errno != ENOENT) && (errno != EINVAL))
523 fatal_cannot("readlink ./supervise");
524 }
525 }
526 svd[0].fdlock = xopen3("log/supervise/lock"+4,
527 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200528 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000529 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000530 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000531 if (haslog) {
532 if (mkdir("log/supervise", 0700) == -1) {
533 r = readlink("log/supervise", buf, 256);
534 if (r != -1) {
535 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000536 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000537 buf[r] = 0;
538 fd = xopen(".", O_RDONLY|O_NDELAY);
539 xchdir("./log");
540 mkdir(buf, 0700);
541 if (fchdir(fd) == -1)
542 fatal_cannot("change back to service directory");
543 close(fd);
544 }
545 else {
546 if ((errno != ENOENT) && (errno != EINVAL))
547 fatal_cannot("readlink ./log/supervise");
548 }
549 }
550 svd[1].fdlock = xopen3("log/supervise/lock",
551 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200552 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000554 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000555 }
556
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000557 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000559 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000561 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000562 update_status(&svd[0]);
563 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000564 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000565 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000566 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000567 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000568 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000569 update_status(&svd[1]);
570 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000571 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000572 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000573 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000574 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000575 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000576 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000577 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000578 }
579 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000580 struct pollfd x[3];
581 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000582 char ch;
583
584 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200585 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000586 startservice(&svd[1]);
587 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200588 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000589 startservice(&svd[0]);
590
Denis Vlasenko37188322008-02-16 13:20:56 +0000591 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000592 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000593 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000594 x[1].events = POLLIN;
595 /* x[2] is used only if haslog == 1 */
596 x[2].fd = svd[1].fdcontrol;
597 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000598 sig_unblock(SIGTERM);
599 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000600 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000601 sig_block(SIGTERM);
602 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000603
Denis Vlasenko37188322008-02-16 13:20:56 +0000604 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000605 continue;
606
Denis Vlasenko04c63862006-11-17 18:58:49 +0000607 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000608 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000609 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000610
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000611 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000612 if (!child)
613 break;
614 if ((child == -1) && (errno != EINTR))
615 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000616 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200617 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000618 svd[0].pid = 0;
619 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200620 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000621 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200622 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000623 if (fd != -1) {
624 close(fd);
625 svd[0].state = S_FINISH;
626 update_status(&svd[0]);
627 continue;
628 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000629 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000630 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000631 deadline = svd[0].start.tv_sec + 1;
632 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000633 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000634 if (LESS(svd[0].start.tv_sec, deadline))
635 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000636 }
637 if (haslog) {
638 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200639 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000640 svd[1].pid = 0;
641 pidchanged = 1;
642 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000643 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000644 deadline = svd[1].start.tv_sec + 1;
645 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000646 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000647 if (LESS(svd[1].start.tv_sec, deadline))
648 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000649 }
650 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000651 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000652 if (read(svd[0].fdcontrol, &ch, 1) == 1)
653 ctrl(&svd[0], ch);
654 if (haslog)
655 if (read(svd[1].fdcontrol, &ch, 1) == 1)
656 ctrl(&svd[1], ch);
657
658 if (sigterm) {
659 ctrl(&svd[0], 'x');
660 sigterm = 0;
661 }
662
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200663 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000664 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000665 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200666 if (svd[1].sd_want != W_EXIT) {
667 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000668 /* stopservice(&svd[1]); */
669 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000670 close(logpipe.wr);
671 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000672 }
673 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000674 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000675 /* not reached */
676 return 0;
677}