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