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