blob: 39929fc49a6a3c568a126a783718256e8c356da3 [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>
33#include "busybox.h"
34#include "runit_lib.h"
35
36#define MAXSERVICES 1000
37
38static char *svdir;
39static unsigned long dev;
40static unsigned long ino;
41static struct service {
42 unsigned long dev;
43 unsigned long ino;
44 int pid;
45 int isgone;
46} *sv;
47static int svnum;
48static int check = 1;
49static char *rplog;
50static int rploglen;
51static int logpipe[2];
52static iopause_fd io[1];
53static struct taia stamplog;
54static int exitsoon;
55static int pgrp;
56
57#define usage() bb_show_usage()
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000058static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000059{
60 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
61 /* was exiting 100 */
62}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000063static void warn3x(const char *m1, const char *m2, const char *m3)
Denis Vlasenko04c63862006-11-17 18:58:49 +000064{
65 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
Denis Vlasenkof7996f32007-01-11 17:20:00 +000066}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000067static void warn2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000068{
69 warn3x("cannot ", m1, m2);
70}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000071static void warnx(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +000072{
73 warn3x(m1, "", "");
Denis Vlasenkof7996f32007-01-11 17:20:00 +000074}
Denis Vlasenko04c63862006-11-17 18:58:49 +000075
76static void s_term(int sig_no)
77{
78 exitsoon = 1;
79}
80static void s_hangup(int sig_no)
81{
82 exitsoon = 2;
83}
84
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000085static void runsv(int no, const char *name)
Denis Vlasenko04c63862006-11-17 18:58:49 +000086{
87 int pid = fork();
88
89 if (pid == -1) {
90 warn2_cannot("fork for ", name);
91 return;
92 }
93 if (pid == 0) {
94 /* child */
95 char *prog[3];
96
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000097 prog[0] = (char*)"runsv";
98 prog[1] = (char*)name;
Denis Vlasenkoe2473f82007-01-27 22:22:17 +000099 prog[2] = NULL;
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000100 if (pgrp)
101 setsid();
102 signal(SIGHUP, SIG_DFL);
103 signal(SIGTERM, SIG_DFL);
Denis Vlasenko1d76f432007-02-06 01:20:12 +0000104 BB_EXECVP(prog[0], prog);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000105 //pathexec_run(*prog, prog, (char* const*)environ);
106 fatal2_cannot("start runsv ", name);
107 }
108 sv[no].pid = pid;
109}
110
111static void runsvdir(void)
112{
113 DIR *dir;
114 direntry *d;
115 int i;
116 struct stat s;
117
118 dir = opendir(".");
119 if (!dir) {
120 warn2_cannot("open directory ", svdir);
121 return;
122 }
123 for (i = 0; i < svnum; i++)
124 sv[i].isgone = 1;
125 errno = 0;
126 while ((d = readdir(dir))) {
127 if (d->d_name[0] == '.') continue;
128 if (stat(d->d_name, &s) == -1) {
129 warn2_cannot("stat ", d->d_name);
130 errno = 0;
131 continue;
132 }
133 if (!S_ISDIR(s.st_mode)) continue;
134 for (i = 0; i < svnum; i++) {
135 if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
136 sv[i].isgone = 0;
137 if (!sv[i].pid)
138 runsv(i, d->d_name);
139 break;
140 }
141 }
142 if (i == svnum) {
143 /* new service */
144 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
145 if (!svnew) {
146 warn3x("cannot start runsv ", d->d_name,
147 " too many services");
148 continue;
149 }
150 sv = svnew;
151 svnum++;
152 memset(&sv[i], 0, sizeof(sv[i]));
153 sv[i].ino = s.st_ino;
154 sv[i].dev = s.st_dev;
155 //sv[i].pid = 0;
156 //sv[i].isgone = 0;
157 runsv(i, d->d_name);
158 check = 1;
159 }
160 }
161 if (errno) {
162 warn2_cannot("read directory ", svdir);
163 closedir(dir);
164 check = 1;
165 return;
166 }
167 closedir(dir);
168
169 /* SIGTERM removed runsv's */
170 for (i = 0; i < svnum; i++) {
171 if (!sv[i].isgone)
172 continue;
173 if (sv[i].pid)
174 kill(sv[i].pid, SIGTERM);
175 sv[i] = sv[--svnum];
176 check = 1;
177 }
178}
179
180static int setup_log(void)
181{
182 rploglen = strlen(rplog);
183 if (rploglen < 7) {
184 warnx("log must have at least seven characters");
185 return 0;
186 }
187 if (pipe(logpipe) == -1) {
188 warnx("cannot create pipe for log");
189 return -1;
190 }
191 coe(logpipe[1]);
192 coe(logpipe[0]);
193 ndelay_on(logpipe[0]);
194 ndelay_on(logpipe[1]);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000195 if (dup2(logpipe[1], 2) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000196 warnx("cannot set filedescriptor for log");
197 return -1;
198 }
199 io[0].fd = logpipe[0];
200 io[0].events = IOPAUSE_READ;
201 taia_now(&stamplog);
202 return 1;
203}
204
Denis Vlasenko06af2162007-02-03 17:28:39 +0000205int runsvdir_main(int argc, char **argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000206int runsvdir_main(int argc, char **argv)
207{
208 struct stat s;
209 time_t mtime = 0;
210 int wstat;
211 int curdir;
212 int pid;
213 struct taia deadline;
214 struct taia now;
215 struct taia stampcheck;
216 char ch;
217 int i;
218
219 argv++;
220 if (!argv || !*argv) usage();
221 if (**argv == '-') {
222 switch (*(*argv + 1)) {
223 case 'P': pgrp = 1;
224 case '-': ++argv;
225 }
226 if (!argv || !*argv) usage();
227 }
228
Denis Vlasenko8c783952007-01-27 22:21:52 +0000229 sig_catch(SIGTERM, s_term);
230 sig_catch(SIGHUP, s_hangup);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000231 svdir = *argv++;
232 if (argv && *argv) {
233 rplog = *argv;
234 if (setup_log() != 1) {
235 rplog = 0;
236 warnx("log service disabled");
237 }
238 }
239 curdir = open_read(".");
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000240 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000241 fatal2_cannot("open current directory", "");
242 coe(curdir);
243
244 taia_now(&stampcheck);
245
246 for (;;) {
247 /* collect children */
248 for (;;) {
249 pid = wait_nohang(&wstat);
250 if (pid <= 0) break;
251 for (i = 0; i < svnum; i++) {
252 if (pid == sv[i].pid) {
253 /* runsv has gone */
254 sv[i].pid = 0;
255 check = 1;
256 break;
257 }
258 }
259 }
260
261 taia_now(&now);
262 if (now.sec.x < (stampcheck.sec.x - 3)) {
263 /* time warp */
264 warnx("time warp: resetting time stamp");
265 taia_now(&stampcheck);
266 taia_now(&now);
267 if (rplog) taia_now(&stamplog);
268 }
269 if (taia_less(&now, &stampcheck) == 0) {
270 /* wait at least a second */
271 taia_uint(&deadline, 1);
272 taia_add(&stampcheck, &now, &deadline);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000273
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274 if (stat(svdir, &s) != -1) {
275 if (check || s.st_mtime != mtime
276 || s.st_ino != ino || s.st_dev != dev
277 ) {
278 /* svdir modified */
279 if (chdir(svdir) != -1) {
280 mtime = s.st_mtime;
281 dev = s.st_dev;
282 ino = s.st_ino;
283 check = 0;
284 if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime))
285 sleep(1);
286 runsvdir();
287 while (fchdir(curdir) == -1) {
288 warn2_cannot("change directory, pausing", "");
289 sleep(5);
290 }
291 } else
292 warn2_cannot("change directory to ", svdir);
293 }
294 } else
295 warn2_cannot("stat ", svdir);
296 }
297
298 if (rplog) {
299 if (taia_less(&now, &stamplog) == 0) {
300 write(logpipe[1], ".", 1);
301 taia_uint(&deadline, 900);
302 taia_add(&stamplog, &now, &deadline);
303 }
304 }
305 taia_uint(&deadline, check ? 1 : 5);
306 taia_add(&deadline, &now, &deadline);
307
Denis Vlasenko8c783952007-01-27 22:21:52 +0000308 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000309 if (rplog)
310 iopause(io, 1, &deadline, &now);
311 else
312 iopause(0, 0, &deadline, &now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000313 sig_unblock(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314
315 if (rplog && (io[0].revents | IOPAUSE_READ))
316 while (read(logpipe[0], &ch, 1) > 0)
317 if (ch) {
318 for (i = 6; i < rploglen; i++)
319 rplog[i-1] = rplog[i];
320 rplog[rploglen-1] = ch;
321 }
322
323 switch (exitsoon) {
324 case 1:
325 _exit(0);
326 case 2:
327 for (i = 0; i < svnum; i++)
328 if (sv[i].pid)
329 kill(sv[i].pid, SIGTERM);
330 _exit(111);
331 }
332 }
333 /* not reached */
334 return 0;
335}