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