config: update size information
[oweals/busybox.git] / runit / sv.c
1 /*
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.org/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 and
40 doesn't end with a slash, it is searched in the default services directory
41 /var/service/, otherwise 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, reload, restart, shutdown,
45 force-stop, force-reload, force-restart, force-shutdown, try-restart.
46
47 status
48     Report the current status of the service, and the appendant log service
49     if available, to standard output.
50 up
51     If the service is not running, start it. If the service stops, restart it.
52 down
53     If the service is running, send it the TERM signal, and the CONT signal.
54     If ./run exits, start ./finish if it exists. After it stops, do not
55     restart service.
56 once
57     If the service is not running, start it. Do not restart it if it stops.
58 pause cont hup alarm interrupt quit 1 2 term kill
59     If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT,
60     USR1, USR2, TERM, or KILL signal respectively.
61 exit
62     If the service is running, send it the TERM signal, and the CONT signal.
63     Do not restart the service. If the service is down, and no log service
64     exists, runsv(8) exits. If the service is down and a log service exists,
65     runsv(8) closes the standard input of the log service and waits for it to
66     terminate. If the log service is down, runsv(8) exits. This command is
67     ignored if it is given to an appendant log service.
68
69 sv actually looks only at the first character of above commands.
70
71 Commands compatible to LSB init script actions:
72
73 status
74     Same as status.
75 start
76     Same as up, but wait up to 7 seconds for the command to take effect.
77     Then report the status or timeout. If the script ./check exists in
78     the service directory, sv runs this script to check whether the service
79     is up and available; it's considered to be available if ./check exits
80     with 0.
81 stop
82     Same as down, but wait up to 7 seconds for the service to become down.
83     Then report the status or timeout.
84 reload
85     Same as hup, and additionally report the status afterwards.
86 restart
87     Send the commands term, cont, and up to the service, and wait up to
88     7 seconds for the service to restart. Then report the status or timeout.
89     If the script ./check exists in the service directory, sv runs this script
90     to check whether the service is up and available again; it's considered
91     to be available if ./check exits with 0.
92 shutdown
93     Same as exit, but wait up to 7 seconds for the runsv(8) process
94     to terminate. Then report the status or timeout.
95 force-stop
96     Same as down, but wait up to 7 seconds for the service to become down.
97     Then report the status, and on timeout send the service the kill command.
98 force-reload
99     Send the service the term and cont commands, and wait up to
100     7 seconds for the service to restart. Then report the status,
101     and on timeout send the service the kill command.
102 force-restart
103     Send the service the term, cont and up commands, and wait up to
104     7 seconds for the service to restart. Then report the status, and
105     on timeout send the service the kill command. If the script ./check
106     exists in the service directory, sv runs this script to check whether
107     the service is up and available again; it's considered to be available
108     if ./check exits with 0.
109 force-shutdown
110     Same as exit, but wait up to 7 seconds for the runsv(8) process to
111     terminate. Then report the status, and on timeout send the service
112     the kill command.
113 try-restart
114     if the service is running, send it the term and cont commands, and wait up to
115     7 seconds for the service to restart. Then report the status or timeout.
116
117 Additional Commands
118
119 check
120     Check for the service to be in the state that's been requested. Wait up to
121     7 seconds for the service to reach the requested state, then report
122     the status or timeout. If the requested state of the service is up,
123     and the script ./check exists in the service directory, sv runs
124     this script to check whether the service is up and running;
125     it's considered to be up if ./check exits with 0.
126
127 Options
128
129 -v
130     If the command is up, down, term, once, cont, or exit, then wait up to 7
131     seconds for the command to take effect. Then report the status or timeout.
132 -w sec
133     Override the default timeout of 7 seconds with sec seconds. Implies -v.
134
135 Environment
136
137 SVDIR
138     The environment variable $SVDIR overrides the default services directory
139     /var/service.
140 SVWAIT
141     The environment variable $SVWAIT overrides the default 7 seconds to wait
142     for a command to take effect. It is overridden by the -w option.
143
144 Exit Codes
145     sv exits 0, if the command was successfully sent to all services, and,
146     if it was told to wait, the command has taken effect to all services.
147
148     For each service that caused an error (e.g. the directory is not
149     controlled by a runsv(8) process, or sv timed out while waiting),
150     sv increases the exit code by one and exits non zero. The maximum
151     is 99. sv exits 100 on error.
152 */
153
154 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
155
156 //config:config SV
157 //config:       bool "sv (8.5 kb)"
158 //config:       default y
159 //config:       help
160 //config:       sv reports the current status and controls the state of services
161 //config:       monitored by the runsv supervisor.
162 //config:
163 //config:config SV_DEFAULT_SERVICE_DIR
164 //config:       string "Default directory for services"
165 //config:       default "/var/service"
166 //config:       depends on SV || SVC || SVOK
167 //config:       help
168 //config:       Default directory for services.
169 //config:       Defaults to "/var/service"
170 //config:
171 //config:config SVC
172 //config:       bool "svc (8.4 kb)"
173 //config:       default y
174 //config:       help
175 //config:       svc controls the state of services monitored by the runsv supervisor.
176 //config:       It is compatible with daemontools command with the same name.
177 //config:
178 //config:config SVOK
179 //config:       bool "svok (1.5 kb)"
180 //config:       default y
181 //config:       help
182 //config:       svok checks whether runsv supervisor is running.
183 //config:       It is compatible with daemontools command with the same name.
184
185 //applet:IF_SV(  APPLET_NOEXEC(sv,   sv,   BB_DIR_USR_BIN, BB_SUID_DROP, sv  ))
186 //applet:IF_SVC( APPLET_NOEXEC(svc,  svc,  BB_DIR_USR_BIN, BB_SUID_DROP, svc ))
187 //applet:IF_SVOK(APPLET_NOEXEC(svok, svok, BB_DIR_USR_BIN, BB_SUID_DROP, svok))
188
189 //kbuild:lib-$(CONFIG_SV) += sv.o
190 //kbuild:lib-$(CONFIG_SVC) += sv.o
191 //kbuild:lib-$(CONFIG_SVOK) += sv.o
192
193 #include <sys/file.h>
194 #include "libbb.h"
195 #include "common_bufsiz.h"
196 #include "runit_lib.h"
197
198 struct globals {
199         const char *acts;
200         char **service;
201         unsigned rc;
202 /* "Bernstein" time format: unix + 0x400000000000000aULL */
203         uint64_t tstart, tnow;
204         svstatus_t svstatus;
205         smallint islog;
206 } FIX_ALIASING;
207 #define G (*(struct globals*)bb_common_bufsiz1)
208 #define acts         (G.acts        )
209 #define service      (G.service     )
210 #define rc           (G.rc          )
211 #define tstart       (G.tstart      )
212 #define tnow         (G.tnow        )
213 #define svstatus     (G.svstatus    )
214 #define islog        (G.islog       )
215 #define INIT_G() do { \
216         setup_common_bufsiz(); \
217         /* need to zero out, svc calls sv() repeatedly */ \
218         memset(&G, 0, sizeof(G)); \
219 } while (0)
220
221
222 #define str_equal(s,t) (strcmp((s), (t)) == 0)
223
224
225 #if ENABLE_SV || ENABLE_SVC
226 static void fatal_cannot(const char *m1) NORETURN;
227 static void fatal_cannot(const char *m1)
228 {
229         bb_perror_msg("fatal: can't %s", m1);
230         _exit(151);
231 }
232
233 static void out(const char *p, const char *m1)
234 {
235         printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
236         if (errno) {
237                 printf(": "STRERROR_FMT STRERROR_ERRNO);
238         }
239         bb_putchar('\n'); /* will also flush the output */
240 }
241
242 #define WARN    "warning: "
243 #define OK      "ok: "
244
245 static void fail(const char *m1)
246 {
247         ++rc;
248         out("fail: ", m1);
249 }
250 static void failx(const char *m1)
251 {
252         errno = 0;
253         fail(m1);
254 }
255 static void warn(const char *m1)
256 {
257         ++rc;
258         /* "warning: <service>: <m1>\n" */
259         out("warning: ", m1);
260 }
261 static void ok(const char *m1)
262 {
263         errno = 0;
264         out(OK, m1);
265 }
266
267 static int svstatus_get(void)
268 {
269         int fd, r;
270
271         fd = open("supervise/ok", O_WRONLY|O_NDELAY);
272         if (fd == -1) {
273                 if (errno == ENODEV) {
274                         *acts == 'x' ? ok("runsv not running")
275                                      : failx("runsv not running");
276                         return 0;
277                 }
278                 warn("can't open supervise/ok");
279                 return -1;
280         }
281         close(fd);
282         fd = open("supervise/status", O_RDONLY|O_NDELAY);
283         if (fd == -1) {
284                 warn("can't open supervise/status");
285                 return -1;
286         }
287         r = read(fd, &svstatus, 20);
288         close(fd);
289         switch (r) {
290         case 20:
291                 break;
292         case -1:
293                 warn("can't read supervise/status");
294                 return -1;
295         default:
296                 errno = 0;
297                 warn("can't read supervise/status: bad format");
298                 return -1;
299         }
300         return 1;
301 }
302
303 static unsigned svstatus_print(const char *m)
304 {
305         int diff;
306         int pid;
307         int normallyup = 0;
308         struct stat s;
309         uint64_t timestamp;
310
311         if (stat("down", &s) == -1) {
312                 if (errno != ENOENT) {
313                         bb_perror_msg(WARN"can't stat %s/down", *service);
314                         return 0;
315                 }
316                 normallyup = 1;
317         }
318         pid = SWAP_LE32(svstatus.pid_le32);
319         timestamp = SWAP_BE64(svstatus.time_be64);
320         switch (svstatus.run_or_finish) {
321                 case 0: printf("down: "); break;
322                 case 1: printf("run: "); break;
323                 case 2: printf("finish: "); break;
324         }
325         printf("%s: ", m);
326         if (svstatus.run_or_finish)
327                 printf("(pid %d) ", pid);
328         diff = tnow - timestamp;
329         printf("%us", (diff < 0 ? 0 : diff));
330         if (pid) {
331                 if (!normallyup) printf(", normally down");
332                 if (svstatus.paused) printf(", paused");
333                 if (svstatus.want == 'd') printf(", want down");
334                 if (svstatus.got_term) printf(", got TERM");
335         } else {
336                 if (normallyup) printf(", normally up");
337                 if (svstatus.want == 'u') printf(", want up");
338         }
339         return pid ? 1 : 2;
340 }
341
342 static int status(const char *unused UNUSED_PARAM)
343 {
344         int r;
345
346         if (svstatus_get() <= 0)
347                 return 0;
348
349         r = svstatus_print(*service);
350         islog = 1;
351         if (chdir("log") == -1) {
352                 if (errno != ENOENT) {
353                         printf("; ");
354                         warn("can't change directory");
355                 } else
356                         bb_putchar('\n');
357         } else {
358                 printf("; ");
359                 if (svstatus_get()) {
360                         r = svstatus_print("log");
361                         bb_putchar('\n');
362                 }
363         }
364         islog = 0;
365         return r;
366 }
367
368 static int checkscript(void)
369 {
370         char *prog[2];
371         struct stat s;
372         int pid, w;
373
374         if (stat("check", &s) == -1) {
375                 if (errno == ENOENT) return 1;
376                 bb_perror_msg(WARN"can't stat %s/check", *service);
377                 return 0;
378         }
379         /* if (!(s.st_mode & S_IXUSR)) return 1; */
380         prog[0] = (char*)"./check";
381         prog[1] = NULL;
382         pid = spawn(prog);
383         if (pid <= 0) {
384                 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
385                 return 0;
386         }
387         while (safe_waitpid(pid, &w, 0) == -1) {
388                 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
389                 return 0;
390         }
391         return WEXITSTATUS(w) == 0;
392 }
393
394 static int check(const char *a)
395 {
396         int r;
397         unsigned pid_le32;
398         uint64_t timestamp;
399
400         r = svstatus_get();
401         if (r == -1)
402                 return -1;
403         while (*a) {
404                 if (r == 0) {
405                         if (*a == 'x')
406                                 return 1;
407                         return -1;
408                 }
409                 pid_le32 = svstatus.pid_le32;
410                 switch (*a) {
411                 case 'x':
412                         return 0;
413                 case 'u':
414                         if (!pid_le32 || svstatus.run_or_finish != 1)
415                                 return 0;
416                         if (!checkscript())
417                                 return 0;
418                         break;
419                 case 'd':
420                         if (pid_le32 || svstatus.run_or_finish != 0)
421                                 return 0;
422                         break;
423                 case 'C':
424                         if (pid_le32 && !checkscript())
425                                 return 0;
426                         break;
427                 case 't':
428                 case 'k':
429                         if (!pid_le32 && svstatus.want == 'd')
430                                 break;
431                         timestamp = SWAP_BE64(svstatus.time_be64);
432                         if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
433                                 return 0;
434                         break;
435                 case 'o':
436                         timestamp = SWAP_BE64(svstatus.time_be64);
437                         if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
438                                 return 0;
439                         break;
440                 case 'p':
441                         if (pid_le32 && !svstatus.paused)
442                                 return 0;
443                         break;
444                 case 'c':
445                         if (pid_le32 && svstatus.paused)
446                                 return 0;
447                         break;
448                 }
449                 ++a;
450         }
451         printf(OK);
452         svstatus_print(*service);
453         bb_putchar('\n'); /* will also flush the output */
454         return 1;
455 }
456
457 static int control(const char *a)
458 {
459         int fd, r, l;
460
461         if (svstatus_get() <= 0)
462                 return -1;
463         if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
464                 return 0;
465         fd = open("supervise/control", O_WRONLY|O_NDELAY);
466         if (fd == -1) {
467                 if (errno != ENODEV)
468                         warn("can't open supervise/control");
469                 else
470                         *a == 'x' ? ok("runsv not running") : failx("runsv not running");
471                 return -1;
472         }
473         l = strlen(a);
474         r = write(fd, a, l);
475         close(fd);
476         if (r != l) {
477                 warn("can't write to supervise/control");
478                 return -1;
479         }
480         return 1;
481 }
482
483 //usage:#define sv_trivial_usage
484 //usage:       "[-v] [-w SEC] CMD SERVICE_DIR..."
485 //usage:#define sv_full_usage "\n\n"
486 //usage:       "Control services monitored by runsv supervisor.\n"
487 //usage:       "Commands (only first character is enough):\n"
488 //usage:       "\n"
489 //usage:       "status: query service status\n"
490 //usage:       "up: if service isn't running, start it. If service stops, restart it\n"
491 //usage:       "once: like 'up', but if service stops, don't restart it\n"
492 //usage:       "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
493 //usage:       "        if it exists. After it stops, don't restart service\n"
494 //usage:       "exit: send TERM and CONT signals to service and log service. If they exit,\n"
495 //usage:       "        runsv exits too\n"
496 //usage:       "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
497 //usage:       "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
498 static int sv(char **argv)
499 {
500         char *x;
501         char *action;
502         const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
503         unsigned waitsec = 7;
504         smallint kll = 0;
505         int verbose = 0;
506         int (*act)(const char*);
507         int (*cbk)(const char*);
508         int curdir;
509
510         INIT_G();
511
512         xfunc_error_retval = 100;
513
514         x = getenv("SVDIR");
515         if (x) varservice = x;
516         x = getenv("SVWAIT");
517         if (x) waitsec = xatou(x);
518
519         getopt32(argv, "^" "w:+v" "\0" "vv" /* -w N, -v is a counter */,
520                         &waitsec, &verbose
521         );
522         argv += optind;
523         action = *argv++;
524         if (!action || !*argv) bb_show_usage();
525
526         tnow = time(NULL) + 0x400000000000000aULL;
527         tstart = tnow;
528         curdir = open(".", O_RDONLY|O_NDELAY);
529         if (curdir == -1)
530                 fatal_cannot("open current directory");
531
532         act = &control;
533         acts = "s";
534         cbk = &check;
535
536         switch (*action) {
537         case 'x':
538         case 'e':
539                 acts = "x";
540                 if (!verbose) cbk = NULL;
541                 break;
542         case 'X':
543         case 'E':
544                 acts = "x";
545                 kll = 1;
546                 break;
547         case 'D':
548                 acts = "d";
549                 kll = 1;
550                 break;
551         case 'T':
552                 acts = "tc";
553                 kll = 1;
554                 break;
555         case 't':
556                 if (str_equal(action, "try-restart")) {
557                         acts = "tc";
558                         break;
559                 }
560         case 'c':
561                 if (str_equal(action, "check")) {
562                         act = NULL;
563                         acts = "C";
564                         break;
565                 }
566         case 'u': case 'd': case 'o': case 'p': case 'h':
567         case 'a': case 'i': case 'k': case 'q': case '1': case '2':
568                 action[1] = '\0';
569                 acts = action;
570                 if (!verbose)
571                         cbk = NULL;
572                 break;
573         case 's':
574                 if (str_equal(action, "shutdown")) {
575                         acts = "x";
576                         break;
577                 }
578                 if (str_equal(action, "start")) {
579                         acts = "u";
580                         break;
581                 }
582                 if (str_equal(action, "stop")) {
583                         acts = "d";
584                         break;
585                 }
586                 /* "status" */
587                 act = &status;
588                 cbk = NULL;
589                 break;
590         case 'r':
591                 if (str_equal(action, "restart")) {
592                         acts = "tcu";
593                         break;
594                 }
595                 if (str_equal(action, "reload")) {
596                         acts = "h";
597                         break;
598                 }
599                 bb_show_usage();
600         case 'f':
601                 if (str_equal(action, "force-reload")) {
602                         acts = "tc";
603                         kll = 1;
604                         break;
605                 }
606                 if (str_equal(action, "force-restart")) {
607                         acts = "tcu";
608                         kll = 1;
609                         break;
610                 }
611                 if (str_equal(action, "force-shutdown")) {
612                         acts = "x";
613                         kll = 1;
614                         break;
615                 }
616                 if (str_equal(action, "force-stop")) {
617                         acts = "d";
618                         kll = 1;
619                         break;
620                 }
621         default:
622                 bb_show_usage();
623         }
624
625         service = argv;
626         while ((x = *service) != NULL) {
627                 if (x[0] != '/' && x[0] != '.'
628                  && !last_char_is(x, '/')
629                 ) {
630                         if (chdir(varservice) == -1)
631                                 goto chdir_failed_0;
632                 }
633                 if (chdir(x) == -1) {
634  chdir_failed_0:
635                         fail("can't change to service directory");
636                         goto nullify_service_0;
637                 }
638                 if (act && (act(acts) == -1)) {
639  nullify_service_0:
640                         *service = (char*) -1L; /* "dead" */
641                 }
642                 if (fchdir(curdir) == -1)
643                         fatal_cannot("change to original directory");
644                 service++;
645         }
646
647         if (cbk) while (1) {
648                 int want_exit;
649                 int diff;
650
651                 diff = tnow - tstart;
652                 service = argv;
653                 want_exit = 1;
654                 while ((x = *service) != NULL) {
655                         if (x == (char*) -1L) /* "dead" */
656                                 goto next;
657                         if (x[0] != '/' && x[0] != '.') {
658                                 if (chdir(varservice) == -1)
659                                         goto chdir_failed;
660                         }
661                         if (chdir(x) == -1) {
662  chdir_failed:
663                                 fail("can't change to service directory");
664                                 goto nullify_service;
665                         }
666                         if (cbk(acts) != 0)
667                                 goto nullify_service;
668                         want_exit = 0;
669                         if (diff >= waitsec) {
670                                 printf(kll ? "kill: " : "timeout: ");
671                                 if (svstatus_get() > 0) {
672                                         svstatus_print(x);
673                                         ++rc;
674                                 }
675                                 bb_putchar('\n'); /* will also flush the output */
676                                 if (kll)
677                                         control("k");
678  nullify_service:
679                                 *service = (char*) -1L; /* "dead" */
680                         }
681                         if (fchdir(curdir) == -1)
682                                 fatal_cannot("change to original directory");
683  next:
684                         service++;
685                 }
686                 if (want_exit) break;
687                 usleep(420000);
688                 tnow = time(NULL) + 0x400000000000000aULL;
689         }
690         return rc > 99 ? 99 : rc;
691 }
692 #endif
693
694 #if ENABLE_SV
695 int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
696 int sv_main(int argc UNUSED_PARAM, char **argv)
697 {
698         return sv(argv);
699 }
700 #endif
701
702 //usage:#define svc_trivial_usage
703 //usage:       "[-udopchaitkx] SERVICE_DIR..."
704 //usage:#define svc_full_usage "\n\n"
705 //usage:       "Control services monitored by runsv supervisor"
706 //usage:   "\n"
707 //usage:   "\n""        -u      If service is not running, start it; restart if it stops"
708 //usage:   "\n""        -d      If service is running, send TERM+CONT signals; do not restart it"
709 //usage:   "\n""        -o      Once: if service is not running, start it; do not restart it"
710 //usage:   "\n""        -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
711 //usage:   "\n""        -x      Exit: runsv will exit as soon as the service is down"
712 #if ENABLE_SVC
713 int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
714 int svc_main(int argc UNUSED_PARAM, char **argv)
715 {
716         char command[2];
717         const char *optstring;
718         unsigned opts;
719
720         optstring = "udopchaitkx";
721         opts = getopt32(argv, optstring);
722         argv += optind;
723         if (!argv[0] || !opts)
724                 bb_show_usage();
725
726         argv -= 2;
727         if (optind > 2) {
728                 argv--;
729                 argv[2] = (char*)"--";
730         }
731         argv[0] = (char*)"sv";
732         argv[1] = command;
733         command[1] = '\0';
734
735         do {
736                 if (opts & 1) {
737                         int r;
738
739                         command[0] = *optstring;
740
741                         /* getopt() was already called by getopt32():
742                          * reset the libc getopt() function's internal state.
743                          */
744                         GETOPT_RESET();
745                         r = sv(argv);
746                         if (r)
747                                 return 1;
748                 }
749                 optstring++;
750                 opts >>= 1;
751         } while (opts);
752
753         return 0;
754 }
755 #endif
756
757 //usage:#define svok_trivial_usage
758 //usage:       "SERVICE_DIR"
759 //usage:#define svok_full_usage "\n\n"
760 //usage:       "Check whether runsv supervisor is running.\n"
761 //usage:       "Exit code is 0 if it does, 100 if it does not,\n"
762 //usage:       "111 (with error message) if SERVICE_DIR does not exist."
763 #if ENABLE_SVOK
764 int svok_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
765 int svok_main(int argc UNUSED_PARAM, char **argv)
766 {
767         const char *dir = argv[1];
768
769         if (!dir)
770                 bb_show_usage();
771
772         xfunc_error_retval = 111;
773
774         /*
775          * daemontools has no concept of "default service dir", runit does.
776          * Let's act as runit.
777          */
778         if (dir[0] != '/' && dir[0] != '.'
779          && !last_char_is(dir, '/')
780         ) {
781                 xchdir(CONFIG_SV_DEFAULT_SERVICE_DIR);
782         }
783
784         xchdir(dir);
785         if (open("supervise/ok", O_WRONLY) < 0) {
786                 if (errno == ENOENT || errno == ENXIO)
787                         return 100;
788                 bb_perror_msg_and_die("can't open '%s'", "supervise/ok");
789         }
790
791         return 0;
792 }
793 #endif