1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58#include <sys/file.h>
59#include "libbb.h"
60#include "common_bufsiz.h"
61#include "runit_lib.h"
62
63#define MAXSERVICES 1000
64
65
66#define CHECK_DEVNO_TOO 0
67
68struct service {
69#if CHECK_DEVNO_TOO
70 dev_t dev;
71#endif
72 ino_t ino;
73 pid_t pid;
74 smallint isgone;
75};
76
77struct globals {
78 struct service *sv;
79 char *svdir;
80 int svnum;
81#if ENABLE_FEATURE_RUNSVDIR_LOG
82 char *rplog;
83 struct fd_pair logpipe;
84 struct pollfd pfd[1];
85 unsigned stamplog;
86#endif
87} FIX_ALIASING;
88#define G (*(struct globals*)bb_common_bufsiz1)
89#define sv (G.sv )
90#define svdir (G.svdir )
91#define svnum (G.svnum )
92#define rplog (G.rplog )
93#define logpipe (G.logpipe )
94#define pfd (G.pfd )
95#define stamplog (G.stamplog )
96#define INIT_G() do { setup_common_bufsiz(); } while (0)
97
98static void fatal2_cannot(const char *m1, const char *m2)
99{
100 bb_perror_msg_and_die("%s: fatal: can't %s%s", svdir, m1, m2);
101
102}
103static void warn3x(const char *m1, const char *m2, const char *m3)
104{
105 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
106}
107static void warn2_cannot(const char *m1, const char *m2)
108{
109 warn3x("can't ", m1, m2);
110}
111#if ENABLE_FEATURE_RUNSVDIR_LOG
112static void warnx(const char *m1)
113{
114 warn3x(m1, "", "");
115}
116#endif
117
118
119static NOINLINE pid_t runsv(const char *name)
120{
121 pid_t pid;
122
123
124 if (bb_got_signal)
125 return 0;
126
127 pid = vfork();
128 if (pid == -1) {
129 warn2_cannot("vfork", "");
130 return 0;
131 }
132 if (pid == 0) {
133
134 if (option_mask32 & 1)
135 setsid();
136
137
138
139
140#if 0
141 bb_signals(0
142 | (1 << SIGHUP)
143 | (1 << SIGTERM)
144 , SIG_DFL);
145#endif
146 execlp("runsv", "runsv", name, (char *) NULL);
147 fatal2_cannot("start runsv ", name);
148 }
149 return pid;
150}
151
152
153static NOINLINE int do_rescan(void)
154{
155 DIR *dir;
156 struct dirent *d;
157 int i;
158 struct stat s;
159 int need_rescan = 0;
160
161 dir = opendir(".");
162 if (!dir) {
163 warn2_cannot("open directory ", svdir);
164 return 1;
165 }
166 for (i = 0; i < svnum; i++)
167 sv[i].isgone = 1;
168
169 while (1) {
170 errno = 0;
171 d = readdir(dir);
172 if (!d)
173 break;
174 if (d->d_name[0] == '.')
175 continue;
176 if (stat(d->d_name, &s) == -1) {
177 warn2_cannot("stat ", d->d_name);
178 continue;
179 }
180 if (!S_ISDIR(s.st_mode))
181 continue;
182
183 for (i = 0; i < svnum; i++) {
184 if (sv[i].ino == s.st_ino
185#if CHECK_DEVNO_TOO
186 && sv[i].dev == s.st_dev
187#endif
188 ) {
189 if (sv[i].pid == 0)
190 goto run_ith_sv;
191 sv[i].isgone = 0;
192 goto next_dentry;
193 }
194 }
195 {
196 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
197 if (!svnew) {
198 warn2_cannot("start runsv ", d->d_name);
199 need_rescan = 1;
200 continue;
201 }
202 sv = svnew;
203 svnum++;
204#if CHECK_DEVNO_TOO
205 sv[i].dev = s.st_dev;
206#endif
207 sv[i].ino = s.st_ino;
208 run_ith_sv:
209 sv[i].pid = runsv(d->d_name);
210 sv[i].isgone = 0;
211 }
212 next_dentry: ;
213 }
214 i = errno;
215 closedir(dir);
216 if (i) {
217 warn2_cannot("read directory ", svdir);
218 return 1;
219 }
220
221
222
223 for (i = 0; i < svnum; i++) {
224 if (!sv[i].isgone)
225 continue;
226 if (sv[i].pid)
227 kill(sv[i].pid, SIGTERM);
228 svnum--;
229 sv[i] = sv[svnum];
230 i--;
231 }
232 return need_rescan;
233}
234
235int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
236int runsvdir_main(int argc UNUSED_PARAM, char **argv)
237{
238 struct stat s;
239 dev_t last_dev = last_dev;
240 ino_t last_ino = last_ino;
241 time_t last_mtime;
242 int curdir;
243 unsigned stampcheck;
244 int i;
245 int need_rescan;
246 bool i_am_init;
247 char *opt_s_argv[3];
248
249 INIT_G();
250
251 opt_s_argv[0] = NULL;
252 opt_s_argv[2] = NULL;
253 getopt32(argv, "^" "Ps:" "\0" "-1", &opt_s_argv[0]);
254 argv += optind;
255
256 i_am_init = (getpid() == 1);
257 bb_signals(0
258 | (1 << SIGTERM)
259 | (1 << SIGHUP)
260
261
262
263
264
265
266
267
268
269
270 | (i_am_init ? ((1 << SIGUSR1) | (1 << SIGUSR2) | (1 << SIGINT)) : 0)
271 , record_signo);
272 svdir = *argv++;
273
274#if ENABLE_FEATURE_RUNSVDIR_LOG
275
276 if (*argv) {
277 rplog = *argv;
278 if (strlen(rplog) < 7) {
279 warnx("log must have at least seven characters");
280 } else if (piped_pair(logpipe)) {
281 warnx("can't create pipe for log");
282 } else {
283 close_on_exec_on(logpipe.rd);
284 close_on_exec_on(logpipe.wr);
285 ndelay_on(logpipe.rd);
286 ndelay_on(logpipe.wr);
287 if (dup2(logpipe.wr, 2) == -1) {
288 warnx("can't set filedescriptor for log");
289 } else {
290 pfd[0].fd = logpipe.rd;
291 pfd[0].events = POLLIN;
292 stamplog = monotonic_sec();
293 goto run;
294 }
295 }
296 rplog = NULL;
297 warnx("log service disabled");
298 }
299 run:
300#endif
301 curdir = open(".", O_RDONLY|O_NDELAY);
302 if (curdir == -1)
303 fatal2_cannot("open current directory", "");
304 close_on_exec_on(curdir);
305
306 stampcheck = monotonic_sec();
307 need_rescan = 1;
308 last_mtime = 0;
309
310 for (;;) {
311 unsigned now;
312 unsigned sig;
313
314
315 for (;;) {
316 pid_t pid = wait_any_nohang(NULL);
317 if (pid <= 0)
318 break;
319 for (i = 0; i < svnum; i++) {
320 if (pid == sv[i].pid) {
321
322 sv[i].pid = 0;
323 need_rescan = 1;
324 }
325 }
326 }
327
328 now = monotonic_sec();
329 if ((int)(now - stampcheck) >= 0) {
330
331 stampcheck = now + 1;
332
333 if (stat(svdir, &s) != -1) {
334 if (need_rescan || s.st_mtime != last_mtime
335 || s.st_ino != last_ino || s.st_dev != last_dev
336 ) {
337
338 if (chdir(svdir) != -1) {
339 last_mtime = s.st_mtime;
340 last_dev = s.st_dev;
341 last_ino = s.st_ino;
342
343
344
345 while (time(NULL) == last_mtime)
346 usleep(100000);
347 need_rescan = do_rescan();
348 while (fchdir(curdir) == -1) {
349 warn2_cannot("change directory, pausing", "");
350 sleep(5);
351 }
352 } else {
353 warn2_cannot("change directory to ", svdir);
354 }
355 }
356 } else {
357 warn2_cannot("stat ", svdir);
358 }
359 }
360
361#if ENABLE_FEATURE_RUNSVDIR_LOG
362 if (rplog) {
363 if ((int)(now - stamplog) >= 0) {
364 write(logpipe.wr, ".", 1);
365 stamplog = now + 900;
366 }
367 }
368 pfd[0].revents = 0;
369#endif
370 {
371 unsigned deadline = (need_rescan ? 1 : 5);
372#if ENABLE_FEATURE_RUNSVDIR_LOG
373 if (rplog)
374 poll(pfd, 1, deadline*1000);
375 else
376#endif
377 sleep(deadline);
378 }
379
380#if ENABLE_FEATURE_RUNSVDIR_LOG
381 if (pfd[0].revents & POLLIN) {
382 char ch;
383 while (read(logpipe.rd, &ch, 1) > 0) {
384 if (ch < ' ')
385 ch = ' ';
386 for (i = 6; rplog[i] != '\0'; i++)
387 rplog[i-1] = rplog[i];
388 rplog[i-1] = ch;
389 }
390 }
391#endif
392 sig = bb_got_signal;
393 if (!sig)
394 continue;
395 bb_got_signal = 0;
396
397
398
399
400 if (opt_s_argv[0]) {
401 pid_t pid;
402
403
404 opt_s_argv[1] = utoa(sig);
405 pid = spawn(opt_s_argv);
406 if (pid > 0) {
407
408
409 while (wait(NULL) != pid)
410 continue;
411 }
412 }
413
414 if (sig == SIGHUP) {
415 for (i = 0; i < svnum; i++)
416 if (sv[i].pid)
417 kill(sv[i].pid, SIGTERM);
418 }
419
420
421 if (!i_am_init)
422 return (SIGHUP == sig) ? 111 : EXIT_SUCCESS;
423
424
425 }
426}
427