blob: 9238eec82e4ed326e2edeb24439aadc769ebdc53 [file] [log] [blame]
Denis Vlasenko04c63862006-11-17 18:58:49 +00001/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
2/* TODO: depends on runit_lib.c - review and reduce/eliminate */
3
4#include <sys/poll.h>
5#include <sys/file.h>
6#include "busybox.h"
7#include "runit_lib.h"
8
9#define MAXSERVICES 1000
10
11static char *svdir;
12static unsigned long dev;
13static unsigned long ino;
14static struct service {
15 unsigned long dev;
16 unsigned long ino;
17 int pid;
18 int isgone;
19} *sv;
20static int svnum;
21static int check = 1;
22static char *rplog;
23static int rploglen;
24static int logpipe[2];
25static iopause_fd io[1];
26static struct taia stamplog;
27static int exitsoon;
28static int pgrp;
29
30#define usage() bb_show_usage()
31static void fatal2_cannot(char *m1, char *m2)
32{
33 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
34 /* was exiting 100 */
35}
36static void warn3x(char *m1, char *m2, char *m3)
37{
38 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
39}
40static void warn2_cannot(char *m1, char *m2)
41{
42 warn3x("cannot ", m1, m2);
43}
44static void warnx(char *m1)
45{
46 warn3x(m1, "", "");
47}
48
49static void s_term(int sig_no)
50{
51 exitsoon = 1;
52}
53static void s_hangup(int sig_no)
54{
55 exitsoon = 2;
56}
57
58static void runsv(int no, char *name)
59{
60 int pid = fork();
61
62 if (pid == -1) {
63 warn2_cannot("fork for ", name);
64 return;
65 }
66 if (pid == 0) {
67 /* child */
68 char *prog[3];
69
70 prog[0] = "runsv";
71 prog[1] = name;
72 prog[2] = 0;
73 sig_uncatch(sig_hangup);
74 sig_uncatch(sig_term);
75 if (pgrp) setsid();
76 execvp(prog[0], prog);
77 //pathexec_run(*prog, prog, (char* const*)environ);
78 fatal2_cannot("start runsv ", name);
79 }
80 sv[no].pid = pid;
81}
82
83static void runsvdir(void)
84{
85 DIR *dir;
86 direntry *d;
87 int i;
88 struct stat s;
89
90 dir = opendir(".");
91 if (!dir) {
92 warn2_cannot("open directory ", svdir);
93 return;
94 }
95 for (i = 0; i < svnum; i++)
96 sv[i].isgone = 1;
97 errno = 0;
98 while ((d = readdir(dir))) {
99 if (d->d_name[0] == '.') continue;
100 if (stat(d->d_name, &s) == -1) {
101 warn2_cannot("stat ", d->d_name);
102 errno = 0;
103 continue;
104 }
105 if (!S_ISDIR(s.st_mode)) continue;
106 for (i = 0; i < svnum; i++) {
107 if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
108 sv[i].isgone = 0;
109 if (!sv[i].pid)
110 runsv(i, d->d_name);
111 break;
112 }
113 }
114 if (i == svnum) {
115 /* new service */
116 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
117 if (!svnew) {
118 warn3x("cannot start runsv ", d->d_name,
119 " too many services");
120 continue;
121 }
122 sv = svnew;
123 svnum++;
124 memset(&sv[i], 0, sizeof(sv[i]));
125 sv[i].ino = s.st_ino;
126 sv[i].dev = s.st_dev;
127 //sv[i].pid = 0;
128 //sv[i].isgone = 0;
129 runsv(i, d->d_name);
130 check = 1;
131 }
132 }
133 if (errno) {
134 warn2_cannot("read directory ", svdir);
135 closedir(dir);
136 check = 1;
137 return;
138 }
139 closedir(dir);
140
141 /* SIGTERM removed runsv's */
142 for (i = 0; i < svnum; i++) {
143 if (!sv[i].isgone)
144 continue;
145 if (sv[i].pid)
146 kill(sv[i].pid, SIGTERM);
147 sv[i] = sv[--svnum];
148 check = 1;
149 }
150}
151
152static int setup_log(void)
153{
154 rploglen = strlen(rplog);
155 if (rploglen < 7) {
156 warnx("log must have at least seven characters");
157 return 0;
158 }
159 if (pipe(logpipe) == -1) {
160 warnx("cannot create pipe for log");
161 return -1;
162 }
163 coe(logpipe[1]);
164 coe(logpipe[0]);
165 ndelay_on(logpipe[0]);
166 ndelay_on(logpipe[1]);
167 if (fd_copy(2, logpipe[1]) == -1) {
168 warnx("cannot set filedescriptor for log");
169 return -1;
170 }
171 io[0].fd = logpipe[0];
172 io[0].events = IOPAUSE_READ;
173 taia_now(&stamplog);
174 return 1;
175}
176
177int runsvdir_main(int argc, char **argv)
178{
179 struct stat s;
180 time_t mtime = 0;
181 int wstat;
182 int curdir;
183 int pid;
184 struct taia deadline;
185 struct taia now;
186 struct taia stampcheck;
187 char ch;
188 int i;
189
190 argv++;
191 if (!argv || !*argv) usage();
192 if (**argv == '-') {
193 switch (*(*argv + 1)) {
194 case 'P': pgrp = 1;
195 case '-': ++argv;
196 }
197 if (!argv || !*argv) usage();
198 }
199
200 sig_catch(sig_term, s_term);
201 sig_catch(sig_hangup, s_hangup);
202 svdir = *argv++;
203 if (argv && *argv) {
204 rplog = *argv;
205 if (setup_log() != 1) {
206 rplog = 0;
207 warnx("log service disabled");
208 }
209 }
210 curdir = open_read(".");
211 if (curdir == -1)
212 fatal2_cannot("open current directory", "");
213 coe(curdir);
214
215 taia_now(&stampcheck);
216
217 for (;;) {
218 /* collect children */
219 for (;;) {
220 pid = wait_nohang(&wstat);
221 if (pid <= 0) break;
222 for (i = 0; i < svnum; i++) {
223 if (pid == sv[i].pid) {
224 /* runsv has gone */
225 sv[i].pid = 0;
226 check = 1;
227 break;
228 }
229 }
230 }
231
232 taia_now(&now);
233 if (now.sec.x < (stampcheck.sec.x - 3)) {
234 /* time warp */
235 warnx("time warp: resetting time stamp");
236 taia_now(&stampcheck);
237 taia_now(&now);
238 if (rplog) taia_now(&stamplog);
239 }
240 if (taia_less(&now, &stampcheck) == 0) {
241 /* wait at least a second */
242 taia_uint(&deadline, 1);
243 taia_add(&stampcheck, &now, &deadline);
244
245 if (stat(svdir, &s) != -1) {
246 if (check || s.st_mtime != mtime
247 || s.st_ino != ino || s.st_dev != dev
248 ) {
249 /* svdir modified */
250 if (chdir(svdir) != -1) {
251 mtime = s.st_mtime;
252 dev = s.st_dev;
253 ino = s.st_ino;
254 check = 0;
255 if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime))
256 sleep(1);
257 runsvdir();
258 while (fchdir(curdir) == -1) {
259 warn2_cannot("change directory, pausing", "");
260 sleep(5);
261 }
262 } else
263 warn2_cannot("change directory to ", svdir);
264 }
265 } else
266 warn2_cannot("stat ", svdir);
267 }
268
269 if (rplog) {
270 if (taia_less(&now, &stamplog) == 0) {
271 write(logpipe[1], ".", 1);
272 taia_uint(&deadline, 900);
273 taia_add(&stamplog, &now, &deadline);
274 }
275 }
276 taia_uint(&deadline, check ? 1 : 5);
277 taia_add(&deadline, &now, &deadline);
278
279 sig_block(sig_child);
280 if (rplog)
281 iopause(io, 1, &deadline, &now);
282 else
283 iopause(0, 0, &deadline, &now);
284 sig_unblock(sig_child);
285
286 if (rplog && (io[0].revents | IOPAUSE_READ))
287 while (read(logpipe[0], &ch, 1) > 0)
288 if (ch) {
289 for (i = 6; i < rploglen; i++)
290 rplog[i-1] = rplog[i];
291 rplog[rploglen-1] = ch;
292 }
293
294 switch (exitsoon) {
295 case 1:
296 _exit(0);
297 case 2:
298 for (i = 0; i < svnum; i++)
299 if (sv[i].pid)
300 kill(sv[i].pid, SIGTERM);
301 _exit(111);
302 }
303 }
304 /* not reached */
305 return 0;
306}