Initial public busybox upstream commit
[busybox4maemo] / runit / runsvdir.c
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, 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
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /* Busyboxed by Denys 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 "libbb.h"
34 #include "runit_lib.h"
35
36 #define MAXSERVICES 1000
37
38 struct service {
39         dev_t dev;
40         ino_t ino;
41         pid_t pid;
42         smallint isgone;
43 };
44
45 struct globals {
46         struct service *sv;
47         char *svdir;
48         char *rplog;
49         int svnum;
50         int rploglen;
51         struct fd_pair logpipe;
52         struct pollfd pfd[1];
53         unsigned stamplog;
54         smallint check; /* = 1; */
55         smallint exitsoon;
56         smallint set_pgrp;
57 };
58 #define G (*(struct globals*)&bb_common_bufsiz1)
59 #define sv        (G.sv        )
60 #define svdir     (G.svdir     )
61 #define rplog     (G.rplog     )
62 #define svnum     (G.svnum     )
63 #define rploglen  (G.rploglen  )
64 #define logpipe   (G.logpipe   )
65 #define pfd       (G.pfd       )
66 #define stamplog  (G.stamplog  )
67 #define check     (G.check     )
68 #define exitsoon  (G.exitsoon  )
69 #define set_pgrp  (G.set_pgrp  )
70 #define INIT_G() do { \
71         check = 1; \
72 } while (0)
73
74 static void fatal2_cannot(const char *m1, const char *m2)
75 {
76         bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
77         /* was exiting 100 */
78 }
79 static 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 }
83 static void warn2_cannot(const char *m1, const char *m2)
84 {
85         warn3x("cannot ", m1, m2);
86 }
87 static void warnx(const char *m1)
88 {
89         warn3x(m1, "", "");
90 }
91
92 static void s_term(int sig_no ATTRIBUTE_UNUSED)
93 {
94         exitsoon = 1;
95 }
96 static void s_hangup(int sig_no ATTRIBUTE_UNUSED)
97 {
98         exitsoon = 2;
99 }
100
101 static void runsv(int no, const char *name)
102 {
103         pid_t pid;
104         char *prog[3];
105
106         prog[0] = (char*)"runsv";
107         prog[1] = (char*)name;
108         prog[2] = NULL;
109
110         pid = vfork();
111
112         if (pid == -1) {
113                 warn2_cannot("vfork", "");
114                 return;
115         }
116         if (pid == 0) {
117                 /* child */
118                 if (set_pgrp)
119                         setsid();
120                 bb_signals(0
121                         + (1 << SIGHUP)
122                         + (1 << SIGTERM)
123                         , SIG_DFL);
124                 execvp(prog[0], prog);
125                 fatal2_cannot("start runsv ", name);
126         }
127         sv[no].pid = pid;
128 }
129
130 static void runsvdir(void)
131 {
132         DIR *dir;
133         direntry *d;
134         int i;
135         struct stat s;
136
137         dir = opendir(".");
138         if (!dir) {
139                 warn2_cannot("open directory ", svdir);
140                 return;
141         }
142         for (i = 0; i < svnum; i++)
143                 sv[i].isgone = 1;
144         errno = 0;
145         while ((d = readdir(dir))) {
146                 if (d->d_name[0] == '.')
147                         continue;
148                 if (stat(d->d_name, &s) == -1) {
149                         warn2_cannot("stat ", d->d_name);
150                         errno = 0;
151                         continue;
152                 }
153                 if (!S_ISDIR(s.st_mode))
154                         continue;
155                 for (i = 0; i < svnum; i++) {
156                         if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
157                                 sv[i].isgone = 0;
158                                 if (!sv[i].pid)
159                                         runsv(i, d->d_name);
160                                 break;
161                         }
162                 }
163                 if (i == svnum) {
164                         /* new service */
165                         struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
166                         if (!svnew) {
167                                 warn3x("cannot start runsv ", d->d_name,
168                                                 " too many services");
169                                 continue;
170                         }
171                         sv = svnew;
172                         svnum++;
173                         memset(&sv[i], 0, sizeof(sv[i]));
174                         sv[i].ino = s.st_ino;
175                         sv[i].dev = s.st_dev;
176                         /*sv[i].pid = 0;*/
177                         /*sv[i].isgone = 0;*/
178                         runsv(i, d->d_name);
179                         check = 1;
180                 }
181         }
182         if (errno) {
183                 warn2_cannot("read directory ", svdir);
184                 closedir(dir);
185                 check = 1;
186                 return;
187         }
188         closedir(dir);
189
190         /* SIGTERM removed runsv's */
191         for (i = 0; i < svnum; i++) {
192                 if (!sv[i].isgone)
193                         continue;
194                 if (sv[i].pid)
195                         kill(sv[i].pid, SIGTERM);
196                 sv[i] = sv[--svnum];
197                 check = 1;
198         }
199 }
200
201 static int setup_log(void)
202 {
203         rploglen = strlen(rplog);
204         if (rploglen < 7) {
205                 warnx("log must have at least seven characters");
206                 return 0;
207         }
208         if (piped_pair(logpipe)) {
209                 warnx("cannot create pipe for log");
210                 return -1;
211         }
212         close_on_exec_on(logpipe.rd);
213         close_on_exec_on(logpipe.wr);
214         ndelay_on(logpipe.rd);
215         ndelay_on(logpipe.wr);
216         if (dup2(logpipe.wr, 2) == -1) {
217                 warnx("cannot set filedescriptor for log");
218                 return -1;
219         }
220         pfd[0].fd = logpipe.rd;
221         pfd[0].events = POLLIN;
222         stamplog = monotonic_sec();
223         return 1;
224 }
225
226 int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
227 int runsvdir_main(int argc ATTRIBUTE_UNUSED, char **argv)
228 {
229         struct stat s;
230         dev_t last_dev = last_dev; /* for gcc */
231         ino_t last_ino = last_ino; /* for gcc */
232         time_t last_mtime = 0;
233         int wstat;
234         int curdir;
235         int pid;
236         unsigned deadline;
237         unsigned now;
238         unsigned stampcheck;
239         char ch;
240         int i;
241
242         INIT_G();
243
244         argv++;
245         if (!*argv)
246                 bb_show_usage();
247         if (argv[0][0] == '-') {
248                 switch (argv[0][1]) {
249                 case 'P': set_pgrp = 1;
250                 case '-': ++argv;
251                 }
252                 if (!*argv)
253                         bb_show_usage();
254         }
255
256         bb_signals_recursive(1 << SIGTERM, s_term);
257         bb_signals_recursive(1 << SIGHUP, s_hangup);
258         svdir = *argv++;
259         if (argv && *argv) {
260                 rplog = *argv;
261                 if (setup_log() != 1) {
262                         rplog = 0;
263                         warnx("log service disabled");
264                 }
265         }
266         curdir = open_read(".");
267         if (curdir == -1)
268                 fatal2_cannot("open current directory", "");
269         close_on_exec_on(curdir);
270
271         stampcheck = monotonic_sec();
272
273         for (;;) {
274                 /* collect children */
275                 for (;;) {
276                         pid = wait_any_nohang(&wstat);
277                         if (pid <= 0)
278                                 break;
279                         for (i = 0; i < svnum; i++) {
280                                 if (pid == sv[i].pid) {
281                                         /* runsv has gone */
282                                         sv[i].pid = 0;
283                                         check = 1;
284                                         break;
285                                 }
286                         }
287                 }
288
289                 now = monotonic_sec();
290                 if ((int)(now - stampcheck) >= 0) {
291                         /* wait at least a second */
292                         stampcheck = now + 1;
293
294                         if (stat(svdir, &s) != -1) {
295                                 if (check || s.st_mtime != last_mtime
296                                  || s.st_ino != last_ino || s.st_dev != last_dev
297                                 ) {
298                                         /* svdir modified */
299                                         if (chdir(svdir) != -1) {
300                                                 last_mtime = s.st_mtime;
301                                                 last_dev = s.st_dev;
302                                                 last_ino = s.st_ino;
303                                                 check = 0;
304                                                 //if (now <= mtime)
305                                                 //      sleep(1);
306                                                 runsvdir();
307                                                 while (fchdir(curdir) == -1) {
308                                                         warn2_cannot("change directory, pausing", "");
309                                                         sleep(5);
310                                                 }
311                                         } else
312                                                 warn2_cannot("change directory to ", svdir);
313                                 }
314                         } else
315                                 warn2_cannot("stat ", svdir);
316                 }
317
318                 if (rplog) {
319                         if ((int)(now - stamplog) >= 0) {
320                                 write(logpipe.wr, ".", 1);
321                                 stamplog = now + 900;
322                         }
323                 }
324
325                 pfd[0].revents = 0;
326                 sig_block(SIGCHLD);
327                 deadline = (check ? 1 : 5);
328                 if (rplog)
329                         poll(pfd, 1, deadline*1000);
330                 else
331                         sleep(deadline);
332                 sig_unblock(SIGCHLD);
333
334                 if (pfd[0].revents & POLLIN) {
335                         while (read(logpipe.rd, &ch, 1) > 0) {
336                                 if (ch) {
337                                         for (i = 6; i < rploglen; i++)
338                                                 rplog[i-1] = rplog[i];
339                                         rplog[rploglen-1] = ch;
340                                 }
341                         }
342                 }
343
344                 switch (exitsoon) {
345                 case 1:
346                         _exit(0);
347                 case 2:
348                         for (i = 0; i < svnum; i++)
349                                 if (sv[i].pid)
350                                         kill(sv[i].pid, SIGTERM);
351                         _exit(111);
352                 }
353         }
354         /* not reached */
355         return 0;
356 }