svok: new applet (daemontools compat)
[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 (7.8 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 (7.8 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"
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 static void fatal_cannot(const char *m1) NORETURN;
226 static void fatal_cannot(const char *m1)
227 {
228         bb_perror_msg("fatal: can't %s", m1);
229         _exit(151);
230 }
231
232 static void out(const char *p, const char *m1)
233 {
234         printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
235         if (errno) {
236                 printf(": "STRERROR_FMT STRERROR_ERRNO);
237         }
238         bb_putchar('\n'); /* will also flush the output */
239 }
240
241 #define WARN    "warning: "
242 #define OK      "ok: "
243
244 static void fail(const char *m1)
245 {
246         ++rc;
247         out("fail: ", m1);
248 }
249 static void failx(const char *m1)
250 {
251         errno = 0;
252         fail(m1);
253 }
254 static void warn(const char *m1)
255 {
256         ++rc;
257         /* "warning: <service>: <m1>\n" */
258         out("warning: ", m1);
259 }
260 static void ok(const char *m1)
261 {
262         errno = 0;
263         out(OK, m1);
264 }
265
266 static int svstatus_get(void)
267 {
268         int fd, r;
269
270         fd = open("supervise/ok", O_WRONLY|O_NDELAY);
271         if (fd == -1) {
272                 if (errno == ENODEV) {
273                         *acts == 'x' ? ok("runsv not running")
274                                      : failx("runsv not running");
275                         return 0;
276                 }
277                 warn("can't open supervise/ok");
278                 return -1;
279         }
280         close(fd);
281         fd = open("supervise/status", O_RDONLY|O_NDELAY);
282         if (fd == -1) {
283                 warn("can't open supervise/status");
284                 return -1;
285         }
286         r = read(fd, &svstatus, 20);
287         close(fd);
288         switch (r) {
289         case 20:
290                 break;
291         case -1:
292                 warn("can't read supervise/status");
293                 return -1;
294         default:
295                 errno = 0;
296                 warn("can't read supervise/status: bad format");
297                 return -1;
298         }
299         return 1;
300 }
301
302 static unsigned svstatus_print(const char *m)
303 {
304         int diff;
305         int pid;
306         int normallyup = 0;
307         struct stat s;
308         uint64_t timestamp;
309
310         if (stat("down", &s) == -1) {
311                 if (errno != ENOENT) {
312                         bb_perror_msg(WARN"can't stat %s/down", *service);
313                         return 0;
314                 }
315                 normallyup = 1;
316         }
317         pid = SWAP_LE32(svstatus.pid_le32);
318         timestamp = SWAP_BE64(svstatus.time_be64);
319         switch (svstatus.run_or_finish) {
320                 case 0: printf("down: "); break;
321                 case 1: printf("run: "); break;
322                 case 2: printf("finish: "); break;
323         }
324         printf("%s: ", m);
325         if (svstatus.run_or_finish)
326                 printf("(pid %d) ", pid);
327         diff = tnow - timestamp;
328         printf("%us", (diff < 0 ? 0 : diff));
329         if (pid) {
330                 if (!normallyup) printf(", normally down");
331                 if (svstatus.paused) printf(", paused");
332                 if (svstatus.want == 'd') printf(", want down");
333                 if (svstatus.got_term) printf(", got TERM");
334         } else {
335                 if (normallyup) printf(", normally up");
336                 if (svstatus.want == 'u') printf(", want up");
337         }
338         return pid ? 1 : 2;
339 }
340
341 static int status(const char *unused UNUSED_PARAM)
342 {
343         int r;
344
345         if (svstatus_get() <= 0)
346                 return 0;
347
348         r = svstatus_print(*service);
349         islog = 1;
350         if (chdir("log") == -1) {
351                 if (errno != ENOENT) {
352                         printf("; ");
353                         warn("can't change directory");
354                 } else
355                         bb_putchar('\n');
356         } else {
357                 printf("; ");
358                 if (svstatus_get()) {
359                         r = svstatus_print("log");
360                         bb_putchar('\n');
361                 }
362         }
363         islog = 0;
364         return r;
365 }
366
367 static int checkscript(void)
368 {
369         char *prog[2];
370         struct stat s;
371         int pid, w;
372
373         if (stat("check", &s) == -1) {
374                 if (errno == ENOENT) return 1;
375                 bb_perror_msg(WARN"can't stat %s/check", *service);
376                 return 0;
377         }
378         /* if (!(s.st_mode & S_IXUSR)) return 1; */
379         prog[0] = (char*)"./check";
380         prog[1] = NULL;
381         pid = spawn(prog);
382         if (pid <= 0) {
383                 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
384                 return 0;
385         }
386         while (safe_waitpid(pid, &w, 0) == -1) {
387                 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
388                 return 0;
389         }
390         return WEXITSTATUS(w) == 0;
391 }
392
393 static int check(const char *a)
394 {
395         int r;
396         unsigned pid_le32;
397         uint64_t timestamp;
398
399         r = svstatus_get();
400         if (r == -1)
401                 return -1;
402         while (*a) {
403                 if (r == 0) {
404                         if (*a == 'x')
405                                 return 1;
406                         return -1;
407                 }
408                 pid_le32 = svstatus.pid_le32;
409                 switch (*a) {
410                 case 'x':
411                         return 0;
412                 case 'u':
413                         if (!pid_le32 || svstatus.run_or_finish != 1)
414                                 return 0;
415                         if (!checkscript())
416                                 return 0;
417                         break;
418                 case 'd':
419                         if (pid_le32 || svstatus.run_or_finish != 0)
420                                 return 0;
421                         break;
422                 case 'C':
423                         if (pid_le32 && !checkscript())
424                                 return 0;
425                         break;
426                 case 't':
427                 case 'k':
428                         if (!pid_le32 && svstatus.want == 'd')
429                                 break;
430                         timestamp = SWAP_BE64(svstatus.time_be64);
431                         if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
432                                 return 0;
433                         break;
434                 case 'o':
435                         timestamp = SWAP_BE64(svstatus.time_be64);
436                         if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
437                                 return 0;
438                         break;
439                 case 'p':
440                         if (pid_le32 && !svstatus.paused)
441                                 return 0;
442                         break;
443                 case 'c':
444                         if (pid_le32 && svstatus.paused)
445                                 return 0;
446                         break;
447                 }
448                 ++a;
449         }
450         printf(OK);
451         svstatus_print(*service);
452         bb_putchar('\n'); /* will also flush the output */
453         return 1;
454 }
455
456 static int control(const char *a)
457 {
458         int fd, r, l;
459
460         if (svstatus_get() <= 0)
461                 return -1;
462         if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
463                 return 0;
464         fd = open("supervise/control", O_WRONLY|O_NDELAY);
465         if (fd == -1) {
466                 if (errno != ENODEV)
467                         warn("can't open supervise/control");
468                 else
469                         *a == 'x' ? ok("runsv not running") : failx("runsv not running");
470                 return -1;
471         }
472         l = strlen(a);
473         r = write(fd, a, l);
474         close(fd);
475         if (r != l) {
476                 warn("can't write to supervise/control");
477                 return -1;
478         }
479         return 1;
480 }
481
482 //usage:#define sv_trivial_usage
483 //usage:       "[-v] [-w SEC] CMD SERVICE_DIR..."
484 //usage:#define sv_full_usage "\n\n"
485 //usage:       "Control services monitored by runsv supervisor.\n"
486 //usage:       "Commands (only first character is enough):\n"
487 //usage:       "\n"
488 //usage:       "status: query service status\n"
489 //usage:       "up: if service isn't running, start it. If service stops, restart it\n"
490 //usage:       "once: like 'up', but if service stops, don't restart it\n"
491 //usage:       "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
492 //usage:       "        if it exists. After it stops, don't restart service\n"
493 //usage:       "exit: send TERM and CONT signals to service and log service. If they exit,\n"
494 //usage:       "        runsv exits too\n"
495 //usage:       "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
496 //usage:       "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
497 static int sv(char **argv)
498 {
499         char *x;
500         char *action;
501         const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
502         unsigned waitsec = 7;
503         smallint kll = 0;
504         int verbose = 0;
505         int (*act)(const char*);
506         int (*cbk)(const char*);
507         int curdir;
508
509         INIT_G();
510
511         xfunc_error_retval = 100;
512
513         x = getenv("SVDIR");
514         if (x) varservice = x;
515         x = getenv("SVWAIT");
516         if (x) waitsec = xatou(x);
517
518         getopt32(argv, "^" "w:+v" "\0" "vv" /* -w N, -v is a counter */,
519                         &waitsec, &verbose
520         );
521         argv += optind;
522         action = *argv++;
523         if (!action || !*argv) bb_show_usage();
524
525         tnow = time(NULL) + 0x400000000000000aULL;
526         tstart = tnow;
527         curdir = open(".", O_RDONLY|O_NDELAY);
528         if (curdir == -1)
529                 fatal_cannot("open current directory");
530
531         act = &control;
532         acts = "s";
533         cbk = &check;
534
535         switch (*action) {
536         case 'x':
537         case 'e':
538                 acts = "x";
539                 if (!verbose) cbk = NULL;
540                 break;
541         case 'X':
542         case 'E':
543                 acts = "x";
544                 kll = 1;
545                 break;
546         case 'D':
547                 acts = "d";
548                 kll = 1;
549                 break;
550         case 'T':
551                 acts = "tc";
552                 kll = 1;
553                 break;
554         case 't':
555                 if (str_equal(action, "try-restart")) {
556                         acts = "tc";
557                         break;
558                 }
559         case 'c':
560                 if (str_equal(action, "check")) {
561                         act = NULL;
562                         acts = "C";
563                         break;
564                 }
565         case 'u': case 'd': case 'o': case 'p': case 'h':
566         case 'a': case 'i': case 'k': case 'q': case '1': case '2':
567                 action[1] = '\0';
568                 acts = action;
569                 if (!verbose)
570                         cbk = NULL;
571                 break;
572         case 's':
573                 if (str_equal(action, "shutdown")) {
574                         acts = "x";
575                         break;
576                 }
577                 if (str_equal(action, "start")) {
578                         acts = "u";
579                         break;
580                 }
581                 if (str_equal(action, "stop")) {
582                         acts = "d";
583                         break;
584                 }
585                 /* "status" */
586                 act = &status;
587                 cbk = NULL;
588                 break;
589         case 'r':
590                 if (str_equal(action, "restart")) {
591                         acts = "tcu";
592                         break;
593                 }
594                 if (str_equal(action, "reload")) {
595                         acts = "h";
596                         break;
597                 }
598                 bb_show_usage();
599         case 'f':
600                 if (str_equal(action, "force-reload")) {
601                         acts = "tc";
602                         kll = 1;
603                         break;
604                 }
605                 if (str_equal(action, "force-restart")) {
606                         acts = "tcu";
607                         kll = 1;
608                         break;
609                 }
610                 if (str_equal(action, "force-shutdown")) {
611                         acts = "x";
612                         kll = 1;
613                         break;
614                 }
615                 if (str_equal(action, "force-stop")) {
616                         acts = "d";
617                         kll = 1;
618                         break;
619                 }
620         default:
621                 bb_show_usage();
622         }
623
624         service = argv;
625         while ((x = *service) != NULL) {
626                 if (x[0] != '/' && x[0] != '.'
627                  && !last_char_is(x, '/')
628                 ) {
629                         if (chdir(varservice) == -1)
630                                 goto chdir_failed_0;
631                 }
632                 if (chdir(x) == -1) {
633  chdir_failed_0:
634                         fail("can't change to service directory");
635                         goto nullify_service_0;
636                 }
637                 if (act && (act(acts) == -1)) {
638  nullify_service_0:
639                         *service = (char*) -1L; /* "dead" */
640                 }
641                 if (fchdir(curdir) == -1)
642                         fatal_cannot("change to original directory");
643                 service++;
644         }
645
646         if (cbk) while (1) {
647                 int want_exit;
648                 int diff;
649
650                 diff = tnow - tstart;
651                 service = argv;
652                 want_exit = 1;
653                 while ((x = *service) != NULL) {
654                         if (x == (char*) -1L) /* "dead" */
655                                 goto next;
656                         if (x[0] != '/' && x[0] != '.') {
657                                 if (chdir(varservice) == -1)
658                                         goto chdir_failed;
659                         }
660                         if (chdir(x) == -1) {
661  chdir_failed:
662                                 fail("can't change to service directory");
663                                 goto nullify_service;
664                         }
665                         if (cbk(acts) != 0)
666                                 goto nullify_service;
667                         want_exit = 0;
668                         if (diff >= waitsec) {
669                                 printf(kll ? "kill: " : "timeout: ");
670                                 if (svstatus_get() > 0) {
671                                         svstatus_print(x);
672                                         ++rc;
673                                 }
674                                 bb_putchar('\n'); /* will also flush the output */
675                                 if (kll)
676                                         control("k");
677  nullify_service:
678                                 *service = (char*) -1L; /* "dead" */
679                         }
680                         if (fchdir(curdir) == -1)
681                                 fatal_cannot("change to original directory");
682  next:
683                         service++;
684                 }
685                 if (want_exit) break;
686                 usleep(420000);
687                 tnow = time(NULL) + 0x400000000000000aULL;
688         }
689         return rc > 99 ? 99 : rc;
690 }
691
692 #if ENABLE_SV
693 int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
694 int sv_main(int argc UNUSED_PARAM, char **argv)
695 {
696         return sv(argv);
697 }
698 #endif
699
700 //usage:#define svc_trivial_usage
701 //usage:       "[-udopchaitkx] SERVICE_DIR..."
702 //usage:#define svc_full_usage "\n\n"
703 //usage:       "Control services monitored by runsv supervisor"
704 //usage:   "\n"
705 //usage:   "\n""        -u      If service is not running, start it; restart if it stops"
706 //usage:   "\n""        -d      If service is running, send TERM+CONT signals; do not restart it"
707 //usage:   "\n""        -o      Once: if service is not running, start it; do not restart it"
708 //usage:   "\n""        -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
709 //usage:   "\n""        -x      Exit: runsv will exit as soon as the service is down"
710 #if ENABLE_SVC
711 int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
712 int svc_main(int argc UNUSED_PARAM, char **argv)
713 {
714         char command[2];
715         const char *optstring;
716         unsigned opts;
717
718         optstring = "udopchaitkx";
719         opts = getopt32(argv, optstring);
720         argv += optind;
721         if (!argv[0] || !opts)
722                 bb_show_usage();
723
724         argv -= 2;
725         if (optind > 2) {
726                 argv--;
727                 argv[2] = (char*)"--";
728         }
729         argv[0] = (char*)"sv";
730         argv[1] = command;
731         command[1] = '\0';
732
733         do {
734                 if (opts & 1) {
735                         int r;
736
737                         command[0] = *optstring;
738
739                         /* getopt() was already called by getopt32():
740                          * reset the libc getopt() function's internal state.
741                          */
742                         GETOPT_RESET();
743                         r = sv(argv);
744                         if (r)
745                                 return 1;
746                 }
747                 optstring++;
748                 opts >>= 1;
749         } while (opts);
750
751         return 0;
752 }
753 #endif
754
755 //usage:#define svok_trivial_usage
756 //usage:       "SERVICE_DIR"
757 //usage:#define svok_full_usage "\n\n"
758 //usage:       "Check whether runsv supervisor is running.\n"
759 //usage:       "Exit code is 0 if it does, 100 if it does not,\n"
760 //usage:       "111 (with error message) if SERVICE_DIR does not exist."
761 #if ENABLE_SVOK
762 int svok_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
763 int svok_main(int argc UNUSED_PARAM, char **argv)
764 {
765         const char *dir = argv[1];
766
767         if (!dir)
768                 bb_show_usage();
769
770         xfunc_error_retval = 111;
771
772         /*
773          * daemontools has no concept of "default service dir", runit does.
774          * Let's act as runit.
775          */
776         if (dir[0] != '/' && dir[0] != '.'
777          && !last_char_is(dir, '/')
778         ) {
779                 xchdir(CONFIG_SV_DEFAULT_SERVICE_DIR);
780         }
781
782         xchdir(dir);
783         if (open("supervise/ok", O_WRONLY) < 0) {
784                 if (errno == ENOENT || errno == ENXIO)
785                         return 100;
786                 bb_perror_msg_and_die("can't open '%s'", "supervise/ok");
787         }
788
789         return 0;
790 }
791 #endif