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