Magellan Linux

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

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

revision 815 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    /* Taken from http://smarden.sunsite.dk/runit/sv.8.html:
29    
30    sv - control and manage services monitored by runsv
31    
32    sv [-v] [-w sec] command services
33    /etc/init.d/service [-w sec] command
34    
35    The sv program reports the current status and controls the state of services
36    monitored by the runsv(8) supervisor.
37    
38    services consists of one or more arguments, each argument naming a directory
39    service used by runsv(8). If service doesn't start with a dot or slash,
40    it is searched in the default services directory /var/service/, otherwise
41    relative to the current directory.
42    
43    command is one of up, down, status, once, pause, cont, hup, alarm, interrupt,
44    1, 2, term, kill, or exit, or start, stop, restart, shutdown, force-stop,
45    force-reload, force-restart, force-shutdown.
46    
47    The sv program can be sym-linked to /etc/init.d/ to provide an LSB init
48    script interface. The service to be controlled then is specified by the
49    base name of the "init script".
50    
51    status
52        Report the current status of the service, and the appendant log service
53        if available, to standard output.
54    up
55        If the service is not running, start it. If the service stops, restart it.
56    down
57        If the service is running, send it the TERM signal, and the CONT signal.
58        If ./run exits, start ./finish if it exists. After it stops, do not
59        restart service.
60    once
61        If the service is not running, start it. Do not restart it if it stops.
62    pause cont hup alarm interrupt quit 1 2 term kill
63        If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT,
64        USR1, USR2, TERM, or KILL signal respectively.
65    exit
66        If the service is running, send it the TERM signal, and the CONT signal.
67        Do not restart the service. If the service is down, and no log service
68        exists, runsv(8) exits. If the service is down and a log service exists,
69        send the TERM signal to the log service. If the log service is down,
70        runsv(8) exits. This command is ignored if it is given to an appendant
71        log service.
72    
73    sv actually looks only at the first character of above commands.
74    
75    Commands compatible to LSB init script actions:
76    
77    status
78        Same as status.
79    start
80        Same as up, but wait up to 7 seconds for the command to take effect.
81        Then report the status or timeout. If the script ./check exists in
82        the service directory, sv runs this script to check whether the service
83        is up and available; it's considered to be available if ./check exits
84        with 0.
85    stop
86        Same as down, but wait up to 7 seconds for the service to become down.
87        Then report the status or timeout.
88    restart
89        Send the commands term, cont, and up to the service, and wait up to
90        7 seconds for the service to restart. Then report the status or timeout.
91        If the script ./check exists in the service directory, sv runs this script
92        to check whether the service is up and available again; it's considered
93        to be available if ./check exits with 0.
94    shutdown
95        Same as exit, but wait up to 7 seconds for the runsv(8) process
96        to terminate. Then report the status or timeout.
97    force-stop
98        Same as down, but wait up to 7 seconds for the service to become down.
99        Then report the status, and on timeout send the service the kill command.
100    force-reload
101        Send the service the term and cont commands, and wait up to
102        7 seconds for the service to restart. Then report the status,
103        and on timeout send the service the kill command.
104    force-restart
105        Send the service the term, cont and up commands, and wait up to
106        7 seconds for the service to restart. Then report the status, and
107        on timeout send the service the kill command. If the script ./check
108        exists in the service directory, sv runs this script to check whether
109        the service is up and available again; it's considered to be available
110        if ./check exits with 0.
111    force-shutdown
112        Same as exit, but wait up to 7 seconds for the runsv(8) process to
113        terminate. Then report the status, and on timeout send the service
114        the kill command.
115    
116    Additional Commands
117    
118    check
119        Check for the service to be in the state that's been requested. Wait up to
120        7 seconds for the service to reach the requested state, then report
121        the status or timeout. If the requested state of the service is up,
122        and the script ./check exists in the service directory, sv runs
123        this script to check whether the service is up and running;
124        it's considered to be up if ./check exits with 0.
125    
126    Options
127    
128    -v
129        wait up to 7 seconds for the command to take effect.
130        Then report the status or timeout.
131    -w sec
132        Override the default timeout of 7 seconds with sec seconds. Implies -v.
133    
134    Environment
135    
136    SVDIR
137        The environment variable $SVDIR overrides the default services directory
138        /var/service.
139    SVWAIT
140        The environment variable $SVWAIT overrides the default 7 seconds to wait
141        for a command to take effect. It is overridden by the -w option.
142    
143    Exit Codes
144        sv exits 0, if the command was successfully sent to all services, and,
145        if it was told to wait, the command has taken effect to all services.
146    
147        For each service that caused an error (e.g. the directory is not
148        controlled by a runsv(8) process, or sv timed out while waiting),
149        sv increases the exit code by one and exits non zero. The maximum
150        is 99. sv exits 100 on error.
151    */
152    
153    /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
154  /* TODO: depends on runit_lib.c - review and reduce/eliminate */  /* TODO: depends on runit_lib.c - review and reduce/eliminate */
155    
156  #include <sys/poll.h>  #include <sys/poll.h>
157  #include <sys/file.h>  #include <sys/file.h>
158  #include "busybox.h"  #include "libbb.h"
159  #include "runit_lib.h"  #include "runit_lib.h"
160    
161  static char *action;  struct globals {
162  static char *acts;   const char *acts;
163  static char *varservice = "/var/service/";   char **service;
164  static char **service;   unsigned rc;
165  static char **servicex;  /* "Bernstein" time format: unix + 0x400000000000000aULL */
166  static unsigned services;   uint64_t tstart, tnow;
167  static unsigned rc = 0;   svstatus_t svstatus;
168  static unsigned verbose = 0;  };
169  static unsigned long waitsec = 7;  #define G (*(struct globals*)&bb_common_bufsiz1)
170  static unsigned kll = 0;  #define acts         (G.acts        )
171  static struct taia tstart, tnow, tdiff;  #define service      (G.service     )
172  static struct tai tstatus;  #define rc           (G.rc          )
173    #define tstart       (G.tstart      )
174  static int (*act)(char*) = 0;  #define tnow         (G.tnow        )
175  static int (*cbk)(char*) = 0;  #define svstatus     (G.svstatus    )
176    #define INIT_G() do { } while (0)
 static int curdir, fd, r;  
 static char svstatus[20];  
177    
 #define usage() bb_show_usage()  
178    
179  static void fatal_cannot(char *m1)  static void fatal_cannot(const char *m1) NORETURN;
180    static void fatal_cannot(const char *m1)
181  {  {
182   bb_perror_msg("fatal: cannot %s", m1);   bb_perror_msg("fatal: can't %s", m1);
183   _exit(151);   _exit(151);
184  }  }
185    
186  static void out(char *p, char *m1)  static void out(const char *p, const char *m1)
187  {  {
188   printf("%s%s: %s", p, *service, m1);   printf("%s%s: %s", p, *service, m1);
189   if (errno) {   if (errno) {
190   printf(": %s", strerror(errno));   printf(": %s", strerror(errno));
191   }   }
192   puts(""); /* will also flush the output */   bb_putchar('\n'); /* will also flush the output */
193  }  }
194    
 #define FAIL    "fail: "  
195  #define WARN    "warning: "  #define WARN    "warning: "
196  #define OK      "ok: "  #define OK      "ok: "
197  #define RUN     "run: "  
198  #define FINISH  "finish: "  static void fail(const char *m1)
199  #define DOWN    "down: "  {
200  #define TIMEOUT "timeout: "   ++rc;
201  #define KILL    "kill: "   out("fail: ", m1);
202    }
203  static void fail(char *m1) { ++rc; out(FAIL, m1); }  static void failx(const char *m1)
204  static void failx(char *m1) { errno = 0; fail(m1); }  {
205  static void warn_cannot(char *m1) { ++rc; out("warning: cannot ", m1); }   errno = 0;
206  static void warnx_cannot(char *m1) { errno = 0; warn_cannot(m1); }   fail(m1);
207  static void ok(char *m1) { errno = 0; out(OK, m1); }  }
208    static void warn(const char *m1)
209    {
210     ++rc;
211     /* "warning: <service>: <m1>\n" */
212     out("warning: ", m1);
213    }
214    static void ok(const char *m1)
215    {
216     errno = 0;
217     out(OK, m1);
218    }
219    
220  static int svstatus_get(void)  static int svstatus_get(void)
221  {  {
222   if ((fd = open_write("supervise/ok")) == -1) {   int fd, r;
223    
224     fd = open_write("supervise/ok");
225     if (fd == -1) {
226   if (errno == ENODEV) {   if (errno == ENODEV) {
227   *acts == 'x' ? ok("runsv not running")   *acts == 'x' ? ok("runsv not running")
228               : failx("runsv not running");               : failx("runsv not running");
229   return 0;   return 0;
230   }   }
231   warn_cannot("open supervise/ok");   warn("cannot open supervise/ok");
232   return -1;   return -1;
233   }   }
234   close(fd);   close(fd);
235   if ((fd = open_read("supervise/status")) == -1) {   fd = open_read("supervise/status");
236   warn_cannot("open supervise/status");   if (fd == -1) {
237     warn("cannot open supervise/status");
238   return -1;   return -1;
239   }   }
240   r = read(fd, svstatus, 20);   r = read(fd, &svstatus, 20);
241   close(fd);   close(fd);
242   switch (r) {   switch (r) {
243   case 20: break;   case 20:
244   case -1: warn_cannot("read supervise/status"); return -1;   break;
245   default: warnx_cannot("read supervise/status: bad format"); return -1;   case -1:
246     warn("cannot read supervise/status");
247     return -1;
248     default:
249     errno = 0;
250     warn("cannot read supervise/status: bad format");
251     return -1;
252   }   }
253   return 1;   return 1;
254  }  }
255    
256  static unsigned svstatus_print(char *m)  static unsigned svstatus_print(const char *m)
257  {  {
258     int diff;
259   int pid;   int pid;
260   int normallyup = 0;   int normallyup = 0;
261   struct stat s;   struct stat s;
262     uint64_t timestamp;
263    
264   if (stat("down", &s) == -1) {   if (stat("down", &s) == -1) {
265   if (errno != ENOENT) {   if (errno != ENOENT) {
# Line 96  static unsigned svstatus_print(char *m) Line 268  static unsigned svstatus_print(char *m)
268   }   }
269   normallyup = 1;   normallyup = 1;
270   }   }
271   pid = (unsigned char) svstatus[15];   pid = SWAP_LE32(svstatus.pid_le32);
272   pid <<= 8; pid += (unsigned char)svstatus[14];   timestamp = SWAP_BE64(svstatus.time_be64);
  pid <<= 8; pid += (unsigned char)svstatus[13];  
  pid <<= 8; pid += (unsigned char)svstatus[12];  
  tai_unpack(svstatus, &tstatus);  
273   if (pid) {   if (pid) {
274   switch (svstatus[19]) {   switch (svstatus.run_or_finish) {
275   case 1: printf(RUN); break;   case 1: printf("run: "); break;
276   case 2: printf(FINISH); break;   case 2: printf("finish: "); break;
277   }   }
278   printf("%s: (pid %d) ", m, pid);   printf("%s: (pid %d) ", m, pid);
279     } else {
280     printf("down: %s: ", m);
281   }   }
282   else {   diff = tnow - timestamp;
283   printf(DOWN"%s: ", m);   printf("%us", (diff < 0 ? 0 : diff));
284     if (pid) {
285     if (!normallyup) printf(", normally down");
286     if (svstatus.paused) printf(", paused");
287     if (svstatus.want == 'd') printf(", want down");
288     if (svstatus.got_term) printf(", got TERM");
289     } else {
290     if (normallyup) printf(", normally up");
291     if (svstatus.want == 'u') printf(", want up");
292   }   }
  printf("%lus", (unsigned long)(tnow.sec.x < tstatus.x ? 0 : tnow.sec.x-tstatus.x));  
  if (pid && !normallyup) printf(", normally down");  
  if (!pid && normallyup) printf(", normally up");  
  if (pid && svstatus[16]) printf(", paused");  
  if (!pid && (svstatus[17] == 'u')) printf(", want up");  
  if (pid && (svstatus[17] == 'd')) printf(", want down");  
  if (pid && svstatus[18]) printf(", got TERM");  
293   return pid ? 1 : 2;   return pid ? 1 : 2;
294  }  }
295    
296  static int status(char *unused)  static int status(const char *unused UNUSED_PARAM)
297  {  {
298     int r;
299    
300   r = svstatus_get();   r = svstatus_get();
301   switch (r) { case -1: case 0: return 0; }   switch (r) { case -1: case 0: return 0; }
302    
303   r = svstatus_print(*service);   r = svstatus_print(*service);
304   if (chdir("log") == -1) {   if (chdir("log") == -1) {
305   if (errno != ENOENT) {   if (errno != ENOENT) {
# Line 135  static int status(char *unused) Line 310  static int status(char *unused)
310   printf("; ");   printf("; ");
311   svstatus_print("log");   svstatus_print("log");
312   }   }
313   puts(""); /* will also flush the output */   bb_putchar('\n'); /* will also flush the output */
314   return r;   return r;
315  }  }
316    
# Line 151  static int checkscript(void) Line 326  static int checkscript(void)
326   return 0;   return 0;
327   }   }
328   /* if (!(s.st_mode & S_IXUSR)) return 1; */   /* if (!(s.st_mode & S_IXUSR)) return 1; */
329   if ((pid = fork()) == -1) {   prog[0] = (char*)"./check";
330   bb_perror_msg(WARN"cannot fork for %s/check", *service);   prog[1] = NULL;
331     pid = spawn(prog);
332     if (pid <= 0) {
333     bb_perror_msg(WARN"cannot %s child %s/check", "run", *service);
334   return 0;   return 0;
335   }   }
336   if (!pid) {   while (safe_waitpid(pid, &w, 0) == -1) {
337   prog[0] = "./check";   bb_perror_msg(WARN"cannot %s child %s/check", "wait for", *service);
  prog[1] = 0;  
  close(1);  
  execve("check", prog, environ);  
  bb_perror_msg(WARN"cannot run %s/check", *service);  
  _exit(0);  
  }  
  while (wait_pid(&w, pid) == -1) {  
  if (errno == EINTR) continue;  
  bb_perror_msg(WARN"cannot wait for child %s/check", *service);  
338   return 0;   return 0;
339   }   }
340   return !wait_exitcode(w);   return !wait_exitcode(w);
341  }  }
342    
343  static int check(char *a)  static int check(const char *a)
344  {  {
345     int r;
346   unsigned pid;   unsigned pid;
347     uint64_t timestamp;
348    
349   if ((r = svstatus_get()) == -1) return -1;   r = svstatus_get();
350   if (r == 0) { if (*a == 'x') return 1; return -1; }   if (r == -1)
351   pid = (unsigned char)svstatus[15];   return -1;
352   pid <<= 8; pid += (unsigned char)svstatus[14];   if (r == 0) {
353   pid <<= 8; pid += (unsigned char)svstatus[13];   if (*a == 'x')
354   pid <<= 8; pid += (unsigned char)svstatus[12];   return 1;
355     return -1;
356     }
357     pid = SWAP_LE32(svstatus.pid_le32);
358   switch (*a) {   switch (*a) {
359   case 'x': return 0;   case 'x':
360     return 0;
361   case 'u':   case 'u':
362   if (!pid || svstatus[19] != 1) return 0;   if (!pid || svstatus.run_or_finish != 1) return 0;
363   if (!checkscript()) return 0;   if (!checkscript()) return 0;
364   break;   break;
365   case 'd': if (pid) return 0; break;   case 'd':
366   case 'c': if (pid) if (!checkscript()) return 0; break;   if (pid) return 0;
367     break;
368     case 'c':
369     if (pid && !checkscript()) return 0;
370     break;
371   case 't':   case 't':
372   if (!pid && svstatus[17] == 'd') break;   if (!pid && svstatus.want == 'd') break;
373   tai_unpack(svstatus, &tstatus);   timestamp = SWAP_BE64(svstatus.time_be64);
374   if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript())   if ((tstart > timestamp) || !pid || svstatus.got_term || !checkscript())
375   return 0;   return 0;
376   break;   break;
377   case 'o':   case 'o':
378   tai_unpack(svstatus, &tstatus);   timestamp = SWAP_BE64(svstatus.time_be64);
379   if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd'))   if ((!pid && tstart > timestamp) || (pid && svstatus.want != 'd'))
380   return 0;   return 0;
381   }   }
382   printf(OK); svstatus_print(*service); puts(""); /* will also flush the output */   printf(OK);
383     svstatus_print(*service);
384     bb_putchar('\n'); /* will also flush the output */
385   return 1;   return 1;
386  }  }
387    
388  static int control(char *a)  static int control(const char *a)
389  {  {
390   if (svstatus_get() <= 0) return -1;   int fd, r;
391   if (svstatus[17] == *a) return 0;  
392   if ((fd = open_write("supervise/control")) == -1) {   if (svstatus_get() <= 0)
393     return -1;
394     if (svstatus.want == *a)
395     return 0;
396     fd = open_write("supervise/control");
397     if (fd == -1) {
398   if (errno != ENODEV)   if (errno != ENODEV)
399   warn_cannot("open supervise/control");   warn("cannot open supervise/control");
400   else   else
401   *a == 'x' ? ok("runsv not running") : failx("runsv not running");   *a == 'x' ? ok("runsv not running") : failx("runsv not running");
402   return -1;   return -1;
# Line 218  static int control(char *a) Line 404  static int control(char *a)
404   r = write(fd, a, strlen(a));   r = write(fd, a, strlen(a));
405   close(fd);   close(fd);
406   if (r != strlen(a)) {   if (r != strlen(a)) {
407   warn_cannot("write to supervise/control");   warn("cannot write to supervise/control");
408   return -1;   return -1;
409   }   }
410   return 1;   return 1;
411  }  }
412    
413    int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
414  int sv_main(int argc, char **argv)  int sv_main(int argc, char **argv)
415  {  {
416   unsigned opt;   unsigned opt;
417   unsigned i, want_exit;   unsigned i, want_exit;
418   char *x;   char *x;
419     char *action;
420   for (i = strlen(*argv); i; --i)   const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
421   if ((*argv)[i-1] == '/')   unsigned services;
422   break;   char **servicex;
423   *argv += i;   unsigned waitsec = 7;
424     smallint kll = 0;
425     int verbose = 0;
426     int (*act)(const char*);
427     int (*cbk)(const char*);
428     int curdir;
429    
430     INIT_G();
431    
432     xfunc_error_retval = 100;
433    
434     x = getenv("SVDIR");
435     if (x) varservice = x;
436     x = getenv("SVWAIT");
437     if (x) waitsec = xatou(x);
438    
439     opt_complementary = "w+:vv"; /* -w N, -v is a counter */
440     opt = getopt32(argv, "w:v", &waitsec, &verbose);
441     argc -= optind;
442     argv += optind;
443     action = *argv++;
444     if (!action || !*argv) bb_show_usage();
445   service = argv;   service = argv;
446   services = 1;   services = argc - 1;
  if ((x = getenv("SVDIR"))) varservice = x;  
  if ((x = getenv("SVWAIT"))) waitsec = xatoul(x);  
  /* TODO: V can be handled internally by getopt_ulflags */  
  opt = getopt32(argc, argv, "w:vV", &x);  
  if (opt & 1) waitsec = xatoul(x);  
  if (opt & 2) verbose = 1;  
  if (opt & 4) usage();  
  if (!(action = *argv++)) usage();  
  --argc;  
  service = argv; services = argc;  
  if (!*service) usage();  
447    
448   taia_now(&tnow); tstart = tnow;   tnow = time(0) + 0x400000000000000aULL;
449   if ((curdir = open_read(".")) == -1)   tstart = tnow;
450     curdir = open_read(".");
451     if (curdir == -1)
452   fatal_cannot("open current directory");   fatal_cannot("open current directory");
453    
454   act = &control; acts = "s";   act = &control;
455   if (verbose) cbk = &check;   acts = "s";
456     cbk = &check;
457    
458   switch (*action) {   switch (*action) {
459   case 'x': case 'e':   case 'x':
460   acts = "x"; break;   case 'e':
461   case 'X': case 'E':   acts = "x";
462   acts = "x"; kll = 1; cbk = &check; break;   if (!verbose) cbk = NULL;
463     break;
464     case 'X':
465     case 'E':
466     acts = "x";
467     kll = 1;
468     break;
469   case 'D':   case 'D':
470   acts = "d"; kll = 1; cbk = &check; break;   acts = "d";
471     kll = 1;
472     break;
473   case 'T':   case 'T':
474   acts = "tc"; kll = 1; cbk = &check; break;   acts = "tc";
475     kll = 1;
476     break;
477   case 'c':   case 'c':
478   if (!str_diff(action, "check")) {   if (str_equal(action, "check")) {
479   act = 0;   act = NULL;
480   acts = "c";   acts = "c";
  cbk = &check;  
481   break;   break;
482   }   }
483   case 'u': case 'd': case 'o': case 't': case 'p': case 'h':   case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
484   case 'a': case 'i': case 'k': case 'q': case '1': case '2':   case 'a': case 'i': case 'k': case 'q': case '1': case '2':
485   action[1] = 0; acts = action; break;   action[1] = '\0';
486     acts = action;
487     if (!verbose) cbk = NULL;
488     break;
489   case 's':   case 's':
490   if (!str_diff(action, "shutdown")) {   if (str_equal(action, "shutdown")) {
491   acts = "x";   acts = "x";
  cbk = &check;  
492   break;   break;
493   }   }
494   if (!str_diff(action, "start")) {   if (str_equal(action, "start")) {
495   acts = "u";   acts = "u";
  cbk = &check;  
496   break;   break;
497   }   }
498   if (!str_diff(action, "stop")) {   if (str_equal(action, "stop")) {
499   acts = "d";   acts = "d";
  cbk = &check;  
500   break;   break;
501   }   }
502   act = &status; cbk = 0; break;   /* "status" */
503     act = &status;
504     cbk = NULL;
505     break;
506   case 'r':   case 'r':
507   if (!str_diff(action, "restart")) {   if (str_equal(action, "restart")) {
508   acts = "tcu";   acts = "tcu";
  cbk = &check;  
509   break;   break;
510   }   }
511   usage();   bb_show_usage();
512   case 'f':   case 'f':
513   if (!str_diff(action, "force-reload"))   if (str_equal(action, "force-reload")) {
514   { acts = "tc"; kll = 1; cbk = &check; break; }   acts = "tc";
515   if (!str_diff(action, "force-restart"))   kll = 1;
516   { acts = "tcu"; kll = 1; cbk = &check; break; }   break;
517   if (!str_diff(action, "force-shutdown"))   }
518   { acts = "x"; kll = 1; cbk = &check; break; }   if (str_equal(action, "force-restart")) {
519   if (!str_diff(action, "force-stop"))   acts = "tcu";
520   { acts = "d"; kll = 1; cbk = &check; break; }   kll = 1;
521     break;
522     }
523     if (str_equal(action, "force-shutdown")) {
524     acts = "x";
525     kll = 1;
526     break;
527     }
528     if (str_equal(action, "force-stop")) {
529     acts = "d";
530     kll = 1;
531     break;
532     }
533   default:   default:
534   usage();   bb_show_usage();
535   }   }
536    
537   servicex = service;   servicex = service;
538   for (i = 0; i < services; ++i) {   for (i = 0; i < services; ++i) {
539   if ((**service != '/') && (**service != '.')) {   if ((**service != '/') && (**service != '.')) {
540   if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {   if (chdir(varservice) == -1)
541   fail("cannot change to service directory");   goto chdir_failed_0;
542   *service = 0;   }
543   }   if (chdir(*service) == -1) {
544   } else if (chdir(*service) == -1) {   chdir_failed_0:
545   fail("cannot change to service directory");   fail("cannot change to service directory");
546   *service = 0;   goto nullify_service_0;
547   }   }
548   if (*service) if (act && (act(acts) == -1)) *service = 0;   if (act && (act(acts) == -1)) {
549   if (fchdir(curdir) == -1) fatal_cannot("change to original directory");   nullify_service_0:
550     *service = NULL;
551     }
552     if (fchdir(curdir) == -1)
553     fatal_cannot("change to original directory");
554   service++;   service++;
555   }   }
556    
557   if (*cbk)   if (cbk) while (1) {
558   for (;;) {   int diff;
559   taia_sub(&tdiff, &tnow, &tstart);  
560   service = servicex; want_exit = 1;   diff = tnow - tstart;
561   for (i = 0; i < services; ++i, ++service) {   service = servicex;
562   if (!*service) continue;   want_exit = 1;
563   if ((**service != '/') && (**service != '.')) {   for (i = 0; i < services; ++i, ++service) {
564   if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {   if (!*service)
565   fail("cannot change to service directory");   continue;
566   *service = 0;   if ((**service != '/') && (**service != '.')) {
567   }   if (chdir(varservice) == -1)
568   } else if (chdir(*service) == -1) {   goto chdir_failed;
569   fail("cannot change to service directory");   }
570   *service = 0;   if (chdir(*service) == -1) {
571   }   chdir_failed:
572   if (*service) { if (cbk(acts) != 0) *service = 0; else want_exit = 0; }   fail("cannot change to service directory");
573   if (*service && taia_approx(&tdiff) > waitsec) {   goto nullify_service;
574   kll ? printf(KILL) : printf(TIMEOUT);   }
575   if (svstatus_get() > 0) { svstatus_print(*service); ++rc; }   if (cbk(acts) != 0)
576   puts(""); /* will also flush the output */   goto nullify_service;
577   if (kll) control("k");   want_exit = 0;
578   *service = 0;   if (diff >= waitsec) {
579     printf(kll ? "kill: " : "timeout: ");
580     if (svstatus_get() > 0) {
581     svstatus_print(*service);
582     ++rc;
583   }   }
584   if (fchdir(curdir) == -1)   bb_putchar('\n'); /* will also flush the output */
585   fatal_cannot("change to original directory");   if (kll)
586     control("k");
587     nullify_service:
588     *service = NULL;
589   }   }
590   if (want_exit) break;   if (fchdir(curdir) == -1)
591   usleep(420000);   fatal_cannot("change to original directory");
  taia_now(&tnow);  
592   }   }
593     if (want_exit) break;
594     usleep(420000);
595     tnow = time(0) + 0x400000000000000aULL;
596     }
597   return rc > 99 ? 99 : rc;   return rc > 99 ? 99 : rc;
598  }  }

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