Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/runit/runsvdir.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 532 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 1  Line 1 
1  /* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */  /*
2    Copyright (c) 2001-2006, Gerrit Pape
3    All rights reserved.
4    
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions are met:
7    
8       1. Redistributions of source code must retain the above copyright notice,
9          this list of conditions and the following disclaimer.
10       2. Redistributions in binary form must reproduce the above copyright
11          notice, this list of conditions and the following disclaimer in the
12          documentation and/or other materials provided with the distribution.
13       3. The name of the author may not be used to endorse or promote products
14          derived from this software without specific prior written permission.
15    
16    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17    WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19    EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22    OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26    */
27    
28    /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29  /* TODO: depends on runit_lib.c - review and reduce/eliminate */  /* TODO: depends on runit_lib.c - review and reduce/eliminate */
30    
31  #include <sys/poll.h>  #include <sys/poll.h>
32  #include <sys/file.h>  #include <sys/file.h>
33  #include "busybox.h"  #include "libbb.h"
34  #include "runit_lib.h"  #include "runit_lib.h"
35    
36  #define MAXSERVICES 1000  #define MAXSERVICES 1000
37    
38  static char *svdir;  /* Should be not needed - all dirs are on same FS, right? */
39  static unsigned long dev;  #define CHECK_DEVNO_TOO 0
40  static unsigned long ino;  
41  static struct service {  struct service {
42   unsigned long dev;  #if CHECK_DEVNO_TOO
43   unsigned long ino;   dev_t dev;
44   int pid;  #endif
45   int isgone;   ino_t ino;
46  } *sv;   pid_t pid;
47  static int svnum;   smallint isgone;
48  static int check = 1;  };
49  static char *rplog;  
50  static int rploglen;  struct globals {
51  static int logpipe[2];   struct service *sv;
52  static iopause_fd io[1];   char *svdir;
53  static struct taia stamplog;   int svnum;
54  static int exitsoon;  #if ENABLE_FEATURE_RUNSVDIR_LOG
55  static int pgrp;   char *rplog;
56     int rploglen;
57     struct fd_pair logpipe;
58     struct pollfd pfd[1];
59     unsigned stamplog;
60    #endif
61    };
62    #define G (*(struct globals*)&bb_common_bufsiz1)
63    #define sv          (G.sv          )
64    #define svdir       (G.svdir       )
65    #define svnum       (G.svnum       )
66    #define rplog       (G.rplog       )
67    #define rploglen    (G.rploglen    )
68    #define logpipe     (G.logpipe     )
69    #define pfd         (G.pfd         )
70    #define stamplog    (G.stamplog    )
71    #define INIT_G() do { \
72    } while (0)
73    
74  #define usage() bb_show_usage()  static void fatal2_cannot(const char *m1, const char *m2)
 static void fatal2_cannot(char *m1, char *m2)  
75  {  {
76   bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);   bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
77   /* was exiting 100 */   /* was exiting 100 */
78  }  }
79  static void warn3x(char *m1, char *m2, char *m3)  static void warn3x(const char *m1, const char *m2, const char *m3)
80  {  {
81   bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);   bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
82  }  }
83  static void warn2_cannot(char *m1, char *m2)  static void warn2_cannot(const char *m1, const char *m2)
84  {  {
85   warn3x("cannot ", m1, m2);   warn3x("cannot ", m1, m2);
86  }  }
87  static void warnx(char *m1)  #if ENABLE_FEATURE_RUNSVDIR_LOG
88    static void warnx(const char *m1)
89  {  {
90   warn3x(m1, "", "");   warn3x(m1, "", "");
91  }  }
92    #endif
93    
94  static void s_term(int sig_no)  /* inlining + vfork -> bigger code */
95  {  static NOINLINE pid_t runsv(const char *name)
  exitsoon = 1;  
 }  
 static void s_hangup(int sig_no)  
96  {  {
97   exitsoon = 2;   pid_t pid;
 }  
98    
99  static void runsv(int no, char *name)   /* If we got signaled, stop spawning children at once! */
100  {   if (bb_got_signal)
101   int pid = fork();   return 0;
102    
103     pid = vfork();
104   if (pid == -1) {   if (pid == -1) {
105   warn2_cannot("fork for ", name);   warn2_cannot("vfork", "");
106   return;   return 0;
107   }   }
108   if (pid == 0) {   if (pid == 0) {
109   /* child */   /* child */
110   char *prog[3];   if (option_mask32 & 1) /* -P option? */
111     setsid();
112   prog[0] = "runsv";  /* man execv:
113   prog[1] = name;   * "Signals set to be caught by the calling process image
114   prog[2] = 0;   *  shall be set to the default action in the new process image."
115   sig_uncatch(sig_hangup);   * Therefore, we do not need this: */
116   sig_uncatch(sig_term);  #if 0
117   if (pgrp) setsid();   bb_signals(0
118   execvp(prog[0], prog);   | (1 << SIGHUP)
119   //pathexec_run(*prog, prog, (char* const*)environ);   | (1 << SIGTERM)
120     , SIG_DFL);
121    #endif
122     execlp("runsv", "runsv", name, NULL);
123   fatal2_cannot("start runsv ", name);   fatal2_cannot("start runsv ", name);
124   }   }
125   sv[no].pid = pid;   return pid;
126  }  }
127    
128  static void runsvdir(void)  /* gcc 4.3.0 does better with NOINLINE */
129    static NOINLINE int do_rescan(void)
130  {  {
131   DIR *dir;   DIR *dir;
132   direntry *d;   direntry *d;
133   int i;   int i;
134   struct stat s;   struct stat s;
135     int need_rescan = 0;
136    
137   dir = opendir(".");   dir = opendir(".");
138   if (!dir) {   if (!dir) {
139   warn2_cannot("open directory ", svdir);   warn2_cannot("open directory ", svdir);
140   return;   return 1; /* need to rescan again soon */
141   }   }
142   for (i = 0; i < svnum; i++)   for (i = 0; i < svnum; i++)
143   sv[i].isgone = 1;   sv[i].isgone = 1;
144   errno = 0;  
145   while ((d = readdir(dir))) {   while (1) {
146   if (d->d_name[0] == '.') continue;   errno = 0;
147     d = readdir(dir);
148     if (!d)
149     break;
150     if (d->d_name[0] == '.')
151     continue;
152   if (stat(d->d_name, &s) == -1) {   if (stat(d->d_name, &s) == -1) {
153   warn2_cannot("stat ", d->d_name);   warn2_cannot("stat ", d->d_name);
  errno = 0;  
154   continue;   continue;
155   }   }
156   if (!S_ISDIR(s.st_mode)) continue;   if (!S_ISDIR(s.st_mode))
157     continue;
158     /* Do we have this service listed already? */
159   for (i = 0; i < svnum; i++) {   for (i = 0; i < svnum; i++) {
160   if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {   if ((sv[i].ino == s.st_ino)
161   sv[i].isgone = 0;  #if CHECK_DEVNO_TOO
162   if (!sv[i].pid)   && (sv[i].dev == s.st_dev)
163   runsv(i, d->d_name);  #endif
164   break;   ) {
165     if (sv[i].pid == 0) /* restart if it has died */
166     goto run_ith_sv;
167     sv[i].isgone = 0; /* "we still see you" */
168     goto next_dentry;
169   }   }
170   }   }
171   if (i == svnum) {   { /* Not found, make new service */
  /* new service */  
172   struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));   struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
173   if (!svnew) {   if (!svnew) {
174   warn3x("cannot start runsv ", d->d_name,   warn2_cannot("start runsv ", d->d_name);
175   " too many services");   need_rescan = 1;
176   continue;   continue;
177   }   }
178   sv = svnew;   sv = svnew;
179   svnum++;   svnum++;
180   memset(&sv[i], 0, sizeof(sv[i]));  #if CHECK_DEVNO_TOO
  sv[i].ino = s.st_ino;  
181   sv[i].dev = s.st_dev;   sv[i].dev = s.st_dev;
182   //sv[i].pid = 0;  #endif
183   //sv[i].isgone = 0;   sv[i].ino = s.st_ino;
184   runsv(i, d->d_name);   run_ith_sv:
185   check = 1;   sv[i].pid = runsv(d->d_name);
186     sv[i].isgone = 0;
187   }   }
188     next_dentry: ;
189   }   }
190   if (errno) {   i = errno;
191     closedir(dir);
192     if (i) { /* readdir failed */
193   warn2_cannot("read directory ", svdir);   warn2_cannot("read directory ", svdir);
194   closedir(dir);   return 1; /* need to rescan again soon */
  check = 1;  
  return;  
195   }   }
  closedir(dir);  
196    
197   /* SIGTERM removed runsv's */   /* Send SIGTERM to runsv whose directories
198     * were no longer found (-> must have been removed) */
199   for (i = 0; i < svnum; i++) {   for (i = 0; i < svnum; i++) {
200   if (!sv[i].isgone)   if (!sv[i].isgone)
201   continue;   continue;
202   if (sv[i].pid)   if (sv[i].pid)
203   kill(sv[i].pid, SIGTERM);   kill(sv[i].pid, SIGTERM);
204   sv[i] = sv[--svnum];   svnum--;
205   check = 1;   sv[i] = sv[svnum];
206     i--; /* so that we don't skip new sv[i] (bug was here!) */
207   }   }
208     return need_rescan;
209  }  }
210    
211  static int setup_log(void)  int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
212  {  int runsvdir_main(int argc UNUSED_PARAM, char **argv)
  rploglen = strlen(rplog);  
  if (rploglen < 7) {  
  warnx("log must have at least seven characters");  
  return 0;  
  }  
  if (pipe(logpipe) == -1) {  
  warnx("cannot create pipe for log");  
  return -1;  
  }  
  coe(logpipe[1]);  
  coe(logpipe[0]);  
  ndelay_on(logpipe[0]);  
  ndelay_on(logpipe[1]);  
  if (fd_copy(2, logpipe[1]) == -1) {  
  warnx("cannot set filedescriptor for log");  
  return -1;  
  }  
  io[0].fd = logpipe[0];  
  io[0].events = IOPAUSE_READ;  
  taia_now(&stamplog);  
  return 1;  
 }  
   
 int runsvdir_main(int argc, char **argv)  
213  {  {
214   struct stat s;   struct stat s;
215   time_t mtime = 0;   dev_t last_dev = last_dev; /* for gcc */
216     ino_t last_ino = last_ino; /* for gcc */
217     time_t last_mtime = 0;
218   int wstat;   int wstat;
219   int curdir;   int curdir;
220   int pid;   pid_t pid;
221   struct taia deadline;   unsigned deadline;
222   struct taia now;   unsigned now;
223   struct taia stampcheck;   unsigned stampcheck;
  char ch;  
224   int i;   int i;
225     int need_rescan = 1;
226     char *opt_s_argv[3];
227    
228   argv++;   INIT_G();
  if (!argv || !*argv) usage();  
  if (**argv == '-') {  
  switch (*(*argv + 1)) {  
  case 'P': pgrp = 1;  
  case '-': ++argv;  
  }  
  if (!argv || !*argv) usage();  
  }  
229    
230   sig_catch(sig_term, s_term);   opt_complementary = "-1";
231   sig_catch(sig_hangup, s_hangup);   opt_s_argv[0] = NULL;
232     opt_s_argv[2] = NULL;
233     getopt32(argv, "Ps:", &opt_s_argv[0]);
234     argv += optind;
235    
236     bb_signals(0
237     | (1 << SIGTERM)
238     | (1 << SIGHUP)
239     /* For busybox's init, SIGTERM == reboot,
240     * SIGUSR1 == halt
241     * SIGUSR2 == poweroff
242     * so we need to intercept SIGUSRn too.
243     * Note that we do not implement actual reboot
244     * (killall(TERM) + umount, etc), we just pause
245     * respawing and avoid exiting (-> making kernel oops).
246     * The user is responsible for the rest. */
247     | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
248     , record_signo);
249   svdir = *argv++;   svdir = *argv++;
250   if (argv && *argv) {  
251    #if ENABLE_FEATURE_RUNSVDIR_LOG
252     /* setup log */
253     if (*argv) {
254   rplog = *argv;   rplog = *argv;
255   if (setup_log() != 1) {   rploglen = strlen(rplog);
256   rplog = 0;   if (rploglen < 7) {
257   warnx("log service disabled");   warnx("log must have at least seven characters");
258     } else if (piped_pair(logpipe)) {
259     warnx("cannot create pipe for log");
260     } else {
261     close_on_exec_on(logpipe.rd);
262     close_on_exec_on(logpipe.wr);
263     ndelay_on(logpipe.rd);
264     ndelay_on(logpipe.wr);
265     if (dup2(logpipe.wr, 2) == -1) {
266     warnx("cannot set filedescriptor for log");
267     } else {
268     pfd[0].fd = logpipe.rd;
269     pfd[0].events = POLLIN;
270     stamplog = monotonic_sec();
271     goto run;
272     }
273   }   }
274     rplog = NULL;
275     warnx("log service disabled");
276   }   }
277     run:
278    #endif
279   curdir = open_read(".");   curdir = open_read(".");
280   if (curdir == -1)   if (curdir == -1)
281   fatal2_cannot("open current directory", "");   fatal2_cannot("open current directory", "");
282   coe(curdir);   close_on_exec_on(curdir);
283    
284   taia_now(&stampcheck);   stampcheck = monotonic_sec();
285    
286   for (;;) {   for (;;) {
287   /* collect children */   /* collect children */
288   for (;;) {   for (;;) {
289   pid = wait_nohang(&wstat);   pid = wait_any_nohang(&wstat);
290   if (pid <= 0) break;   if (pid <= 0)
291     break;
292   for (i = 0; i < svnum; i++) {   for (i = 0; i < svnum; i++) {
293   if (pid == sv[i].pid) {   if (pid == sv[i].pid) {
294   /* runsv has gone */   /* runsv has died */
295   sv[i].pid = 0;   sv[i].pid = 0;
296   check = 1;   need_rescan = 1;
  break;  
297   }   }
298   }   }
299   }   }
300    
301   taia_now(&now);   now = monotonic_sec();
302   if (now.sec.x < (stampcheck.sec.x - 3)) {   if ((int)(now - stampcheck) >= 0) {
  /* time warp */  
  warnx("time warp: resetting time stamp");  
  taia_now(&stampcheck);  
  taia_now(&now);  
  if (rplog) taia_now(&stamplog);  
  }  
  if (taia_less(&now, &stampcheck) == 0) {  
303   /* wait at least a second */   /* wait at least a second */
304   taia_uint(&deadline, 1);   stampcheck = now + 1;
  taia_add(&stampcheck, &now, &deadline);  
305    
306   if (stat(svdir, &s) != -1) {   if (stat(svdir, &s) != -1) {
307   if (check || s.st_mtime != mtime   if (need_rescan || s.st_mtime != last_mtime
308   || s.st_ino != ino || s.st_dev != dev   || s.st_ino != last_ino || s.st_dev != last_dev
309   ) {   ) {
310   /* svdir modified */   /* svdir modified */
311   if (chdir(svdir) != -1) {   if (chdir(svdir) != -1) {
312   mtime = s.st_mtime;   last_mtime = s.st_mtime;
313   dev = s.st_dev;   last_dev = s.st_dev;
314   ino = s.st_ino;   last_ino = s.st_ino;
315   check = 0;   //if (now <= mtime)
316   if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime))   // sleep(1);
317   sleep(1);   need_rescan = do_rescan();
  runsvdir();  
318   while (fchdir(curdir) == -1) {   while (fchdir(curdir) == -1) {
319   warn2_cannot("change directory, pausing", "");   warn2_cannot("change directory, pausing", "");
320   sleep(5);   sleep(5);
321   }   }
322   } else   } else {
323   warn2_cannot("change directory to ", svdir);   warn2_cannot("change directory to ", svdir);
324     }
325   }   }
326   } else   } else {
327   warn2_cannot("stat ", svdir);   warn2_cannot("stat ", svdir);
328     }
329   }   }
330    
331    #if ENABLE_FEATURE_RUNSVDIR_LOG
332   if (rplog) {   if (rplog) {
333   if (taia_less(&now, &stamplog) == 0) {   if ((int)(now - stamplog) >= 0) {
334   write(logpipe[1], ".", 1);   write(logpipe.wr, ".", 1);
335   taia_uint(&deadline, 900);   stamplog = now + 900;
  taia_add(&stamplog, &now, &deadline);  
336   }   }
337   }   }
338   taia_uint(&deadline, check ? 1 : 5);   pfd[0].revents = 0;
339   taia_add(&deadline, &now, &deadline);  #endif
340     deadline = (need_rescan ? 1 : 5);
341   sig_block(sig_child);   sig_block(SIGCHLD);
342    #if ENABLE_FEATURE_RUNSVDIR_LOG
343   if (rplog)   if (rplog)
344   iopause(io, 1, &deadline, &now);   poll(pfd, 1, deadline*1000);
345   else   else
346   iopause(0, 0, &deadline, &now);  #endif
347   sig_unblock(sig_child);   sleep(deadline);
348     sig_unblock(SIGCHLD);
349    
350    #if ENABLE_FEATURE_RUNSVDIR_LOG
351     if (pfd[0].revents & POLLIN) {
352     char ch;
353     while (read(logpipe.rd, &ch, 1) > 0) {
354     if (ch < ' ')
355     ch = ' ';
356     for (i = 6; i < rploglen; i++)
357     rplog[i-1] = rplog[i];
358     rplog[rploglen-1] = ch;
359     }
360     }
361    #endif
362     if (!bb_got_signal)
363     continue;
364    
365   if (rplog && (io[0].revents | IOPAUSE_READ))   /* -s SCRIPT: useful if we are init.
366   while (read(logpipe[0], &ch, 1) > 0)   * In this case typically script never returns,
367   if (ch) {   * it halts/powers off/reboots the system. */
368   for (i = 6; i < rploglen; i++)   if (opt_s_argv[0]) {
369   rplog[i-1] = rplog[i];   /* Single parameter: signal# */
370   rplog[rploglen-1] = ch;   opt_s_argv[1] = utoa(bb_got_signal);
371   }   pid = spawn(opt_s_argv);
372     if (pid > 0) {
373     /* Remebering to wait for _any_ children,
374     * not just pid */
375     while (wait(NULL) != pid)
376     continue;
377     }
378     }
379    
380   switch (exitsoon) {   switch (bb_got_signal) {
381   case 1:   case SIGHUP:
  _exit(0);  
  case 2:  
382   for (i = 0; i < svnum; i++)   for (i = 0; i < svnum; i++)
383   if (sv[i].pid)   if (sv[i].pid)
384   kill(sv[i].pid, SIGTERM);   kill(sv[i].pid, SIGTERM);
385   _exit(111);   /* Fall through */
386     default: /* SIGTERM (or SIGUSRn if we are init) */
387     /* Exit unless we are init */
388     if (getpid() == 1)
389     break;
390     return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;
391   }   }
392   }  
393   /* not reached */   bb_got_signal = 0;
394   return 0;   } /* for (;;) */
395  }  }

Legend:
Removed from v.532  
changed lines
  Added in v.816