1 #include <unistd.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <signal.h>
5 #include "sig.h"
6 #include "direntry.h"
7 #include "strerr.h"
8 #include "error.h"
9 #include "open.h"
10 #include "wait.h"
11 #include "closeonexec.h"
12 #include "fd.h"
13 #include "env.h"
14 #include "str.h"
15 #include "byte.h"
16 #include "pathexec.h"
17 
18 #define SERVICES 1000
19 
20 #define INFO "svscan: info: "
21 #define WARNING "svscan: warning: "
22 #define FATAL "svscan: fatal: "
23 
24 struct {
25   unsigned long dev;
26   unsigned long ino;
27   int flagactive;
28   int flaglog;
29   int pid; /* 0 if not running */
30   int pidlog; /* 0 if not running */
31   int pi[2]; /* defined if flaglog */
32 } x[SERVICES];
33 int numx = 0;
34 int logx = -1;
35 int logpipe[2];
36 const char *logdir = 0;
37 int all_stopped, exit_asap=0;
38 
39 char fnlog[260];
40 
catch_sig(int sig)41 void catch_sig(int sig)
42 {
43   exit_asap = 1;
44 }
45 
start(const char * fn)46 void start(const char *fn)
47 {
48   unsigned int fnlen;
49   struct stat st;
50   int child;
51   int i;
52   const char *args[3];
53   int islog;
54 
55   islog = logdir && !str_diff(fn,logdir);
56   if (fn[0] == '.' && !islog) return;
57 
58   if (stat(fn,&st) == -1) {
59     strerr_warn4sys(WARNING,"unable to stat ",fn,"");
60     return;
61   }
62 
63   if ((st.st_mode & S_IFMT) != S_IFDIR) return;
64 
65   for (i = 0;i < numx;++i)
66     if (x[i].ino == st.st_ino)
67       if (x[i].dev == st.st_dev)
68 	break;
69 
70   if (i == numx) {
71     if (numx >= SERVICES) {
72       strerr_warn4(WARNING,"unable to start ",fn,": running too many services",0);
73       return;
74     }
75     x[i].ino = st.st_ino;
76     x[i].dev = st.st_dev;
77     x[i].pid = 0;
78     x[i].pidlog = 0;
79     x[i].flaglog = 0;
80 
81     fnlen = str_len(fn);
82     if (fnlen <= 255) {
83       byte_copy(fnlog,fnlen,fn);
84       byte_copy(fnlog + fnlen,5,"/log");
85       if (stat(fnlog,&st) == 0)
86 	x[i].flaglog = S_ISDIR(st.st_mode);
87       else
88 	if (errno != error_noent) {
89           strerr_warn4sys(WARNING,"unable to stat ",fn,"/log");
90           return;
91 	}
92     }
93 
94     if (x[i].flaglog) {
95       if (pipe(x[i].pi) == -1) {
96         strerr_warn4sys(WARNING,"unable to create pipe for ",fn,"");
97         return;
98       }
99       closeonexec(x[i].pi[0]);
100       closeonexec(x[i].pi[1]);
101     }
102     ++numx;
103   }
104 
105   x[i].flagactive = 1;
106 
107   if (!x[i].pid && !exit_asap)
108     switch(child = fork()) {
109       case -1:
110         strerr_warn4sys(WARNING,"unable to fork for ",fn,"");
111         return;
112       case 0:
113         if (x[i].flaglog)
114 	  if (fd_move(1,x[i].pi[1]) == -1)
115             strerr_die3sys(111,WARNING,"unable to set up descriptors for ",fn);
116 	if (i == logx)
117 	  if (fd_move(0,logpipe[0]) == -1)
118 	    strerr_die3sys(111,WARNING,"unable to set up descriptors for ",fn);
119         args[0] = "supervise";
120         args[1] = fn;
121         args[2] = 0;
122 	pathexec_run(*args,args,(const char*const*)environ);
123         strerr_die3sys(111,WARNING,"unable to start supervise ",fn);
124       default:
125 	x[i].pid = child;
126         all_stopped = 0;
127     }
128   else
129     if (x[i].pid) {
130       all_stopped = 0;
131       if (exit_asap)
132         kill(x[i].pid,SIGTERM);
133     }
134 
135   if (x[i].flaglog && !x[i].pidlog && !exit_asap)
136     switch(child = fork()) {
137       case -1:
138         strerr_warn4sys(WARNING,"unable to fork for ",fn,"/log");
139         return;
140       case 0:
141         if (fd_move(0,x[i].pi[0]) == -1)
142           strerr_die4sys(111,WARNING,"unable to set up descriptors for ",fn,"/log");
143 	if (chdir(fn) == -1)
144           strerr_die3sys(111,WARNING,"unable to switch to ",fn);
145         args[0] = "supervise";
146         args[1] = "log";
147         args[2] = 0;
148 	pathexec_run(*args,args,(const char*const*)environ);
149         strerr_die4sys(111,WARNING,"unable to start supervise ",fn,"/log");
150       default:
151 	x[i].pidlog = child;
152         all_stopped = 0;
153     }
154   else
155     if (x[i].flaglog && x[i].pidlog) {
156       all_stopped = 0;
157       if (exit_asap && !x[i].pid)
158         kill(x[i].pidlog,SIGTERM);
159     }
160 }
161 
direrror(void)162 void direrror(void)
163 {
164   strerr_warn2sys(WARNING,"unable to read directory");
165 }
166 
doit(void)167 void doit(void)
168 {
169   DIR *dir;
170   direntry *d;
171   int i;
172   int r;
173   int wstat;
174 
175   for (;;) {
176     r = wait_nohang(&wstat);
177     if (!r) break;
178     if (r == -1) {
179       if (errno == error_intr) continue; /* impossible */
180       break;
181     }
182 
183     for (i = 0;i < numx;++i) {
184       if (x[i].pid == r) { x[i].pid = 0; break; }
185       if (x[i].pidlog == r) { x[i].pidlog = 0; break; }
186     }
187   }
188 
189   for (i = 0;i < numx;++i)
190     x[i].flagactive = 0;
191 
192   dir = opendir(".");
193   if (!dir) {
194     direrror();
195     return;
196   }
197   for (;;) {
198     errno = 0;
199     d = readdir(dir);
200     if (!d) break;
201     start(d->d_name);
202   }
203   if (errno) {
204     direrror();
205     closedir(dir);
206     return;
207   }
208   closedir(dir);
209 
210   i = 0;
211   while (i < numx) {
212     if (!x[i].flagactive && !x[i].pid && !x[i].pidlog) {
213       if (x[i].flaglog) {
214         close(x[i].pi[0]);
215         close(x[i].pi[1]);
216         x[i].flaglog = 0;
217       }
218       x[i] = x[--numx];
219       continue;
220     }
221     ++i;
222   }
223 }
224 
start_log(void)225 static void start_log(void)
226 {
227   struct stat st;
228 
229   /* Make sure the standard file descriptors are open before creating any pipes. */
230   if (fstat(0,&st) != 0 && errno == EBADF)
231     (void) open_read("/dev/null");
232   if (fstat(1,&st) != 0 && errno == EBADF)
233     (void) open_write("/dev/null");
234   if (fstat(2,&st) != 0 && errno == EBADF)
235     (void) open_write("/dev/null");
236 
237   if (logdir && stat(logdir,&st) == 0 && S_ISDIR(st.st_mode)) {
238     logx = numx;
239     if (pipe(logpipe) == -1)
240       strerr_die3sys(111,FATAL,"unable to create pipe for ",logdir);
241     closeonexec(logpipe[0]);
242     closeonexec(logpipe[1]);
243     start(logdir);
244     if (numx > logx && x[logx].pid != 0) {
245       (void) fd_copy(1,logpipe[1]);
246       (void) fd_move(2,logpipe[1]);
247       strerr_warn2(INFO,"starting",0);
248     }
249   }
250 }
251 
main(int argc,char ** argv)252 int main(int argc,char **argv)
253 {
254   if (argc >= 2)
255     if (chdir(argv[1]) == -1)
256       strerr_die3sys(111,FATAL,"unable to chdir to ",argv[1]);
257   if (argc >= 3)
258     logdir = argv[2];
259 
260   start_log();
261   sig_catch(SIGTERM,catch_sig);
262 
263   for (;;) {
264     all_stopped = 1;
265     doit();
266     if (all_stopped) break;
267     sleep(exit_asap ? 1 : 5);
268   }
269   return 0;
270 }
271