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 }