busybox/runit/sv.c
<<
>>
Prefs
   1/*
   2Copyright (c) 2001-2006, Gerrit Pape
   3All rights reserved.
   4
   5Redistribution and use in source and binary forms, with or without
   6modification, 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
  16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
  17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26*/
  27
  28/* Taken from http://smarden.org/runit/sv.8.html:
  29
  30sv - control and manage services monitored by runsv
  31
  32sv [-v] [-w sec] command services
  33/etc/init.d/service [-w sec] command
  34
  35The sv program reports the current status and controls the state of services
  36monitored by the runsv(8) supervisor.
  37
  38services consists of one or more arguments, each argument naming a directory
  39service used by runsv(8). If service doesn't start with a dot or slash and
  40doesn't end with a slash, it is searched in the default services directory
  41/var/service/, otherwise relative to the current directory.
  42
  43command is one of up, down, status, once, pause, cont, hup, alarm, interrupt,
  441, 2, term, kill, or exit, or start, stop, reload, restart, shutdown,
  45force-stop, force-reload, force-restart, force-shutdown, try-restart.
  46
  47status
  48    Report the current status of the service, and the appendant log service
  49    if available, to standard output.
  50up
  51    If the service is not running, start it. If the service stops, restart it.
  52down
  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.
  56once
  57    If the service is not running, start it. Do not restart it if it stops.
  58pause 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.
  61exit
  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
  69sv actually looks only at the first character of above commands.
  70
  71Commands compatible to LSB init script actions:
  72
  73status
  74    Same as status.
  75start
  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.
  81stop
  82    Same as down, but wait up to 7 seconds for the service to become down.
  83    Then report the status or timeout.
  84reload
  85    Same as hup, and additionally report the status afterwards.
  86restart
  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.
  92shutdown
  93    Same as exit, but wait up to 7 seconds for the runsv(8) process
  94    to terminate. Then report the status or timeout.
  95force-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.
  98force-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.
 102force-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.
 109force-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.
 113try-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
 117Additional Commands
 118
 119check
 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
 127Options
 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
 135Environment
 136
 137SVDIR
 138    The environment variable $SVDIR overrides the default services directory
 139    /var/service.
 140SVWAIT
 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
 144Exit 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
 198struct 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
 226static void fatal_cannot(const char *m1) NORETURN;
 227static void fatal_cannot(const char *m1)
 228{
 229        bb_perror_msg("fatal: can't %s", m1);
 230        _exit(151);
 231}
 232
 233static 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
 245static void fail(const char *m1)
 246{
 247        ++rc;
 248        out("fail: ", m1);
 249}
 250static void failx(const char *m1)
 251{
 252        errno = 0;
 253        fail(m1);
 254}
 255static void warn(const char *m1)
 256{
 257        ++rc;
 258        /* "warning: <service>: <m1>\n" */
 259        out("warning: ", m1);
 260}
 261static void ok(const char *m1)
 262{
 263        errno = 0;
 264        out(OK, m1);
 265}
 266
 267static 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
 303static 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
 342static 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
 368static 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
 394static 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
 457static 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"
 498static 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
 695int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 696int 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
 713int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 714int 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
 764int svok_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 765int 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
 794