0ab672ef4f81526b9b949e5db87e8a1e32335b3f
[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 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 static struct service *sv;
46 static char *svdir;
47 static int svnum;
48 static char *rplog;
49 static int rploglen;
50 static struct fd_pair logpipe;
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                 bb_signals(0
104                         + (1 << SIGHUP)
105                         + (1 << SIGTERM)
106                         , SIG_DFL);
107                 execvp(prog[0], prog);
108                 fatal2_cannot("start runsv ", name);
109         }
110         sv[no].pid = pid;
111 }
112
113 static void runsvdir(void)
114 {
115         DIR *dir;
116         direntry *d;
117         int i;
118         struct stat s;
119
120         dir = opendir(".");
121         if (!dir) {
122                 warn2_cannot("open directory ", svdir);
123                 return;
124         }
125         for (i = 0; i < svnum; i++)
126                 sv[i].isgone = 1;
127         errno = 0;
128         while ((d = readdir(dir))) {
129                 if (d->d_name[0] == '.')
130                         continue;
131                 if (stat(d->d_name, &s) == -1) {
132                         warn2_cannot("stat ", d->d_name);
133                         errno = 0;
134                         continue;
135                 }
136                 if (!S_ISDIR(s.st_mode))
137                         continue;
138                 for (i = 0; i < svnum; i++) {
139                         if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
140                                 sv[i].isgone = 0;
141                                 if (!sv[i].pid)
142                                         runsv(i, d->d_name);
143                                 break;
144                         }
145                 }
146                 if (i == svnum) {
147                         /* new service */
148                         struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
149                         if (!svnew) {
150                                 warn3x("cannot start runsv ", d->d_name,
151                                                 " too many services");
152                                 continue;
153                         }
154                         sv = svnew;
155                         svnum++;
156                         memset(&sv[i], 0, sizeof(sv[i]));
157                         sv[i].ino = s.st_ino;
158                         sv[i].dev = s.st_dev;
159                         /*sv[i].pid = 0;*/
160                         /*sv[i].isgone = 0;*/
161                         runsv(i, d->d_name);
162                         check = 1;
163                 }
164         }
165         if (errno) {
166                 warn2_cannot("read directory ", svdir);
167                 closedir(dir);
168                 check = 1;
169                 return;
170         }
171         closedir(dir);
172
173         /* SIGTERM removed runsv's */
174         for (i = 0; i < svnum; i++) {
175                 if (!sv[i].isgone)
176                         continue;
177                 if (sv[i].pid)
178                         kill(sv[i].pid, SIGTERM);
179                 sv[i] = sv[--svnum];
180                 check = 1;
181         }
182 }
183
184 static int setup_log(void)
185 {
186         rploglen = strlen(rplog);
187         if (rploglen < 7) {
188                 warnx("log must have at least seven characters");
189                 return 0;
190         }
191         if (piped_pair(logpipe)) {
192                 warnx("cannot create pipe for log");
193                 return -1;
194         }
195         close_on_exec_on(logpipe.rd);
196         close_on_exec_on(logpipe.wr);
197         ndelay_on(logpipe.rd);
198         ndelay_on(logpipe.wr);
199         if (dup2(logpipe.wr, 2) == -1) {
200                 warnx("cannot set filedescriptor for log");
201                 return -1;
202         }
203         pfd[0].fd = logpipe.rd;
204         pfd[0].events = POLLIN;
205         stamplog = monotonic_sec();
206         return 1;
207 }
208
209 int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
210 int runsvdir_main(int argc, char **argv)
211 {
212         struct stat s;
213         dev_t last_dev = last_dev; /* for gcc */
214         ino_t last_ino = last_ino; /* for gcc */
215         time_t last_mtime = 0;
216         int wstat;
217         int curdir;
218         int pid;
219         unsigned deadline;
220         unsigned now;
221         unsigned stampcheck;
222         char ch;
223         int i;
224
225         argv++;
226         if (!*argv)
227                 bb_show_usage();
228         if (argv[0][0] == '-') {
229                 switch (argv[0][1]) {
230                 case 'P': set_pgrp = 1;
231                 case '-': ++argv;
232                 }
233                 if (!*argv)
234                         bb_show_usage();
235         }
236
237         bb_signals_recursive(1 << SIGTERM, s_term);
238         bb_signals_recursive(1 << SIGHUP, s_hangup);
239         svdir = *argv++;
240         if (argv && *argv) {
241                 rplog = *argv;
242                 if (setup_log() != 1) {
243                         rplog = 0;
244                         warnx("log service disabled");
245                 }
246         }
247         curdir = open_read(".");
248         if (curdir == -1)
249                 fatal2_cannot("open current directory", "");
250         close_on_exec_on(curdir);
251
252         stampcheck = monotonic_sec();
253
254         for (;;) {
255                 /* collect children */
256                 for (;;) {
257                         pid = wait_any_nohang(&wstat);
258                         if (pid <= 0)
259                                 break;
260                         for (i = 0; i < svnum; i++) {
261                                 if (pid == sv[i].pid) {
262                                         /* runsv has gone */
263                                         sv[i].pid = 0;
264                                         check = 1;
265                                         break;
266                                 }
267                         }
268                 }
269
270                 now = monotonic_sec();
271                 if ((int)(now - stampcheck) >= 0) {
272                         /* wait at least a second */
273                         stampcheck = now + 1;
274
275                         if (stat(svdir, &s) != -1) {
276                                 if (check || s.st_mtime != last_mtime
277                                  || s.st_ino != last_ino || s.st_dev != last_dev
278                                 ) {
279                                         /* svdir modified */
280                                         if (chdir(svdir) != -1) {
281                                                 last_mtime = s.st_mtime;
282                                                 last_dev = s.st_dev;
283                                                 last_ino = s.st_ino;
284                                                 check = 0;
285                                                 //if (now <= mtime)
286                                                 //      sleep(1);
287                                                 runsvdir();
288                                                 while (fchdir(curdir) == -1) {
289                                                         warn2_cannot("change directory, pausing", "");
290                                                         sleep(5);
291                                                 }
292                                         } else
293                                                 warn2_cannot("change directory to ", svdir);
294                                 }
295                         } else
296                                 warn2_cannot("stat ", svdir);
297                 }
298
299                 if (rplog) {
300                         if ((int)(now - stamplog) >= 0) {
301                                 write(logpipe.wr, ".", 1);
302                                 stamplog = now + 900;
303                         }
304                 }
305
306                 pfd[0].revents = 0;
307                 sig_block(SIGCHLD);
308                 deadline = (check ? 1 : 5);
309                 if (rplog)
310                         poll(pfd, 1, deadline*1000);
311                 else
312                         sleep(deadline);
313                 sig_unblock(SIGCHLD);
314
315                 if (pfd[0].revents & POLLIN) {
316                         while (read(logpipe.rd, &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                 }
324
325                 switch (exitsoon) {
326                 case 1:
327                         _exit(0);
328                 case 2:
329                         for (i = 0; i < svnum; i++)
330                                 if (sv[i].pid)
331                                         kill(sv[i].pid, SIGTERM);
332                         _exit(111);
333                 }
334         }
335         /* not reached */
336         return 0;
337 }