s6

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

s6-log.c (36269B)


      1 /* ISC license. */
      2 
      3 #include <fcntl.h>
      4 #include <sys/stat.h>
      5 #include <sys/wait.h>
      6 #include <stdint.h>
      7 #include <string.h>
      8 #include <errno.h>
      9 #include <signal.h>
     10 #include <unistd.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <regex.h>
     14 
     15 #include <skalibs/posixplz.h>
     16 #include <skalibs/uint64.h>
     17 #include <skalibs/types.h>
     18 #include <skalibs/allreadwrite.h>
     19 #include <skalibs/buffer.h>
     20 #include <skalibs/bytestr.h>
     21 #include <skalibs/error.h>
     22 #include <skalibs/sgetopt.h>
     23 #include <skalibs/strerr.h>
     24 #include <skalibs/bufalloc.h>
     25 #include <skalibs/stralloc.h>
     26 #include <skalibs/tai.h>
     27 #include <skalibs/djbtime.h>
     28 #include <skalibs/iopause.h>
     29 #include <skalibs/djbunix.h>
     30 #include <skalibs/direntry.h>
     31 #include <skalibs/sig.h>
     32 #include <skalibs/selfpipe.h>
     33 #include <skalibs/siovec.h>
     34 #include <skalibs/cspawn.h>
     35 
     36 #include <s6/config.h>
     37 
     38 #ifdef S6_USE_EXECLINE
     39 #include <execline/config.h>
     40 #endif
     41 
     42 #define USAGE "s6-log [ -d notif ] [ -q | -v ] [ -b ] [ -p ] [ -l linelimit ] [ -t lastlinetimeout ] [ -- ] logging_script"
     43 #define dieusage() strerr_dieusage(100, USAGE)
     44 #define dienomem() strerr_diefu1sys(111, "stralloc_catb")
     45 
     46 #define LINELIMIT_MIN 48
     47 
     48 static mode_t mask ;
     49 static int flagprotect = 0 ;
     50 static int flagexiting = 0 ;
     51 static unsigned int verbosity = 1 ;
     52 static tain lastlinetto = TAIN_INFINITE_RELATIVE ;
     53 static tain exit_deadline = TAIN_INFINITE ;
     54 
     55 static stralloc indata = STRALLOC_ZERO ;
     56 
     57  /* Data types */
     58 
     59 typedef int qcmp_func (void const *, void const *) ;
     60 typedef qcmp_func *qcmp_func_ref ;
     61 
     62 typedef enum rotstate_e rotstate_t, *rotstate_t_ref ;
     63 enum rotstate_e
     64 {
     65   ROTSTATE_WRITABLE,
     66   ROTSTATE_START,
     67   ROTSTATE_RENAME,
     68   ROTSTATE_NEWCURRENT,
     69   ROTSTATE_CHMODPREVIOUS,
     70   ROTSTATE_FINISHPREVIOUS,
     71   ROTSTATE_RUNPROCESSOR,
     72   ROTSTATE_WAITPROCESSOR,
     73   ROTSTATE_SYNCPROCESSED,
     74   ROTSTATE_SYNCNEWSTATE,
     75   ROTSTATE_UNLINKPREVIOUS,
     76   ROTSTATE_RENAMESTATE,
     77   ROTSTATE_FINISHPROCESSED,
     78   ROTSTATE_ENDFCHMOD,
     79   ROTSTATE_END
     80 } ;
     81 
     82 typedef enum seltype_e seltype_t, *seltype_t_ref ;
     83 enum seltype_e
     84 {
     85   SELTYPE_DEFAULT,
     86   SELTYPE_PLUS,
     87   SELTYPE_MINUS,
     88   SELTYPE_PHAIL
     89 } ;
     90 
     91 typedef struct sel_s sel_t, *sel_t_ref ;
     92 struct sel_s
     93 {
     94   seltype_t type ;
     95   regex_t re ;
     96 } ;
     97 
     98 #define SEL_ZERO { .type = SELTYPE_PHAIL }
     99 
    100 typedef enum acttype_e acttype_t, *acttype_t_ref ;
    101 enum acttype_e
    102 {
    103   ACTTYPE_NOTHING,
    104   ACTTYPE_FD1,
    105   ACTTYPE_FD2,
    106   ACTTYPE_STATUS,
    107   ACTTYPE_DIR,
    108   ACTTYPE_PHAIL
    109 } ;
    110 
    111 typedef struct as_status_s as_status_t, *as_status_t_ref ;
    112 struct as_status_s
    113 {
    114   char const *file ;
    115   size_t filelen ;
    116 } ;
    117 
    118 typedef union actstuff_u actstuff_t, *actstuff_t_ref ;
    119 union actstuff_u
    120 {
    121   size_t fd2_size ;
    122   as_status_t status ;
    123   unsigned int ld ;
    124 } ;
    125 
    126 typedef struct act_s act_t, *act_t_ref ;
    127 struct act_s
    128 {
    129   acttype_t type ;
    130   actstuff_t data ;
    131   unsigned int flags ;
    132   char const *prefix ;
    133   size_t prefixlen ;
    134 } ;
    135 
    136 typedef struct scriptelem_s scriptelem_t, *scriptelem_t_ref ;
    137 struct scriptelem_s
    138 {
    139   sel_t const *sels ;
    140   unsigned int sellen ;
    141   act_t const *acts ;
    142   unsigned int actlen ;
    143 } ;
    144 
    145 typedef void inputproc_func (scriptelem_t const *, unsigned int, size_t, unsigned int) ;
    146 typedef inputproc_func *inputproc_func_ref ;
    147 
    148 typedef struct logdir_s logdir_t, *logdir_t_ref ;
    149 struct logdir_s
    150 {
    151   bufalloc out ;
    152   unsigned int xindex ;
    153   tain retrytto ;
    154   tain deadline ;
    155   uint64_t maxdirsize ;
    156   uint32_t b ;
    157   uint32_t n ;
    158   uint32_t s ;
    159   uint32_t tolerance ;
    160   pid_t pid ;
    161   char const *dir ;
    162   char const *processor ;
    163   unsigned int flags ;
    164   int fd ;
    165   int fdlock ;
    166   rotstate_t rstate ;
    167 } ;
    168 
    169 #define LOGDIR_ZERO { \
    170   .out = BUFALLOC_ZERO, \
    171   .xindex = 0, \
    172   .retrytto = TAIN_ZERO, \
    173   .deadline = TAIN_ZERO, \
    174   .maxdirsize = 0, \
    175   .b = 0, \
    176   .n = 0, \
    177   .s = 0, \
    178   .tolerance = 0, \
    179   .pid = 0, \
    180   .dir = 0, \
    181   .processor = 0, \
    182   .fd = -1, \
    183   .fdlock = -1, \
    184   .rstate = ROTSTATE_WRITABLE \
    185 }
    186 
    187 struct filedesc_s
    188 {
    189   off_t size ;
    190   char name[28] ;
    191 } ;
    192 
    193 
    194  /* Logdirs */
    195 
    196 static logdir_t *logdirs ;
    197 static unsigned int llen = 0 ;
    198 
    199 static int filedesc_cmp (struct filedesc_s const *a, struct filedesc_s const *b)
    200 {
    201   return memcmp(a->name+1, b->name+1, 26) ;
    202 }
    203 
    204 static int name_is_relevant (char const *name)
    205 {
    206   tain dummy ;
    207   if (strlen(name) != 27) return 0 ;
    208   if (!timestamp_scan(name, &dummy)) return 0 ;
    209   if (name[25] != '.') return 0 ;
    210   if ((name[26] != 's') && (name[26] != 'u')) return 0 ;
    211   return 1 ;
    212 }
    213 
    214 static inline int logdir_trim (logdir_t *ldp)
    215 {
    216   unsigned int n = 0 ;
    217   DIR *dir = opendir(ldp->dir) ;
    218   if (!dir) return -1 ;
    219   for (;;)
    220   {
    221     direntry *d ;
    222     errno = 0 ;
    223     d = readdir(dir) ;
    224     if (!d) break ;
    225     if (name_is_relevant(d->d_name)) n++ ;
    226   }
    227   if (errno)
    228   {
    229     dir_close(dir) ;
    230     return -1 ;
    231   }
    232   if (!n)
    233   {
    234     dir_close(dir) ;
    235     return 0 ;
    236   }
    237 
    238   rewinddir(dir) ;
    239 
    240   {
    241     uint64_t totalsize = 0 ;
    242     size_t dirlen = strlen(ldp->dir) ;
    243     unsigned int i = 0 ;
    244     struct filedesc_s archive[n] ;
    245     char fullname[dirlen + 29] ;
    246     memcpy(fullname, ldp->dir, dirlen) ;
    247     fullname[dirlen] = '/' ;
    248     for (;;)
    249     {
    250       struct stat st ;
    251       direntry *d ;
    252       errno = 0 ;
    253       d = readdir(dir) ;
    254       if (!d) break ;
    255       if (!name_is_relevant(d->d_name)) continue ;
    256       if (i >= n) { errno = EBUSY ; break ; }
    257       memcpy(fullname + dirlen + 1, d->d_name, 28) ;
    258       if (stat(fullname, &st) < 0)
    259       {
    260         if (verbosity) strerr_warnwu2sys("stat ", fullname) ;
    261         continue ;
    262       }
    263       memcpy(archive[i].name, d->d_name, 28) ;
    264       archive[i].size = st.st_size ;
    265       totalsize += st.st_size ;
    266       i++ ;
    267     }
    268     if (errno)
    269     {
    270       dir_close(dir) ;
    271       return -1 ;
    272     }
    273     dir_close(dir) ;
    274     if ((i <= ldp->n) && (!ldp->maxdirsize || (totalsize <= ldp->maxdirsize)))
    275       return 0 ;
    276     qsort(archive, i, sizeof(struct filedesc_s), (qcmp_func_ref)&filedesc_cmp) ;
    277     n = 0 ;
    278     while ((i > ldp->n + n) || (ldp->maxdirsize && (totalsize > ldp->maxdirsize)))
    279     {
    280       memcpy(fullname + dirlen + 1, archive[n].name, 28) ;
    281       if (unlink(fullname) < 0)
    282       {
    283         if (errno == ENOENT) totalsize -= archive[n].size ;
    284         if (verbosity) strerr_warnwu2sys("unlink ", fullname) ;
    285       }
    286       else totalsize -= archive[n].size ;
    287       n++ ;
    288     }
    289   }
    290   return n ;
    291 }
    292 
    293 static int finish (logdir_t *ldp, char const *name, char suffix)
    294 {
    295   struct stat st ;
    296   size_t dirlen = strlen(ldp->dir) ;
    297   size_t namelen = strlen(name) ;
    298   char x[dirlen + namelen + 2] ;
    299   memcpy(x, ldp->dir, dirlen) ;
    300   x[dirlen] = '/' ;
    301   memcpy(x + dirlen + 1, name, namelen + 1) ;
    302   if (stat(x, &st) < 0) return errno == ENOENT ? 0 : -1 ;
    303   if (st.st_nlink == 1)
    304   {
    305     char y[dirlen + 29] ;
    306     memcpy(y, ldp->dir, dirlen) ;
    307     y[dirlen] = '/' ;
    308     timestamp_g(y + dirlen + 1) ;
    309     y[dirlen + 26] = '.' ;
    310     y[dirlen + 27] = suffix ;
    311     y[dirlen + 28] = 0 ;
    312     if (link(x, y) < 0) return -1 ;
    313   }
    314   if (unlink(x) < 0) return -1 ;
    315   return logdir_trim(ldp) ;
    316 }
    317 
    318 static int rotator (logdir_t *ldp)
    319 {
    320   size_t dirlen = strlen(ldp->dir) ;
    321   switch (ldp->rstate)
    322   {
    323     case ROTSTATE_START :
    324       if (fd_sync(ldp->fd) < 0)
    325       {
    326         if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ;
    327         goto fail ;
    328       }
    329       tain_now_g() ;
    330       ldp->rstate = ROTSTATE_RENAME ;
    331     case ROTSTATE_RENAME :
    332     {
    333       char current[dirlen + 9] ;
    334       char previous[dirlen + 10] ;
    335       memcpy(current, ldp->dir, dirlen) ;
    336       memcpy(current + dirlen, "/current", 9) ;
    337       memcpy(previous, ldp->dir, dirlen) ;
    338       memcpy(previous + dirlen, "/previous", 10) ;
    339       if (rename(current, previous) < 0)
    340       {
    341         if (verbosity) strerr_warnwu4sys("rename ", current, " to ", previous) ;
    342         goto fail ;
    343       }
    344       ldp->rstate = ROTSTATE_NEWCURRENT ;
    345     }
    346     case ROTSTATE_NEWCURRENT :
    347     {
    348       int fd ;
    349       char x[dirlen + 9] ;
    350       memcpy(x, ldp->dir, dirlen) ;
    351       memcpy(x + dirlen, "/current", 9) ;
    352       fd = openc_append(x) ;
    353       if (fd < 0)
    354       {
    355         if (verbosity) strerr_warnwu2sys("open_append ", x) ;
    356         goto fail ;
    357       }
    358       fd_close(ldp->fd) ;
    359       ldp->fd = fd ;
    360       ldp->b = 0 ;
    361       ldp->rstate = ROTSTATE_CHMODPREVIOUS ;
    362     }
    363     case ROTSTATE_CHMODPREVIOUS :
    364     {
    365       char x[dirlen + 10] ;
    366       memcpy(x, ldp->dir, dirlen) ;
    367       memcpy(x + dirlen, "/previous", 10) ;
    368       if (chmod(x, mask | S_IXUSR) < 0)
    369       {
    370         if (verbosity) strerr_warnwu2sys("chmod ", x) ;
    371         goto fail ;
    372       }
    373       if (ldp->processor) goto runprocessor ;
    374       ldp->rstate = ROTSTATE_FINISHPREVIOUS ;
    375     }
    376     case ROTSTATE_FINISHPREVIOUS :
    377       if (finish(ldp, "previous", 's') < 0)
    378       {
    379         if (verbosity) strerr_warnwu2sys("finish previous .s to logdir ", ldp->dir) ;
    380         goto fail ;
    381       }
    382       tain_copynow(&ldp->deadline) ;
    383       ldp->rstate = ROTSTATE_WRITABLE ;
    384       break ;
    385    runprocessor:
    386       ldp->rstate = ROTSTATE_RUNPROCESSOR ;
    387     case ROTSTATE_RUNPROCESSOR :
    388     {
    389 #ifdef S6_USE_EXECLINE
    390       char const *cargv[4] = { ldp->flags & 4 ? "/bin/sh" : EXECLINE_EXTBINPREFIX "execlineb", ldp->flags & 4 ? "-c" : "-Pc", ldp->processor, 0 } ;
    391 #else
    392       char const *cargv[4] = { "/bin/sh", "-c", ldp->processor, 0 } ;
    393 #endif
    394       cspawn_fileaction fa[5] =
    395       {
    396         [0] = { .type = CSPAWN_FA_CHDIR, .x = { .path = ldp->dir } }, 
    397         [1] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 0, .file = "previous", .oflag = O_RDONLY, .mode = 0644 } } }, 
    398         [2] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 1, .file = "processed", .oflag = O_WRONLY | O_CREAT | O_TRUNC, .mode = 0666 } } }, 
    399         [3] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 4, .file = "state", .oflag = O_RDONLY, .mode = 0644 } } }, 
    400         [4] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 5, .file = "newstate", .oflag = O_WRONLY | O_CREAT | O_TRUNC, .mode = 0666 } } }
    401       } ;
    402       pid_t pid = cspawn(cargv[0], cargv, (char const *const *)environ, CSPAWN_FLAGS_SELFPIPE_FINISH, fa, 5) ;
    403       if (!pid)
    404       {
    405         if (verbosity) strerr_warnwu2sys("spawn processor for logdir ", ldp->dir) ;
    406         goto fail ;
    407       }
    408       ldp->pid = pid ;
    409       tain_add_g(&ldp->deadline, &tain_infinite_relative) ;
    410       ldp->rstate = ROTSTATE_WAITPROCESSOR ;
    411     }
    412     case ROTSTATE_WAITPROCESSOR :
    413       return (errno = EAGAIN, 0) ;
    414     case ROTSTATE_SYNCPROCESSED :
    415     {
    416       int fd ;
    417       char x[dirlen + 11] ;
    418       memcpy(x, ldp->dir, dirlen) ;
    419       memcpy(x + dirlen, "/processed", 11) ;
    420       fd = open_append(x) ;
    421       if (fd < 0)
    422       {
    423         if (verbosity) strerr_warnwu2sys("open_append ", x) ;
    424         goto fail ;
    425       }
    426       if (fd_sync(fd) < 0)
    427       {
    428         fd_close(fd) ;
    429         if (verbosity) strerr_warnwu2sys("fd_sync ", x) ;
    430         goto fail ;
    431       }
    432       tain_now_g() ;
    433       if (fd_chmod(fd, mask | S_IXUSR) < 0)
    434       {
    435         fd_close(fd) ;
    436         if (verbosity) strerr_warnwu2sys("fd_chmod ", x) ;
    437         goto fail ;
    438       }
    439       fd_close(fd) ;
    440       ldp->rstate = ROTSTATE_SYNCNEWSTATE ;
    441     }
    442     case ROTSTATE_SYNCNEWSTATE :
    443     {
    444       int fd ;
    445       char x[dirlen + 10] ;
    446       memcpy(x, ldp->dir, dirlen) ;
    447       memcpy(x + dirlen, "/newstate", 10) ;
    448       fd = open_append(x) ;
    449       if (fd < 0)
    450       {
    451         if (verbosity) strerr_warnwu2sys("open_append ", x) ;
    452         goto fail ;
    453       }
    454       if (fd_sync(fd) < 0)
    455       {
    456         if (verbosity) strerr_warnwu2sys("fd_sync ", x) ;
    457         goto fail ;
    458       }
    459       tain_now_g() ;
    460       fd_close(fd) ;
    461       ldp->rstate = ROTSTATE_UNLINKPREVIOUS ;
    462     }
    463     case ROTSTATE_UNLINKPREVIOUS :
    464     {
    465       char x[dirlen + 10] ;
    466       memcpy(x, ldp->dir, dirlen) ;
    467       memcpy(x + dirlen, "/previous", 10) ;
    468       if ((unlink(x) < 0) && (errno != ENOENT))
    469       {
    470         if (verbosity) strerr_warnwu2sys("open_append ", x) ;
    471         goto fail ;
    472       }
    473       ldp->rstate = ROTSTATE_RENAMESTATE ;
    474     }
    475     case ROTSTATE_RENAMESTATE :
    476     {
    477       char newstate[dirlen + 10] ;
    478       char state[dirlen + 7] ;
    479       memcpy(newstate, ldp->dir, dirlen) ;
    480       memcpy(state, ldp->dir, dirlen) ;
    481       memcpy(newstate + dirlen, "/newstate", 10) ;
    482       memcpy(state + dirlen, "/state", 7) ;
    483       if (rename(newstate, state) < 0)
    484       {
    485         if (verbosity) strerr_warnwu4sys("rename ", newstate, " to ", state) ;
    486         goto fail ;
    487       }
    488       ldp->rstate = ROTSTATE_FINISHPROCESSED ;
    489     }
    490     case ROTSTATE_FINISHPROCESSED :
    491       if (finish(ldp, "processed", 's') < 0)
    492       {
    493         if (verbosity) strerr_warnwu2sys("finish processed .s to logdir ", ldp->dir) ;
    494         goto fail ;
    495       }
    496       tain_copynow(&ldp->deadline) ;
    497       ldp->rstate = ROTSTATE_WRITABLE ;
    498       break ;
    499     default : strerr_dief1x(101, "inconsistent state in rotator()") ;
    500   }
    501   return 1 ;
    502  fail:
    503    tain_add_g(&ldp->deadline, &ldp->retrytto) ;
    504    return 0 ;
    505 }
    506 
    507 static ssize_t logdir_write (int i, char const *s, size_t len)
    508 {
    509   logdir_t *ldp = logdirs + i ;
    510   ssize_t r ;
    511   size_t n = len ;
    512   {
    513     size_t m = byte_rchr(s, n, '\n') ;
    514     if (m < n) n = m+1 ;
    515   }
    516   r = fd_write(ldp->fd, s, n) ;
    517   if (r < 0)
    518   {
    519     if (!error_isagain(errno))
    520     {
    521       tain_add_g(&ldp->deadline, &ldp->retrytto) ;
    522       if (verbosity) strerr_warnwu3sys("write to ", ldp->dir, "/current") ;
    523     }
    524     return r ;
    525   }
    526   ldp->b += r ;
    527   if ((ldp->b + ldp->tolerance >= ldp->s) && (s[r-1] == '\n'))
    528   {
    529     ldp->rstate = ROTSTATE_START ;
    530     rotator(ldp) ;
    531   }
    532   return r ;
    533 }
    534 
    535 static inline void rotate_or_flush (logdir_t *ldp)
    536 {
    537   if ((ldp->rstate != ROTSTATE_WRITABLE) && !rotator(ldp)) return ;
    538   if (ldp->b >= ldp->s)
    539   {
    540     ldp->rstate = ROTSTATE_START ;
    541     if (!rotator(ldp)) return ;
    542   }
    543   bufalloc_flush(&ldp->out) ;
    544 }
    545 
    546 static inline void logdir_init (unsigned int index, uint32_t s, uint32_t n, uint32_t tolerance, uint64_t maxdirsize, tain const *retrytto, char const *processor, char const *name, unsigned int flags)
    547 {
    548   logdir_t *ldp = logdirs + index ;
    549   struct stat st ;
    550   size_t dirlen = strlen(name) ;
    551   int r ;
    552   char x[dirlen + 11] ;
    553   ldp->s = s ;
    554   ldp->n = n ;
    555   ldp->pid = 0 ;
    556   ldp->tolerance = tolerance ;
    557   ldp->maxdirsize = maxdirsize ;
    558   ldp->retrytto = *retrytto ;
    559   ldp->processor = processor ;
    560   ldp->flags = flags ;
    561   ldp->dir = name ;
    562   ldp->fd = -1 ;
    563   ldp->rstate = ROTSTATE_WRITABLE ;
    564   r = mkdir(ldp->dir, S_IRWXU | S_ISGID) ;
    565   if (r < 0 && errno != EEXIST) strerr_diefu2sys(111, "mkdir ", name) ;
    566   memcpy(x, name, dirlen) ;
    567   memcpy(x + dirlen, "/lock", 6) ;
    568   ldp->fdlock = openc_create(x) ;
    569   if (ldp->fdlock < 0) strerr_diefu2sys(111, "open ", x) ;
    570   r = fd_lock(ldp->fdlock, 1, 1) ;
    571   if (!r) errno = EBUSY ;
    572   if (r < 1) strerr_diefu2sys(111, "lock ", x) ;
    573   memcpy(x + dirlen + 1, "current", 8) ;
    574   if (stat(x, &st) < 0)
    575   {
    576     if (errno != ENOENT) strerr_diefu2sys(111, "stat ", x) ;
    577   }
    578   else if (st.st_mode & S_IXUSR) goto opencurrent ;
    579   memcpy(x + dirlen + 1, "state", 6) ;
    580   unlink_void(x) ;
    581   memcpy(x + dirlen + 1, "newstate", 9) ;
    582   unlink_void(x) ;
    583   {
    584     int flagprocessed = 0 ;
    585     memcpy(x + dirlen + 1, "processed", 10) ;
    586     if (stat(x, &st) < 0)
    587     {
    588       if (errno != ENOENT) strerr_diefu2sys(111, "stat ", x) ;
    589     }
    590     else if (st.st_mode & S_IXUSR) flagprocessed = 1 ;
    591     if (flagprocessed)
    592     {
    593       memcpy(x + dirlen + 1, "previous", 9) ;
    594       unlink_void(x) ;
    595       if (finish(ldp, "processed", 's') < 0)
    596         strerr_diefu2sys(111, "finish processed .s for logdir ", ldp->dir) ;
    597     }
    598     else
    599     {
    600       unlink_void(x) ;
    601       if (finish(ldp, "previous", 'u') < 0)
    602         strerr_diefu2sys(111, "finish previous .u for logdir ", ldp->dir) ;
    603     }
    604   }
    605   if (finish(ldp, "current", 'u') < 0)
    606     strerr_diefu2sys(111, "finish current .u for logdir ", ldp->dir) ;
    607   memcpy(x + dirlen + 1, "state", 6) ;
    608   r = open_trunc(x) ;
    609   if (r == -1) strerr_diefu2sys(111, "open_trunc ", x) ;
    610   fd_close(r) ;
    611   st.st_size = 0 ;
    612   memcpy(x + dirlen + 1, "current", 8) ;
    613  opencurrent:
    614   ldp->fd = openc_append(x) ;
    615   if (ldp->fd < 0) strerr_diefu2sys(111, "open_append ", x) ;
    616   if (fd_chmod(ldp->fd, mask) == -1)
    617     strerr_diefu2sys(111, "fd_chmod ", x) ;
    618   ldp->b = st.st_size ;
    619   tain_copynow(&ldp->deadline) ;
    620   bufalloc_init(&ldp->out, &logdir_write, index) ;
    621 }
    622 
    623 static inline int logdir_finalize (logdir_t *ldp)
    624 {
    625   switch (ldp->rstate)
    626   {
    627     case ROTSTATE_WRITABLE :
    628     {
    629       if (fd_sync(ldp->fd) < 0)
    630       {
    631         if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ;
    632         goto fail ;
    633       }
    634       tain_now_g() ;
    635       ldp->rstate = ROTSTATE_ENDFCHMOD ;
    636     }
    637     case ROTSTATE_ENDFCHMOD :
    638     {
    639       if (fd_chmod(ldp->fd, mask | S_IXUSR) < 0)
    640       {
    641         if (verbosity) strerr_warnwu3sys("fd_chmod ", ldp->dir, "/current") ;
    642         goto fail ;
    643       }
    644       ldp->rstate = ROTSTATE_END ;
    645       break ;
    646     }
    647     default : strerr_dief1x(101, "inconsistent state in logdir_finalize()") ;
    648   }
    649   return 1 ;
    650  fail:
    651   tain_add_g(&ldp->deadline, &ldp->retrytto) ;
    652   return 0 ;
    653 }
    654 
    655 static inline void finalize (void)
    656 {
    657   unsigned int n = llen ;
    658   for (;;)
    659   {
    660     unsigned int i = 0 ;
    661     tain deadline ;
    662     tain_addsec_g(&deadline, 2) ;
    663     for (; i < llen ; i++)
    664       if (logdirs[i].rstate != ROTSTATE_END)
    665       {
    666         if (logdir_finalize(logdirs + i)) n-- ;
    667         else if (tain_less(&logdirs[i].deadline, &deadline))
    668           deadline = logdirs[i].deadline ;
    669       }
    670     if (!n) break ;
    671     {
    672       iopause_fd x ;
    673       iopause_g(&x, 0, &deadline) ;
    674     }
    675   }
    676 }
    677 
    678 
    679  /* Script */
    680  
    681 static inline void script_firstpass (char const *const *argv, unsigned int *sellen, unsigned int *actlen, unsigned int *scriptlen, unsigned int *gflags)
    682 {
    683   unsigned int se = 0, ac = 0, sc = 0, gf = *gflags ;
    684   int flagacted = 0 ;
    685   for (; *argv ; argv++)
    686   {
    687     switch ((*argv)[0])
    688     {
    689       case 'f' :
    690         if ((*argv)[1]) goto fail ;
    691       case '+' :
    692       case '-' :
    693         if (flagacted)
    694         {
    695           sc++ ;
    696           flagacted = 0 ;
    697         }
    698         se++ ;
    699       case 'n' :
    700       case 's' :
    701       case 'S' :
    702       case 'l' :
    703       case 'r' :
    704       case 'E' :
    705       case '^' :
    706       case 'p' :
    707 #ifdef S6_USE_EXECLINE
    708       case '!' :
    709 #endif
    710       case '?' :
    711         break ;
    712       case 't' :
    713         if ((*argv)[1]) goto fail ;
    714         gf |= 1 ;
    715         break ;
    716       case 'T' :
    717         if ((*argv)[1]) goto fail ;
    718         gf |= 2 ;
    719         break ;
    720       case '1' :
    721       case '2' :
    722         if ((*argv)[1]) goto fail ;
    723         flagacted = 1 ;
    724         ac++ ;
    725         break ;
    726       case '.' :
    727       case '/' :
    728         llen++ ;
    729         flagacted = 1 ;
    730         ac++ ;
    731         break ;
    732       case '=' :
    733         if (!(*argv)[1]) goto fail ;
    734         flagacted = 1 ;
    735         ac++ ;
    736         break ;
    737       default : strerr_dief2x(100, "unrecognized directive: ", *argv) ;
    738     }
    739   }
    740   if (flagacted) sc++ ;
    741   else if (sc)
    742   {
    743     if (verbosity)
    744       strerr_warnw1x("ignoring extraneous non-action directives") ;
    745   }
    746   else strerr_dief1x(100, "no action directive specified") ;
    747   *sellen = se ;
    748   *actlen = ac ;
    749   *scriptlen = sc ;
    750   *gflags = gf ;
    751   return ;
    752  fail :
    753   strerr_dief2x(100, "syntax error at directive: ", *argv) ;
    754 }
    755 
    756 static inline void script_secondpass (char const *const *argv, scriptelem_t *script, sel_t *selections, act_t *actions)
    757 {
    758   tain retrytto ;
    759   unsigned int fd2_size = 200 ;
    760   unsigned int status_size = 1001 ;
    761   uint32_t s = 99999 ;
    762   uint32_t n = 10 ;
    763   uint32_t tolerance = 2000 ;
    764   uint64_t maxdirsize = 0 ;
    765   char const *processor = 0 ;
    766   char const *prefix = 0 ;
    767   size_t prefixlen = 0 ;
    768   unsigned int sel = 0, act = 0, lidx = 0, flags = 0 ;
    769   int flagacted = 0 ;
    770   tain_uint(&retrytto, 2) ;
    771   
    772   for (; *argv ; argv++)
    773   {
    774     switch (**argv)
    775     {
    776       case 'f' :
    777       case '+' :
    778       case '-' :
    779       {
    780         sel_t selitem = { .type = (*argv)[0] != 'f' ? (*argv)[0] == '+' ? SELTYPE_PLUS : SELTYPE_MINUS : SELTYPE_DEFAULT } ;
    781         if ((*argv)[0] != 'f')
    782         {
    783           int r = skalibs_regcomp(&selitem.re, *argv + 1, REG_EXTENDED | REG_NOSUB | REG_NEWLINE) ;
    784           if (r == REG_ESPACE)
    785           {
    786             errno = ENOMEM ;
    787             strerr_diefu1sys(111, "initialize script") ;
    788           }
    789           if (r) goto fail ;
    790         }
    791         if (flagacted)
    792         {
    793           flagacted = 0 ;
    794           script->sels = selections ;
    795           script->sellen = sel ;
    796           script->acts = actions ;
    797           script->actlen = act ;
    798           selections += sel ; sel = 0 ;
    799           actions += act ; act = 0 ;
    800           script++ ;
    801         }
    802         selections[sel++] = selitem ;
    803         break ;
    804       }
    805       case 'n' :
    806         if (!uint320_scan(*argv + 1, &n)) goto fail ;
    807         break ;
    808       case 's' :
    809         if (!uint320_scan(*argv + 1, &s)) goto fail ;
    810         if (s < 4096) s = 4096 ;
    811         if (s > 268435455) s = 268435455 ;
    812         break ;
    813       case 'S' :
    814         if (!uint640_scan(*argv + 1, &maxdirsize)) goto fail ;
    815         break ;
    816       case 'l' :
    817         if (!uint320_scan(*argv + 1, &tolerance)) goto fail ;
    818         if (tolerance > (s >> 1))
    819           strerr_dief3x(100, "directive ", *argv, " conflicts with previous s directive") ;
    820         break ;
    821       case 'r' :
    822       {
    823         uint32_t t ;
    824         if (!uint320_scan(*argv + 1, &t)) goto fail ;
    825         if (!tain_from_millisecs(&retrytto, t)) goto fail ;
    826         break ;
    827       }
    828       case 'E' :
    829         if (!uint0_scan(*argv + 1, &fd2_size)) goto fail ;
    830         break ;
    831       case '^' :
    832         if (!uint0_scan(*argv + 1, &status_size)) goto fail ;
    833         break ;
    834       case 'p' :
    835         if ((*argv)[1]) { prefix = *argv + 1 ; prefixlen = strlen(prefix) ; }
    836         else { prefix = 0 ; prefixlen = 0 ; }
    837         break ;
    838 #ifdef S6_USE_EXECLINE
    839       case '!' :
    840         processor = (*argv)[1] ? *argv + 1 : 0 ;
    841         flags &= ~4 ;
    842         break ;
    843 #endif
    844       case '?' :
    845         processor = (*argv)[1] ? *argv + 1 : 0 ;
    846         flags |= 4 ;
    847         break ;
    848       case 't' :
    849         flags |= 1 ;
    850         break ;
    851       case 'T' :
    852         flags |= 2 ;
    853         break ;
    854       case '1' :
    855       {
    856         act_t a = { .type = ACTTYPE_FD1, .flags = flags, .prefix = prefix, .prefixlen = prefixlen } ;
    857         actions[act++] = a ; flagacted = 1 ; flags = 0 ;
    858         break ;
    859       }
    860       case '2' :
    861       {
    862         act_t a = { .type = ACTTYPE_FD2, .flags = flags, .prefix = prefix, .prefixlen = prefixlen, .data = { .fd2_size = fd2_size } } ;
    863         actions[act++] = a ; flagacted = 1 ; flags = 0 ;
    864         break ;
    865       }
    866       case '=' :
    867       {
    868         act_t a = { .type = ACTTYPE_STATUS, .flags = flags, .prefix = prefix, .prefixlen = prefixlen, .data = { .status = { .file = *argv + 1, .filelen = status_size } } } ;
    869         actions[act++] = a ; flagacted = 1 ; flags = 0 ;
    870         break ;
    871       }
    872       case '.' : 
    873       case '/' :
    874       {
    875         act_t a = { .type = ACTTYPE_DIR, .flags = flags, .prefix = prefix, .prefixlen = prefixlen, .data = { .ld = lidx } } ;
    876         logdir_init(lidx, s, n, tolerance, maxdirsize, &retrytto, processor, *argv, flags) ;
    877         lidx++ ;
    878         actions[act++] = a ; flagacted = 1 ; flags = 0 ;
    879         break ;
    880       }
    881       default : goto fail ;
    882     }
    883   }
    884   if (flagacted)
    885   {
    886     script->sels = selections ;
    887     script->sellen = sel ;
    888     script->acts = actions ;
    889     script->actlen = act ;
    890   }
    891   return ;
    892  fail:
    893   strerr_dief2x(100, "unrecognized directive: ", *argv) ;
    894 }
    895 
    896 static void script_run (scriptelem_t const *script, unsigned int scriptlen, char const *s, size_t len, unsigned int gflags)
    897 {
    898   int flagselected = 1, flagacted = 0 ;
    899   unsigned int i = 0 ;
    900   size_t hlen = 0 ;
    901   char hstamp[32] ;
    902   char tstamp[TIMESTAMP+1] ;
    903   if (gflags & 3)
    904   {
    905     tain now ;
    906     tain_wallclock_read(&now) ;
    907     if (gflags & 1)
    908     {
    909       timestamp_fmt(tstamp, &now) ;
    910       tstamp[TIMESTAMP] = ' ' ;
    911     }
    912     if (gflags & 2)
    913     {
    914       localtmn l ;
    915       localtmn_from_tain(&l, &now, 1) ;
    916       hlen = localtmn_fmt(hstamp, &l) ;
    917       hstamp[hlen++] = ' ' ;
    918       hstamp[hlen++] = ' ' ;
    919     }
    920   }
    921   
    922   for (; i < scriptlen ; i++)
    923   {
    924     unsigned int j = 0 ;
    925     for (; j < script[i].sellen ; j++)
    926     {
    927       switch (script[i].sels[j].type)
    928       {
    929         case SELTYPE_DEFAULT :
    930           flagselected = !flagacted ;
    931           break ;
    932         case SELTYPE_PLUS :
    933 	  if (!flagselected && !regexec(&script[i].sels[j].re, s, 0, 0, 0)) flagselected = 1 ;
    934           break ;
    935         case SELTYPE_MINUS :
    936 	  if (flagselected && !regexec(&script[i].sels[j].re, s, 0, 0, 0)) flagselected = 0 ;
    937           break ;
    938         default :
    939           strerr_dief2x(101, "internal consistency error in ", "selection type") ;
    940       }
    941     }
    942     if (flagselected)
    943     {
    944       flagacted = 1 ;
    945       for (j = 0 ; j < script[i].actlen ; j++)
    946       {
    947         act_t const *act = script[i].acts + j ;
    948         unsigned int m = 0 ;
    949         struct iovec v[6] ;
    950         if (act->flags & 1)
    951         {
    952           v[m].iov_base = tstamp ;
    953           v[m++].iov_len = TIMESTAMP+1 ;
    954         }
    955         if (act->flags & 2)
    956         {
    957           v[m].iov_base = hstamp ;
    958           v[m++].iov_len = hlen ;
    959         }
    960         if (act->prefix)
    961         {
    962           v[m].iov_base = (char *)act->prefix ;
    963           v[m++].iov_len = act->prefixlen ;
    964           v[m].iov_base = " " ;
    965           v[m++].iov_len = 1 ;
    966         }
    967         v[m].iov_base = (char *)s ;
    968         v[m++].iov_len = len ;
    969         v[m].iov_base = "\n" ;
    970         v[m++].iov_len = 1 ;
    971         switch (act->type)
    972         {
    973           case ACTTYPE_FD1 :
    974             if (!bufalloc_putv(bufalloc_1, v, m)) dienomem() ;
    975           case ACTTYPE_NOTHING :
    976             break ;
    977 
    978           case ACTTYPE_FD2 :
    979             buffer_puts(buffer_2, PROG) ;
    980             buffer_puts(buffer_2, ": alert: ") ;
    981             if (act->data.fd2_size && act->data.fd2_size + 3 < len)
    982             {
    983               v[m-2].iov_len = act->data.fd2_size ;
    984               v[m-1].iov_base = "...\n" ;
    985               v[m-1].iov_len = 4 ;
    986             }
    987             buffer_putv(buffer_2, v, m) ;
    988             buffer_flush(buffer_2) ; /* if it blocks, too bad */
    989             break ;
    990 
    991           case ACTTYPE_STATUS :
    992             if (act->data.status.filelen)
    993             {
    994               size_t reallen = siovec_len(v, m) ;
    995               if (reallen > act->data.status.filelen)
    996                 siovec_trunc(v, m, act->data.status.filelen) ;
    997               else
    998               {
    999                 size_t k = act->data.status.filelen - reallen + 1 ;
   1000                 char pad[k] ;
   1001                 v[m-1].iov_base = pad ;
   1002                 v[m-1].iov_len = k ;
   1003                 while (k--) pad[k] = '\n' ;
   1004                 if (!openwritevnclose_suffix(act->data.status.file, v, m, ".new") && verbosity)
   1005                   strerr_warnwu2sys("write status file ", act->data.status.file) ;
   1006                 break ;
   1007               }
   1008             }
   1009             if (!openwritevnclose_suffix(act->data.status.file, v, m, ".new") && verbosity)
   1010               strerr_warnwu2sys("write status file ", act->data.status.file) ;
   1011             break ;
   1012 
   1013           case ACTTYPE_DIR :
   1014             if (!bufalloc_putv(&logdirs[act->data.ld].out, v, m)) dienomem() ;
   1015             break ;
   1016 
   1017           default :
   1018             strerr_dief2x(101, "internal consistency error in ", "action type") ;
   1019         }
   1020       }
   1021     }
   1022   }
   1023   if (gflags & 3) tain_now_g() ;
   1024 }
   1025 
   1026 
   1027  /* Input */
   1028 
   1029 static void prepare_to_exit (void)
   1030 {
   1031   fd_close(0) ;
   1032   flagexiting = 1 ;
   1033 }
   1034 
   1035 static inline int getchunk (buffer *b, stralloc *sa, size_t linelimit)
   1036 {
   1037   struct iovec v[2] ;
   1038   size_t pos ;
   1039   int r ;
   1040   buffer_rpeek(b, v) ;
   1041   pos = siovec_bytein(v, 2, "\n", 2) ;
   1042   if (linelimit && sa->len + pos > linelimit)
   1043   {
   1044     r = 2 ;
   1045     pos = linelimit - sa->len ;
   1046   }
   1047   else
   1048   {
   1049     r = pos < buffer_len(b) ;
   1050     pos += r ;
   1051   }
   1052   if (!stralloc_readyplus(sa, pos + (r == 2))) return -1 ;
   1053   buffer_getnofill(b, sa->s + sa->len, pos) ; sa->len += pos ;
   1054   if (r == 2) sa->s[sa->len++] = 0 ;
   1055   return r ;
   1056 }
   1057 
   1058 static void normal_stdin (scriptelem_t const *script, unsigned int scriptlen, size_t linelimit, unsigned int gflags)
   1059 {
   1060   ssize_t r = sanitize_read(buffer_fill(buffer_0)) ;
   1061   if (r < 0)
   1062   {
   1063     if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ;
   1064     prepare_to_exit() ;
   1065   }
   1066   else if (r) for (;;)
   1067   {
   1068     r = getchunk(buffer_0, &indata, linelimit) ;
   1069     if (r < 0) dienomem() ;
   1070     else if (!r) break ;
   1071     indata.s[indata.len - 1] = 0 ;
   1072     script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ;
   1073     indata.len = 0 ;
   1074   }
   1075 }
   1076 
   1077 static void process_partial_line (scriptelem_t const *script, unsigned int scriptlen, unsigned int gflags)
   1078 {
   1079   if (!stralloc_0(&indata)) dienomem() ;
   1080   script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ;
   1081   indata.len = 0 ;
   1082 }
   1083 
   1084 static void last_stdin (scriptelem_t const *script, unsigned int scriptlen, size_t linelimit, unsigned int gflags)
   1085 {
   1086   for (;;)
   1087   {
   1088     char c ;
   1089     switch (sanitize_read(fd_read(0, &c, 1)))
   1090     {
   1091       case 0 : return ;
   1092       case -1 :
   1093         if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ;
   1094         if (indata.len) goto lastline ;
   1095         goto end ;
   1096       case 1 :
   1097         if (c == '\n' || !c) goto lastline ;
   1098         if (!stralloc_catb(&indata, &c, 1)) dienomem() ;
   1099         if (linelimit && indata.len >= linelimit)
   1100         {
   1101           if (verbosity) strerr_warnw2x("input line too long, ", "stopping before the end") ;
   1102           goto lastline ;
   1103         }
   1104         else break ;
   1105     }
   1106   }
   1107  lastline:
   1108   process_partial_line(script, scriptlen, gflags) ;
   1109  end:
   1110   prepare_to_exit() ;
   1111 }
   1112 
   1113 static inputproc_func_ref handle_stdin = &normal_stdin ;
   1114 
   1115 
   1116  /* Signals */
   1117 
   1118 static inline void processor_died (logdir_t *ldp, int wstat)
   1119 {
   1120   ldp->pid = 0 ;
   1121   if (WIFSIGNALED(wstat))
   1122   {
   1123     if (verbosity) strerr_warnw2x("processor crashed in ", ldp->dir) ;
   1124     tain_add_g(&ldp->deadline, &ldp->retrytto) ;
   1125     ldp->rstate = ROTSTATE_RUNPROCESSOR ;
   1126   }
   1127   else if (WEXITSTATUS(wstat))
   1128   {
   1129     if (verbosity) strerr_warnw2x("processor failed in ", ldp->dir) ;
   1130     tain_add_g(&ldp->deadline, &ldp->retrytto) ;
   1131     ldp->rstate = ROTSTATE_RUNPROCESSOR ;
   1132   }
   1133   else
   1134   {
   1135     ldp->rstate = ROTSTATE_SYNCPROCESSED ;
   1136     rotator(ldp) ;
   1137   }
   1138 }
   1139 
   1140 static inline int handle_signals (void)
   1141 {
   1142   int e = 0 ;
   1143   for (;;)
   1144   {
   1145     switch (selfpipe_read())
   1146     {
   1147       case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
   1148       case 0 : return e ;
   1149       case SIGALRM :
   1150       {
   1151         unsigned int i = 0 ;
   1152         for (; i < llen ; i++)
   1153           if ((logdirs[i].rstate == ROTSTATE_WRITABLE) && logdirs[i].b)
   1154           {
   1155             logdirs[i].rstate = ROTSTATE_START ;
   1156             rotator(logdirs + i) ;
   1157           }
   1158         break ;
   1159       }
   1160       case SIGTERM :
   1161         if (flagprotect) break ;
   1162       case SIGHUP :
   1163         handle_stdin = &last_stdin ;
   1164         tain_add_g(&exit_deadline, &lastlinetto) ;
   1165         if (!indata.len) { prepare_to_exit() ; e = 1 ; }
   1166         break ;
   1167       case SIGCHLD :
   1168       {
   1169         for (;;)
   1170         {
   1171           int wstat ;
   1172           unsigned int i = 0 ;
   1173           pid_t r = wait_nohang(&wstat) ;
   1174           if (r <= 0) break ;
   1175           for (; i < llen ; i++) if (r == logdirs[i].pid) break ;
   1176           if (i < llen) processor_died(logdirs + i, wstat) ;
   1177         }
   1178         break ;
   1179       }
   1180       default : strerr_dief1x(101, "internal consistency error with signal handling") ;
   1181     }
   1182   }
   1183 }
   1184 
   1185 
   1186  /* Main */
   1187 
   1188 int main (int argc, char const *const *argv)
   1189 {
   1190   unsigned int sellen, actlen, scriptlen ;
   1191   unsigned int linelimit = 8192 ;
   1192   unsigned int notif = 0 ;
   1193   unsigned int gflags = 0 ;
   1194   int flagblock = 0 ;
   1195   PROG = "s6-log" ;
   1196   {
   1197     subgetopt l = SUBGETOPT_ZERO ;
   1198     unsigned int t = 2000 ;
   1199     for (;;)
   1200     {
   1201       int opt = subgetopt_r(argc, argv, "qvbpl:d:t:", &l) ;
   1202       if (opt == -1) break ;
   1203       switch (opt)
   1204       {
   1205         case 'q' : if (verbosity) verbosity-- ; break ;
   1206         case 'v' : verbosity++ ; break ;
   1207         case 'b' : flagblock = 1 ; break ;
   1208         case 'p' : flagprotect = 1 ; break ;
   1209         case 'l' : if (!uint0_scan(l.arg, &linelimit)) dieusage() ; break ;
   1210         case 'd' :
   1211           if (!uint0_scan(l.arg, &notif)) dieusage() ;
   1212           if (notif < 3) strerr_dief1x(100, "notification fd must be 3 or more") ;
   1213           if (fcntl(notif, F_GETFD) < 0) strerr_dief1sys(100, "invalid notification fd") ;
   1214           break ;
   1215         case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
   1216         default : dieusage() ;
   1217       }
   1218     }
   1219     argc -= l.ind ; argv += l.ind ;
   1220     if (t) tain_from_millisecs(&lastlinetto, t) ;
   1221     else lastlinetto = tain_infinite_relative ;
   1222   }
   1223   if (!argc) dieusage() ;
   1224   if (linelimit && linelimit < LINELIMIT_MIN) linelimit = LINELIMIT_MIN ;
   1225   if (!fd_sanitize()) strerr_diefu1sys(111, "ensure stdin/stdout/stderr are open") ;
   1226   if (!tain_now_set_stopwatch_g() && verbosity)
   1227     strerr_warnwu1sys("set monotonic clock and read current time - timestamps may be wrong for a while") ;
   1228   if (ndelay_on(0) < 0) strerr_diefu3sys(111, "set std", "in", " non-blocking") ;
   1229   if (ndelay_on(1) < 0) strerr_diefu3sys(111, "set std", "out", " non-blocking") ;
   1230   mask = umask(0) ;
   1231   umask(mask) ;
   1232   mask = ~mask & 0666 ;
   1233   script_firstpass(argv, &sellen, &actlen, &scriptlen, &gflags) ;
   1234   {
   1235     sel_t selections[sellen ? sellen : 1] ;
   1236     act_t actions[actlen] ;
   1237     scriptelem_t script[scriptlen] ;
   1238     logdir_t logdirblob[llen] ;
   1239     iopause_fd x[3 + llen] ;
   1240     logdirs = logdirblob ;
   1241     script_secondpass(argv, script, selections, actions) ;
   1242     x[0].fd = selfpipe_init() ;
   1243     if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
   1244     if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "sig_ignore(SIGPIPE)") ;
   1245     {
   1246       sigset_t set ;
   1247       sigemptyset(&set) ;
   1248       sigaddset(&set, SIGTERM) ;
   1249       sigaddset(&set, SIGHUP) ;
   1250       sigaddset(&set, SIGALRM) ;
   1251       sigaddset(&set, SIGCHLD) ;
   1252       if (!selfpipe_trapset(&set))
   1253         strerr_diefu1sys(111, "selfpipe_trapset") ;
   1254     }
   1255     x[0].events = IOPAUSE_READ ;
   1256     if (notif)
   1257     {
   1258       fd_write(notif, "\n", 1) ;
   1259       fd_close(notif) ;
   1260     }
   1261 
   1262     for (;;)
   1263     {
   1264       tain deadline = exit_deadline ;
   1265       int r = 0 ;
   1266       unsigned int xindex0, xindex1 ;
   1267       unsigned int i = 0, j = 1 ;
   1268       if (bufalloc_1->fd == 1 && bufalloc_len(bufalloc_1))
   1269       {
   1270         r = 1 ;
   1271         x[j].fd = 1 ;
   1272         x[j].events = IOPAUSE_EXCEPT | (bufalloc_len(bufalloc_1) ? IOPAUSE_WRITE : 0) ;
   1273         xindex1 = j++ ;
   1274       }
   1275       else xindex1 = 0 ;
   1276 
   1277       for (; i < llen ; i++)
   1278       {
   1279         logdirs[i].xindex = 0 ;
   1280         if (bufalloc_len(&logdirs[i].out) || (logdirs[i].rstate != ROTSTATE_WRITABLE))
   1281         {
   1282           r = 1 ;
   1283           if (tain_less(&logdirs[i].deadline, &deadline))
   1284             deadline = logdirs[i].deadline ;
   1285         }
   1286       }
   1287       if (!flagexiting && !(flagblock && r))
   1288       {
   1289         x[j].fd = 0 ;
   1290         x[j].events = IOPAUSE_READ ;
   1291         xindex0 = j++ ;
   1292       }
   1293       else xindex0 = 0 ;
   1294 
   1295       if (flagexiting && !r) break ;
   1296 
   1297       r = iopause_g(x, j, &deadline) ;
   1298       if (r < 0) strerr_diefu1sys(111, "iopause") ;
   1299       else if (!r)
   1300       {
   1301         if (!tain_future(&exit_deadline))
   1302         {
   1303           if (indata.len) process_partial_line(script, scriptlen, gflags) ;
   1304           prepare_to_exit() ;
   1305         }
   1306         for (i = 0 ; i < llen ; i++)
   1307           if (!tain_future(&logdirs[i].deadline))
   1308             rotate_or_flush(logdirs + i) ;
   1309         continue ;
   1310       }
   1311 
   1312       if (x[0].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT) && handle_signals()) continue ;
   1313 
   1314       if (xindex1 && x[xindex1].revents)
   1315       {
   1316         if (!bufalloc_flush(bufalloc_1) && !error_isagain(errno))
   1317         {
   1318           unsigned int i = actlen ;
   1319           strerr_warnwu1sys("write to stdout, closing the stream - error was") ;
   1320           fd_close(1) ;
   1321           bufalloc_1->fd = -1 ;
   1322           bufalloc_free(bufalloc_1) ;
   1323           while (i--)
   1324             if (actions[i].type == ACTTYPE_FD1)
   1325               actions[i].type = ACTTYPE_NOTHING ;
   1326         }
   1327       }
   1328 
   1329       for (i = 0 ; i < llen ; i++)
   1330        if (logdirs[i].xindex && x[logdirs[i].xindex].revents & IOPAUSE_WRITE)
   1331            rotate_or_flush(logdirs + i) ;
   1332 
   1333       if (xindex0 && x[xindex0].revents)
   1334       {
   1335         if (x[xindex0].revents & IOPAUSE_READ)
   1336           (*handle_stdin)(script, scriptlen, linelimit, gflags) ;
   1337         else
   1338         {
   1339           if (indata.len) process_partial_line(script, scriptlen, gflags) ;
   1340           prepare_to_exit() ;
   1341         }
   1342       }
   1343     }
   1344     finalize() ;
   1345   }
   1346   return 0 ;
   1347 }