add NOMMU fixme's; move move_fd from runit_lib to libbb; nuke fd_copy
[platform/upstream/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                 sig_uncatch(SIGHUP);
101                 sig_uncatch(SIGTERM);
102                 if (pgrp) setsid();
103                 BB_EXECVP(prog[0], prog);
104                 //pathexec_run(*prog, prog, (char* const*)environ);
105                 fatal2_cannot("start runsv ", name);
106         }
107         sv[no].pid = pid;
108 }
109
110 static void runsvdir(void)
111 {
112         DIR *dir;
113         direntry *d;
114         int i;
115         struct stat s;
116
117         dir = opendir(".");
118         if (!dir) {
119                 warn2_cannot("open directory ", svdir);
120                 return;
121         }
122         for (i = 0; i < svnum; i++)
123                 sv[i].isgone = 1;
124         errno = 0;
125         while ((d = readdir(dir))) {
126                 if (d->d_name[0] == '.') continue;
127                 if (stat(d->d_name, &s) == -1) {
128                         warn2_cannot("stat ", d->d_name);
129                         errno = 0;
130                         continue;
131                 }
132                 if (!S_ISDIR(s.st_mode)) continue;
133                 for (i = 0; i < svnum; i++) {
134                         if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
135                                 sv[i].isgone = 0;
136                                 if (!sv[i].pid)
137                                         runsv(i, d->d_name);
138                                 break;
139                         }
140                 }
141                 if (i == svnum) {
142                         /* new service */
143                         struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
144                         if (!svnew) {
145                                 warn3x("cannot start runsv ", d->d_name,
146                                                 " too many services");
147                                 continue;
148                         }
149                         sv = svnew;
150                         svnum++;
151                         memset(&sv[i], 0, sizeof(sv[i]));
152                         sv[i].ino = s.st_ino;
153                         sv[i].dev = s.st_dev;
154                         //sv[i].pid = 0;
155                         //sv[i].isgone = 0;
156                         runsv(i, d->d_name);
157                         check = 1;
158                 }
159         }
160         if (errno) {
161                 warn2_cannot("read directory ", svdir);
162                 closedir(dir);
163                 check = 1;
164                 return;
165         }
166         closedir(dir);
167
168         /* SIGTERM removed runsv's */
169         for (i = 0; i < svnum; i++) {
170                 if (!sv[i].isgone)
171                         continue;
172                 if (sv[i].pid)
173                         kill(sv[i].pid, SIGTERM);
174                 sv[i] = sv[--svnum];
175                 check = 1;
176         }
177 }
178
179 static int setup_log(void)
180 {
181         rploglen = strlen(rplog);
182         if (rploglen < 7) {
183                 warnx("log must have at least seven characters");
184                 return 0;
185         }
186         if (pipe(logpipe) == -1) {
187                 warnx("cannot create pipe for log");
188                 return -1;
189         }
190         coe(logpipe[1]);
191         coe(logpipe[0]);
192         ndelay_on(logpipe[0]);
193         ndelay_on(logpipe[1]);
194         if (dup2(logpipe[1], 2) == -1) {
195                 warnx("cannot set filedescriptor for log");
196                 return -1;
197         }
198         io[0].fd = logpipe[0];
199         io[0].events = IOPAUSE_READ;
200         taia_now(&stamplog);
201         return 1;
202 }
203
204 int runsvdir_main(int argc, char **argv);
205 int runsvdir_main(int argc, char **argv)
206 {
207         struct stat s;
208         time_t mtime = 0;
209         int wstat;
210         int curdir;
211         int pid;
212         struct taia deadline;
213         struct taia now;
214         struct taia stampcheck;
215         char ch;
216         int i;
217
218         argv++;
219         if (!argv || !*argv) usage();
220         if (**argv == '-') {
221                 switch (*(*argv + 1)) {
222                 case 'P': pgrp = 1;
223                 case '-': ++argv;
224                 }
225                 if (!argv || !*argv) usage();
226         }
227
228         sig_catch(SIGTERM, s_term);
229         sig_catch(SIGHUP, s_hangup);
230         svdir = *argv++;
231         if (argv && *argv) {
232                 rplog = *argv;
233                 if (setup_log() != 1) {
234                         rplog = 0;
235                         warnx("log service disabled");
236                 }
237         }
238         curdir = open_read(".");
239         if (curdir == -1)
240                 fatal2_cannot("open current directory", "");
241         coe(curdir);
242
243         taia_now(&stampcheck);
244
245         for (;;) {
246                 /* collect children */
247                 for (;;) {
248                         pid = wait_nohang(&wstat);
249                         if (pid <= 0) break;
250                         for (i = 0; i < svnum; i++) {
251                                 if (pid == sv[i].pid) {
252                                         /* runsv has gone */
253                                         sv[i].pid = 0;
254                                         check = 1;
255                                         break;
256                                 }
257                         }
258                 }
259
260                 taia_now(&now);
261                 if (now.sec.x < (stampcheck.sec.x - 3)) {
262                         /* time warp */
263                         warnx("time warp: resetting time stamp");
264                         taia_now(&stampcheck);
265                         taia_now(&now);
266                         if (rplog) taia_now(&stamplog);
267                 }
268                 if (taia_less(&now, &stampcheck) == 0) {
269                         /* wait at least a second */
270                         taia_uint(&deadline, 1);
271                         taia_add(&stampcheck, &now, &deadline);
272
273                         if (stat(svdir, &s) != -1) {
274                                 if (check || s.st_mtime != mtime
275                                  || s.st_ino != ino || s.st_dev != dev
276                                 ) {
277                                         /* svdir modified */
278                                         if (chdir(svdir) != -1) {
279                                                 mtime = s.st_mtime;
280                                                 dev = s.st_dev;
281                                                 ino = s.st_ino;
282                                                 check = 0;
283                                                 if (now.sec.x <= (4611686018427387914ULL + (uint64_t)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 (taia_less(&now, &stamplog) == 0) {
299                                 write(logpipe[1], ".", 1);
300                                 taia_uint(&deadline, 900);
301                                 taia_add(&stamplog, &now, &deadline);
302                         }
303                 }
304                 taia_uint(&deadline, check ? 1 : 5);
305                 taia_add(&deadline, &now, &deadline);
306
307                 sig_block(SIGCHLD);
308                 if (rplog)
309                         iopause(io, 1, &deadline, &now);
310                 else
311                         iopause(0, 0, &deadline, &now);
312                 sig_unblock(SIGCHLD);
313
314                 if (rplog && (io[0].revents | IOPAUSE_READ))
315                         while (read(logpipe[0], &ch, 1) > 0)
316                                 if (ch) {
317                                         for (i = 6; i < rploglen; i++)
318                                                 rplog[i-1] = rplog[i];
319                                         rplog[rploglen-1] = ch;
320                                 }
321
322                 switch (exitsoon) {
323                 case 1:
324                         _exit(0);
325                 case 2:
326                         for (i = 0; i < svnum; i++)
327                                 if (sv[i].pid)
328                                         kill(sv[i].pid, SIGTERM);
329                         _exit(111);
330                 }
331         }
332         /* not reached */
333         return 0;
334 }