runit

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

sv.c (12201B)


      1 #include <sys/types.h>
      2 #include <sys/stat.h>
      3 #include <unistd.h>
      4 #include "str.h"
      5 #include "strerr.h"
      6 #include "error.h"
      7 #include "sgetopt.h"
      8 #include "open.h"
      9 #include "env.h"
     10 #include "buffer.h"
     11 #include "fmt.h"
     12 #include "scan.h"
     13 #include "tai.h"
     14 #include "taia.h"
     15 #include "wait.h"
     16 
     17 #define USAGE " [-v] [-w sec] command service ..."
     18 #define USAGELSB " [-w sec] command"
     19 
     20 #define FATAL   "fatal: "
     21 #define FAIL    "fail: "
     22 #define WARN    "warning: "
     23 #define OK      "ok: "
     24 #define RUN     "run: "
     25 #define FINISH  "finish: "
     26 #define DOWN    "down: "
     27 #define TIMEOUT "timeout: "
     28 #define KILL    "kill: "
     29 
     30 static char *progname;
     31 static char *action;
     32 static char *acts;
     33 static char *varservice ="/var/service/";
     34 static char **service;
     35 static char **servicex;
     36 static unsigned int services;
     37 static unsigned int rc =0;
     38 static unsigned int lsb;
     39 static unsigned int verbose =0;
     40 static unsigned long wait =7;
     41 static unsigned int kll =0;
     42 static unsigned int islog =0;
     43 static struct taia tstart, tnow, tdiff;
     44 static struct tai tstatus;
     45 
     46 static int (*act)(char*) =0;
     47 static int (*cbk)(char*) =0;
     48 
     49 static int curdir, fd, r;
     50 static char svstatus[20];
     51 static char sulong[FMT_ULONG];
     52 
     53 static void usage()
     54 {
     55   if (!lsb) strerr_die4x(100, "usage: ", progname, USAGE, "\n");
     56   strerr_die4x(2, "usage: ", progname, USAGELSB, "\n");
     57 }
     58 static void done(unsigned int e) { if (curdir != -1) fchdir(curdir); _exit(e); }
     59 static void fatal(char *m1)
     60 {
     61   strerr_warn3(FATAL, m1, ": ", &strerr_sys);
     62   done(lsb ? 151 : 100);
     63 }
     64 static void fatal2(char *m1, char *m2)
     65 {
     66   strerr_warn4(FATAL, m1, m2, ": ", &strerr_sys);
     67   done(lsb ? 151 : 100);
     68 }
     69 static void out(char *p, char *m1)
     70 {
     71   buffer_puts(buffer_1, p);
     72   buffer_puts(buffer_1, *service);
     73   if (islog) buffer_puts(buffer_1, "/log");
     74   buffer_puts(buffer_1, ": ");
     75   buffer_puts(buffer_1, m1);
     76   if (errno) {
     77     buffer_puts(buffer_1, ": ");
     78     buffer_puts(buffer_1, error_str(errno));
     79   }
     80   buffer_puts(buffer_1, "\n");
     81   buffer_flush(buffer_1);
     82 }
     83 static void fail(char *m1) { ++rc; out(FAIL, m1); }
     84 static void failx(char *m1) { errno =0; fail(m1); }
     85 static void warn(char *m1) { ++rc; out(WARN, m1); }
     86 static void warnx(char *m1) { errno =0; warn(m1); }
     87 static void ok(char *m1) { errno =0; out(OK, m1); }
     88 
     89 static void outs(const char *s) { buffer_puts(buffer_1, s); }
     90 static void flush(const char *s) { outs(s); buffer_flush(buffer_1); }
     91 static void outs2(const char *s) { buffer_puts(buffer_2, s); }
     92 static void flush2(const char *s) { outs2(s); buffer_flush(buffer_2); }
     93 
     94 static int svstatus_get()
     95 {
     96   if ((fd =open_write("supervise/ok")) == -1) {
     97     if (errno == error_nodevice) {
     98       *acts == 'x' ? ok("runsv not running") : failx("runsv not running");
     99       return(0);
    100     }
    101     warn("unable to open supervise/ok");
    102     return(-1);
    103   }
    104   close(fd);
    105   if ((fd =open_read("supervise/status")) == -1) {
    106     warn("unable to open supervise/status");
    107     return(-1);
    108   }
    109   r =read(fd, svstatus, 20);
    110   close(fd);
    111   switch(r) {
    112   case 20: break;
    113   case -1: warn("unable to read supervise/status"); return(-1);
    114   default: warnx("unable to read supervise/status: bad format"); return(-1);
    115   }
    116   return(1);
    117 }
    118 static unsigned int svstatus_print(char *m) {
    119   int pid;
    120   int normallyup =0;
    121   struct stat s;
    122  
    123   if (stat("down", &s) == -1) {
    124     if (errno != error_noent) {
    125       outs2(WARN); outs2("unable to stat "); outs2(*service); outs2("/down: ");
    126       outs2(error_str(errno)); flush2("\n");
    127       return(0);
    128     }
    129     normallyup =1;
    130   }
    131   pid =(unsigned char) svstatus[15];
    132   pid <<=8; pid +=(unsigned char)svstatus[14];
    133   pid <<=8; pid +=(unsigned char)svstatus[13];
    134   pid <<=8; pid +=(unsigned char)svstatus[12];
    135   tai_unpack(svstatus, &tstatus);
    136   switch (svstatus[19]) {
    137   case 0: outs(DOWN); break;
    138   case 1: outs(RUN); break;
    139   case 2: outs(FINISH); break;
    140   }
    141   outs(m); outs(": ");
    142   if (svstatus[19]) {
    143     outs("(pid "); sulong[fmt_ulong(sulong, pid)] =0;
    144     outs(sulong); outs(") ");
    145   }
    146   buffer_put(buffer_1, sulong,
    147     fmt_ulong(sulong, tnow.sec.x < tstatus.x ? 0 : tnow.sec.x -tstatus.x));
    148   outs("s");
    149   if (pid && !normallyup) outs(", normally down");
    150   if (!pid && normallyup) outs(", normally up");
    151   if (pid && svstatus[16]) outs(", paused");
    152   if (!pid && (svstatus[17] == 'u')) outs(", want up");
    153   if (pid && (svstatus[17] == 'd')) outs(", want down");
    154   if (pid && svstatus[18]) outs(", got TERM");
    155   return(pid ? 1 : 2);
    156 }
    157 static int status(char *unused)
    158 {
    159   int rc;
    160 
    161   rc =svstatus_get();
    162   switch(rc) {
    163     case -1:
    164       if (lsb) done(4);
    165       __attribute__((fallthrough));
    166     case 0:
    167       return(0);
    168   }
    169   rc =svstatus_print(*service);
    170   islog =1;
    171   if (chdir("log") == -1) {
    172     if (errno != error_noent) {
    173       outs("; ");
    174       warn("unable to change directory");
    175     }
    176     else outs("\n");
    177   }
    178   else {
    179     outs("; ");
    180     if (svstatus_get()) { rc =svstatus_print("log"); outs("\n"); }
    181   }
    182   islog =0;
    183   flush("");
    184   if (lsb) switch(rc) {
    185     case 1:
    186       done(0);
    187       __attribute__((fallthrough));
    188     case 2:
    189       done(3);
    190       __attribute__((fallthrough));
    191     case 0: done(4);
    192   }
    193 
    194   return(rc);
    195 }
    196 
    197 static int checkscript()
    198 {
    199   char *prog[2];
    200   struct stat s;
    201   int pid, w;
    202 
    203   if (stat("check", &s) == -1) {
    204     if (errno == error_noent) return(1);
    205     outs2(WARN); outs2("unable to stat "); outs2(*service); outs2("/check: ");
    206     outs2(error_str(errno)); flush2("\n");
    207     return(0);
    208   }
    209   /* if (!(s.st_mode & S_IXUSR)) return(1); */
    210   if ((pid =fork()) == -1) {
    211     outs2(WARN); outs2("unable to fork for "); outs2(*service);
    212     outs2("/check: "); outs2(error_str(errno)); flush2("\n");
    213     return(0);
    214   }
    215   if (!pid) {
    216     prog[0] ="./check";
    217     prog[1] =0;
    218     close(1);
    219     execve("check", prog, environ);
    220     outs2(WARN); outs2("unable to run "); outs2(*service); outs2("/check: ");
    221     outs2(error_str(errno)); flush2("\n");
    222     _exit(0);
    223   }
    224   while (wait_pid(&w, pid) == -1) {
    225     if (errno == error_intr) continue;
    226     outs2(WARN); outs2("unable to wait for child "); outs2(*service);
    227     outs2("/check: "); outs2(error_str(errno)); flush2("\n");
    228     return(0);
    229   }
    230   return(!wait_exitcode(w));
    231 }
    232 
    233 static int check(char *a)
    234 {
    235   unsigned int pid;
    236 
    237   if ((r =svstatus_get()) == -1) return(-1);
    238   while (*a) {
    239     if (r == 0) { if (*a == 'x') return(1); return(-1); }
    240     pid =(unsigned char)svstatus[15];
    241     pid <<=8; pid +=(unsigned char)svstatus[14];
    242     pid <<=8; pid +=(unsigned char)svstatus[13];
    243     pid <<=8; pid +=(unsigned char)svstatus[12];
    244     switch (*a) {
    245     case 'x': return(0);
    246     case 'u':
    247       if (!pid || svstatus[19] != 1) return(0);
    248       if (!checkscript()) return(0);
    249       break;
    250     case 'd': if (pid || svstatus[19] != 0) return(0); break;
    251     case 'C': if (pid) if (!checkscript()) return(0); break;
    252     case 't':
    253     case 'k':
    254       if (!pid && svstatus[17] == 'd') break;
    255       tai_unpack(svstatus, &tstatus);
    256       if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript())
    257         return(0);
    258       break;
    259     case 'o':
    260       tai_unpack(svstatus, &tstatus);
    261       if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd'))
    262         return(0);
    263       break;
    264     case 'p': if (pid && !svstatus[16]) return(0); break;
    265     case 'c': if (pid && svstatus[16]) return(0); break;
    266     }
    267     ++a;
    268   }
    269   outs(OK); svstatus_print(*service); flush("\n");
    270   return(1);
    271 }
    272 static int control(char *a)
    273 {
    274   if (svstatus_get() <= 0) return(-1);
    275   if (svstatus[17] == *a)
    276     if (*a != 'd' || svstatus[18] == 1) return(0); /* once w/o term */
    277   if ((fd =open_write("supervise/control")) == -1) {
    278     if (errno != error_nodevice)
    279       warn("unable to open supervise/control");
    280     else
    281       *a == 'x' ? ok("runsv not running") : failx("runsv not running");
    282     return(-1);
    283   }
    284   r =write(fd, a, str_len(a));
    285   close(fd);
    286   if (r != str_len(a)) {
    287     warn("unable to write to supervise/control");
    288     return(-1);
    289   }
    290   return(1);
    291 }
    292 
    293 int main(int argc, char **argv)
    294 {
    295   unsigned int i, done;
    296   char *x;
    297 
    298   progname =*argv;
    299   for (i =str_len(*argv); i; --i) if ((*argv)[i -1] == '/') break;
    300   *argv +=i;
    301   optprogname =progname =*argv;
    302   service =argv;
    303   services =1;
    304   lsb =(str_diff(progname, "sv"));
    305   if ((x =env_get("SVDIR"))) varservice =x;
    306   if ((x =env_get("SVWAIT"))) scan_ulong(x, &wait);
    307   while ((i =getopt(argc, (const char* const*)argv, "w:vV")) != opteof) {
    308     switch(i) {
    309     case 'w':
    310       scan_ulong(optarg, &wait);
    311       __attribute__((fallthrough));
    312     case 'v': verbose =1; break;
    313     case 'V':
    314       strerr_warn1(VERSION, 0);
    315       __attribute__((fallthrough));
    316     case '?': usage();
    317     }
    318   }
    319   argv +=optind; argc -=optind;
    320   if (!(action =*argv++)) usage(); --argc;
    321   if (!lsb) { service =argv; services =argc; }
    322   if (!*service) usage();
    323 
    324   taia_now(&tnow); tstart =tnow;
    325   if ((curdir =open_read(".")) == -1)
    326     fatal("unable to open current directory");
    327 
    328   act =&control; acts ="s";
    329   if (verbose) cbk =&check;
    330   switch (*action) {
    331   case 'x': case 'e':
    332     acts ="x"; break;
    333   case 'X': case 'E':
    334     acts ="x"; kll =1; cbk =&check; break;
    335   case 'D':
    336     acts ="d"; kll =1; cbk =&check; break;
    337   case 'T':
    338     acts ="tc"; kll =1; cbk =&check; break;
    339   case 't':
    340     if (!str_diff(action, "try-restart")) { acts ="tc"; cbk =&check; break; }
    341     __attribute__((fallthrough));
    342   case 'c':
    343     if (!str_diff(action, "check")) { act =0; acts ="C"; cbk =&check; break; }
    344     __attribute__((fallthrough));
    345   case 'u': case 'd': case 'o': case 'p': case 'h':
    346   case 'a': case 'i': case 'k': case 'q': case '1': case '2':
    347     action[1] =0; acts =action; break;
    348   case 's':
    349     if (!str_diff(action, "shutdown")) { acts ="x"; cbk =&check; break; }
    350     if (!str_diff(action, "start")) { acts ="u"; cbk =&check; break; }
    351     if (!str_diff(action, "stop")) { acts ="d"; cbk =&check; break; }
    352     if (lsb && str_diff(action, "status")) usage();
    353     act =&status; cbk =0; break;
    354   case 'r':
    355     if (!str_diff(action, "restart")) { acts ="tcu"; cbk =&check; break; }
    356     if (!str_diff(action, "reload")) { acts ="h"; cbk =&check; break; }
    357     usage();
    358     __attribute__((fallthrough));
    359   case 'f':
    360     if (!str_diff(action, "force-reload"))
    361       { acts ="tc"; kll =1; cbk =&check; break; }
    362     if (!str_diff(action, "force-restart"))
    363       { acts ="tcu"; kll =1; cbk =&check; break; }
    364     if (!str_diff(action, "force-shutdown"))
    365       { acts ="x"; kll =1; cbk =&check; break; }
    366     if (!str_diff(action, "force-stop"))
    367       { acts ="d"; kll =1; cbk =&check; break; }
    368     __attribute__((fallthrough));
    369   default:
    370     usage();
    371   }
    372 
    373   servicex =service;
    374   for (i =0; i < services; ++i) {
    375     if ((**service != '/') && (**service != '.') && **service &&
    376         ((*service)[str_len(*service) -1] != '/')) {
    377       if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {
    378         fail("unable to change to service directory");
    379         *service =0;
    380       }
    381     }
    382     else
    383       if (chdir(*service) == -1) {
    384         fail("unable to change to service directory");
    385         *service =0;
    386       }
    387     if (*service) if (act && (act(acts) == -1)) *service =0;
    388     if (fchdir(curdir) == -1) fatal("unable to change to original directory");
    389     service++;
    390   }
    391 
    392   if (*cbk)
    393     for (;;) {
    394       taia_sub(&tdiff, &tnow, &tstart);
    395       service =servicex; done =1;
    396       for (i =0; i < services; ++i, ++service) {
    397         if (!*service) continue;
    398         if ((**service != '/') && (**service != '.')) {
    399           if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {
    400             fail("unable to change to service directory");
    401             *service =0;
    402           }
    403         }
    404         else
    405           if (chdir(*service) == -1) {
    406             fail("unable to change to service directory");
    407             *service =0;
    408           }
    409         if (*service) { if (cbk(acts) != 0) *service =0; else done =0; }
    410         if (*service && taia_approx(&tdiff) > wait) {
    411           kll ? outs(KILL) : outs(TIMEOUT);
    412           if (svstatus_get() > 0) { svstatus_print(*service); ++rc; }
    413           flush("\n");
    414           if (kll) control("k");
    415           *service =0;
    416         }
    417         if (fchdir(curdir) == -1)
    418           fatal("unable to change to original directory");
    419       }
    420       if (done) break;
    421       usleep(420000);
    422       taia_now(&tnow);
    423     }
    424   return(rc > 99 ? 99 : rc);
    425 }