s6

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

s6-fdholderd.c (23572B)


      1 /* ISC license. */
      2 
      3 #include <sys/uio.h>
      4 #include <sys/stat.h>
      5 #include <sys/resource.h>
      6 #include <string.h>
      7 #include <stdint.h>
      8 #include <unistd.h>
      9 #include <fcntl.h>
     10 #include <errno.h>
     11 #include <signal.h>
     12 #include <regex.h>
     13 
     14 #include <skalibs/posixplz.h>
     15 #include <skalibs/types.h>
     16 #include <skalibs/allreadwrite.h>
     17 #include <skalibs/sgetopt.h>
     18 #include <skalibs/env.h>
     19 #include <skalibs/bytestr.h>
     20 #include <skalibs/error.h>
     21 #include <skalibs/strerr.h>
     22 #include <skalibs/tai.h>
     23 #include <skalibs/djbunix.h>
     24 #include <skalibs/sig.h>
     25 #include <skalibs/iopause.h>
     26 #include <skalibs/selfpipe.h>
     27 #include <skalibs/cdb.h>
     28 #include <skalibs/socket.h>
     29 #include <skalibs/genset.h>
     30 #include <skalibs/avltreen.h>
     31 #include <skalibs/unixmessage.h>
     32 #include <skalibs/unixconnection.h>
     33 
     34 #include <s6/accessrules.h>
     35 #include <s6/fdholder.h>
     36 
     37 #include <skalibs/posixishard.h>
     38 
     39 #define USAGE "s6-fdholderd [ -v verbosity ] [ -1 ] [ -c maxconn ] [ -n maxfds ] [ -t timeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ]"
     40 #define dieusage() strerr_dieusage(100, USAGE) ;
     41 
     42 static unsigned int verbosity = 1 ;
     43 static int cont = 1 ;
     44 static tain answertto = TAIN_INFINITE_RELATIVE ;
     45 static tain lameduckdeadline = TAIN_INFINITE_RELATIVE ;
     46 static tain const nano1 = { .sec = TAI_ZERO, .nano = 1 } ;
     47 
     48 static unsigned int rulestype = 0 ;
     49 static char const *rules = 0 ;
     50 static cdb cdbmap = CDB_ZERO ;
     51 
     52 static void handle_signals (void)
     53 {
     54   for (;;) switch (selfpipe_read())
     55   {
     56     case -1 : strerr_diefu1sys(111, "selfpipe_read()") ;
     57     case 0 : return ;
     58     case SIGTERM :
     59     {
     60       if (cont)
     61       {
     62         cont = 0 ;
     63         tain_add_g(&lameduckdeadline, &lameduckdeadline) ;
     64       }
     65       break ;
     66     }
     67     case SIGHUP :
     68     {
     69       cdb c = CDB_ZERO ;
     70       if (rulestype != 2) break ;
     71       if (!cdb_init(&c, rules)) break ;
     72       cdb_free(&cdbmap) ;
     73       cdbmap = c ;
     74     }
     75     break ;
     76     default : break ;
     77   }
     78 }
     79 
     80 
     81  /* fd store */
     82 
     83 static genset *fdstore ;
     84 #define FD(i) genset_p(s6_fdholder_fd_t, fdstore, (i))
     85 static unsigned int maxfds = 1000 ;
     86 #define numfds genset_n(fdstore)
     87 static avltreen *fds_by_id ;
     88 static avltreen *fds_by_deadline ;
     89 
     90 static void *fds_id_dtok (uint32_t d, void *x)
     91 {
     92   (void)x ;
     93   return FD(d)->id ;
     94 }
     95 
     96 static int fds_id_cmp (void const *a, void const *b, void *x)
     97 {
     98   (void)x ;
     99   return strcmp((char const *)a, (char const *)b) ;
    100 }
    101 
    102 static void *fds_deadline_dtok (uint32_t d, void *x)
    103 {
    104   (void)x ;
    105   return &FD(d)->limit ;
    106 }
    107 
    108 static int fds_deadline_cmp (void const *a, void const *b, void *x)
    109 {
    110   tain const *aa = (tain const *)a ;
    111   tain const *bb = (tain const *)b ;
    112   (void)x ;
    113   return tain_less(aa, bb) ? -1 : tain_less(bb, aa) ;
    114 }
    115 
    116 static void fds_delete (uint32_t pp)
    117 {
    118   avltreen_delete(fds_by_id, fds_id_dtok(pp, 0)) ;
    119   avltreen_delete(fds_by_deadline, fds_deadline_dtok(pp, 0)) ;
    120   genset_delete(fdstore, pp) ;
    121 }
    122 
    123 static void fds_close_and_delete (uint32_t pp)
    124 {
    125   fd_close(FD(pp)->fd) ;
    126   fds_delete(pp) ;
    127 }
    128 
    129 
    130  /* client connection */
    131 
    132 typedef struct client_s client_t, *client_t_ref ;
    133 struct client_s
    134 {
    135   uint32_t next ;
    136   uint32_t xindex ;
    137   tain deadline ;
    138   regex_t rre ;
    139   regex_t wre ;
    140   unsigned int dumping ;
    141   unsigned int flags ;
    142   unixconnection connection ;
    143 } ;
    144 
    145 static genset *clients ;
    146 static uint32_t sentinel ;
    147 #define CLIENT(i) genset_p(client_t, clients, (i))
    148 #define numconn (genset_n(clients) - 1)
    149 
    150 static inline void client_free (client_t *c)
    151 {
    152   fd_close(unixmessage_sender_fd(&c->connection.out)) ;
    153   unixconnection_free(&c->connection) ;
    154   regfree(&c->rre) ;
    155   regfree(&c->wre) ;
    156 }
    157 
    158 static inline void client_delete (uint32_t cc, uint32_t prev)
    159 {
    160   client_t *c = CLIENT(cc) ;
    161   CLIENT(prev)->next = c->next ;
    162   client_free(c) ;
    163   genset_delete(clients, cc) ;
    164 }
    165 
    166 static void removeclient (uint32_t *i, uint32_t j)
    167 {
    168   client_delete(*i, j) ;
    169   *i = j ;
    170 }
    171 
    172 static void client_setdeadline (client_t *c)
    173 {
    174   tain blah ;
    175   tain_half(&blah, &tain_infinite_relative) ;
    176   tain_add_g(&blah, &blah) ;
    177   if (tain_less(&blah, &c->deadline))
    178     tain_add_g(&c->deadline, &answertto) ;
    179 }
    180 
    181 static inline int client_prepare_iopause (uint32_t i, tain *deadline, iopause_fd *x, uint32_t *j)
    182 {
    183   client_t *c = CLIENT(i) ;
    184   if (tain_less(&c->deadline, deadline)) *deadline = c->deadline ;
    185   if (!unixmessage_sender_isempty(&c->connection.out) || !unixmessage_receiver_isempty(&c->connection.in) || (cont && !unixmessage_receiver_isfull(&c->connection.in)))
    186   {
    187     x[*j].fd = unixmessage_sender_fd(&c->connection.out) ;
    188     x[*j].events = (!unixmessage_receiver_isempty(&c->connection.in) || (cont && !unixmessage_receiver_isfull(&c->connection.in)) ? IOPAUSE_READ : 0) | (!unixmessage_sender_isempty(&c->connection.out) ? IOPAUSE_WRITE : 0) ;
    189     c->xindex = (*j)++ ;
    190   }
    191   else c->xindex = 0 ;
    192   return !!c->xindex ;
    193 }
    194 
    195 static inline void client_add (uint32_t *cc, int fd, regex_t const *rre, regex_t const *wre, unsigned int flags)
    196 {
    197   uint32_t i ;
    198   client_t *c ;
    199   i = genset_new(clients) ;
    200   c = CLIENT(i) ;
    201   tain_add_g(&c->deadline, &answertto) ;
    202   c->rre = *rre ;
    203   c->wre = *wre ;
    204   c->dumping = 0 ;
    205   c->flags = flags ;
    206   unixconnection_init(&c->connection, fd, fd) ;
    207   c->next = CLIENT(sentinel)->next ;
    208   CLIENT(sentinel)->next = i ;
    209   *cc = i ;
    210 }
    211 
    212 static inline int client_flush (uint32_t i, iopause_fd const *x)
    213 {
    214   client_t *c = CLIENT(i) ;
    215   if (c->xindex && (x[c->xindex].revents & IOPAUSE_WRITE))
    216   {
    217     if (unixconnection_flush(&c->connection))
    218       tain_add_g(&c->deadline, &tain_infinite_relative) ;
    219     else if (!error_isagain(errno)) return 0 ;
    220   }
    221   return 1 ;
    222 }
    223 
    224 static int answer (client_t *c, char e)
    225 {
    226   unixmessage m = { .s = &e, .len = 1, .fds = 0, .nfds = 0 } ;
    227   if (!unixmessage_put(&c->connection.out, &m)) return 0 ;
    228   client_setdeadline(c) ;
    229   return 1 ;
    230 }
    231 
    232 static int do_store (uint32_t cc, unixmessage const *m)
    233 {
    234   uint32_t pp, idlen ;
    235   client_t *c = CLIENT(cc) ;
    236   s6_fdholder_fd_t *p ;
    237   if (c->dumping || m->len < TAIN_PACK + 3 || m->nfds != 1) return (errno = EPROTO, 0) ;
    238   idlen = (unsigned char)m->s[TAIN_PACK] ;
    239   if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 2 + TAIN_PACK != m->len || m->s[1 + TAIN_PACK + idlen]) return (errno = EPROTO, 0) ;
    240   if (regexec(&c->wre, m->s + TAIN_PACK + 1, 0, 0, 0))
    241   {
    242     unixmessage_drop(m) ;
    243     return answer(c, EPERM) ;
    244   }
    245   if (numfds >= maxfds)
    246   {
    247     unixmessage_drop(m) ;
    248     return answer(c, ENFILE) ;
    249   }
    250   if (avltreen_search(fds_by_id, m->s + TAIN_PACK + 1, &pp))
    251   {
    252     unixmessage_drop(m) ;
    253     return answer(c, EBUSY) ;
    254   }
    255   if (!answer(c, 0)) return 0 ;
    256   pp = genset_new(fdstore) ; p = FD(pp) ;
    257   tain_unpack(m->s, &p->limit) ; p->fd = m->fds[0] ;
    258   memcpy(p->id, m->s + TAIN_PACK + 1, idlen) ;
    259   memset(p->id + idlen, 0, S6_FDHOLDER_ID_SIZE + 1 - idlen) ;
    260   for (;;)
    261   {
    262     uint32_t dummy ;
    263     if (!avltreen_search(fds_by_deadline, &p->limit, &dummy)) break ;
    264     tain_add(&p->limit, &p->limit, &nano1) ;
    265   }
    266   avltreen_insert(fds_by_id, pp) ;
    267   avltreen_insert(fds_by_deadline, pp) ;
    268   return 1 ;
    269 }
    270 
    271 static int do_delete (uint32_t cc, unixmessage const *m)
    272 {
    273   uint32_t pp, idlen ;
    274   client_t *c = CLIENT(cc) ;
    275   if (c->dumping || m->len < 3 || m->nfds) return (errno = EPROTO, 0) ;
    276   idlen = (unsigned char)m->s[0] ;
    277   if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 2 != m->len || m->s[idlen + 1]) return (errno = EPROTO, 0) ;
    278   if (regexec(&c->wre, m->s + 1, 0, 0, 0)) return answer(c, EPERM) ;
    279   if (!avltreen_search(fds_by_id, m->s + 1, &pp)) return answer(c, ENOENT) ;
    280   if (!answer(c, 0)) return 0 ;
    281   fds_close_and_delete(pp) ;
    282   return 1 ;
    283 }
    284 
    285 static int do_retrieve (uint32_t cc, unixmessage const *m)
    286 {
    287   int fd ;
    288   unixmessage ans = { .s = "", .len = 1, .fds = &fd, .nfds = 1 } ;
    289   uint32_t pp, idlen ;
    290   client_t *c = CLIENT(cc) ;
    291   if (c->dumping || m->len < 4 || m->nfds) return (errno = EPROTO, 0) ;
    292   idlen = (unsigned char)m->s[1] ;
    293   if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 3 != m->len || m->s[idlen + 2]) return (errno = EPROTO, 0) ;
    294   if (regexec(&c->rre, m->s + 2, 0, 0, 0)) return answer(c, EPERM) ;
    295   if (m->s[0] && regexec(&c->wre, m->s + 2, 0, 0, 0)) return answer(c, EPERM) ;
    296   if (!avltreen_search(fds_by_id, m->s + 2, &pp)) return answer(c, ENOENT) ;
    297   fd = FD(pp)->fd ;
    298   if (!unixmessage_put_and_close(&c->connection.out, &ans, m->s[0] ? unixmessage_bits_closeall : unixmessage_bits_closenone)) return 0 ;
    299   if (m->s[0]) fds_delete(pp) ;
    300   return 1 ;
    301 }
    302 
    303 static int fill_siovec_with_ids_iter (void *thing, void *data)
    304 {
    305   struct iovec *v = (*(struct iovec **)data)++ ;
    306   s6_fdholder_fd_t *p = thing ;
    307   v->iov_base = p->id ;
    308   v->iov_len = strlen(p->id) + 1 ;
    309   return 1 ;
    310 }
    311 
    312 static int do_list (uint32_t cc, unixmessage const *m)
    313 {
    314   client_t *c = CLIENT(cc) ;
    315   struct iovec v[1+numfds] ;
    316   unixmessagev ans = { .v = v, .vlen = 1+numfds, .fds = 0, .nfds = 0 } ;
    317   struct iovec *vp = v + 1 ;
    318   char pack[5] = "" ;
    319   if (c->dumping || m->len || m->nfds) return (errno = EPROTO, 0) ;
    320   if (!(c->flags & 4)) return answer(c, EPERM) ;
    321   uint32_pack_big(pack + 1, (uint32_t)numfds) ;
    322   v[0].iov_base = pack ; v[0].iov_len = 5 ;
    323   genset_iter(fdstore, &fill_siovec_with_ids_iter, &vp) ;
    324   if (!unixmessage_putv(&c->connection.out, &ans)) return answer(c, errno) ;
    325   client_setdeadline(c) ;
    326   return 1 ;
    327 }
    328 
    329 typedef struct getdumpiter_s getdumpiter_t, *getdumpiter_t_ref ;
    330 struct getdumpiter_s
    331 {
    332   struct iovec *v ;
    333   int *fd ;
    334   char *limit ;
    335 } ;
    336 
    337 static int getdump_iter (void *thing, void *stuff)
    338 {
    339   s6_fdholder_fd_t *p = thing ;
    340   getdumpiter_t *blah = stuff ;
    341   unsigned char len = strlen(p->id) ;
    342   tain_pack(blah->limit, &p->limit) ;
    343   blah->limit[TAIN_PACK] = len ;
    344   blah->v->iov_base = p->id ;
    345   blah->v->iov_len = len + 1 ;
    346   *blah->fd++ = p->fd ;
    347   blah->v += 2 ;
    348   blah->limit += TAIN_PACK + 1 ;
    349   return 1 ;
    350 }
    351 
    352 static int do_getdump (uint32_t cc, unixmessage const *m)
    353 {
    354   uint32_t n = numfds ? 1 + ((numfds-1) / UNIXMESSAGE_MAXFDS) : 0 ;
    355   client_t *c = CLIENT(cc) ;
    356   if (c->dumping || m->len || m->nfds) return (errno = EPROTO, 0) ;
    357   if (!(c->flags & 1)) return answer(c, EPERM) ;
    358   {
    359     char pack[9] = "" ;
    360     unixmessage ans = { .s = pack, .len = 9, .fds = 0, .nfds = 0 } ;
    361     uint32_pack_big(pack+1, n) ;
    362     uint32_pack_big(pack+5, numfds) ;
    363     if (!unixmessage_put(&c->connection.out, &ans)) return answer(c, errno) ;
    364   }
    365   if (n)
    366   {
    367     uint32_t i = 0 ;
    368     unixmessagev ans[n] ;
    369     struct iovec v[numfds << 1] ;
    370     int fds[numfds] ;
    371     char limits[(TAIN_PACK+1) * numfds] ;
    372     for (; i < numfds ; i++)
    373     {
    374       v[i<<1].iov_base = limits + i * (TAIN_PACK+1) ;
    375       v[i<<1].iov_len = TAIN_PACK+1 ;
    376     }
    377     {
    378       getdumpiter_t state = { .v = v+1, .fd = fds, .limit = limits } ;
    379       genset_iter(fdstore, &getdump_iter, &state) ;
    380     }
    381     for (i = 0 ; i < n-1 ; i++)
    382     {
    383       ans[i].v = v + (i<<1) * UNIXMESSAGE_MAXFDS ;
    384       ans[i].vlen = UNIXMESSAGE_MAXFDS << 1 ;
    385       ans[i].fds = fds + i * UNIXMESSAGE_MAXFDS ;
    386       ans[i].nfds = UNIXMESSAGE_MAXFDS ;
    387     }
    388     ans[n-1].v = v + ((n-1)<<1) * UNIXMESSAGE_MAXFDS ;
    389     ans[n-1].vlen = (1 + (numfds-1) % UNIXMESSAGE_MAXFDS) << 1 ;
    390     ans[n-1].fds = fds + (n-1) * UNIXMESSAGE_MAXFDS ;
    391     ans[n-1].nfds = 1 + (numfds - 1) % UNIXMESSAGE_MAXFDS ;
    392     for (i = 0 ; i < n ; i++)
    393     {
    394       if (!unixmessage_putv(&c->connection.out, ans + i))
    395       {
    396         int e = errno ;
    397         ++i ;
    398         while (i--) unixmessage_unput(&c->connection.out) ;
    399         return answer(c, e) ;
    400       }
    401     }
    402   }
    403   client_setdeadline(c) ;
    404   return 1 ;
    405 }
    406 
    407 static int do_setdump (uint32_t cc, unixmessage const *m)
    408 {
    409   char pack[5] = "" ;
    410   uint32_t n ;
    411   unixmessage ans = { .s = pack, .len = 5, .fds = 0, .nfds = 0 } ;
    412   client_t *c = CLIENT(cc) ;
    413   if (c->dumping || m->len != 4 || m->nfds) return (errno = EPROTO, 0) ;
    414   if (!(c->flags & 2)) return answer(c, EPERM) ;
    415   uint32_unpack_big(m->s, &n) ;
    416   if (n > maxfds || n + numfds > maxfds) return answer(c, ENFILE) ;
    417   c->dumping = n ;
    418   n = n ? 1 + (n-1) / UNIXMESSAGE_MAXFDS : 0 ;
    419   uint32_pack_big(pack+1, n) ;
    420   if (!unixmessage_put(&c->connection.out, &ans)) return answer(c, errno) ;
    421   return 1 ;
    422 }
    423 
    424 static int do_setdump_data (uint32_t cc, unixmessage const *m)
    425 {
    426   client_t *c = CLIENT(cc) ;
    427   if (!m->nfds || m->nfds > c->dumping || m->len < m->nfds * (TAIN_PACK + 3))
    428     return (errno = EPROTO, 0) ;
    429   if (!(c->flags & 2))
    430   {
    431     unixmessage_drop(m) ;
    432     return answer(c, EPERM) ;
    433   }
    434   if (numfds + m->nfds > maxfds)
    435   {
    436     unixmessage_drop(m) ;
    437     return answer(c, ENFILE) ;
    438   }
    439   {
    440     char const *s = m->s ;
    441     size_t len = m->len ;
    442     uint32_t i = 0 ;
    443     uint32_t indices[m->nfds] ;
    444     for (; i < m->nfds ; i++)
    445     {
    446       s6_fdholder_fd_t *p ;
    447       uint32_t oldid, idlen ;
    448       if (len < TAIN_PACK + 3) break ;
    449       idlen = (unsigned char)s[TAIN_PACK] ;
    450       if (idlen > S6_FDHOLDER_ID_SIZE || len < TAIN_PACK + 2 + idlen || s[TAIN_PACK + 1 + idlen]) break ;
    451       indices[i] = genset_new(fdstore) ;
    452       p = FD(indices[i]) ;
    453       tain_unpack(s, &p->limit) ;
    454       memcpy(p->id, s + TAIN_PACK + 1, idlen+1) ;
    455       p->fd = m->fds[i] ;
    456       if (avltreen_search(fds_by_id, p->id, &oldid)) fds_close_and_delete(oldid) ;
    457       avltreen_insert(fds_by_id, indices[i]) ;
    458       avltreen_insert(fds_by_deadline, indices[i]) ;
    459       s += TAIN_PACK + 2 + idlen ; len -= TAIN_PACK + 2 + idlen ;
    460     }
    461     if (i < m->nfds || len)
    462     {
    463       while (i--) fds_delete(indices[i]) ;
    464       return (errno = EPROTO, 0) ;
    465     }
    466   }
    467   c->dumping -= m->nfds ;
    468   return answer(c, c->dumping ? EAGAIN : 0) ;
    469 }
    470 
    471 static int do_error (uint32_t cc, unixmessage const *m)
    472 {
    473   (void)cc ;
    474   (void)m ;
    475   return (errno = EPROTO, 0) ;
    476 }
    477 
    478 typedef int parse_func (uint32_t, unixmessage const *) ;
    479 typedef parse_func *parse_func_ref ;
    480 
    481 static inline int parse_protocol (unixmessage const *m, void *p)
    482 {
    483   static parse_func_ref const f[8] =
    484   {
    485     &do_store,
    486     &do_delete,
    487     &do_retrieve,
    488     &do_list,
    489     &do_getdump,
    490     &do_setdump,
    491     &do_setdump_data,
    492     &do_error
    493   } ;
    494   unixmessage mcopy = { .s = m->s + 1, .len = m->len - 1, .fds = m->fds, .nfds = m->nfds } ;
    495   if (!m->len)
    496   {
    497     unixmessage_drop(m) ;
    498     return (errno = EPROTO, 0) ;
    499   }
    500   if (!(*f[byte_chr("SDRL?!.", 7, m->s[0])])(*(uint32_t *)p, &mcopy))
    501   {
    502     unixmessage_drop(m) ;
    503     return 0 ;
    504   }
    505   return 1 ;
    506 }
    507 
    508 static inline int client_read (uint32_t cc, iopause_fd const *x)
    509 {
    510   client_t *c = CLIENT(cc) ;
    511   return !unixmessage_receiver_isempty(&c->connection.in) || (c->xindex && (x[c->xindex].revents & IOPAUSE_READ)) ?
    512     unixmessage_handle(&c->connection.in, &parse_protocol, &cc) > 0 : 1 ;
    513 }
    514 
    515 
    516  /* Environment on new connections */
    517 
    518 static int makere (regex_t *re, char const *s, char const *var)
    519 {
    520   size_t varlen = strlen(var) ;
    521   if (str_start(s, var) && (s[varlen] == '='))
    522   {
    523     int r = skalibs_regcomp(re, s + varlen + 1, REG_EXTENDED | REG_NOSUB) ;
    524     if (r)
    525     {
    526       if (verbosity)
    527       {
    528         char buf[256] ;
    529         regerror(r, re, buf, 256) ;
    530         strerr_warnw6x("invalid ", var, " value: ", s + varlen + 1, ": ", buf) ;
    531       }
    532       return -1 ;
    533     }
    534     else return 1 ;
    535   }
    536   return 0 ;
    537 }
    538 
    539 static void defaultre (regex_t *re)
    540 {
    541   int r = skalibs_regcomp(re, ".^", REG_EXTENDED | REG_NOSUB) ;
    542   if (r)
    543   {
    544     char buf[256] ;
    545     regerror(r, re, buf, 256) ;
    546     strerr_diefu2x(100, "compile .^ into a regular expression: ", buf) ;
    547   }
    548 }
    549 
    550 static inline int parse_env (char const *const *envp, regex_t *rre, regex_t *wre, unsigned int *flags, unsigned int *donep)
    551 {
    552   unsigned int done = 0 ;
    553   unsigned int fl = 0 ;
    554   for (; *envp ; envp++)
    555   {
    556     if (str_start(*envp, "S6_FDHOLDER_GETDUMP=")) fl |= 1 ;
    557     if (str_start(*envp, "S6_FDHOLDER_SETDUMP=")) fl |= 2 ;
    558     if (str_start(*envp, "S6_FDHOLDER_LIST=")) fl |= 4 ;
    559     if (!(done & 1))
    560     {
    561       int r = makere(rre, *envp, "S6_FDHOLDER_RETRIEVE_REGEX") ;
    562       if (r < 0)
    563       {
    564         if (done & 2) regfree(wre) ;
    565         return 0 ;
    566       }
    567       else if (r) done |= 1 ;
    568     }
    569     if (!(done & 2))
    570     {
    571       int r = makere(wre, *envp, "S6_FDHOLDER_STORE_REGEX") ;
    572       if (r < 0)
    573       {
    574         if (done & 1) regfree(rre) ;
    575         return 0 ;
    576       }
    577       else if (r) done |= 2 ;
    578     }
    579   }
    580   *flags = fl ;
    581   *donep = done ;
    582   return 1 ;
    583 }
    584 
    585 static inline int new_connection (int fd, regex_t *rre, regex_t *wre, unsigned int *flags)
    586 {
    587   s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ;
    588   s6_accessrules_result_t result = S6_ACCESSRULES_ERROR ;
    589   uid_t uid ;
    590   gid_t gid ;
    591   unsigned int done = 0 ;
    592 
    593   if (getpeereid(fd, &uid, &gid) < 0)
    594   {
    595     if (verbosity) strerr_warnwu1sys("getpeereid") ;
    596     return 0 ;
    597   }
    598 
    599   switch (rulestype)
    600   {
    601     case 1 :
    602       result = s6_accessrules_uidgid_fs(uid, gid, rules, &params) ; break ;
    603     case 2 :
    604       result = s6_accessrules_uidgid_cdb(uid, gid, &cdbmap, &params) ; break ;
    605     default : break ;
    606   }
    607   if (result != S6_ACCESSRULES_ALLOW)
    608   {
    609     if (verbosity && (result == S6_ACCESSRULES_ERROR))
    610        strerr_warnw1sys("error while checking rules") ;
    611     return 0 ;
    612   }
    613   if (params.exec.len && verbosity)
    614   {
    615     char fmtuid[UID_FMT] ;
    616     char fmtgid[GID_FMT] ;
    617     fmtuid[uid_fmt(fmtuid, uid)] = 0 ;
    618     fmtgid[gid_fmt(fmtgid, gid)] = 0 ;
    619     strerr_warnw4x("unused exec string in rules for uid ", fmtuid, " gid ", fmtgid) ;
    620   }
    621   if (params.env.s)
    622   {
    623     size_t n = byte_count(params.env.s, params.env.len, '\0') ;
    624     char const *envp[n+1] ;
    625     if (!env_make(envp, n, params.env.s, params.env.len))
    626     {
    627       if (verbosity) strerr_warnwu1sys("env_make") ;
    628       s6_accessrules_params_free(&params) ;
    629       return 0 ;
    630     }
    631     envp[n] = 0 ;
    632     if (!parse_env(envp, rre, wre, flags, &done))
    633     {
    634       if (verbosity) strerr_warnwu1sys("parse_env") ;
    635       s6_accessrules_params_free(&params) ;
    636       return 0 ;
    637     }
    638   }
    639   s6_accessrules_params_free(&params) ;
    640   if (!(done & 1)) defaultre(rre) ;
    641   if (!(done & 2)) defaultre(wre) ;
    642   return 1 ;
    643 }
    644 
    645 
    646 int main (int argc, char const *const *argv, char const *const *envp)
    647 {
    648   int flag1 = 0 ;
    649   uint32_t maxconn = 16 ;
    650   PROG = "s6-fdholderd" ;
    651 
    652   {
    653     subgetopt l = SUBGETOPT_ZERO ;
    654     unsigned int t = 0, T = 0 ;
    655     for (;;)
    656     {
    657       int opt = subgetopt_r(argc, argv, "v:1c:n:i:x:t:T:", &l) ;
    658       if (opt == -1) break ;
    659       switch (opt)
    660       {
    661         case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ;
    662         case '1' : flag1 = 1 ; break ;
    663         case 'i' : rules = l.arg ; rulestype = 1 ; break ;
    664         case 'x' : rules = l.arg ; rulestype = 2 ; break ;
    665         case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
    666         case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ;
    667         case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ;
    668         case 'n' : if (!uint0_scan(l.arg, &maxfds)) dieusage() ; break ;
    669         default : dieusage() ;
    670       }
    671     }
    672     argc -= l.ind ; argv += l.ind ;
    673     if (t) tain_from_millisecs(&answertto, t) ;
    674     if (T) tain_from_millisecs(&lameduckdeadline, T) ;
    675   }
    676   if (!rulestype) strerr_dief1x(100, "no access rights specified!") ;
    677   if (maxconn > S6_FDHOLDER_MAX) maxconn = S6_FDHOLDER_MAX ;
    678   if (!maxconn) maxconn = 1 ;
    679   {
    680     struct rlimit fdlimit ;
    681     if (getrlimit(RLIMIT_NOFILE, &fdlimit) < 0)
    682       strerr_diefu1sys(111, "getrlimit") ;
    683     if (fdlimit.rlim_cur != RLIM_INFINITY)
    684     {
    685       if (fdlimit.rlim_cur < 7)
    686         strerr_dief1x(111, "open file limit too low") ;
    687       if (maxfds > fdlimit.rlim_cur) maxfds = fdlimit.rlim_cur - 6 ;
    688     }
    689   }
    690   if (!maxfds) maxfds = 1 ;
    691   {
    692     struct stat st ;
    693     if (fstat(0, &st) < 0) strerr_diefu1sys(111, "fstat stdin") ;
    694     if (!S_ISSOCK(st.st_mode)) strerr_dief1x(100, "stdin is not a socket") ;
    695   }
    696   if (flag1)
    697   {
    698     if (fcntl(1, F_GETFD) < 0)
    699       strerr_dief1sys(100, "called with option -1 but stdout said") ;
    700   }
    701   else close(1) ;
    702   if (selfpipe_init() == -1) strerr_diefu1sys(111, "selfpipe_init") ;
    703   if (!sig_ignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ;
    704   {
    705     sigset_t set ;
    706     sigemptyset(&set) ;
    707     sigaddset(&set, SIGTERM) ;
    708     sigaddset(&set, SIGHUP) ;
    709     if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ;
    710   }
    711 
    712   if (rulestype == 2)
    713   {
    714     if (!cdb_init(&cdbmap, rules))
    715       strerr_diefu2sys(111, "cdb_init ", rules) ;
    716   }
    717 
    718   {
    719      /* Hello, stack. I have a present for you. */
    720     genset clientinfo, fdinfo ;
    721     avltreen fdidinfo, fddeadlineinfo ;
    722     iopause_fd x[2 + maxconn] ;
    723     client_t clientstorage[1+maxconn] ;
    724     uint32_t clientfreelist[1+maxconn] ;
    725     s6_fdholder_fd_t fdstorage[maxfds] ;
    726     uint32_t fdfreelist[maxfds] ;
    727     avlnode fdidstorage[maxfds] ;
    728     uint32_t fdidfreelist[maxfds] ;
    729     avlnode fddeadlinestorage[maxfds] ;
    730     uint32_t fddeadlinefreelist[maxfds] ;
    731      /* Hope you enjoyed it! Have a nice day! */
    732 
    733     GENSET_init(&clientinfo, client_t, clientstorage, clientfreelist, 1+maxconn) ;
    734     clients = &clientinfo ;
    735     sentinel = genset_new(clients) ;
    736     clientstorage[sentinel].next = sentinel ;
    737     GENSET_init(&fdinfo, s6_fdholder_fd_t, fdstorage, fdfreelist, maxfds) ;
    738     fdstore = &fdinfo ;
    739     avltreen_init(&fdidinfo, fdidstorage, fdidfreelist, maxfds, &fds_id_dtok, &fds_id_cmp, 0) ;
    740     fds_by_id = &fdidinfo ;
    741     avltreen_init(&fddeadlineinfo, fddeadlinestorage, fddeadlinefreelist, maxfds, &fds_deadline_dtok, &fds_deadline_cmp, 0) ;
    742     fds_by_deadline = &fddeadlineinfo ;
    743 
    744     x[0].fd = selfpipe_fd() ; x[0].events = IOPAUSE_READ ;
    745     x[1].fd = 0 ;
    746       
    747     if (flag1)
    748     {
    749       fd_write(1, "\n", 1) ;
    750       fd_close(1) ;
    751     }
    752 
    753     /* We are long-lived and have to check absolute fd deadlines,
    754        so we purposefully remain in wallclock mode. */
    755     tain_now_g() ;
    756 
    757     for (;;)
    758     {
    759       tain deadline ;
    760       uint32_t j = 2 ;
    761       uint32_t i ;
    762       int r = 1 ;
    763 
    764       if (cont) tain_add_g(&deadline, &tain_infinite_relative) ;
    765       else deadline = lameduckdeadline ;
    766       if (avltreen_min(fds_by_deadline, &i) && tain_less(&FD(i)->limit, &deadline)) deadline = FD(i)->limit ;
    767       x[1].events = (cont && (numconn < maxconn)) ? IOPAUSE_READ : 0 ;
    768       for (i = clientstorage[sentinel].next ; i != sentinel ; i = clientstorage[i].next)
    769         if (client_prepare_iopause(i, &deadline, x, &j)) r = 0 ;
    770       if (!cont && r) break ;
    771 
    772       r = iopause_g(x, j, &deadline) ;
    773       if (r < 0) strerr_diefu1sys(111, "iopause") ;
    774 
    775       if (!r)
    776       {
    777         if (!cont && !tain_future(&lameduckdeadline)) break ;
    778         for (;;)
    779         {
    780           if (!avltreen_min(fds_by_deadline, &i)) break ;
    781           if (tain_future(&FD(i)->limit)) break ;
    782           fds_close_and_delete(i) ;
    783         }
    784         for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next)
    785           if (!tain_future(&clientstorage[i].deadline)) removeclient(&i, j) ;
    786         continue ;
    787       }
    788 
    789       if (x[0].revents & IOPAUSE_READ) handle_signals() ;
    790 
    791       for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next)
    792         if (!client_flush(i, x)) removeclient(&i, j) ;
    793 
    794       for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next)
    795         if (!client_read(i, x)) removeclient(&i, j) ;
    796 
    797       if (x[1].revents & IOPAUSE_READ)
    798       {
    799         regex_t rre, wre ;
    800         unsigned int flags = 0 ;
    801         int dummy ;
    802         int fd = ipc_accept_nb(x[1].fd, 0, 0, &dummy) ;
    803         if (fd < 0)
    804           if (!error_isagain(errno)) strerr_diefu1sys(111, "accept") ;
    805           else continue ;
    806         else if (!new_connection(fd, &rre, &wre, &flags)) fd_close(fd) ;
    807         else client_add(&i, fd, &rre, &wre, flags) ;
    808       }
    809     }
    810     return ((!!numfds) | (!!numconn << 1)) ;
    811   }
    812 }