s6

Mirror/fork of https://skarnet.org/software/s6/
git clone https://ccx.te2000.cz/git/s6
Log | Files | Refs | README | LICENSE

s6-supervise.c (19496B)


      1 /* ISC license. */
      2 
      3 /* For SIGWINCH */
      4 #include <skalibs/nonposix.h>
      5 
      6 #include <stdint.h>
      7 #include <unistd.h>
      8 #include <string.h>
      9 #include <strings.h>
     10 #include <errno.h>
     11 #include <limits.h>
     12 #include <signal.h>
     13 #include <fcntl.h>
     14 #include <sys/resource.h>
     15 #include <sys/stat.h>
     16 #include <sys/wait.h>
     17 
     18 #include <skalibs/posixplz.h>
     19 #include <skalibs/allreadwrite.h>
     20 #include <skalibs/bytestr.h>
     21 #include <skalibs/types.h>
     22 #include <skalibs/strerr.h>
     23 #include <skalibs/tai.h>
     24 #include <skalibs/iopause.h>
     25 #include <skalibs/cspawn.h>
     26 #include <skalibs/djbunix.h>
     27 #include <skalibs/sig.h>
     28 #include <skalibs/selfpipe.h>
     29 #include <skalibs/skamisc.h>
     30 
     31 #include <s6/config.h>
     32 #include <s6/ftrigw.h>
     33 #include <s6/supervise.h>
     34 
     35 #define USAGE "s6-supervise dir"
     36 #define CTL S6_SUPERVISE_CTLDIR "/control"
     37 #define LCK S6_SUPERVISE_CTLDIR "/lock"
     38 #define SLCK S6_SUPERVISE_CTLDIR "/service-lock"
     39 
     40 #define S6_PATH_MAX 512
     41 
     42 typedef enum trans_e trans_t, *trans_t_ref ;
     43 enum trans_e
     44 {
     45   V_TIMEOUT, V_CHLD, V_TERM, V_HUP, V_QUIT, V_INT,
     46   V_a, V_b, V_q, V_h, V_k, V_t, V_i, V_1, V_2, V_p, V_c, V_y, V_r,
     47   V_o, V_d, V_u, V_D, V_U, V_x, V_O, V_Q
     48 } ;
     49 
     50 typedef enum state_e state_t, *state_t_ref ;
     51 enum state_e
     52 {
     53   DOWN,
     54   UP,
     55   FINISH,
     56   LASTUP,
     57   LASTFINISH
     58 } ;
     59 
     60 struct gflags_s
     61 {
     62   uint8_t cont : 1 ;
     63   uint8_t dying : 1 ;
     64 } gflags =
     65 {
     66   .cont = 1,
     67   .dying = 0
     68 } ;
     69 
     70 typedef void action_t (void) ;
     71 typedef action_t *action_t_ref ;
     72 
     73 static tain deadline ;
     74 static tain nextstart = TAIN_ZERO ;
     75 static s6_svstatus_t status = S6_SVSTATUS_ZERO ;
     76 static state_t state = DOWN ;
     77 static int notifyfd = -1 ;
     78 static char const *servicename = 0 ;
     79 static rlim_t maxfd ;
     80 
     81 static inline void settimeout (int secs)
     82 {
     83   tain_addsec_g(&deadline, secs) ;
     84 }
     85 
     86 static inline void settimeout_infinite (void)
     87 {
     88   tain_add_g(&deadline, &tain_infinite_relative) ;
     89 }
     90 
     91 static inline void announce (void)
     92 {
     93   if (!s6_svstatus_write(".", &status))
     94     strerr_warnwu1sys("write status file") ;
     95 }
     96 
     97 static int read_file (char const *file, char *buf, size_t n)
     98 {
     99   ssize_t r = openreadnclose_nb(file, buf, n) ;
    100   if (r == -1)
    101   {
    102     if (errno != ENOENT) strerr_warnwu2sys("open ", file) ;
    103     return 0 ;
    104   }
    105   buf[byte_chr(buf, r, '\n')] = 0 ;
    106   return 1 ;
    107 }
    108 
    109 static int read_uint (char const *file, unsigned int *fd)
    110 {
    111   char buf[UINT_FMT + 1] ;
    112   if (!read_file(file, buf, UINT_FMT)) return 0 ;
    113   if (!uint0_scan(buf, fd))
    114   {
    115     strerr_warnw2x("invalid ", file) ;
    116     return 0 ;
    117   }
    118   return 1 ;
    119 }
    120 
    121 static inline int read_downsig (void)
    122 {
    123   int sig = SIGTERM ;
    124   char buf[16] ;
    125   if (read_file("down-signal", buf, 15) && !sig0_scan(buf, &sig))
    126     strerr_warnw1x("invalid down-signal") ;
    127   return sig ;
    128 }
    129 
    130 static void set_down_and_ready (char const *s, unsigned int n)
    131 {
    132   status.pid = 0 ;
    133   status.flagfinishing = 0 ;
    134   status.flagready = 1 ;
    135   state = DOWN ;
    136   if (tai_sec(tain_secp(&nextstart))) deadline = nextstart ;
    137   else tain_addsec_g(&deadline, 1) ;
    138   tain_wallclock_read(&status.readystamp) ;
    139   announce() ;
    140   ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, s, n) ;
    141 }
    142 
    143 
    144 /* The action array. */
    145 
    146 static void nop (void)
    147 {
    148 }
    149 
    150 static void bail (void)
    151 {
    152   gflags.cont = 0 ;
    153 }
    154 
    155 static void sigint (void)
    156 {
    157   pid_t pgid = getpgid(status.pid) ;
    158   if (pgid == -1) strerr_warnwu1sys("getpgid") ;
    159   else killpg(pgid, SIGINT) ;
    160   bail() ;
    161 }
    162 
    163 static void closethem (void)
    164 {
    165   fd_close(0) ;
    166   fd_close(1) ;
    167   if (open_readb("/dev/null"))
    168     strerr_warnwu2sys("open /dev/null for ", "reading") ;
    169   else if (open_write("/dev/null") != 1 || ndelay_off(1) < 0)
    170       strerr_warnwu2sys("open /dev/null for ", "writing") ;
    171 }
    172 
    173 static void adddown (void)
    174 {
    175   if (!openwritenclose_unsafe("down", "", 0))
    176     strerr_warnwu2sys("create ", "./down file") ;
    177 }
    178 
    179 static void deldown (void)
    180 {
    181   int e = errno ;
    182   if (unlink("down") == -1 && errno != ENOENT)
    183     strerr_warnwu2sys("unlink ", "./down file") ;
    184   errno = e ;
    185 }
    186 
    187 static void killa (void)
    188 {
    189   kill(status.pid, SIGALRM) ;
    190 }
    191 
    192 static void killb (void)
    193 {
    194   kill(status.pid, SIGABRT) ;
    195 }
    196 
    197 static void killh (void)
    198 {
    199   kill(status.pid, SIGHUP) ;
    200 }
    201 
    202 static void killq (void)
    203 {
    204   kill(status.pid, SIGQUIT) ;
    205 }
    206 
    207 static void killk (void)
    208 {
    209   kill(status.pid, SIGKILL) ;
    210 }
    211 
    212 static void killt (void)
    213 {
    214   kill(status.pid, SIGTERM) ;
    215 }
    216 
    217 static void killi (void)
    218 {
    219   kill(status.pid, SIGINT) ;
    220 }
    221 
    222 static void kill1 (void)
    223 {
    224   kill(status.pid, SIGUSR1) ;
    225 }
    226 
    227 static void kill2 (void)
    228 {
    229   kill(status.pid, SIGUSR2) ;
    230 }
    231 
    232 static void killp (void)
    233 {
    234   kill(status.pid, SIGSTOP) ;
    235   status.flagpaused = 1 ;
    236   announce() ;
    237 }
    238 
    239 static void killc (void)
    240 {
    241   kill(status.pid, SIGCONT) ;
    242   status.flagpaused = 0 ;
    243   announce() ;
    244 }
    245 
    246 static void killy (void)
    247 {
    248   kill(status.pid, SIGWINCH) ;
    249 }
    250 
    251 static void killr (void)
    252 {
    253   kill(status.pid, read_downsig()) ;
    254 }
    255 
    256 static void trystart (void)
    257 {
    258   cspawn_fileaction fa[2] =
    259   {
    260     [0] = { .type = CSPAWN_FA_CLOSE },
    261     [1] = { .type = CSPAWN_FA_MOVE },
    262   } ;
    263   char lkfmt[UINT_FMT] ;
    264   char const *cargv[7] = { S6_BINPREFIX "s6-setlock", "-d", lkfmt, "--", "./run", servicename, 0 } ;
    265   size_t orig = 4 ;
    266   int notifyp[2] = { -1, -1 } ;
    267   unsigned int lk = 0, notif = 0 ;
    268 
    269   if (read_uint("lock-fd", &lk))
    270   {
    271     if (lk > maxfd) strerr_warnw2x("invalid ", "lock-fd") ;
    272     else
    273     {
    274       struct stat st ;
    275       int islocked ;
    276       int lfd = open_write(SLCK) ;
    277       if (lfd == -1)
    278       {
    279         settimeout(60) ;
    280         strerr_warnwu4sys("open ", SLCK, " for writing", " (waiting 60 seconds)") ;
    281         goto errn ;
    282       }
    283       if (fstat(lfd, &st) == -1)
    284       {
    285         settimeout(60) ;
    286         strerr_warnwu3sys("stat ", SLCK, " (waiting 60 seconds)") ;
    287         fd_close(lfd) ;
    288         return ;
    289       }
    290       if (st.st_size)
    291       {
    292         ftruncate(lfd, 0) ;
    293         strerr_warnw1x("a previous instance of the service wrote to the lock file!") ;
    294       }
    295       islocked = fd_islocked(lfd) ;
    296       if (islocked == -1)
    297       {
    298         settimeout(60) ;
    299         strerr_warnwu3sys("read lock state on ", SLCK, " (waiting 60 seconds)") ;
    300         fd_close(lfd) ;
    301         return ;
    302       }
    303       if (islocked)
    304         strerr_warnw1x("another instance of the service is already running, child will block") ;
    305       fd_close(lfd) ;
    306       lkfmt[uint_fmt(lkfmt, lk)] = 0 ;
    307       orig = 0 ;
    308     }
    309   }
    310 
    311   if (read_uint("notification-fd", &notif))
    312   {
    313     if (notif > maxfd) strerr_warnw2x("invalid ", "notification-fd") ;
    314     if (!orig && notif == lk)
    315     {
    316       settimeout_infinite() ;
    317       strerr_warnwu1x("start service: notification-fd and lock-fd are the same") ;
    318       return ;
    319     }
    320     if (pipe(notifyp) == -1)
    321     {
    322       settimeout(60) ;
    323       strerr_warnwu2sys("create notification pipe", " (waiting 60 seconds)") ;
    324       return ;
    325     }
    326     fa[0].x.fd = notifyp[0] ;
    327     fa[1].x.fd2[0] = notif ;
    328     fa[1].x.fd2[1] = notifyp[1] ;
    329   }
    330 
    331   uint16_t cspawn_flags = CSPAWN_FLAGS_SELFPIPE_FINISH | CSPAWN_FLAGS_SETSID;
    332   if (access("clone-newpid", F_OK) == 0)
    333   {
    334     cspawn_flags |= CSPAWN_FLAGS_LINUX_NEWPID;
    335   }
    336   else if(errno != ENOENT) {
    337     strerr_warnwu1sys("check for clone-newpid file") ;
    338     return ;
    339     /* TODO: Close stuff from above? Exit instead? */
    340   }
    341 
    342   status.pid = cspawn(cargv[orig], cargv + orig, (char const *const *)environ, cspawn_flags, fa, notifyp[1] >= 0 ? 2 : 0) ;
    343   if (!status.pid)
    344   {
    345     settimeout(60) ;
    346     strerr_warnwu3sys("spawn ", cargv[orig], " (waiting 60 seconds)") ;
    347     goto errn ;
    348   }
    349 
    350   if (notifyp[1] >= 0)
    351   {
    352     fd_close(notifyp[1]) ;
    353     notifyfd = notifyp[0] ;
    354   }
    355   settimeout_infinite() ;
    356   nextstart = tain_zero ;
    357   state = UP ;
    358   status.flagready = 0 ;
    359   tain_wallclock_read(&status.stamp) ;
    360   announce() ;
    361   ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "u", 1) ;
    362   return ;
    363 
    364  errn:
    365   if (notifyp[1] >= 0)
    366   {
    367     fd_close(notifyp[1]) ;
    368     fd_close(notifyp[0]) ;
    369   }
    370 }
    371 
    372 static void wantdown (void)
    373 {
    374   status.flagwantup = 0 ;
    375   announce() ;
    376 }
    377 
    378 static void wantup (void)
    379 {
    380   status.flagwantup = 1 ;
    381   announce() ;
    382 }
    383 
    384 static void wantDOWN (void)
    385 {
    386   adddown() ;
    387   wantdown() ;
    388 }
    389 
    390 static void wantUP (void)
    391 {
    392   deldown() ;
    393   wantup() ;
    394 }
    395 
    396 static void downtimeout (void)
    397 {
    398   if (status.flagwantup) trystart() ;
    399   else settimeout_infinite() ;
    400 }
    401 
    402 static void down_o (void)
    403 {
    404   wantdown() ;
    405   trystart() ;
    406 }
    407 
    408 static void down_u (void)
    409 {
    410   wantup() ;
    411   trystart() ;
    412 }
    413 
    414 static void down_U (void)
    415 {
    416   wantUP() ;
    417   trystart() ;
    418 }
    419 
    420 static int uplastup_z (void)
    421 {
    422   unsigned int n ;
    423   char fmt0[UINT_FMT] ;
    424   char fmt1[UINT_FMT] ;
    425   char const *cargv[5] = { "finish", fmt0, fmt1, servicename, 0 } ;
    426 
    427   status.wstat = (int)status.pid ;
    428   status.flagpaused = 0 ;
    429   status.flagready = 0 ;
    430   gflags.dying = 0 ;
    431   tain_wallclock_read(&status.stamp) ;
    432   if (notifyfd >= 0)
    433   {
    434     fd_close(notifyfd) ;
    435     notifyfd = -1 ;
    436   }
    437   fmt0[uint_fmt(fmt0, WIFSIGNALED(status.wstat) ? 256 : WEXITSTATUS(status.wstat))] = 0 ;
    438   fmt1[uint_fmt(fmt1, WTERMSIG(status.wstat))] = 0 ;
    439 
    440   if (!read_uint("max-death-tally", &n)) n = 100 ;
    441   if (n > S6_MAX_DEATH_TALLY) n = S6_MAX_DEATH_TALLY ;
    442   if (n)
    443   {
    444     s6_dtally_t tab[n+1] ;
    445     ssize_t m = s6_dtally_read(".", tab, n) ;
    446     if (m < 0) strerr_warnwu2sys("read ", S6_DTALLY_FILENAME) ;
    447     else
    448     {
    449       tab[m].stamp = status.stamp ;
    450       tab[m].sig = WIFSIGNALED(status.wstat) ? WTERMSIG(status.wstat) : 0 ;
    451       tab[m].exitcode = WIFSIGNALED(status.wstat) ? 128 + WTERMSIG(status.wstat) : WEXITSTATUS(status.wstat) ;
    452       if (!(m >= n ? s6_dtally_write(".", tab+1, n) : s6_dtally_write(".", tab, m+1)))
    453         strerr_warnwu2sys("write ", S6_DTALLY_FILENAME) ;
    454     }
    455   }
    456 
    457   status.pid = cspawn("./finish", cargv, (char const *const *)environ, CSPAWN_FLAGS_SELFPIPE_FINISH | CSPAWN_FLAGS_SETSID, 0, 0) ;
    458   if (!status.pid)
    459   {
    460     if (errno != ENOENT) strerr_warnwu2sys("spawn ", "./finish") ;
    461     set_down_and_ready("dD", 2) ;
    462     return 0 ;
    463   }
    464   {
    465     tain tto ;
    466     unsigned int timeout ;
    467     if (!read_uint("timeout-finish", &timeout)) timeout = 5000 ;
    468     if (timeout && tain_from_millisecs(&tto, timeout))
    469       tain_add_g(&deadline, &tto) ;
    470     else settimeout_infinite() ;
    471   }
    472   status.flagfinishing = 1 ;
    473   announce() ;
    474   ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "d", 1) ;
    475   return 1 ;
    476 }
    477 
    478 static void up_z (void)
    479 {
    480   if (uplastup_z()) state = FINISH ;
    481 }
    482 
    483 static void lastup_z (void)
    484 {
    485   if (uplastup_z()) state = LASTFINISH ;
    486   else bail() ;
    487 }
    488 
    489 static void uptimeout (void)
    490 {
    491   if (gflags.dying)
    492   {
    493     killk() ;
    494     settimeout(5) ;
    495   }
    496   else
    497   {
    498     settimeout_infinite() ;
    499     strerr_warnw1x("can't happen: timeout while the service is up!") ;
    500   }
    501 }
    502 
    503 static void up_d (void)
    504 {
    505   tain tto ;
    506   unsigned int timeout ;
    507   status.flagwantup = 0 ;
    508   killr() ;
    509   killc() ;
    510   if (!read_uint("timeout-kill", &timeout)) timeout = 0 ;
    511   if (timeout && tain_from_millisecs(&tto, timeout))
    512   {
    513     tain_add_g(&deadline, &tto) ;
    514     gflags.dying = 1 ;
    515   }
    516   else settimeout_infinite() ;
    517 }
    518 
    519 static void up_D (void)
    520 {
    521   adddown() ;
    522   up_d() ;
    523 }
    524 
    525 static void up_x (void)
    526 {
    527   state = LASTUP ;
    528   closethem() ;
    529 }
    530 
    531 static void up_term (void)
    532 {
    533   state = LASTUP ;
    534   up_d() ;
    535 }
    536 
    537 static void finishtimeout (void)
    538 {
    539   strerr_warnw1x("finish script lifetime reached maximum value - sending it a SIGKILL") ;
    540   killc() ; killk() ;
    541   settimeout(5) ;
    542 }
    543 
    544 static void finish_z (void)
    545 {
    546   int wstat = (int)status.pid ;
    547   if (WIFEXITED(wstat) && WEXITSTATUS(wstat) == 125)
    548   {
    549     status.flagwantup = 0 ;
    550     set_down_and_ready("OD", 2) ;
    551   }
    552   else set_down_and_ready("D", 1) ;
    553 }
    554 
    555 static void finish_x (void)
    556 {
    557   state = LASTFINISH ;
    558   closethem() ;
    559 }
    560 
    561 static void lastfinish_z (void)
    562 {
    563   finish_z() ;
    564   bail() ;
    565 }
    566 
    567 static action_t_ref const actions[5][27] =
    568 {
    569   { &downtimeout, &nop, &bail, &bail, &bail, &bail,
    570     &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop,
    571     &down_o, &wantdown, &down_u, &wantDOWN, &down_U, &bail, &wantdown, &wantDOWN },
    572   { &uptimeout, &up_z, &up_term, &up_x, &bail, &sigint,
    573     &killa, &killb, &killq, &killh, &killk, &killt, &killi, &kill1, &kill2, &killp, &killc, &killy, &killr,
    574     &wantdown, &up_d, &wantup, &up_D, &wantUP, &up_x, &wantdown, &wantDOWN },
    575   { &finishtimeout, &finish_z, &finish_x, &finish_x, &bail, &sigint,
    576     &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop,
    577     &wantdown, &wantdown, &wantup, &wantDOWN, &wantUP, &finish_x, &wantdown, &wantDOWN },
    578   { &uptimeout, &lastup_z, &up_d, &closethem, &bail, &sigint,
    579     &killa, &killb, &killq, &killh, &killk, &killt, &killi, &kill1, &kill2, &killp, &killc, &killy, &killr,
    580     &wantdown, &up_d, &wantup, &up_D, &wantUP, &closethem, &wantdown, &wantDOWN },
    581   { &finishtimeout, &lastfinish_z, &nop, &closethem, &bail, &sigint,
    582     &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop,
    583     &wantdown, &wantdown, &wantup, &wantDOWN, &wantUP, &closethem, &wantdown, &wantDOWN }
    584 } ;
    585 
    586 
    587 
    588 /* The main loop.
    589    It just loops around the iopause(), calling snippets of code in "actions" when needed. */
    590 
    591 
    592 static inline void handle_notifyfd (void)
    593 {
    594   char buf[512] ;
    595   ssize_t r = 1 ;
    596   while (r > 0)
    597   {
    598     r = sanitize_read(fd_read(notifyfd, buf, 512)) ;
    599     if (r > 0 && memchr(buf, '\n', r))
    600     {
    601       tain_addsec_g(&nextstart, 1) ;
    602       tain_wallclock_read(&status.readystamp) ;
    603       status.flagready = 1 ;
    604       announce() ;
    605       ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "U", 1) ;
    606       r = -1 ;
    607     }
    608     if (r < 0)
    609     {
    610       fd_close(notifyfd) ;
    611       notifyfd = -1 ;
    612     }
    613   }
    614 }
    615 
    616 static inline void handle_signals (void)
    617 {
    618   for (;;)
    619   {
    620     switch (selfpipe_read())
    621     {
    622       case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
    623       case 0 : return ;
    624       case SIGCHLD :
    625         if (!status.pid) wait_reap() ;
    626         else
    627         {
    628           int wstat ;
    629           int r = wait_pid_nohang(status.pid, &wstat) ;
    630           if (r < 0)
    631             if (errno != ECHILD) strerr_diefu1sys(111, "wait_pid_nohang") ;
    632             else break ;
    633           else if (!r) break ;
    634           status.pid = (pid_t)wstat ; /* don't overwrite status.wstat if it's ./finish */
    635           (*actions[state][V_CHLD])() ;
    636         }
    637         break ;
    638       case SIGTERM :
    639         (*actions[state][V_TERM])() ;
    640         break ;
    641       case SIGHUP :
    642         (*actions[state][V_HUP])() ;
    643         break ;
    644       case SIGQUIT :
    645         (*actions[state][V_QUIT])() ;
    646         break ;
    647       case SIGINT :
    648         (*actions[state][V_INT])() ;
    649         break ;
    650       default :
    651         strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ;
    652     }
    653   }
    654 }
    655 
    656 static inline void handle_control (int fd)
    657 {
    658   for (;;)
    659   {
    660     char c ;
    661     ssize_t r = sanitize_read(fd_read(fd, &c, 1)) ;
    662     if (r < 0) strerr_diefu1sys(111, "read " S6_SUPERVISE_CTLDIR "/control") ;
    663     else if (!r) break ;
    664     else
    665     {
    666       size_t pos = byte_chr("abqhkti12pcyroduDUxOQ", 21, c) ;
    667       if (pos < 21) (*actions[state][V_a + pos])() ;
    668     }
    669   }
    670 }
    671 
    672 static int trymkdir (char const *s)
    673 {
    674   char buf[S6_PATH_MAX] ;
    675   ssize_t r ;
    676   if (mkdir(s, 0700) >= 0) return 1 ;
    677   if (errno != EEXIST) strerr_diefu2sys(111, "mkdir ", s) ;
    678   r = readlink(s, buf, S6_PATH_MAX) ;
    679   if (r < 0)
    680   {
    681     struct stat st ;
    682     if (errno != EINVAL)
    683     {
    684       errno = EEXIST ;
    685       strerr_diefu2sys(111, "mkdir ", s) ;
    686     }
    687     if (stat(s, &st) < 0)
    688       strerr_diefu2sys(111, "stat ", s) ;
    689     if (!S_ISDIR(st.st_mode))
    690       strerr_dief2x(100, s, " exists and is not a directory") ;
    691     return 0 ;
    692   }
    693   else if (r == S6_PATH_MAX)
    694   {
    695     errno = ENAMETOOLONG ;
    696     strerr_diefu2sys(111, "readlink ", s) ;
    697   }
    698   else
    699   {
    700     buf[r] = 0 ;
    701     if (mkdir(buf, 0700) < 0)
    702       strerr_diefu2sys(111, "mkdir ", buf) ;
    703     return 1 ;
    704   }
    705 }
    706 
    707 static inline int control_init (void)
    708 {
    709   mode_t m = umask(0) ;
    710   int fdctl, fdlck, r ;
    711   if (trymkdir(S6_SUPERVISE_EVENTDIR))
    712   {
    713     if (chown(S6_SUPERVISE_EVENTDIR, -1, getegid()) < 0)
    714       strerr_diefu1sys(111, "chown " S6_SUPERVISE_EVENTDIR) ;
    715     if (chmod(S6_SUPERVISE_EVENTDIR, 03730) < 0)
    716       strerr_diefu1sys(111, "chmod " S6_SUPERVISE_EVENTDIR) ;
    717   }
    718 
    719   trymkdir(S6_SUPERVISE_CTLDIR) ;
    720   fdlck = open3(LCK, O_WRONLY | O_NONBLOCK | O_CREAT | O_CLOEXEC, 0644) ;
    721   if (fdlck < 0) strerr_diefu1sys(111, "open " LCK) ;
    722   r = fd_lock(fdlck, 1, 1) ;
    723   if (r < 0) strerr_diefu1sys(111, "lock " LCK) ;
    724   if (!r) strerr_dief1x(100, "another instance of s6-supervise is already running") ;
    725  /* fdlck leaks but it's coe */
    726 
    727   if (mkfifo(CTL, 0600) < 0)
    728   {
    729     struct stat st ;
    730     if (errno != EEXIST)
    731       strerr_diefu1sys(111, "mkfifo " CTL) ;
    732     if (stat(CTL, &st) < 0)
    733       strerr_diefu1sys(111, "stat " CTL) ;
    734     if (!S_ISFIFO(st.st_mode))
    735       strerr_dief1x(100, CTL " is not a FIFO") ;
    736   }
    737   fdctl = openc_read(CTL) ;
    738   if (fdctl < 0)
    739     strerr_diefu1sys(111, "open " CTL " for reading") ;
    740   r = openc_write(CTL) ;
    741   if (r < 0)
    742     strerr_diefu1sys(111, "open " CTL " for writing") ;
    743  /* r leaks but it's coe */
    744 
    745   umask(m) ;
    746   return fdctl ;
    747 }
    748 
    749 int main (int argc, char const *const *argv)
    750 {
    751   iopause_fd x[3] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ;
    752   PROG = "s6-supervise" ;
    753   if (argc < 2) strerr_dieusage(100, USAGE) ;
    754   if (chdir(argv[1]) < 0) strerr_diefu2sys(111, "chdir to ", argv[1]) ;
    755   servicename = argv[1] ;
    756   {
    757     size_t proglen = strlen(PROG) ;
    758     size_t namelen = strlen(argv[1]) ;
    759     char progname[proglen + namelen + 10] ;
    760     memcpy(progname, PROG, proglen) ;
    761     progname[proglen] = ' ' ;
    762     memcpy(progname + proglen + 1, argv[1], namelen + 1) ;
    763     memcpy(progname + proglen + 2 + namelen, "(child)", 8) ;
    764     PROG = progname ;
    765     if (!fd_sanitize()) strerr_diefu1sys(111, "sanitize stdin and stdout") ;
    766     {
    767       struct rlimit rl ;
    768       if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
    769         strerr_diefu1sys(111, "getrlimit") ;
    770       maxfd = rl.rlim_cur ;
    771     }
    772     x[1].fd = control_init() ;
    773     x[0].fd = selfpipe_init() ;
    774     if (x[0].fd == -1) strerr_diefu1sys(111, "init selfpipe") ;
    775     if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ;
    776     {
    777       sigset_t set ;
    778       sigemptyset(&set) ;
    779       sigaddset(&set, SIGCHLD) ;
    780       sigaddset(&set, SIGTERM) ;
    781       sigaddset(&set, SIGHUP) ;
    782       sigaddset(&set, SIGQUIT) ;
    783       sigaddset(&set, SIGINT) ;
    784       if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ;
    785     }
    786     
    787     if (!ftrigw_clean(S6_SUPERVISE_EVENTDIR))
    788       strerr_warnwu2sys("ftrigw_clean ", S6_SUPERVISE_EVENTDIR) ;
    789     {
    790       int fd = open_trunc(S6_DTALLY_FILENAME) ;
    791       if (fd < 0) strerr_diefu2sys(111, "truncate ", S6_DTALLY_FILENAME) ;
    792       fd_close(fd) ;
    793     }
    794 
    795     if (access("down", F_OK) == 0) status.flagwantup = 0 ;
    796     else if (errno != ENOENT)
    797       strerr_diefu1sys(111, "access ./down") ;
    798 
    799     tain_now_set_stopwatch_g() ;
    800     settimeout(0) ;
    801     tain_copynow(&status.stamp) ;
    802     status.readystamp = status.stamp ;
    803     announce() ;
    804     ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "s", 1) ;
    805 
    806     while (gflags.cont)
    807     {
    808       int r ;
    809       x[2].fd = notifyfd ;
    810       r = iopause_g(x, 2 + (notifyfd >= 0), &deadline) ;
    811       if (r < 0) strerr_diefu1sys(111, "iopause") ;
    812       else if (!r) (*actions[state][V_TIMEOUT])() ;
    813       else
    814       {
    815         if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT)
    816           strerr_diefu1x(111, "iopause: trouble with pipes") ;
    817         if (notifyfd >= 0 && x[2].revents & IOPAUSE_READ) handle_notifyfd() ;
    818         if (x[0].revents & IOPAUSE_READ) handle_signals() ;
    819         else if (x[1].revents & IOPAUSE_READ) handle_control(x[1].fd) ;
    820       }
    821     }
    822 
    823     ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "x", 1) ;
    824   }
    825   return 0 ;
    826 }