preparatory patch for -Wwrite-strings #4
[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 {
179         struct stat s;
180         time_t mtime = 0;
181         int wstat;
182         int curdir;
183         int pid;
184         struct taia deadline;
185         struct taia now;
186         struct taia stampcheck;
187         char ch;
188         int i;
189
190         argv++;
191         if (!argv || !*argv) usage();
192         if (**argv == '-') {
193                 switch (*(*argv + 1)) {
194                 case 'P': pgrp = 1;
195                 case '-': ++argv;
196                 }
197                 if (!argv || !*argv) usage();
198         }
199
200         sig_catch(SIGTERM, s_term);
201         sig_catch(SIGHUP, s_hangup);
202         svdir = *argv++;
203         if (argv && *argv) {
204                 rplog = *argv;
205                 if (setup_log() != 1) {
206                         rplog = 0;
207                         warnx("log service disabled");
208                 }
209         }
210         curdir = open_read(".");
211         if (curdir == -1)
212                 fatal2_cannot("open current directory", "");
213         coe(curdir);
214
215         taia_now(&stampcheck);
216
217         for (;;) {
218                 /* collect children */
219                 for (;;) {
220                         pid = wait_nohang(&wstat);
221                         if (pid <= 0) break;
222                         for (i = 0; i < svnum; i++) {
223                                 if (pid == sv[i].pid) {
224                                         /* runsv has gone */
225                                         sv[i].pid = 0;
226                                         check = 1;
227                                         break;
228                                 }
229                         }
230                 }
231
232                 taia_now(&now);
233                 if (now.sec.x < (stampcheck.sec.x - 3)) {
234                         /* time warp */
235                         warnx("time warp: resetting time stamp");
236                         taia_now(&stampcheck);
237                         taia_now(&now);
238                         if (rplog) taia_now(&stamplog);
239                 }
240                 if (taia_less(&now, &stampcheck) == 0) {
241                         /* wait at least a second */
242                         taia_uint(&deadline, 1);
243                         taia_add(&stampcheck, &now, &deadline);
244
245                         if (stat(svdir, &s) != -1) {
246                                 if (check || s.st_mtime != mtime
247                                  || s.st_ino != ino || s.st_dev != dev
248                                 ) {
249                                         /* svdir modified */
250                                         if (chdir(svdir) != -1) {
251                                                 mtime = s.st_mtime;
252                                                 dev = s.st_dev;
253                                                 ino = s.st_ino;
254                                                 check = 0;
255                                                 if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime))
256                                                         sleep(1);
257                                                 runsvdir();
258                                                 while (fchdir(curdir) == -1) {
259                                                         warn2_cannot("change directory, pausing", "");
260                                                         sleep(5);
261                                                 }
262                                         } else
263                                                 warn2_cannot("change directory to ", svdir);
264                                 }
265                         } else
266                                 warn2_cannot("stat ", svdir);
267                 }
268
269                 if (rplog) {
270                         if (taia_less(&now, &stamplog) == 0) {
271                                 write(logpipe[1], ".", 1);
272                                 taia_uint(&deadline, 900);
273                                 taia_add(&stamplog, &now, &deadline);
274                         }
275                 }
276                 taia_uint(&deadline, check ? 1 : 5);
277                 taia_add(&deadline, &now, &deadline);
278
279                 sig_block(SIGCHLD);
280                 if (rplog)
281                         iopause(io, 1, &deadline, &now);
282                 else
283                         iopause(0, 0, &deadline, &now);
284                 sig_unblock(SIGCHLD);
285
286                 if (rplog && (io[0].revents | IOPAUSE_READ))
287                         while (read(logpipe[0], &ch, 1) > 0)
288                                 if (ch) {
289                                         for (i = 6; i < rploglen; i++)
290                                                 rplog[i-1] = rplog[i];
291                                         rplog[rploglen-1] = ch;
292                                 }
293
294                 switch (exitsoon) {
295                 case 1:
296                         _exit(0);
297                 case 2:
298                         for (i = 0; i < svnum; i++)
299                                 if (sv[i].pid)
300                                         kill(sv[i].pid, SIGTERM);
301                         _exit(111);
302                 }
303         }
304         /* not reached */
305         return 0;
306 }