source: branches/2.2.5/mindi-busybox/runit/sv.c @ 1765

Last change on this file since 1765 was 1765, checked in by Bruno Cornec, 13 years ago

Update to busybox 1.7.2

  • Property svn:eol-style set to native
File size: 15.0 KB
Line 
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.sunsite.dk/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,
40it is searched in the default services directory /var/service/, otherwise
41relative 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, restart, shutdown, force-stop,
45force-reload, force-restart, force-shutdown.
46
47The sv program can be sym-linked to /etc/init.d/ to provide an LSB init
48script interface. The service to be controlled then is specified by the
49base name of the "init script".
50
51status
52    Report the current status of the service, and the appendant log service
53    if available, to standard output.
54up
55    If the service is not running, start it. If the service stops, restart it.
56down
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.
60once
61    If the service is not running, start it. Do not restart it if it stops.
62pause 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.
65exit
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
73sv actually looks only at the first character of above commands.
74
75Commands compatible to LSB init script actions:
76
77status
78    Same as status.
79start
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.
85stop
86    Same as down, but wait up to 7 seconds for the service to become down.
87    Then report the status or timeout.
88restart
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.
94shutdown
95    Same as exit, but wait up to 7 seconds for the runsv(8) process
96    to terminate. Then report the status or timeout.
97force-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.
100force-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.
104force-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.
111force-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
116Additional Commands
117
118check
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
126Options
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
134Environment
135
136SVDIR
137    The environment variable $SVDIR overrides the default services directory
138    /var/service.
139SVWAIT
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
143Exit 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 Denis Vlasenko <vda.linux@googlemail.com> */
154/* TODO: depends on runit_lib.c - review and reduce/eliminate */
155
156#include <sys/poll.h>
157#include <sys/file.h>
158#include "libbb.h"
159#include "runit_lib.h"
160
161static const char *acts;
162static char **service;
163static unsigned rc;
164/* "Bernstein" time format: unix + 0x400000000000000aULL */
165static uint64_t tstart, tnow;
166svstatus_t svstatus;
167
168
169static void fatal_cannot(const char *m1) ATTRIBUTE_NORETURN;
170static void fatal_cannot(const char *m1)
171{
172    bb_perror_msg("fatal: cannot %s", m1);
173    _exit(151);
174}
175
176static void out(const char *p, const char *m1)
177{
178    printf("%s%s: %s", p, *service, m1);
179    if (errno) {
180        printf(": %s", strerror(errno));
181    }
182    puts(""); /* will also flush the output */
183}
184
185#define WARN    "warning: "
186#define OK      "ok: "
187
188static void fail(const char *m1)
189{
190    ++rc;
191    out("fail: ", m1);
192}
193static void failx(const char *m1)
194{
195    errno = 0;
196    fail(m1);
197}
198static void warn(const char *m1)
199{
200    ++rc;
201    /* "warning: <service>: <m1>\n" */
202    out("warning: ", m1);
203}
204static void ok(const char *m1)
205{
206    errno = 0;
207    out(OK, m1);
208}
209
210static int svstatus_get(void)
211{
212    int fd, r;
213
214    fd = open_write("supervise/ok");
215    if (fd == -1) {
216        if (errno == ENODEV) {
217            *acts == 'x' ? ok("runsv not running")
218                         : failx("runsv not running");
219            return 0;
220        }
221        warn("cannot open supervise/ok");
222        return -1;
223    }
224    close(fd);
225    fd = open_read("supervise/status");
226    if (fd == -1) {
227        warn("cannot open supervise/status");
228        return -1;
229    }
230    r = read(fd, &svstatus, 20);
231    close(fd);
232    switch (r) {
233    case 20:
234        break;
235    case -1:
236        warn("cannot read supervise/status");
237        return -1;
238    default:
239        errno = 0;
240        warn("cannot read supervise/status: bad format");
241        return -1;
242    }
243    return 1;
244}
245
246static unsigned svstatus_print(const char *m)
247{
248    int diff;
249    int pid;
250    int normallyup = 0;
251    struct stat s;
252    uint64_t timestamp;
253
254    if (stat("down", &s) == -1) {
255        if (errno != ENOENT) {
256            bb_perror_msg(WARN"cannot stat %s/down", *service);
257            return 0;
258        }
259        normallyup = 1;
260    }
261    pid = SWAP_LE32(svstatus.pid_le32);
262    timestamp = SWAP_BE64(svstatus.time_be64);
263    if (pid) {
264        switch (svstatus.run_or_finish) {
265        case 1: printf("run: "); break;
266        case 2: printf("finish: "); break;
267        }
268        printf("%s: (pid %d) ", m, pid);
269    } else {
270        printf("down: %s: ", m);
271    }
272    diff = tnow - timestamp;
273    printf("%us", (diff < 0 ? 0 : diff));
274    if (pid) {
275        if (!normallyup) printf(", normally down");
276        if (svstatus.paused) printf(", paused");
277        if (svstatus.want == 'd') printf(", want down");
278        if (svstatus.got_term) printf(", got TERM");
279    } else {
280        if (normallyup) printf(", normally up");
281        if (svstatus.want == 'u') printf(", want up");
282    }
283    return pid ? 1 : 2;
284}
285
286static int status(const char *unused)
287{
288    int r;
289
290    r = svstatus_get();
291    switch (r) { case -1: case 0: return 0; }
292
293    r = svstatus_print(*service);
294    if (chdir("log") == -1) {
295        if (errno != ENOENT) {
296            printf("; log: "WARN"cannot change to log service directory: %s",
297                    strerror(errno));
298        }
299    } else if (svstatus_get()) {
300        printf("; ");
301        svstatus_print("log");
302    }
303    puts(""); /* will also flush the output */
304    return r;
305}
306
307static int checkscript(void)
308{
309    char *prog[2];
310    struct stat s;
311    int pid, w;
312
313    if (stat("check", &s) == -1) {
314        if (errno == ENOENT) return 1;
315        bb_perror_msg(WARN"cannot stat %s/check", *service);
316        return 0;
317    }
318    /* if (!(s.st_mode & S_IXUSR)) return 1; */
319    prog[0] = (char*)"./check";
320    prog[1] = NULL;
321    pid = spawn(prog);
322    if (pid <= 0) {
323        bb_perror_msg(WARN"cannot %s child %s/check", "run", *service);
324        return 0;
325    }
326    while (wait_pid(&w, pid) == -1) {
327        if (errno == EINTR) continue;
328        bb_perror_msg(WARN"cannot %s child %s/check", "wait for", *service);
329        return 0;
330    }
331    return !wait_exitcode(w);
332}
333
334static int check(const char *a)
335{
336    int r;
337    unsigned pid;
338    uint64_t timestamp;
339
340    r = svstatus_get();
341    if (r == -1)
342        return -1;
343    if (r == 0) {
344        if (*a == 'x')
345            return 1;
346        return -1;
347    }
348    pid = SWAP_LE32(svstatus.pid_le32);
349    switch (*a) {
350    case 'x':
351        return 0;
352    case 'u':
353        if (!pid || svstatus.run_or_finish != 1) return 0;
354        if (!checkscript()) return 0;
355        break;
356    case 'd':
357        if (pid) return 0;
358        break;
359    case 'c':
360        if (pid && !checkscript()) return 0;
361        break;
362    case 't':
363        if (!pid && svstatus.want == 'd') break;
364        timestamp = SWAP_BE64(svstatus.time_be64);
365        if ((tstart > timestamp) || !pid || svstatus.got_term || !checkscript())
366            return 0;
367        break;
368    case 'o':
369        timestamp = SWAP_BE64(svstatus.time_be64);
370        if ((!pid && tstart > timestamp) || (pid && svstatus.want != 'd'))
371            return 0;
372    }
373    printf(OK);
374    svstatus_print(*service);
375    puts(""); /* will also flush the output */
376    return 1;
377}
378
379static int control(const char *a)
380{
381    int fd, r;
382
383    if (svstatus_get() <= 0)
384        return -1;
385    if (svstatus.want == *a)
386        return 0;
387    fd = open_write("supervise/control");
388    if (fd == -1) {
389        if (errno != ENODEV)
390            warn("cannot open supervise/control");
391        else
392            *a == 'x' ? ok("runsv not running") : failx("runsv not running");
393        return -1;
394    }
395    r = write(fd, a, strlen(a));
396    close(fd);
397    if (r != strlen(a)) {
398        warn("cannot write to supervise/control");
399        return -1;
400    }
401    return 1;
402}
403
404int sv_main(int argc, char **argv);
405int sv_main(int argc, char **argv)
406{
407    unsigned opt;
408    unsigned i, want_exit;
409    char *x;
410    char *action;
411    const char *varservice = "/var/service/";
412    unsigned services;
413    char **servicex;
414    unsigned waitsec = 7;
415    smallint kll = 0;
416    smallint verbose = 0;
417    int (*act)(const char*);
418    int (*cbk)(const char*);
419    int curdir;
420
421    xfunc_error_retval = 100;
422
423    x = getenv("SVDIR");
424    if (x) varservice = x;
425    x = getenv("SVWAIT");
426    if (x) waitsec = xatou(x);
427
428    opt = getopt32(argv, "w:v", &x);
429    if (opt & 1) waitsec = xatou(x); // -w
430    if (opt & 2) verbose = 1; // -v
431    argc -= optind;
432    argv += optind;
433    action = *argv++;
434    if (!action || !*argv) bb_show_usage();
435    service = argv;
436    services = argc - 1;
437
438    tnow = time(0) + 0x400000000000000aULL;
439    tstart = tnow;
440    curdir = open_read(".");
441    if (curdir == -1)
442        fatal_cannot("open current directory");
443
444    act = &control;
445    acts = "s";
446    cbk = &check;
447
448    switch (*action) {
449    case 'x':
450    case 'e':
451        acts = "x";
452        if (!verbose) cbk = NULL;
453        break;
454    case 'X':
455    case 'E':
456        acts = "x";
457        kll = 1;
458        break;
459    case 'D':
460        acts = "d";
461        kll = 1;
462        break;
463    case 'T':
464        acts = "tc";
465        kll = 1;
466        break;
467    case 'c':
468        if (str_equal(action, "check")) {
469            act = NULL;
470            acts = "c";
471            break;
472        }
473    case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
474    case 'a': case 'i': case 'k': case 'q': case '1': case '2':
475        action[1] = '\0';
476        acts = action;
477        if (!verbose) cbk = NULL;
478        break;
479    case 's':
480        if (str_equal(action, "shutdown")) {
481            acts = "x";
482            break;
483        }
484        if (str_equal(action, "start")) {
485            acts = "u";
486            break;
487        }
488        if (str_equal(action, "stop")) {
489            acts = "d";
490            break;
491        }
492        /* "status" */
493        act = &status;
494        cbk = NULL;
495        break;
496    case 'r':
497        if (str_equal(action, "restart")) {
498            acts = "tcu";
499            break;
500        }
501        bb_show_usage();
502    case 'f':
503        if (str_equal(action, "force-reload")) {
504            acts = "tc";
505            kll = 1;
506            break;
507        }
508        if (str_equal(action, "force-restart")) {
509            acts = "tcu";
510            kll = 1;
511            break;
512        }
513        if (str_equal(action, "force-shutdown")) {
514            acts = "x";
515            kll = 1;
516            break;
517        }
518        if (str_equal(action, "force-stop")) {
519            acts = "d";
520            kll = 1;
521            break;
522        }
523    default:
524        bb_show_usage();
525    }
526
527    servicex = service;
528    for (i = 0; i < services; ++i) {
529        if ((**service != '/') && (**service != '.')) {
530            if (chdir(varservice) == -1)
531                goto chdir_failed_0;
532        }
533        if (chdir(*service) == -1) {
534 chdir_failed_0:
535            fail("cannot change to service directory");
536            goto nullify_service_0;
537        }
538        if (act && (act(acts) == -1)) {
539 nullify_service_0:
540            *service = NULL;
541        }
542        if (fchdir(curdir) == -1)
543            fatal_cannot("change to original directory");
544        service++;
545    }
546
547    if (cbk) while (1) {
548        int diff;
549
550        diff = tnow - tstart;
551        service = servicex;
552        want_exit = 1;
553        for (i = 0; i < services; ++i, ++service) {
554            if (!*service)
555                continue;
556            if ((**service != '/') && (**service != '.')) {
557                if (chdir(varservice) == -1)
558                    goto chdir_failed;
559            }
560            if (chdir(*service) == -1) {
561 chdir_failed:
562                fail("cannot change to service directory");
563                goto nullify_service;
564            }
565            if (cbk(acts) != 0)
566                goto nullify_service;
567            want_exit = 0;
568            if (diff >= waitsec) {
569                printf(kll ? "kill: " : "timeout: ");
570                if (svstatus_get() > 0) {
571                    svstatus_print(*service);
572                    ++rc;
573                }
574                puts(""); /* will also flush the output */
575                if (kll)
576                    control("k");
577 nullify_service:
578                *service = NULL;
579            }
580            if (fchdir(curdir) == -1)
581                fatal_cannot("change to original directory");
582        }
583        if (want_exit) break;
584        usleep(420000);
585        tnow = time(0) + 0x400000000000000aULL;
586    }
587    return rc > 99 ? 99 : rc;
588}
Note: See TracBrowser for help on using the repository browser.