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