blob: 7e22862cd8a125d208a29ae3b2f7818f4271dcd8 [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
Denys Vlasenko95f79532017-08-02 14:26:33 +020016THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
Denis Vlasenko8a164052007-03-12 23:34:52 +000017WARRANTIES, 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
Denys Vlasenkob097a842018-12-28 03:20:17 +010031//config: bool "runsv (7.8 kb)"
Denys Vlasenko0863e1a2015-10-19 00:41:28 +020032//config: default y
33//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020034//config: runsv starts and monitors a service and optionally an appendant log
35//config: service.
Denys Vlasenko0863e1a2015-10-19 00:41:28 +020036
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
Denis Vlasenko7bc53602007-08-31 21:45:52 +000054static void gettimeofday_ns(struct timespec *ts)
55{
Denys Vlasenkobe5a5052019-10-24 16:26:55 +020056 clock_gettime(CLOCK_REALTIME, ts);
Denis Vlasenko7bc53602007-08-31 21:45:52 +000057}
58#else
59static void gettimeofday_ns(struct timespec *ts)
60{
Denys Vlasenkoab3964d2015-10-13 14:50:20 +020061 BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
62 BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
63 /* Cheat */
64 gettimeofday((void*)ts, NULL);
65 ts->tv_nsec *= 1000;
Denis Vlasenko7bc53602007-08-31 21:45:52 +000066}
67#endif
68
69/* Compare possibly overflowing unsigned counters */
70#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
71
Denis Vlasenko04c63862006-11-17 18:58:49 +000072/* state */
73#define S_DOWN 0
74#define S_RUN 1
75#define S_FINISH 2
76/* ctrl */
77#define C_NOOP 0
78#define C_TERM 1
79#define C_PAUSE 2
80/* want */
81#define W_UP 0
82#define W_DOWN 1
83#define W_EXIT 2
84
85struct svdir {
86 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000087 smallint state;
88 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020089 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000090 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000091 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000092 int fdlock;
93 int fdcontrol;
94 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +020095 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +000096};
Denis Vlasenko04c63862006-11-17 18:58:49 +000097
Denis Vlasenkob9256052007-09-28 10:29:17 +000098struct globals {
99 smallint haslog;
100 smallint sigterm;
101 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +0000102 struct fd_pair selfpipe;
103 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000104 char *dir;
105 struct svdir svd[2];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100106} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200107#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkob9256052007-09-28 10:29:17 +0000108#define haslog (G.haslog )
109#define sigterm (G.sigterm )
110#define pidchanged (G.pidchanged )
111#define selfpipe (G.selfpipe )
112#define logpipe (G.logpipe )
113#define dir (G.dir )
114#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000115#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200116 setup_common_bufsiz(); \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000117 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}
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200135static void warn2_cannot(const char *m1, const char *m2)
136{
137 bb_perror_msg("%s: warning: can't %s%s", dir, m1, m2);
138}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000139static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000140{
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200141 warn2_cannot(m, "");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000142}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000143
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000144static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000145{
Denis Vlasenko37188322008-02-16 13:20:56 +0000146 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000147}
148
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000149static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000150{
151 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000152 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000153}
154
Denis Vlasenko04c63862006-11-17 18:58:49 +0000155static int open_trunc_or_warn(const char *name)
156{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200157 /* Why O_NDELAY? */
158 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000159 if (fd < 0)
160 bb_perror_msg("%s: warning: cannot open %s",
161 dir, name);
162 return fd;
163}
164
Denis Vlasenko04c63862006-11-17 18:58:49 +0000165static void update_status(struct svdir *s)
166{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000167 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000168 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000169 svstatus_t status;
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200170 const char *fstatus ="log/supervise/status";
171 const char *fstatusnew ="log/supervise/status.new";
172 const char *f_stat ="log/supervise/stat";
173 const char *fstatnew ="log/supervise/stat.new";
174 const char *fpid ="log/supervise/pid";
175 const char *fpidnew ="log/supervise/pid.new";
176
177 if (!s->islog) {
178 fstatus += 4;
179 fstatusnew += 4;
180 f_stat += 4;
181 fstatnew += 4;
182 fpid += 4;
183 fpidnew += 4;
184 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000185
186 /* pid */
187 if (pidchanged) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200188 fd = open_trunc_or_warn(fpidnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000189 if (fd < 0)
190 return;
191 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000192 char spid[sizeof(int)*3 + 2];
193 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000194 write(fd, spid, size);
195 }
196 close(fd);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200197 if (rename_or_warn(fpidnew, fpid))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000198 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000199 pidchanged = 0;
200 }
201
202 /* stat */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200203 fd = open_trunc_or_warn(fstatnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000204 if (fd < -1)
205 return;
206
207 {
208 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
209 char *p = stat_buf;
210 switch (s->state) {
211 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800212 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000213 break;
214 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800215 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000216 break;
217 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800218 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000219 break;
220 }
Earl Chew80a34182009-08-02 02:23:27 +0200221 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800222 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200223 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800224 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000225 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200226 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000227 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800228 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 break;
230 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800231 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000232 break;
233 }
234 *p++ = '\n';
235 write(fd, stat_buf, p - stat_buf);
236 close(fd);
237 }
238
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200239 rename_or_warn(fstatnew, f_stat);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000240
241 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000242 memset(&status, 0, sizeof(status));
243 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
244 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
245 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000246 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000247 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200248 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000249 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000250 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000251 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000252 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000253 status.got_term = 1;
254 status.run_or_finish = s->state;
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200255 fd = open_trunc_or_warn(fstatusnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000256 if (fd < 0)
257 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000258 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000260 if (sz != sizeof(status)) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200261 warn2_cannot("write ", fstatusnew);
262 unlink(fstatusnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 return;
264 }
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200265 rename_or_warn(fstatusnew, fstatus);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000266}
267
268static unsigned custom(struct svdir *s, char c)
269{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000270 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000271 int w;
272 char a[10];
273 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200275 if (s->islog)
276 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000277 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000278 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 if (stat(a, &st) == 0) {
280 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000281 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000282 if (pid == -1) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200283 warn2_cannot("vfork for ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000284 return 0;
285 }
Earl Chew80a34182009-08-02 02:23:27 +0200286 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000287 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000288 if (haslog && dup2(logpipe.wr, 1) == -1)
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200289 warn2_cannot("setup stdout for ", a);
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000290 execl(a, a, (char *) NULL);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200291 fatal2_cannot("run ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000293 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000294 if (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200295 warn2_cannot("wait for child ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000296 return 0;
297 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200298 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000299 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000300 } else {
301 if (errno != ENOENT)
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200302 warn2_cannot("stat ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303 }
304 return 0;
305}
306
307static void stopservice(struct svdir *s)
308{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000309 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000310 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000311 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000312 update_status(s);
313 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200314 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000315 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000316 custom(s, 'd');
317 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000318 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200319 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000320 kill(s->pid, SIGCONT);
321 custom(s, 'x');
322 }
323}
324
325static void startservice(struct svdir *s)
326{
327 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200328 const char *arg[4];
329 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000330
Earl Chew80a34182009-08-02 02:23:27 +0200331 if (s->state == S_FINISH) {
332/* Two arguments are given to ./finish. The first one is ./run exit code,
333 * or -1 if ./run didnt exit normally. The second one is
334 * the least significant byte of the exit status as determined by waitpid;
335 * for instance it is 0 if ./run exited normally, and the signal number
336 * if ./run was terminated by a signal. If runsv cannot start ./run
337 * for some reason, the exit code is 111 and the status is 0.
338 */
339 arg[0] = "./finish";
340 arg[1] = "-1";
341 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200342 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200343 arg[1] = exitcode;
344 }
345 //arg[2] = "0";
346 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200347 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200348 //}
349 arg[3] = NULL;
350 } else {
351 arg[0] = "./run";
352 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000353 custom(s, 'u');
354 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000355
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000356 if (s->pid != 0)
357 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000358 while ((p = vfork()) == -1) {
359 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000360 sleep(5);
361 }
362 if (p == 0) {
363 /* child */
364 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000365 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000366 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000367 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000368 close(logpipe.wr);
369 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000370 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000371 close(logpipe.rd);
372 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000373 }
374 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000375 /* Non-ignored signals revert to SIG_DFL on exec anyway */
376 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000377 + (1 << SIGCHLD)
378 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000379 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000380 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000381 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200382 execv(arg[0], (char**) arg);
383 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000384 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000385 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000386 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000387 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000388 s->state = S_RUN;
389 }
390 s->pid = p;
391 pidchanged = 1;
392 s->ctrl = C_NOOP;
393 update_status(s);
394}
395
396static int ctrl(struct svdir *s, char c)
397{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000398 int sig;
399
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000400 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000401 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200402 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000403 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200404 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000405 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000406 break;
407 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200408 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000409 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200410 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000411 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000412 break;
413 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000414 if (s->islog)
415 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200416 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000417 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000418 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000419 case 't': /* sig term */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200420 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000421 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000422 break;
423 case 'k': /* sig kill */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200424 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000425 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000426 s->state = S_DOWN;
427 break;
428 case 'p': /* sig pause */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200429 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000430 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000431 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000432 update_status(s);
433 break;
434 case 'c': /* sig cont */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200435 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000436 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000437 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438 update_status(s);
439 break;
440 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200441 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000442 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200443 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000444 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000445 break;
446 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000447 sig = SIGALRM;
448 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000449 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000450 sig = SIGHUP;
451 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000452 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000453 sig = SIGINT;
454 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000455 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000456 sig = SIGQUIT;
457 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000458 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000459 sig = SIGUSR1;
460 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000461 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000462 sig = SIGUSR2;
463 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000464 }
465 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000466 sendsig:
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200467 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000468 kill(s->pid, sig);
469 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000470}
471
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200472static void open_control(const char *f, struct svdir *s)
473{
474 struct stat st;
475 mkfifo(f, 0600);
476 if (stat(f, &st) == -1)
477 fatal2_cannot("stat ", f);
478 if (!S_ISFIFO(st.st_mode))
479 bb_error_msg_and_die("%s: fatal: %s exists but is not a fifo", dir, f);
480 s->fdcontrol = xopen(f, O_RDONLY|O_NDELAY);
481 close_on_exec_on(s->fdcontrol);
482 s->fdcontrolwrite = xopen(f, O_WRONLY|O_NDELAY);
483 close_on_exec_on(s->fdcontrolwrite);
484 update_status(s);
485}
486
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000487int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000488int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000489{
490 struct stat s;
491 int fd;
492 int r;
493 char buf[256];
494
Denis Vlasenkob9256052007-09-28 10:29:17 +0000495 INIT_G();
496
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100497 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000498
Denis Vlasenko37188322008-02-16 13:20:56 +0000499 xpiped_pair(selfpipe);
500 close_on_exec_on(selfpipe.rd);
501 close_on_exec_on(selfpipe.wr);
502 ndelay_on(selfpipe.rd);
503 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000504
Denis Vlasenko8c783952007-01-27 22:21:52 +0000505 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000506 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000507 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000508 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000509
510 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000511 /* bss: svd[0].pid = 0; */
512 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
513 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200514 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000515 /* bss: svd[0].islog = 0; */
516 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000517 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200518 if (stat("down", &s) != -1)
519 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000520
521 if (stat("log", &s) == -1) {
522 if (errno != ENOENT)
523 warn_cannot("stat ./log");
524 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000525 if (!S_ISDIR(s.st_mode)) {
526 errno = 0;
527 warn_cannot("stat log/down: log is not a directory");
528 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000529 haslog = 1;
530 svd[1].state = S_DOWN;
531 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200532 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000533 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000534 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000535 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200536 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000537 xpiped_pair(logpipe);
538 close_on_exec_on(logpipe.rd);
539 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000540 }
541 }
542
543 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000544 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000545 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000546 if (r == sizeof(buf))
547 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000548 buf[r] = 0;
549 mkdir(buf, 0700);
550 } else {
551 if ((errno != ENOENT) && (errno != EINVAL))
552 fatal_cannot("readlink ./supervise");
553 }
554 }
555 svd[0].fdlock = xopen3("log/supervise/lock"+4,
556 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200557 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000559 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 if (haslog) {
561 if (mkdir("log/supervise", 0700) == -1) {
562 r = readlink("log/supervise", buf, 256);
563 if (r != -1) {
564 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000565 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 buf[r] = 0;
567 fd = xopen(".", O_RDONLY|O_NDELAY);
568 xchdir("./log");
569 mkdir(buf, 0700);
570 if (fchdir(fd) == -1)
571 fatal_cannot("change back to service directory");
572 close(fd);
573 }
574 else {
575 if ((errno != ENOENT) && (errno != EINVAL))
576 fatal_cannot("readlink ./log/supervise");
577 }
578 }
579 svd[1].fdlock = xopen3("log/supervise/lock",
580 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200581 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000582 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000583 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 }
585
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200586 open_control("log/supervise/control"+4, &svd[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000587 if (haslog) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200588 open_control("log/supervise/control", &svd[1]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000589 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000590 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000591 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000592 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000593 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000594 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000595 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000596 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000597 }
598 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000599 struct pollfd x[3];
600 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000601 char ch;
602
603 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200604 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000605 startservice(&svd[1]);
606 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200607 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000608 startservice(&svd[0]);
609
Denis Vlasenko37188322008-02-16 13:20:56 +0000610 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000611 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000612 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000613 x[1].events = POLLIN;
614 /* x[2] is used only if haslog == 1 */
615 x[2].fd = svd[1].fdcontrol;
616 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000617 sig_unblock(SIGTERM);
618 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000619 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000620 sig_block(SIGTERM);
621 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000622
Denis Vlasenko37188322008-02-16 13:20:56 +0000623 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000624 continue;
625
Denis Vlasenko04c63862006-11-17 18:58:49 +0000626 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000627 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000628 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000629
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000630 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000631 if (!child)
632 break;
633 if ((child == -1) && (errno != EINTR))
634 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000635 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200636 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000637 svd[0].pid = 0;
638 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200639 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000640 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200641 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000642 if (fd != -1) {
643 close(fd);
644 svd[0].state = S_FINISH;
645 update_status(&svd[0]);
646 continue;
647 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000648 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000649 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000650 deadline = svd[0].start.tv_sec + 1;
651 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000652 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000653 if (LESS(svd[0].start.tv_sec, deadline))
Denys Vlasenkoec16c032020-11-29 11:37:34 +0100654 sleep1();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000655 }
656 if (haslog) {
657 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200658 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000659 svd[1].pid = 0;
660 pidchanged = 1;
661 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000662 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000663 deadline = svd[1].start.tv_sec + 1;
664 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000665 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000666 if (LESS(svd[1].start.tv_sec, deadline))
Denys Vlasenkoec16c032020-11-29 11:37:34 +0100667 sleep1();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000668 }
669 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000670 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000671 if (read(svd[0].fdcontrol, &ch, 1) == 1)
672 ctrl(&svd[0], ch);
673 if (haslog)
674 if (read(svd[1].fdcontrol, &ch, 1) == 1)
675 ctrl(&svd[1], ch);
676
677 if (sigterm) {
678 ctrl(&svd[0], 'x');
679 sigterm = 0;
680 }
681
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200682 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000683 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000684 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200685 if (svd[1].sd_want != W_EXIT) {
686 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000687 /* stopservice(&svd[1]); */
688 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000689 close(logpipe.wr);
690 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000691 }
692 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000693 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000694 /* not reached */
695 return 0;
696}