runit/*: get rid of tai[a] time abstraction, it's too bloaty.
[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 "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 service *sv;
46 static char *svdir;
47 static int svnum;
48 static char *rplog;
49 static int rploglen;
50 static int logpipe[2];
51 static struct pollfd pfd[1];
52 static unsigned stamplog;
53 static smallint check = 1;
54 static smallint exitsoon;
55 static smallint set_pgrp;
56
57 static void fatal2_cannot(const char *m1, const char *m2)
58 {
59         bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
60         /* was exiting 100 */
61 }
62 static void warn3x(const char *m1, const char *m2, const char *m3)
63 {
64         bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
65 }
66 static void warn2_cannot(const char *m1, const char *m2)
67 {
68         warn3x("cannot ", m1, m2);
69 }
70 static void warnx(const char *m1)
71 {
72         warn3x(m1, "", "");
73 }
74
75 static void s_term(int sig_no)
76 {
77         exitsoon = 1;
78 }
79 static void s_hangup(int sig_no)
80 {
81         exitsoon = 2;
82 }
83
84 static void runsv(int no, const char *name)
85 {
86         pid_t pid;
87         char *prog[3];
88
89         prog[0] = (char*)"runsv";
90         prog[1] = (char*)name;
91         prog[2] = NULL;
92
93         pid = vfork();
94
95         if (pid == -1) {
96                 warn2_cannot("vfork", "");
97                 return;
98         }
99         if (pid == 0) {
100                 /* child */
101                 if (set_pgrp)
102                         setsid();
103                 signal(SIGHUP, SIG_DFL);
104                 signal(SIGTERM, SIG_DFL);
105                 execvp(prog[0], prog);
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] == '.')
128                         continue;
129                 if (stat(d->d_name, &s) == -1) {
130                         warn2_cannot("stat ", d->d_name);
131                         errno = 0;
132                         continue;
133                 }
134                 if (!S_ISDIR(s.st_mode))
135                         continue;
136                 for (i = 0; i < svnum; i++) {
137                         if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
138                                 sv[i].isgone = 0;
139                                 if (!sv[i].pid)
140                                         runsv(i, d->d_name);
141                                 break;
142                         }
143                 }
144                 if (i == svnum) {
145                         /* new service */
146                         struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
147                         if (!svnew) {
148                                 warn3x("cannot start runsv ", d->d_name,
149                                                 " too many services");
150                                 continue;
151                         }
152                         sv = svnew;
153                         svnum++;
154                         memset(&sv[i], 0, sizeof(sv[i]));
155                         sv[i].ino = s.st_ino;
156                         sv[i].dev = s.st_dev;
157                         /*sv[i].pid = 0;*/
158                         /*sv[i].isgone = 0;*/
159                         runsv(i, d->d_name);
160                         check = 1;
161                 }
162         }
163         if (errno) {
164                 warn2_cannot("read directory ", svdir);
165                 closedir(dir);
166                 check = 1;
167                 return;
168         }
169         closedir(dir);
170
171         /* SIGTERM removed runsv's */
172         for (i = 0; i < svnum; i++) {
173                 if (!sv[i].isgone)
174                         continue;
175                 if (sv[i].pid)
176                         kill(sv[i].pid, SIGTERM);
177                 sv[i] = sv[--svnum];
178                 check = 1;
179         }
180 }
181
182 static int setup_log(void)
183 {
184         rploglen = strlen(rplog);
185         if (rploglen < 7) {
186                 warnx("log must have at least seven characters");
187                 return 0;
188         }
189         if (pipe(logpipe)) {
190                 warnx("cannot create pipe for log");
191                 return -1;
192         }
193         coe(logpipe[1]);
194         coe(logpipe[0]);
195         ndelay_on(logpipe[0]);
196         ndelay_on(logpipe[1]);
197         if (dup2(logpipe[1], 2) == -1) {
198                 warnx("cannot set filedescriptor for log");
199                 return -1;
200         }
201         pfd[0].fd = logpipe[0];
202         pfd[0].events = POLLIN;
203         stamplog = monotonic_sec();
204         return 1;
205 }
206
207 int runsvdir_main(int argc, char **argv);
208 int runsvdir_main(int argc, char **argv)
209 {
210         struct stat s;
211         dev_t last_dev = last_dev; /* for gcc */
212         ino_t last_ino = last_ino; /* for gcc */
213         time_t last_mtime = 0;
214         int wstat;
215         int curdir;
216         int pid;
217         unsigned deadline;
218         unsigned now;
219         unsigned stampcheck;
220         char ch;
221         int i;
222
223         argv++;
224         if (!*argv)
225                 bb_show_usage();
226         if (argv[0][0] == '-') {
227                 switch (argv[0][1]) {
228                 case 'P': set_pgrp = 1;
229                 case '-': ++argv;
230                 }
231                 if (!*argv)
232                         bb_show_usage();
233         }
234
235         sig_catch(SIGTERM, s_term);
236         sig_catch(SIGHUP, s_hangup);
237         svdir = *argv++;
238         if (argv && *argv) {
239                 rplog = *argv;
240                 if (setup_log() != 1) {
241                         rplog = 0;
242                         warnx("log service disabled");
243                 }
244         }
245         curdir = open_read(".");
246         if (curdir == -1)
247                 fatal2_cannot("open current directory", "");
248         coe(curdir);
249
250         stampcheck = monotonic_sec();
251
252         for (;;) {
253                 /* collect children */
254                 for (;;) {
255                         pid = wait_nohang(&wstat);
256                         if (pid <= 0)
257                                 break;
258                         for (i = 0; i < svnum; i++) {
259                                 if (pid == sv[i].pid) {
260                                         /* runsv has gone */
261                                         sv[i].pid = 0;
262                                         check = 1;
263                                         break;
264                                 }
265                         }
266                 }
267
268                 now = monotonic_sec();
269                 if ((int)(now - stampcheck) >= 0) {
270                         /* wait at least a second */
271                         stampcheck = now + 1;
272
273                         if (stat(svdir, &s) != -1) {
274                                 if (check || s.st_mtime != last_mtime
275                                  || s.st_ino != last_ino || s.st_dev != last_dev
276                                 ) {
277                                         /* svdir modified */
278                                         if (chdir(svdir) != -1) {
279                                                 last_mtime = s.st_mtime;
280                                                 last_dev = s.st_dev;
281                                                 last_ino = s.st_ino;
282                                                 check = 0;
283                                                 //if (now <= mtime)
284                                                 //      sleep(1);
285                                                 runsvdir();
286                                                 while (fchdir(curdir) == -1) {
287                                                         warn2_cannot("change directory, pausing", "");
288                                                         sleep(5);
289                                                 }
290                                         } else
291                                                 warn2_cannot("change directory to ", svdir);
292                                 }
293                         } else
294                                 warn2_cannot("stat ", svdir);
295                 }
296
297                 if (rplog) {
298                         if ((int)(now - stamplog) >= 0) {
299                                 write(logpipe[1], ".", 1);
300                                 stamplog = now + 900;
301                         }
302                 }
303                 deadline = now + (check ? 1 : 5);
304
305                 pfd[0].revents = 0;
306                 sig_block(SIGCHLD);
307                 if (rplog)
308                         poll(pfd, 1, deadline*1000);
309                 else
310                         sleep(deadline);
311                 sig_unblock(SIGCHLD);
312
313                 if (pfd[0].revents & POLLIN) {
314                         while (read(logpipe[0], &ch, 1) > 0) {
315                                 if (ch) {
316                                         for (i = 6; i < rploglen; i++)
317                                                 rplog[i-1] = rplog[i];
318                                         rplog[rploglen-1] = ch;
319                                 }
320                         }
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 }