blob: bd0f3dcc2583bd20534e0163af543a81b77e9bd5 [file] [log] [blame]
Denis Vlasenko8a164052007-03-12 23:34:52 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000028/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denis Vlasenko04c63862006-11-17 18:58:49 +000029/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31#include <sys/poll.h>
32#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000033#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +000034#include "runit_lib.h"
35
Denis Vlasenko7bc53602007-08-31 21:45:52 +000036#if ENABLE_MONOTONIC_SYSCALL
37#include <sys/syscall.h>
38
39/* libc has incredibly messy way of doing this,
40 * typically requiring -lrt. We just skip all this mess */
41static void gettimeofday_ns(struct timespec *ts)
42{
43 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
44}
45#else
46static void gettimeofday_ns(struct timespec *ts)
47{
48 if (sizeof(struct timeval) == sizeof(struct timespec)
49 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
50 ) {
51 /* Cheat */
52 gettimeofday((void*)ts, NULL);
53 ts->tv_nsec *= 1000;
54 } else {
55 extern void BUG_need_to_implement_gettimeofday_ns(void);
56 BUG_need_to_implement_gettimeofday_ns();
57 }
58}
59#endif
60
61/* Compare possibly overflowing unsigned counters */
62#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
63
Denis Vlasenko04c63862006-11-17 18:58:49 +000064/* state */
65#define S_DOWN 0
66#define S_RUN 1
67#define S_FINISH 2
68/* ctrl */
69#define C_NOOP 0
70#define C_TERM 1
71#define C_PAUSE 2
72/* want */
73#define W_UP 0
74#define W_DOWN 1
75#define W_EXIT 2
76
77struct svdir {
78 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000079 smallint state;
80 smallint ctrl;
81 smallint want;
82 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000083 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000084 int fdlock;
85 int fdcontrol;
86 int fdcontrolwrite;
Denis Vlasenko04c63862006-11-17 18:58:49 +000087};
Denis Vlasenko04c63862006-11-17 18:58:49 +000088
Denis Vlasenkob9256052007-09-28 10:29:17 +000089struct globals {
90 smallint haslog;
91 smallint sigterm;
92 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +000093 struct fd_pair selfpipe;
94 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +000095 char *dir;
96 struct svdir svd[2];
97};
98#define G (*(struct globals*)&bb_common_bufsiz1)
99#define haslog (G.haslog )
100#define sigterm (G.sigterm )
101#define pidchanged (G.pidchanged )
102#define selfpipe (G.selfpipe )
103#define logpipe (G.logpipe )
104#define dir (G.dir )
105#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000106#define INIT_G() do { \
107 pidchanged = 1; \
108} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000109
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000110static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000111{
112 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
113 /* was exiting 111 */
114}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000115static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000116{
117 fatal2_cannot(m, "");
118 /* was exiting 111 */
119}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000120static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000121{
122 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
123 /* was exiting 111 */
124}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000125static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000126{
127 bb_perror_msg("%s: warning: cannot %s", dir, m);
128}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000129
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000130static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000131{
Denis Vlasenko37188322008-02-16 13:20:56 +0000132 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000133}
134
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000135static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000136{
137 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000138 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000139}
140
141static char *add_str(char *p, const char *to_add)
142{
143 while ((*p = *to_add) != '\0') {
144 p++;
145 to_add++;
146 }
147 return p;
148}
149
150static int open_trunc_or_warn(const char *name)
151{
152 int fd = open_trunc(name);
153 if (fd < 0)
154 bb_perror_msg("%s: warning: cannot open %s",
155 dir, name);
156 return fd;
157}
158
Denis Vlasenko04c63862006-11-17 18:58:49 +0000159static void update_status(struct svdir *s)
160{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000161 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000162 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000163 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000164
165 /* pid */
166 if (pidchanged) {
167 fd = open_trunc_or_warn("supervise/pid.new");
168 if (fd < 0)
169 return;
170 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000171 char spid[sizeof(int)*3 + 2];
172 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000173 write(fd, spid, size);
174 }
175 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000176 if (rename_or_warn("supervise/pid.new",
177 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000178 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000179 pidchanged = 0;
180 }
181
182 /* stat */
183 fd = open_trunc_or_warn("supervise/stat.new");
184 if (fd < -1)
185 return;
186
187 {
188 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
189 char *p = stat_buf;
190 switch (s->state) {
191 case S_DOWN:
192 p = add_str(p, "down");
193 break;
194 case S_RUN:
195 p = add_str(p, "run");
196 break;
197 case S_FINISH:
198 p = add_str(p, "finish");
199 break;
200 }
201 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
202 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
203 if (s->state != S_DOWN)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000204 switch (s->want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000205 case W_DOWN:
206 p = add_str(p, ", want down");
207 break;
208 case W_EXIT:
209 p = add_str(p, ", want exit");
210 break;
211 }
212 *p++ = '\n';
213 write(fd, stat_buf, p - stat_buf);
214 close(fd);
215 }
216
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000217 rename_or_warn("supervise/stat.new",
218 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000219
220 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000221 memset(&status, 0, sizeof(status));
222 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
223 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
224 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000225 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000226 status.paused = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000227 if (s->want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000228 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000230 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000231 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000232 status.got_term = 1;
233 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234 fd = open_trunc_or_warn("supervise/status.new");
235 if (fd < 0)
236 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000237 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000239 if (sz != sizeof(status)) {
240 warn_cannot("write supervise/status.new");
241 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000242 return;
243 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000244 rename_or_warn("supervise/status.new",
245 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000246}
247
248static unsigned custom(struct svdir *s, char c)
249{
250 int pid;
251 int w;
252 char a[10];
253 struct stat st;
254 char *prog[2];
255
256 if (s->islog) return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000257 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000258 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 if (stat(a, &st) == 0) {
260 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000261 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000262 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000263 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000264 return 0;
265 }
266 if (!pid) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000267 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000268 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269 warn_cannot("setup stdout for control/?");
270 prog[0] = a;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000271 prog[1] = NULL;
Denis Vlasenko847fa772008-01-28 22:45:43 +0000272 execv(a, prog);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000273 fatal_cannot("run control/?");
274 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000275 /* parent */
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000276 while (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000277 warn_cannot("wait for child control/?");
278 return 0;
279 }
280 return !wait_exitcode(w);
281 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000282 } else {
283 if (errno != ENOENT)
284 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000285 }
286 return 0;
287}
288
289static void stopservice(struct svdir *s)
290{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000291 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000293 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294 update_status(s);
295 }
296 if (s->want == W_DOWN) {
297 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000298 custom(s, 'd');
299 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000300 }
301 if (s->want == W_EXIT) {
302 kill(s->pid, SIGCONT);
303 custom(s, 'x');
304 }
305}
306
307static void startservice(struct svdir *s)
308{
309 int p;
310 char *run[2];
311
312 if (s->state == S_FINISH)
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000313 run[0] = (char*)"./finish";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314 else {
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000315 run[0] = (char*)"./run";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000316 custom(s, 'u');
317 }
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000318 run[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000319
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000320 if (s->pid != 0)
321 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000322 while ((p = vfork()) == -1) {
323 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000324 sleep(5);
325 }
326 if (p == 0) {
327 /* child */
328 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000329 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000330 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000331 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000332 close(logpipe.wr);
333 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000334 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000335 close(logpipe.rd);
336 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000337 }
338 }
Denis Vlasenko25591c32008-02-16 22:58:56 +0000339 bb_signals(0
340 + (1 << SIGCHLD)
341 + (1 << SIGTERM)
342 , SIG_DFL);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000343 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000344 sig_unblock(SIGTERM);
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000345 execvp(*run, run);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000346 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000347 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000348 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000349 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000350 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000351 s->state = S_RUN;
352 }
353 s->pid = p;
354 pidchanged = 1;
355 s->ctrl = C_NOOP;
356 update_status(s);
357}
358
359static int ctrl(struct svdir *s, char c)
360{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000361 int sig;
362
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000363 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000364 case 'd': /* down */
365 s->want = W_DOWN;
366 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000367 if (s->pid && s->state != S_FINISH)
368 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000369 break;
370 case 'u': /* up */
371 s->want = W_UP;
372 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000373 if (s->pid == 0)
374 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000375 break;
376 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000377 if (s->islog)
378 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000379 s->want = W_EXIT;
380 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000381 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000382 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000383 if (s->pid && s->state != S_FINISH)
384 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000385 break;
386 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000387 if (s->pid && !custom(s, c))
388 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000389 s->state = S_DOWN;
390 break;
391 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000392 if (s->pid && !custom(s, c))
393 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000394 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000395 update_status(s);
396 break;
397 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000398 if (s->pid && !custom(s, c))
399 kill(s->pid, SIGCONT);
400 if (s->ctrl & C_PAUSE)
401 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000402 update_status(s);
403 break;
404 case 'o': /* once */
405 s->want = W_DOWN;
406 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000407 if (!s->pid)
408 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000409 break;
410 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000411 sig = SIGALRM;
412 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000413 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000414 sig = SIGHUP;
415 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000416 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000417 sig = SIGINT;
418 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000419 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000420 sig = SIGQUIT;
421 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000422 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000423 sig = SIGUSR1;
424 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000425 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000426 sig = SIGUSR2;
427 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000428 }
429 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000430 sendsig:
431 if (s->pid && !custom(s, c))
432 kill(s->pid, sig);
433 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434}
435
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000436int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000437int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438{
439 struct stat s;
440 int fd;
441 int r;
442 char buf[256];
443
Denis Vlasenkob9256052007-09-28 10:29:17 +0000444 INIT_G();
445
Denis Vlasenko45946f82007-08-20 17:27:40 +0000446 if (!argv[1] || argv[2])
447 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000448 dir = argv[1];
449
Denis Vlasenko37188322008-02-16 13:20:56 +0000450 xpiped_pair(selfpipe);
451 close_on_exec_on(selfpipe.rd);
452 close_on_exec_on(selfpipe.wr);
453 ndelay_on(selfpipe.rd);
454 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000455
Denis Vlasenko8c783952007-01-27 22:21:52 +0000456 sig_block(SIGCHLD);
Denis Vlasenko25591c32008-02-16 22:58:56 +0000457 bb_signals_recursive(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000458 sig_block(SIGTERM);
Denis Vlasenko25591c32008-02-16 22:58:56 +0000459 bb_signals_recursive(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000460
461 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000462 /* bss: svd[0].pid = 0; */
463 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
464 if (C_NOOP) svd[0].ctrl = C_NOOP;
465 if (W_UP) svd[0].want = W_UP;
466 /* bss: svd[0].islog = 0; */
467 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000468 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000469 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
470
471 if (stat("log", &s) == -1) {
472 if (errno != ENOENT)
473 warn_cannot("stat ./log");
474 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000475 if (!S_ISDIR(s.st_mode)) {
476 errno = 0;
477 warn_cannot("stat log/down: log is not a directory");
478 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000479 haslog = 1;
480 svd[1].state = S_DOWN;
481 svd[1].ctrl = C_NOOP;
482 svd[1].want = W_UP;
483 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000484 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000485 if (stat("log/down", &s) != -1)
486 svd[1].want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000487 xpiped_pair(logpipe);
488 close_on_exec_on(logpipe.rd);
489 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000490 }
491 }
492
493 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000494 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000495 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000496 if (r == sizeof(buf))
497 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000498 buf[r] = 0;
499 mkdir(buf, 0700);
500 } else {
501 if ((errno != ENOENT) && (errno != EINVAL))
502 fatal_cannot("readlink ./supervise");
503 }
504 }
505 svd[0].fdlock = xopen3("log/supervise/lock"+4,
506 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
507 if (lock_exnb(svd[0].fdlock) == -1)
508 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000509 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000510 if (haslog) {
511 if (mkdir("log/supervise", 0700) == -1) {
512 r = readlink("log/supervise", buf, 256);
513 if (r != -1) {
514 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000515 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000516 buf[r] = 0;
517 fd = xopen(".", O_RDONLY|O_NDELAY);
518 xchdir("./log");
519 mkdir(buf, 0700);
520 if (fchdir(fd) == -1)
521 fatal_cannot("change back to service directory");
522 close(fd);
523 }
524 else {
525 if ((errno != ENOENT) && (errno != EINVAL))
526 fatal_cannot("readlink ./log/supervise");
527 }
528 }
529 svd[1].fdlock = xopen3("log/supervise/lock",
530 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
531 if (lock_ex(svd[1].fdlock) == -1)
532 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000533 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000534 }
535
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000536 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000537 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000538 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000539 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000540 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000541 update_status(&svd[0]);
542 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000543 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000544 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000545 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000546 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000547 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000548 update_status(&svd[1]);
549 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000550 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000552 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000554 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000555 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000556 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000557 }
558 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000559 struct pollfd x[3];
560 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000561 char ch;
562
563 if (haslog)
564 if (!svd[1].pid && svd[1].want == W_UP)
565 startservice(&svd[1]);
566 if (!svd[0].pid)
567 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
568 startservice(&svd[0]);
569
Denis Vlasenko37188322008-02-16 13:20:56 +0000570 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000571 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000572 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000573 x[1].events = POLLIN;
574 /* x[2] is used only if haslog == 1 */
575 x[2].fd = svd[1].fdcontrol;
576 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000577 sig_unblock(SIGTERM);
578 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000579 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000580 sig_block(SIGTERM);
581 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000582
Denis Vlasenko37188322008-02-16 13:20:56 +0000583 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000584 continue;
585
Denis Vlasenko04c63862006-11-17 18:58:49 +0000586 for (;;) {
587 int child;
588 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000589
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000590 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000591 if (!child)
592 break;
593 if ((child == -1) && (errno != EINTR))
594 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000595 if (child == svd[0].pid) {
596 svd[0].pid = 0;
597 pidchanged = 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000598 svd[0].ctrl &=~ C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000599 if (svd[0].state != S_FINISH) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000600 fd = open_read("finish");
601 if (fd != -1) {
602 close(fd);
603 svd[0].state = S_FINISH;
604 update_status(&svd[0]);
605 continue;
606 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000607 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000608 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000609 deadline = svd[0].start.tv_sec + 1;
610 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000611 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000612 if (LESS(svd[0].start.tv_sec, deadline))
613 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000614 }
615 if (haslog) {
616 if (child == svd[1].pid) {
617 svd[1].pid = 0;
618 pidchanged = 1;
619 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000620 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000621 deadline = svd[1].start.tv_sec + 1;
622 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000623 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000624 if (LESS(svd[1].start.tv_sec, deadline))
625 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000626 }
627 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000628 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000629 if (read(svd[0].fdcontrol, &ch, 1) == 1)
630 ctrl(&svd[0], ch);
631 if (haslog)
632 if (read(svd[1].fdcontrol, &ch, 1) == 1)
633 ctrl(&svd[1], ch);
634
635 if (sigterm) {
636 ctrl(&svd[0], 'x');
637 sigterm = 0;
638 }
639
640 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
641 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000642 _exit(EXIT_SUCCESS);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000643 if (svd[1].want != W_EXIT) {
644 svd[1].want = W_EXIT;
645 /* stopservice(&svd[1]); */
646 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000647 close(logpipe.wr);
648 close(logpipe.rd);
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 /* not reached */
653 return 0;
654}