blob: e3b507d255f4620f2321183f29921a04515ea7b9 [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 )
106#define INIT_G() \
107 do { \
108 pidchanged = 1; \
109 } while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000110
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000111static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000112{
113 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
114 /* was exiting 111 */
115}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000116static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000117{
118 fatal2_cannot(m, "");
119 /* was exiting 111 */
120}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000121static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000122{
123 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
124 /* was exiting 111 */
125}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000126static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000127{
128 bb_perror_msg("%s: warning: cannot %s", dir, m);
129}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000130
Denis Vlasenko68404f12008-03-17 09:00:54 +0000131static void s_child(int sig_no ATTRIBUTE_UNUSED)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000132{
Denis Vlasenko37188322008-02-16 13:20:56 +0000133 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000134}
135
Denis Vlasenko68404f12008-03-17 09:00:54 +0000136static void s_term(int sig_no ATTRIBUTE_UNUSED)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000137{
138 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000139 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000140}
141
142static char *add_str(char *p, const char *to_add)
143{
144 while ((*p = *to_add) != '\0') {
145 p++;
146 to_add++;
147 }
148 return p;
149}
150
151static int open_trunc_or_warn(const char *name)
152{
153 int fd = open_trunc(name);
154 if (fd < 0)
155 bb_perror_msg("%s: warning: cannot open %s",
156 dir, name);
157 return fd;
158}
159
Denis Vlasenko04c63862006-11-17 18:58:49 +0000160static void update_status(struct svdir *s)
161{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000162 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000163 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000164 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000165
166 /* pid */
167 if (pidchanged) {
168 fd = open_trunc_or_warn("supervise/pid.new");
169 if (fd < 0)
170 return;
171 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000172 char spid[sizeof(int)*3 + 2];
173 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000174 write(fd, spid, size);
175 }
176 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000177 if (rename_or_warn("supervise/pid.new",
178 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000179 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000180 pidchanged = 0;
181 }
182
183 /* stat */
184 fd = open_trunc_or_warn("supervise/stat.new");
185 if (fd < -1)
186 return;
187
188 {
189 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
190 char *p = stat_buf;
191 switch (s->state) {
192 case S_DOWN:
193 p = add_str(p, "down");
194 break;
195 case S_RUN:
196 p = add_str(p, "run");
197 break;
198 case S_FINISH:
199 p = add_str(p, "finish");
200 break;
201 }
202 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
203 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
204 if (s->state != S_DOWN)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000205 switch (s->want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000206 case W_DOWN:
207 p = add_str(p, ", want down");
208 break;
209 case W_EXIT:
210 p = add_str(p, ", want exit");
211 break;
212 }
213 *p++ = '\n';
214 write(fd, stat_buf, p - stat_buf);
215 close(fd);
216 }
217
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000218 rename_or_warn("supervise/stat.new",
219 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000220
221 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000222 memset(&status, 0, sizeof(status));
223 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
224 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
225 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000226 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000227 status.paused = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228 if (s->want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000229 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000230 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000231 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000232 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000233 status.got_term = 1;
234 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000235 fd = open_trunc_or_warn("supervise/status.new");
236 if (fd < 0)
237 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000238 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000239 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000240 if (sz != sizeof(status)) {
241 warn_cannot("write supervise/status.new");
242 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000243 return;
244 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000245 rename_or_warn("supervise/status.new",
246 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000247}
248
249static unsigned custom(struct svdir *s, char c)
250{
251 int pid;
252 int w;
253 char a[10];
254 struct stat st;
255 char *prog[2];
256
257 if (s->islog) return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000258 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000259 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000260 if (stat(a, &st) == 0) {
261 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000262 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000264 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000265 return 0;
266 }
267 if (!pid) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000268 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000269 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000270 warn_cannot("setup stdout for control/?");
271 prog[0] = a;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000272 prog[1] = NULL;
Denis Vlasenko847fa772008-01-28 22:45:43 +0000273 execv(a, prog);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274 fatal_cannot("run control/?");
275 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000276 /* parent */
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000277 while (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000278 warn_cannot("wait for child control/?");
279 return 0;
280 }
281 return !wait_exitcode(w);
282 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000283 } else {
284 if (errno != ENOENT)
285 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000286 }
287 return 0;
288}
289
290static void stopservice(struct svdir *s)
291{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000292 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000293 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000294 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000295 update_status(s);
296 }
297 if (s->want == W_DOWN) {
298 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000299 custom(s, 'd');
300 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000301 }
302 if (s->want == W_EXIT) {
303 kill(s->pid, SIGCONT);
304 custom(s, 'x');
305 }
306}
307
308static void startservice(struct svdir *s)
309{
310 int p;
311 char *run[2];
312
313 if (s->state == S_FINISH)
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000314 run[0] = (char*)"./finish";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000315 else {
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000316 run[0] = (char*)"./run";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000317 custom(s, 'u');
318 }
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000319 run[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000320
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000321 if (s->pid != 0)
322 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000323 while ((p = vfork()) == -1) {
324 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000325 sleep(5);
326 }
327 if (p == 0) {
328 /* child */
329 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000330 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000331 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000332 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000333 close(logpipe.wr);
334 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000335 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000336 close(logpipe.rd);
337 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000338 }
339 }
Denis Vlasenko25591c32008-02-16 22:58:56 +0000340 bb_signals(0
341 + (1 << SIGCHLD)
342 + (1 << SIGTERM)
343 , SIG_DFL);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000344 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000345 sig_unblock(SIGTERM);
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000346 execvp(*run, run);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000347 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000348 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000349 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000350 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000351 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000352 s->state = S_RUN;
353 }
354 s->pid = p;
355 pidchanged = 1;
356 s->ctrl = C_NOOP;
357 update_status(s);
358}
359
360static int ctrl(struct svdir *s, char c)
361{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000362 int sig;
363
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000364 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000365 case 'd': /* down */
366 s->want = W_DOWN;
367 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000368 if (s->pid && s->state != S_FINISH)
369 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000370 break;
371 case 'u': /* up */
372 s->want = W_UP;
373 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000374 if (s->pid == 0)
375 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000376 break;
377 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000378 if (s->islog)
379 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000380 s->want = W_EXIT;
381 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000382 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000383 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000384 if (s->pid && s->state != S_FINISH)
385 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000386 break;
387 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000388 if (s->pid && !custom(s, c))
389 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000390 s->state = S_DOWN;
391 break;
392 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000393 if (s->pid && !custom(s, c))
394 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000395 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000396 update_status(s);
397 break;
398 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000399 if (s->pid && !custom(s, c))
400 kill(s->pid, SIGCONT);
401 if (s->ctrl & C_PAUSE)
402 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000403 update_status(s);
404 break;
405 case 'o': /* once */
406 s->want = W_DOWN;
407 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000408 if (!s->pid)
409 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000410 break;
411 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000412 sig = SIGALRM;
413 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000414 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000415 sig = SIGHUP;
416 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000417 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000418 sig = SIGINT;
419 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000420 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000421 sig = SIGQUIT;
422 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000423 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000424 sig = SIGUSR1;
425 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000426 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000427 sig = SIGUSR2;
428 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000429 }
430 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000431 sendsig:
432 if (s->pid && !custom(s, c))
433 kill(s->pid, sig);
434 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000435}
436
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000437int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +0000438int runsv_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000439{
440 struct stat s;
441 int fd;
442 int r;
443 char buf[256];
444
Denis Vlasenkob9256052007-09-28 10:29:17 +0000445 INIT_G();
446
Denis Vlasenko45946f82007-08-20 17:27:40 +0000447 if (!argv[1] || argv[2])
448 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000449 dir = argv[1];
450
Denis Vlasenko37188322008-02-16 13:20:56 +0000451 xpiped_pair(selfpipe);
452 close_on_exec_on(selfpipe.rd);
453 close_on_exec_on(selfpipe.wr);
454 ndelay_on(selfpipe.rd);
455 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000456
Denis Vlasenko8c783952007-01-27 22:21:52 +0000457 sig_block(SIGCHLD);
Denis Vlasenko25591c32008-02-16 22:58:56 +0000458 bb_signals_recursive(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000459 sig_block(SIGTERM);
Denis Vlasenko25591c32008-02-16 22:58:56 +0000460 bb_signals_recursive(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000461
462 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000463 /* bss: svd[0].pid = 0; */
464 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
465 if (C_NOOP) svd[0].ctrl = C_NOOP;
466 if (W_UP) svd[0].want = W_UP;
467 /* bss: svd[0].islog = 0; */
468 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000469 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000470 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
471
472 if (stat("log", &s) == -1) {
473 if (errno != ENOENT)
474 warn_cannot("stat ./log");
475 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000476 if (!S_ISDIR(s.st_mode)) {
477 errno = 0;
478 warn_cannot("stat log/down: log is not a directory");
479 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000480 haslog = 1;
481 svd[1].state = S_DOWN;
482 svd[1].ctrl = C_NOOP;
483 svd[1].want = W_UP;
484 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000485 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000486 if (stat("log/down", &s) != -1)
487 svd[1].want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000488 xpiped_pair(logpipe);
489 close_on_exec_on(logpipe.rd);
490 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000491 }
492 }
493
494 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000495 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000496 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000497 if (r == sizeof(buf))
498 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000499 buf[r] = 0;
500 mkdir(buf, 0700);
501 } else {
502 if ((errno != ENOENT) && (errno != EINVAL))
503 fatal_cannot("readlink ./supervise");
504 }
505 }
506 svd[0].fdlock = xopen3("log/supervise/lock"+4,
507 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
508 if (lock_exnb(svd[0].fdlock) == -1)
509 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000510 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000511 if (haslog) {
512 if (mkdir("log/supervise", 0700) == -1) {
513 r = readlink("log/supervise", buf, 256);
514 if (r != -1) {
515 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000516 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000517 buf[r] = 0;
518 fd = xopen(".", O_RDONLY|O_NDELAY);
519 xchdir("./log");
520 mkdir(buf, 0700);
521 if (fchdir(fd) == -1)
522 fatal_cannot("change back to service directory");
523 close(fd);
524 }
525 else {
526 if ((errno != ENOENT) && (errno != EINVAL))
527 fatal_cannot("readlink ./log/supervise");
528 }
529 }
530 svd[1].fdlock = xopen3("log/supervise/lock",
531 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
532 if (lock_ex(svd[1].fdlock) == -1)
533 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000534 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000535 }
536
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000537 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000538 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000539 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000540 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000541 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000542 update_status(&svd[0]);
543 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000544 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000545 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000546 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000547 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000548 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000549 update_status(&svd[1]);
550 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000551 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000552 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000553 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000554 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000555 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000556 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000557 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 }
559 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000560 struct pollfd x[3];
561 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000562 char ch;
563
564 if (haslog)
565 if (!svd[1].pid && svd[1].want == W_UP)
566 startservice(&svd[1]);
567 if (!svd[0].pid)
568 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
569 startservice(&svd[0]);
570
Denis Vlasenko37188322008-02-16 13:20:56 +0000571 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000572 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000573 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000574 x[1].events = POLLIN;
575 /* x[2] is used only if haslog == 1 */
576 x[2].fd = svd[1].fdcontrol;
577 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000578 sig_unblock(SIGTERM);
579 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000580 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000581 sig_block(SIGTERM);
582 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000583
Denis Vlasenko37188322008-02-16 13:20:56 +0000584 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000585 continue;
586
Denis Vlasenko04c63862006-11-17 18:58:49 +0000587 for (;;) {
588 int child;
589 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000590
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000591 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000592 if (!child)
593 break;
594 if ((child == -1) && (errno != EINTR))
595 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000596 if (child == svd[0].pid) {
597 svd[0].pid = 0;
598 pidchanged = 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000599 svd[0].ctrl &=~ C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000600 if (svd[0].state != S_FINISH) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000601 fd = open_read("finish");
602 if (fd != -1) {
603 close(fd);
604 svd[0].state = S_FINISH;
605 update_status(&svd[0]);
606 continue;
607 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000608 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000609 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000610 deadline = svd[0].start.tv_sec + 1;
611 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000612 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000613 if (LESS(svd[0].start.tv_sec, deadline))
614 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000615 }
616 if (haslog) {
617 if (child == svd[1].pid) {
618 svd[1].pid = 0;
619 pidchanged = 1;
620 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000621 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000622 deadline = svd[1].start.tv_sec + 1;
623 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000624 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000625 if (LESS(svd[1].start.tv_sec, deadline))
626 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000627 }
628 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000629 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000630 if (read(svd[0].fdcontrol, &ch, 1) == 1)
631 ctrl(&svd[0], ch);
632 if (haslog)
633 if (read(svd[1].fdcontrol, &ch, 1) == 1)
634 ctrl(&svd[1], ch);
635
636 if (sigterm) {
637 ctrl(&svd[0], 'x');
638 sigterm = 0;
639 }
640
641 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
642 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000643 _exit(EXIT_SUCCESS);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000644 if (svd[1].want != W_EXIT) {
645 svd[1].want = W_EXIT;
646 /* stopservice(&svd[1]); */
647 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000648 close(logpipe.wr);
649 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000650 }
651 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000652 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000653 /* not reached */
654 return 0;
655}