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