blob: 84f5193f5792b0faddd82c5a812477e8e7faecb7 [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 Vlasenko04c63862006-11-17 18:58:49 +000028/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
29/* 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;
93 int selfpipe[2];
94 int logpipe[2];
95 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 Vlasenko04c63862006-11-17 18:58:49 +0000131static void s_child(int sig_no)
132{
133 write(selfpipe[1], "", 1);
134}
135
136static void s_term(int sig_no)
137{
138 sigterm = 1;
139 write(selfpipe[1], "", 1); /* XXX */
140}
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
160static int rename_or_warn(const char *old, const char *new)
161{
162 if (rename(old, new) == -1) {
163 bb_perror_msg("%s: warning: cannot rename %s to %s",
164 dir, old, new);
165 return -1;
166 }
167 return 0;
168}
169
170static void update_status(struct svdir *s)
171{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000172 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000173 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000174 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000175
176 /* pid */
177 if (pidchanged) {
178 fd = open_trunc_or_warn("supervise/pid.new");
179 if (fd < 0)
180 return;
181 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000182 char spid[sizeof(int)*3 + 2];
183 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000184 write(fd, spid, size);
185 }
186 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000187 if (rename_or_warn("supervise/pid.new",
188 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000189 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000190 pidchanged = 0;
191 }
192
193 /* stat */
194 fd = open_trunc_or_warn("supervise/stat.new");
195 if (fd < -1)
196 return;
197
198 {
199 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
200 char *p = stat_buf;
201 switch (s->state) {
202 case S_DOWN:
203 p = add_str(p, "down");
204 break;
205 case S_RUN:
206 p = add_str(p, "run");
207 break;
208 case S_FINISH:
209 p = add_str(p, "finish");
210 break;
211 }
212 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
213 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
214 if (s->state != S_DOWN)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000215 switch (s->want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000216 case W_DOWN:
217 p = add_str(p, ", want down");
218 break;
219 case W_EXIT:
220 p = add_str(p, ", want exit");
221 break;
222 }
223 *p++ = '\n';
224 write(fd, stat_buf, p - stat_buf);
225 close(fd);
226 }
227
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000228 rename_or_warn("supervise/stat.new",
229 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000230
231 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000232 memset(&status, 0, sizeof(status));
233 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
234 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
235 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000236 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000237 status.paused = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 if (s->want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000239 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000240 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000241 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000242 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000243 status.got_term = 1;
244 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000245 fd = open_trunc_or_warn("supervise/status.new");
246 if (fd < 0)
247 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000248 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000249 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000250 if (sz != sizeof(status)) {
251 warn_cannot("write supervise/status.new");
252 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000253 return;
254 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000255 rename_or_warn("supervise/status.new",
256 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000257}
258
259static unsigned custom(struct svdir *s, char c)
260{
261 int pid;
262 int w;
263 char a[10];
264 struct stat st;
265 char *prog[2];
266
267 if (s->islog) return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000268 strcpy(a, "control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269 a[8] = c;
270 if (stat(a, &st) == 0) {
271 if (st.st_mode & S_IXUSR) {
272 pid = fork();
273 if (pid == -1) {
274 warn_cannot("fork for control/?");
275 return 0;
276 }
277 if (!pid) {
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000278 if (haslog && dup2(logpipe[1], 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 warn_cannot("setup stdout for control/?");
280 prog[0] = a;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000281 prog[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000282 execve(a, prog, environ);
283 fatal_cannot("run control/?");
284 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000285 while (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000286 warn_cannot("wait for child control/?");
287 return 0;
288 }
289 return !wait_exitcode(w);
290 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000291 } else {
292 if (errno != ENOENT)
293 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294 }
295 return 0;
296}
297
298static void stopservice(struct svdir *s)
299{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000300 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000301 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000302 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303 update_status(s);
304 }
305 if (s->want == W_DOWN) {
306 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000307 custom(s, 'd');
308 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000309 }
310 if (s->want == W_EXIT) {
311 kill(s->pid, SIGCONT);
312 custom(s, 'x');
313 }
314}
315
316static void startservice(struct svdir *s)
317{
318 int p;
319 char *run[2];
320
321 if (s->state == S_FINISH)
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000322 run[0] = (char*)"./finish";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000323 else {
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000324 run[0] = (char*)"./run";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000325 custom(s, 'u');
326 }
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000327 run[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000328
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000329 if (s->pid != 0)
330 stopservice(s); /* should never happen */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000331 while ((p = fork()) == -1) {
332 warn_cannot("fork, sleeping");
333 sleep(5);
334 }
335 if (p == 0) {
336 /* child */
337 if (haslog) {
338 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000339 xdup2(logpipe[0], 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000340 close(logpipe[1]);
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000341 xchdir("./log");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000342 } else {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000343 xdup2(logpipe[1], 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000344 close(logpipe[0]);
345 }
346 }
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000347 signal(SIGCHLD, SIG_DFL);
348 signal(SIGTERM, SIG_DFL);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000349 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000350 sig_unblock(SIGTERM);
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000351 execvp(*run, run);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000352 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000353 }
354 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000355 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000356 s->state = S_RUN;
357 }
358 s->pid = p;
359 pidchanged = 1;
360 s->ctrl = C_NOOP;
361 update_status(s);
362}
363
364static int ctrl(struct svdir *s, char c)
365{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000366 int sig;
367
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000368 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000369 case 'd': /* down */
370 s->want = W_DOWN;
371 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000372 if (s->pid && s->state != S_FINISH)
373 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000374 break;
375 case 'u': /* up */
376 s->want = W_UP;
377 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000378 if (s->pid == 0)
379 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000380 break;
381 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000382 if (s->islog)
383 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000384 s->want = W_EXIT;
385 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000386 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000387 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000388 if (s->pid && s->state != S_FINISH)
389 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000390 break;
391 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000392 if (s->pid && !custom(s, c))
393 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000394 s->state = S_DOWN;
395 break;
396 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000397 if (s->pid && !custom(s, c))
398 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000399 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000400 update_status(s);
401 break;
402 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000403 if (s->pid && !custom(s, c))
404 kill(s->pid, SIGCONT);
405 if (s->ctrl & C_PAUSE)
406 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000407 update_status(s);
408 break;
409 case 'o': /* once */
410 s->want = W_DOWN;
411 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000412 if (!s->pid)
413 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000414 break;
415 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000416 sig = SIGALRM;
417 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000418 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000419 sig = SIGHUP;
420 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000421 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000422 sig = SIGINT;
423 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000424 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000425 sig = SIGQUIT;
426 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000427 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000428 sig = SIGUSR1;
429 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000430 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000431 sig = SIGUSR2;
432 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000433 }
434 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000435 sendsig:
436 if (s->pid && !custom(s, c))
437 kill(s->pid, sig);
438 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000439}
440
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000441int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000442int runsv_main(int argc, char **argv)
443{
444 struct stat s;
445 int fd;
446 int r;
447 char buf[256];
448
Denis Vlasenkob9256052007-09-28 10:29:17 +0000449 INIT_G();
450
Denis Vlasenko45946f82007-08-20 17:27:40 +0000451 if (!argv[1] || argv[2])
452 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000453 dir = argv[1];
454
Denis Vlasenko5a6aedd2007-05-26 16:44:20 +0000455 xpipe(selfpipe);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000456 close_on_exec_on(selfpipe[0]);
457 close_on_exec_on(selfpipe[1]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000458 ndelay_on(selfpipe[0]);
459 ndelay_on(selfpipe[1]);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000460
Denis Vlasenko8c783952007-01-27 22:21:52 +0000461 sig_block(SIGCHLD);
462 sig_catch(SIGCHLD, s_child);
463 sig_block(SIGTERM);
464 sig_catch(SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000465
466 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000467 /* bss: svd[0].pid = 0; */
468 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
469 if (C_NOOP) svd[0].ctrl = C_NOOP;
470 if (W_UP) svd[0].want = W_UP;
471 /* bss: svd[0].islog = 0; */
472 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000473 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000474 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
475
476 if (stat("log", &s) == -1) {
477 if (errno != ENOENT)
478 warn_cannot("stat ./log");
479 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000480 if (!S_ISDIR(s.st_mode)) {
481 errno = 0;
482 warn_cannot("stat log/down: log is not a directory");
483 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000484 haslog = 1;
485 svd[1].state = S_DOWN;
486 svd[1].ctrl = C_NOOP;
487 svd[1].want = W_UP;
488 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000489 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000490 if (stat("log/down", &s) != -1)
491 svd[1].want = W_DOWN;
Denis Vlasenko5a6aedd2007-05-26 16:44:20 +0000492 xpipe(logpipe);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000493 close_on_exec_on(logpipe[0]);
494 close_on_exec_on(logpipe[1]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000495 }
496 }
497
498 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000499 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000500 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000501 if (r == sizeof(buf))
502 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000503 buf[r] = 0;
504 mkdir(buf, 0700);
505 } else {
506 if ((errno != ENOENT) && (errno != EINVAL))
507 fatal_cannot("readlink ./supervise");
508 }
509 }
510 svd[0].fdlock = xopen3("log/supervise/lock"+4,
511 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
512 if (lock_exnb(svd[0].fdlock) == -1)
513 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000514 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000515 if (haslog) {
516 if (mkdir("log/supervise", 0700) == -1) {
517 r = readlink("log/supervise", buf, 256);
518 if (r != -1) {
519 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000520 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000521 buf[r] = 0;
522 fd = xopen(".", O_RDONLY|O_NDELAY);
523 xchdir("./log");
524 mkdir(buf, 0700);
525 if (fchdir(fd) == -1)
526 fatal_cannot("change back to service directory");
527 close(fd);
528 }
529 else {
530 if ((errno != ENOENT) && (errno != EINVAL))
531 fatal_cannot("readlink ./log/supervise");
532 }
533 }
534 svd[1].fdlock = xopen3("log/supervise/lock",
535 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
536 if (lock_ex(svd[1].fdlock) == -1)
537 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000538 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000539 }
540
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000541 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000542 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000543 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000544 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000545 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000546 update_status(&svd[0]);
547 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000548 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000549 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000550 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000552 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 update_status(&svd[1]);
554 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000555 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000556 fd = xopen("log/supervise/ok"+4, 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 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000559 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000561 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000562 }
563 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000564 struct pollfd x[3];
565 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 char ch;
567
568 if (haslog)
569 if (!svd[1].pid && svd[1].want == W_UP)
570 startservice(&svd[1]);
571 if (!svd[0].pid)
572 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
573 startservice(&svd[0]);
574
575 x[0].fd = selfpipe[0];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000576 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000577 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000578 x[1].events = POLLIN;
579 /* x[2] is used only if haslog == 1 */
580 x[2].fd = svd[1].fdcontrol;
581 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000582 sig_unblock(SIGTERM);
583 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000584 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000585 sig_block(SIGTERM);
586 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000587
588 while (read(selfpipe[0], &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000589 continue;
590
Denis Vlasenko04c63862006-11-17 18:58:49 +0000591 for (;;) {
592 int child;
593 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000594
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000595 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000596 if (!child)
597 break;
598 if ((child == -1) && (errno != EINTR))
599 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000600 if (child == svd[0].pid) {
601 svd[0].pid = 0;
602 pidchanged = 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000603 svd[0].ctrl &=~ C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000604 if (svd[0].state != S_FINISH) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000605 fd = open_read("finish");
606 if (fd != -1) {
607 close(fd);
608 svd[0].state = S_FINISH;
609 update_status(&svd[0]);
610 continue;
611 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000612 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000613 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000614 deadline = svd[0].start.tv_sec + 1;
615 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000616 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000617 if (LESS(svd[0].start.tv_sec, deadline))
618 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000619 }
620 if (haslog) {
621 if (child == svd[1].pid) {
622 svd[1].pid = 0;
623 pidchanged = 1;
624 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000625 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000626 deadline = svd[1].start.tv_sec + 1;
627 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000628 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000629 if (LESS(svd[1].start.tv_sec, deadline))
630 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000631 }
632 }
633 }
634 if (read(svd[0].fdcontrol, &ch, 1) == 1)
635 ctrl(&svd[0], ch);
636 if (haslog)
637 if (read(svd[1].fdcontrol, &ch, 1) == 1)
638 ctrl(&svd[1], ch);
639
640 if (sigterm) {
641 ctrl(&svd[0], 'x');
642 sigterm = 0;
643 }
644
645 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
646 if (svd[1].pid == 0)
647 _exit(0);
648 if (svd[1].want != W_EXIT) {
649 svd[1].want = W_EXIT;
650 /* stopservice(&svd[1]); */
651 update_status(&svd[1]);
652 close(logpipe[1]);
653 close(logpipe[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000654 }
655 }
656 }
657 /* not reached */
658 return 0;
659}