s6

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

s6-ftrigrd.c (6700B)


      1 /* ISC license. */
      2 
      3 #include <sys/uio.h>
      4 #include <string.h>
      5 #include <stdint.h>
      6 #include <errno.h>
      7 #include <signal.h>
      8 #include <regex.h>
      9 
     10 #include <skalibs/posixplz.h>
     11 #include <skalibs/types.h>
     12 #include <skalibs/allreadwrite.h>
     13 #include <skalibs/error.h>
     14 #include <skalibs/strerr.h>
     15 #include <skalibs/buffer.h>
     16 #include <skalibs/stralloc.h>
     17 #include <skalibs/sig.h>
     18 #include <skalibs/tai.h>
     19 #include <skalibs/djbunix.h>
     20 #include <skalibs/iopause.h>
     21 #include <skalibs/textmessage.h>
     22 #include <skalibs/textclient.h>
     23 
     24 #include "ftrig1.h"
     25 #include <s6/ftrigr.h>
     26 
     27 #include <skalibs/posixishard.h>
     28 
     29 #define FTRIGRD_MAXREADS 32
     30 #define FTRIGRD_BUFSIZE 17
     31 
     32 #define dienomem() strerr_diefu1sys(111, "stralloc_catb")
     33 
     34 typedef struct ftrigio_s ftrigio_t, *ftrigio_t_ref ;
     35 struct ftrigio_s
     36 {
     37   unsigned int xindex ;
     38   ftrig1_t trig ;
     39   buffer b ;
     40   char buf[FTRIGRD_BUFSIZE] ;
     41   regex_t re ;
     42   stralloc sa ;
     43   uint32_t options ;
     44   uint16_t id ; /* given by client */
     45 } ;
     46 #define FTRIGIO_ZERO { .xindex = 0, .trig = FTRIG1_ZERO, .b = BUFFER_INIT(0, -1, 0, 0), .buf = "", .sa = STRALLOC_ZERO, .options = 0, .id = 0 }
     47 
     48 static ftrigio_t a[FTRIGR_MAX] ;
     49 static unsigned int n = 0 ;
     50 
     51 static void ftrigio_deepfree (ftrigio_t *p)
     52 {
     53   ftrig1_free(&p->trig) ;
     54   stralloc_free(&p->sa) ;
     55   regfree(&p->re) ;
     56 }
     57 
     58 static void cleanup (void)
     59 {
     60   unsigned int i = 0 ;
     61   for (; i < n ; i++) ftrigio_deepfree(a + i) ;
     62   n = 0 ;
     63 }
     64 
     65 static void trig (uint16_t id, char what, char info)
     66 {
     67   char pack[4] ;
     68   uint16_pack_big(pack, id) ;
     69   pack[2] = what ; pack[3] = info ;
     70   if (!textmessage_put(textmessage_sender_x, pack, 4))
     71   {
     72     cleanup() ;
     73     strerr_diefu1sys(111, "build answer") ;
     74   }
     75 }
     76 
     77 static void answer (unsigned char c)
     78 {
     79   if (!textmessage_put(textmessage_sender_1, (char *)&c, 1))
     80   {
     81     cleanup() ;
     82     strerr_diefu1sys(111, "textmessage_put") ;
     83   }
     84 }
     85 
     86 static void remove (unsigned int i)
     87 {
     88   ftrigio_deepfree(a + i) ;
     89   a[i] = a[--n] ;
     90   a[i].b.c.x = a[i].buf ;
     91 }
     92 
     93 static inline int ftrigio_read (ftrigio_t *p)
     94 {
     95   unsigned int i = FTRIGRD_MAXREADS ;
     96   while (i--)
     97   {
     98     regmatch_t pmatch ;
     99     size_t blen ;
    100     ssize_t r = sanitize_read(buffer_fill(&p->b)) ;
    101     if (!r) break ;
    102     if (r < 0) return (trig(p->id, 'd', errno), 0) ;
    103     blen = buffer_len(&p->b) ;
    104     if (!stralloc_readyplus(&p->sa, blen+1)) dienomem() ;
    105     buffer_getnofill(&p->b, p->sa.s + p->sa.len, blen) ;
    106     p->sa.len += blen ;
    107     p->sa.s[p->sa.len] = 0 ;
    108     while (!regexec(&p->re, p->sa.s, 1, &pmatch, REG_NOTBOL | REG_NOTEOL))
    109     {
    110       trig(p->id, '!', p->sa.s[pmatch.rm_eo - 1]) ;
    111       if (!(p->options & FTRIGR_REPEAT)) return 0 ;
    112       memmove(p->sa.s, p->sa.s + pmatch.rm_eo, p->sa.len + 1 - pmatch.rm_eo) ;
    113       p->sa.len -= pmatch.rm_eo ;
    114     }
    115   }
    116   return 1 ;
    117 }
    118 
    119 static int parse_protocol (struct iovec const *v, void *context)
    120 {
    121   char const *s = v->iov_base ;
    122   uint16_t id ;
    123   if (v->iov_len < 3)
    124   {
    125     cleanup() ;
    126     strerr_dief1x(100, "invalid client request") ;
    127   }
    128   uint16_unpack_big(s, &id) ;
    129   switch (s[2])
    130   {
    131     case 'U' : /* unsubscribe */
    132     {
    133       unsigned int i = 0 ;
    134       for (; i < n ; i++) if (a[i].id == id) break ;
    135       if (i < n)
    136       {
    137         remove(i) ;
    138         answer(0) ;
    139       }
    140       else answer(EINVAL) ;
    141       break ;
    142     }
    143     case 'L' : /* subscribe to path and match re */
    144     {
    145       uint32_t options, pathlen, relen ;
    146       int r ;
    147       if (v->iov_len < 19)
    148       {
    149         answer(EPROTO) ;
    150         break ;
    151       }
    152       uint32_unpack_big(s + 3, &options) ;
    153       uint32_unpack_big(s + 7, &pathlen) ;
    154       uint32_unpack_big(s + 11, &relen) ;
    155       if (((pathlen + relen + 17) != v->iov_len) || s[15 + pathlen] || s[v->iov_len - 1])
    156       {
    157         answer(EPROTO) ;
    158         break ;
    159       }
    160       if (n >= FTRIGR_MAX)
    161       {
    162         answer(ENFILE) ;
    163         break ;
    164       }
    165       r = skalibs_regcomp(&a[n].re, s + 16 + pathlen, REG_EXTENDED) ;
    166       if (r)
    167       {
    168         answer(r == REG_ESPACE ? ENOMEM : EINVAL) ;
    169         break ;
    170       }
    171       if (!ftrig1_make(&a[n].trig, s + 15))
    172       {
    173         regfree(&a[n].re) ;
    174         answer(errno) ;
    175         break ;
    176       }
    177       buffer_init(&a[n].b, &buffer_read, a[n].trig.fd, a[n].buf, FTRIGRD_BUFSIZE) ;
    178       a[n].options = options ;
    179       a[n].id = id ;
    180       a[n].sa = stralloc_zero ;
    181       n++ ;
    182       answer(0) ;
    183       break ;
    184     }
    185     default :
    186     {
    187       cleanup() ;
    188       strerr_dief1x(100, "invalid client request") ;
    189     }
    190   }
    191   (void)context ;
    192   return 1 ;
    193 }
    194 
    195 int main (void)
    196 {
    197   PROG = "s6-ftrigrd" ;
    198 
    199   if (ndelay_on(0) < 0) strerr_diefu2sys(111, "ndelay_on ", "0") ;
    200   if (ndelay_on(1) < 0) strerr_diefu2sys(111, "ndelay_on ", "1") ;
    201   if (!sig_ignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ;
    202 
    203   {
    204     tain deadline ;
    205     tain_now_set_stopwatch_g() ;
    206     tain_addsec_g(&deadline, 2) ;
    207     if (!textclient_server_01x_init_g(FTRIGR_BANNER1, FTRIGR_BANNER1_LEN, FTRIGR_BANNER2, FTRIGR_BANNER2_LEN, &deadline))
    208       strerr_diefu1sys(111, "sync with client") ;
    209   }
    210 
    211   for (;;)
    212   {
    213     iopause_fd x[3 + n] ;
    214     unsigned int i = 0 ;
    215 
    216     x[0].fd = 0 ; x[0].events = IOPAUSE_EXCEPT | IOPAUSE_READ ;
    217     x[1].fd = 1 ; x[1].events = IOPAUSE_EXCEPT | (textmessage_sender_isempty(textmessage_sender_1) ? 0 : IOPAUSE_WRITE) ;
    218     x[2].fd = textmessage_sender_fd(textmessage_sender_x) ;
    219     x[2].events = IOPAUSE_EXCEPT | (textmessage_sender_isempty(textmessage_sender_x) ? 0 : IOPAUSE_WRITE) ;
    220     for (; i < n ; i++)
    221     {
    222       a[i].xindex = 3 + i ;
    223       x[3+i].fd = a[i].trig.fd ;
    224       x[3+i].events = IOPAUSE_READ ;
    225     }
    226 
    227     if (iopause(x, 3 + n, 0, 0) < 0)
    228     {
    229       cleanup() ;
    230       strerr_diefu1sys(111, "iopause") ;
    231     }
    232 
    233    /* client closed */
    234     if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) break ;
    235 
    236    /* client is reading */
    237     if (x[1].revents & IOPAUSE_WRITE)
    238       if (!textmessage_sender_flush(textmessage_sender_1) && !error_isagain(errno))
    239       {
    240         cleanup() ;
    241         strerr_diefu1sys(111, "flush stdout") ;
    242       }
    243     if (x[2].revents & IOPAUSE_WRITE)
    244       if (!textmessage_sender_flush(textmessage_sender_x) && !error_isagain(errno))
    245       {
    246         cleanup() ;
    247         return 1 ;
    248       }
    249 
    250    /* scan listening ftrigs */
    251     for (i = 0 ; i < n ; i++)
    252     {
    253       if (x[a[i].xindex].revents & IOPAUSE_READ)
    254         if (!ftrigio_read(a+i)) remove(i--) ;
    255     }
    256 
    257    /* client is writing */
    258     if (!textmessage_receiver_isempty(textmessage_receiver_0) || x[0].revents & IOPAUSE_READ)
    259     {
    260       if (textmessage_handle(textmessage_receiver_0, &parse_protocol, 0) < 0)
    261       {
    262         if (errno == EPIPE) break ; /* normal exit */
    263         cleanup() ;
    264         strerr_diefu1sys(111, "handle messages from client") ;
    265       }
    266     }
    267   }
    268   cleanup() ;
    269   return 0 ;
    270 }