blob: 8099ebf5c9090c5492aa287161f4528ca173a2e5 [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
36#define MAXSERVICES 1000
37
Denis Vlasenko923a79a2008-09-22 21:34:24 +000038/* Should be not needed - all dirs are on same FS, right? */
39#define CHECK_DEVNO_TOO 0
40
Denis Vlasenko45946f82007-08-20 17:27:40 +000041struct service {
Denis Vlasenko923a79a2008-09-22 21:34:24 +000042#if CHECK_DEVNO_TOO
Denis Vlasenko45946f82007-08-20 17:27:40 +000043 dev_t dev;
Denis Vlasenko923a79a2008-09-22 21:34:24 +000044#endif
Denis Vlasenko45946f82007-08-20 17:27:40 +000045 ino_t ino;
46 pid_t pid;
47 smallint isgone;
48};
49
Denis Vlasenkod0a071a2008-03-17 09:33:45 +000050struct globals {
51 struct service *sv;
52 char *svdir;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +000053 int svnum;
Denis Vlasenko923a79a2008-09-22 21:34:24 +000054#if ENABLE_FEATURE_RUNSVDIR_LOG
55 char *rplog;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +000056 int rploglen;
57 struct fd_pair logpipe;
58 struct pollfd pfd[1];
59 unsigned stamplog;
Denis Vlasenko923a79a2008-09-22 21:34:24 +000060#endif
61 smallint need_rescan; /* = 1; */
Denis Vlasenkod0a071a2008-03-17 09:33:45 +000062 smallint set_pgrp;
63};
64#define G (*(struct globals*)&bb_common_bufsiz1)
Denis Vlasenko923a79a2008-09-22 21:34:24 +000065#define sv (G.sv )
66#define svdir (G.svdir )
67#define svnum (G.svnum )
68#define rplog (G.rplog )
69#define rploglen (G.rploglen )
70#define logpipe (G.logpipe )
71#define pfd (G.pfd )
72#define stamplog (G.stamplog )
73#define need_rescan (G.need_rescan )
74#define set_pgrp (G.set_pgrp )
Denis Vlasenkod0a071a2008-03-17 09:33:45 +000075#define INIT_G() do { \
Denis Vlasenko923a79a2008-09-22 21:34:24 +000076 need_rescan = 1; \
Denis Vlasenkod0a071a2008-03-17 09:33:45 +000077} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +000078
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000079static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000080{
81 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
82 /* was exiting 100 */
83}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000084static void warn3x(const char *m1, const char *m2, const char *m3)
Denis Vlasenko04c63862006-11-17 18:58:49 +000085{
86 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
Denis Vlasenkof7996f32007-01-11 17:20:00 +000087}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000088static void warn2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000089{
90 warn3x("cannot ", m1, m2);
91}
Denis Vlasenko923a79a2008-09-22 21:34:24 +000092#if ENABLE_FEATURE_RUNSVDIR_LOG
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000093static void warnx(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +000094{
95 warn3x(m1, "", "");
Denis Vlasenkof7996f32007-01-11 17:20:00 +000096}
Denis Vlasenko923a79a2008-09-22 21:34:24 +000097#endif
Denis Vlasenko04c63862006-11-17 18:58:49 +000098
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000099static void runsv(int no, const char *name)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000100{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000101 pid_t pid;
102 char *prog[3];
103
104 prog[0] = (char*)"runsv";
105 prog[1] = (char*)name;
106 prog[2] = NULL;
107
108 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000109
110 if (pid == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000111 warn2_cannot("vfork", "");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000112 return;
113 }
114 if (pid == 0) {
115 /* child */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000116 if (set_pgrp)
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000117 setsid();
Denis Vlasenko25591c32008-02-16 22:58:56 +0000118 bb_signals(0
119 + (1 << SIGHUP)
120 + (1 << SIGTERM)
121 , SIG_DFL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000122 execvp(prog[0], prog);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000123 fatal2_cannot("start runsv ", name);
124 }
125 sv[no].pid = pid;
126}
127
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000128static void do_rescan(void)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000129{
130 DIR *dir;
131 direntry *d;
132 int i;
133 struct stat s;
134
135 dir = opendir(".");
136 if (!dir) {
137 warn2_cannot("open directory ", svdir);
138 return;
139 }
140 for (i = 0; i < svnum; i++)
141 sv[i].isgone = 1;
Denis Vlasenko8f740942008-05-31 07:00:33 +0000142
143 while (1) {
144 errno = 0;
145 d = readdir(dir);
146 if (!d)
147 break;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000148 if (d->d_name[0] == '.')
149 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000150 if (stat(d->d_name, &s) == -1) {
151 warn2_cannot("stat ", d->d_name);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000152 continue;
153 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000154 if (!S_ISDIR(s.st_mode))
155 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000156 for (i = 0; i < svnum; i++) {
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000157 if ((sv[i].ino == s.st_ino)
158#if CHECK_DEVNO_TOO
159 && (sv[i].dev == s.st_dev)
160#endif
161 ) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000162 sv[i].isgone = 0;
163 if (!sv[i].pid)
164 runsv(i, d->d_name);
165 break;
166 }
167 }
168 if (i == svnum) {
169 /* new service */
170 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
171 if (!svnew) {
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000172 warn2_cannot("start runsv ", d->d_name);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000173 continue;
174 }
175 sv = svnew;
176 svnum++;
177 memset(&sv[i], 0, sizeof(sv[i]));
178 sv[i].ino = s.st_ino;
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000179#if CHECK_DEVNO_TOO
Denis Vlasenko04c63862006-11-17 18:58:49 +0000180 sv[i].dev = s.st_dev;
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000181#endif
Denis Vlasenko45946f82007-08-20 17:27:40 +0000182 /*sv[i].pid = 0;*/
183 /*sv[i].isgone = 0;*/
Denis Vlasenko04c63862006-11-17 18:58:49 +0000184 runsv(i, d->d_name);
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000185 need_rescan = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000186 }
187 }
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000188 i = errno;
189 closedir(dir);
190 if (i) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000191 warn2_cannot("read directory ", svdir);
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000192 need_rescan = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000193 return;
194 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000195
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000196 /* Send SIGTERM to runsv whose directories were not found (removed) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000197 for (i = 0; i < svnum; i++) {
198 if (!sv[i].isgone)
199 continue;
200 if (sv[i].pid)
201 kill(sv[i].pid, SIGTERM);
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000202 svnum--;
203 sv[i] = sv[svnum];
204 i--; /* so that we don't skip new sv[i] (bug was here!) */
205 need_rescan = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000206 }
207}
208
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000209int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000210int runsvdir_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211{
212 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000213 dev_t last_dev = last_dev; /* for gcc */
214 ino_t last_ino = last_ino; /* for gcc */
215 time_t last_mtime = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000216 int wstat;
217 int curdir;
218 int pid;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000219 unsigned deadline;
220 unsigned now;
221 unsigned stampcheck;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000222 int i;
223
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000224 INIT_G();
225
Denis Vlasenko08ea11a2008-09-11 19:51:11 +0000226 opt_complementary = "-1";
227 set_pgrp = getopt32(argv, "P");
228 argv += optind;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229
Denis Vlasenko08ea11a2008-09-11 19:51:11 +0000230 bb_signals_recursive((1 << SIGTERM) | (1 << SIGHUP), record_signo);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000231 svdir = *argv++;
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000232
233#if ENABLE_FEATURE_RUNSVDIR_LOG
234 /* setup log */
235 if (*argv) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000236 rplog = *argv;
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000237 rploglen = strlen(rplog);
238 if (rploglen < 7) {
239 warnx("log must have at least seven characters");
240 } else if (piped_pair(logpipe)) {
241 warnx("cannot create pipe for log");
242 } else {
243 close_on_exec_on(logpipe.rd);
244 close_on_exec_on(logpipe.wr);
245 ndelay_on(logpipe.rd);
246 ndelay_on(logpipe.wr);
247 if (dup2(logpipe.wr, 2) == -1) {
248 warnx("cannot set filedescriptor for log");
249 } else {
250 pfd[0].fd = logpipe.rd;
251 pfd[0].events = POLLIN;
252 stamplog = monotonic_sec();
253 goto run;
254 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000255 }
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000256 rplog = NULL;
257 warnx("log service disabled");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258 }
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000259run:
260#endif
Denis Vlasenko04c63862006-11-17 18:58:49 +0000261 curdir = open_read(".");
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000262 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 fatal2_cannot("open current directory", "");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000264 close_on_exec_on(curdir);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000265
Denis Vlasenko45946f82007-08-20 17:27:40 +0000266 stampcheck = monotonic_sec();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000267
268 for (;;) {
269 /* collect children */
270 for (;;) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000271 pid = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000272 if (pid <= 0)
273 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274 for (i = 0; i < svnum; i++) {
275 if (pid == sv[i].pid) {
276 /* runsv has gone */
277 sv[i].pid = 0;
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000278 need_rescan = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 break;
280 }
281 }
282 }
283
Denis Vlasenko45946f82007-08-20 17:27:40 +0000284 now = monotonic_sec();
285 if ((int)(now - stampcheck) >= 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000286 /* wait at least a second */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000287 stampcheck = now + 1;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000288
Denis Vlasenko04c63862006-11-17 18:58:49 +0000289 if (stat(svdir, &s) != -1) {
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000290 if (need_rescan || s.st_mtime != last_mtime
Denis Vlasenko45946f82007-08-20 17:27:40 +0000291 || s.st_ino != last_ino || s.st_dev != last_dev
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 ) {
293 /* svdir modified */
294 if (chdir(svdir) != -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000295 last_mtime = s.st_mtime;
296 last_dev = s.st_dev;
297 last_ino = s.st_ino;
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000298 need_rescan = 0;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000299 //if (now <= mtime)
300 // sleep(1);
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000301 do_rescan();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000302 while (fchdir(curdir) == -1) {
303 warn2_cannot("change directory, pausing", "");
304 sleep(5);
305 }
306 } else
307 warn2_cannot("change directory to ", svdir);
308 }
309 } else
310 warn2_cannot("stat ", svdir);
311 }
312
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000313#if ENABLE_FEATURE_RUNSVDIR_LOG
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314 if (rplog) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000315 if ((int)(now - stamplog) >= 0) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000316 write(logpipe.wr, ".", 1);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000317 stamplog = now + 900;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000318 }
319 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000320 pfd[0].revents = 0;
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000321#endif
Denis Vlasenko8c783952007-01-27 22:21:52 +0000322 sig_block(SIGCHLD);
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000323 deadline = (need_rescan ? 1 : 5);
324#if ENABLE_FEATURE_RUNSVDIR_LOG
Denis Vlasenko04c63862006-11-17 18:58:49 +0000325 if (rplog)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000326 poll(pfd, 1, deadline*1000);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000327 else
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000328#endif
Denis Vlasenko45946f82007-08-20 17:27:40 +0000329 sleep(deadline);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000330 sig_unblock(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000331
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000332#if ENABLE_FEATURE_RUNSVDIR_LOG
Denis Vlasenko45946f82007-08-20 17:27:40 +0000333 if (pfd[0].revents & POLLIN) {
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000334 char ch;
Denis Vlasenko37188322008-02-16 13:20:56 +0000335 while (read(logpipe.rd, &ch, 1) > 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000336 if (ch) {
337 for (i = 6; i < rploglen; i++)
338 rplog[i-1] = rplog[i];
339 rplog[rploglen-1] = ch;
340 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000341 }
342 }
Denis Vlasenko923a79a2008-09-22 21:34:24 +0000343#endif
Denis Vlasenko08ea11a2008-09-11 19:51:11 +0000344 switch (bb_got_signal) {
345 case SIGHUP:
Denis Vlasenko04c63862006-11-17 18:58:49 +0000346 for (i = 0; i < svnum; i++)
347 if (sv[i].pid)
348 kill(sv[i].pid, SIGTERM);
Denis Vlasenko08ea11a2008-09-11 19:51:11 +0000349 // N.B. fall through
350 case SIGTERM:
351 _exit((SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000352 }
353 }
354 /* not reached */
355 return 0;
356}