runit

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

runsvdir.c (6782B)


      1 #include <sys/types.h>
      2 #include <sys/stat.h>
      3 #include <unistd.h>
      4 #include <signal.h>
      5 #include <sys/types.h>
      6 #include <dirent.h>
      7 #include <poll.h>
      8 #include <stdint.h>
      9 #include "iopause.h"
     10 #include "strerr.h"
     11 #include "error.h"
     12 #include "wait.h"
     13 #include <sys/wait.h>
     14 #include "env.h"
     15 #include "open.h"
     16 #include "pathexec.h"
     17 #include "fd.h"
     18 #include "str.h"
     19 #include "coe.h"
     20 #include "sig.h"
     21 #include "ndelay.h"
     22 #include "taia.h"
     23 
     24 #define USAGE " [-P] dir"
     25 
     26 #define MAXSERVICES 1000
     27 
     28 static char *progname;
     29 static char *svdir;
     30 static unsigned long dev =0;
     31 static unsigned long ino =0;
     32 static struct {
     33   unsigned long dev;
     34   unsigned long ino;
     35   int pid;
     36   int isgone;
     37 } sv[MAXSERVICES];
     38 static int svnum =0;
     39 static int check =1;
     40 static char *rplog =0;
     41 static int rploglen;
     42 static int logpipe[2];
     43 static struct pollfd io[1];
     44 static struct taia stamplog;
     45 static int exitsoon =0;
     46 static int pgrp =0;
     47 
     48 static void usage ( void ) { strerr_die4x(1, "usage: ", progname, USAGE, "\n"); }
     49 static void fatal(char *m1, char *m2) {
     50   strerr_die6sys(100, "runsvdir ", svdir, ": fatal: ", m1, m2, ": ");
     51 }
     52 static void warn(char *m1, char *m2) {
     53   strerr_warn6("runsvdir ", svdir, ": warning: ", m1, m2, ": ", &strerr_sys);
     54 }
     55 static void warn3x(char *m1, char *m2, char *m3) {
     56   strerr_warn6("runsvdir ", svdir, ": warning: ", m1, m2, m3, 0);
     57 } 
     58 static void s_term() { exitsoon =1; }
     59 static void s_hangup() { exitsoon =2; }
     60 
     61 static void runsv(int no, char *name)
     62 {
     63   int pid;
     64 
     65   if ((pid =fork()) == -1) {
     66     warn("unable to fork for ", name);
     67     return;
     68   }
     69   if (pid == 0) {
     70     /* child */
     71     const char *prog[3];
     72 
     73     prog[0] ="runsv";
     74     prog[1] =name;
     75     prog[2] =0;
     76     sig_uncatch(sig_hangup);
     77     sig_uncatch(sig_term);
     78     if (pgrp) setsid();
     79     pathexec_run(*prog, prog, (const char* const*)environ);
     80     fatal("unable to start runsv ", name);
     81   }
     82   sv[no].pid =pid;
     83 }
     84 
     85 static void runsvdir( void )
     86 {
     87   DIR *dir;
     88   struct dirent *d;
     89   int i;
     90   struct stat s;
     91 
     92   if (! (dir =opendir("."))) {
     93     warn("unable to open directory ", svdir);
     94     return;
     95   }
     96   for (i =0; i < svnum; i++) sv[i].isgone =1;
     97   errno =0;
     98   while ((d =readdir(dir))) {
     99     if (d->d_name[0] == '.') continue;
    100     if (stat(d->d_name, &s) == -1) {
    101       warn("unable to stat ", d->d_name);
    102       errno =0;
    103       continue;
    104     }
    105     if (! S_ISDIR(s.st_mode)) continue;
    106     for (i =0; i < svnum; i++) {
    107       if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
    108         sv[i].isgone =0;
    109         if (! sv[i].pid) runsv(i, d->d_name);
    110         break;
    111       }
    112     }
    113     if (i == svnum) {
    114       /* new service */
    115       if (svnum >= MAXSERVICES) {
    116         warn3x("unable to start runsv ", d->d_name, ": too many services.");
    117         continue;
    118       }
    119       sv[i].ino =s.st_ino;
    120       sv[i].dev =s.st_dev;
    121       sv[i].pid =0;
    122       sv[i].isgone =0;
    123       svnum++;
    124       runsv(i, d->d_name);
    125       check =1;
    126     }
    127   }
    128   if (errno) {
    129     warn("unable to read directory ", svdir);
    130     closedir(dir);
    131     check =1;
    132     return;
    133   }
    134   closedir(dir);
    135 
    136   /* SIGTERM removed runsv's */
    137   for (i =0; i < svnum; i++) {
    138     if (! sv[i].isgone) continue;
    139     if (sv[i].pid) kill(sv[i].pid, SIGTERM);
    140     sv[i] =sv[--svnum];
    141     check =1;
    142   }
    143 }
    144 
    145 static int setup_log( void )
    146 {
    147   if ((rploglen =str_len(rplog)) < 7) {
    148     warn3x("log must have at least seven characters.", 0, 0);
    149     return(0);
    150   }
    151   if (pipe(logpipe) == -1) {
    152     warn3x("unable to create pipe for log.", 0, 0);
    153     return(-1);
    154   }
    155   coe(logpipe[1]);
    156   coe(logpipe[0]);
    157   ndelay_on(logpipe[0]);
    158   ndelay_on(logpipe[1]);
    159   if (fd_copy(2, logpipe[1]) == -1) {
    160     warn3x("unable to set filedescriptor for log.", 0, 0);
    161     return(-1);
    162   }
    163   io[0].fd =logpipe[0];
    164   io[0].events = POLLIN;
    165   taia_now(&stamplog);
    166   return(1);
    167 }
    168 
    169 int main(int argc, char **argv)
    170 {
    171   struct stat s;
    172   time_t mtime =0;
    173   int wstat;
    174   int curdir;
    175   int pid;
    176   struct taia deadline;
    177   struct taia now;
    178   struct taia stampcheck;
    179   char ch;
    180   int i;
    181 
    182   progname =*argv++;
    183   if (! argv || ! *argv) usage();
    184   if (**argv == '-') {
    185     switch (*(*argv +1)) {
    186     case 'P':
    187       pgrp =1;
    188       __attribute__((fallthrough));
    189     case '-': ++argv;
    190     }
    191     if (! argv || ! *argv) usage();
    192   }
    193 
    194   sig_catch(sig_term, s_term);
    195   sig_catch(sig_hangup, s_hangup);
    196   svdir =*argv++;
    197   if (argv && *argv) {
    198     rplog =*argv;
    199     if (setup_log() != 1) {
    200       rplog =0;
    201       warn3x("log service disabled.", 0, 0);
    202     }
    203   }
    204   if ((curdir =open_read(".")) == -1) 
    205     fatal("unable to open current directory", 0);
    206   coe(curdir);
    207 
    208   taia_now(&stampcheck);
    209 
    210   for (;;) {
    211     /* collect children */
    212     for (;;) {
    213       if ((pid =waitpid( -1, &wstat, WNOHANG )) <= 0) break;
    214       for (i =0; i < svnum; i++) {
    215         if (pid == sv[i].pid) {
    216           /* runsv has gone */
    217           sv[i].pid =0;
    218           check =1;
    219           break;
    220         }
    221       }
    222     }
    223 
    224     taia_now(&now);
    225     if (now.sec.x < (stampcheck.sec.x -3)) {
    226       /* time warp */
    227       warn3x("time warp: resetting time stamp.", 0, 0);
    228       taia_now(&stampcheck);
    229       taia_now(&now);
    230       if (rplog) taia_now(&stamplog);
    231     }
    232     if (taia_less(&now, &stampcheck) == 0) {
    233       /* wait at least a second */
    234       taia_uint(&deadline, 1);
    235       taia_add(&stampcheck, &now, &deadline);
    236       
    237       if (stat(svdir, &s) != -1) {
    238         if (check || \
    239             s.st_mtime != mtime || s.st_ino != ino || s.st_dev != dev) {
    240           /* svdir modified */
    241           if (chdir(svdir) != -1) {
    242             mtime =s.st_mtime;
    243             dev =s.st_dev;
    244             ino =s.st_ino;
    245             check =0;
    246             if (now.sec.x <= (4611686018427387914ULL +(uint64_t)mtime))
    247               sleep(1);
    248             runsvdir();
    249             while (fchdir(curdir) == -1) {
    250               warn("unable to change directory, pausing", 0);
    251               sleep(5);
    252             }
    253           }
    254           else
    255             warn("unable to change directory to ", svdir);
    256         }
    257       }
    258       else
    259         warn("unable to stat ", svdir);
    260     }
    261 
    262     if (rplog)
    263       if (taia_less(&now, &stamplog) == 0) {
    264         write(logpipe[1], ".", 1);
    265         taia_uint(&deadline, 900);
    266         taia_add(&stamplog, &now, &deadline);
    267       }
    268     taia_uint(&deadline, check ? 1 : 5);
    269     taia_add(&deadline, &now, &deadline);
    270 
    271     sig_block(sig_child);
    272     if (rplog)
    273       iopause(io, 1, &deadline, &now);
    274     else
    275       iopause(0, 0, &deadline, &now);
    276     sig_unblock(sig_child);
    277 
    278     if (rplog && (io[0].revents | POLLIN))
    279       while (read(logpipe[0], &ch, 1) > 0)
    280         if (ch) {
    281           for (i =6; i < rploglen; i++)
    282             rplog[i -1] =rplog[i];
    283           rplog[rploglen -1] =ch;
    284         }
    285 
    286     switch(exitsoon) {
    287     case 1:
    288       _exit(0);
    289     case 2:
    290       for (i =0; i < svnum; i++) if (sv[i].pid) kill(sv[i].pid, SIGTERM);
    291       _exit(111);
    292     }
    293   }
    294 }