summaryrefslogtreecommitdiff
path: root/src/libs6/s6-ftrigrd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs6/s6-ftrigrd.c')
-rw-r--r--src/libs6/s6-ftrigrd.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/libs6/s6-ftrigrd.c b/src/libs6/s6-ftrigrd.c
new file mode 100644
index 0000000..b766f36
--- /dev/null
+++ b/src/libs6/s6-ftrigrd.c
@@ -0,0 +1,266 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <signal.h>
+#include <regex.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/error.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/bufalloc.h>
+#include <skalibs/sig.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/iopause.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include "ftrig1.h"
+#include <s6/ftrigr.h>
+
+#define FTRIGRD_MAXREADS 32
+#define FTRIGRD_BUFSIZE 16
+
+#define dienomem() strerr_diefu1sys(111, "stralloc_catb")
+
+typedef struct ftrigio_s ftrigio_t, *ftrigio_t_ref ;
+struct ftrigio_s
+{
+ unsigned int xindex ;
+ ftrig1_t trig ;
+ buffer b ;
+ char buf[FTRIGRD_BUFSIZE] ;
+ regex_t re ;
+ stralloc sa ;
+ uint32 options ;
+ uint16 id ; /* given by client */
+} ;
+#define FTRIGIO_ZERO { .xindex = 0, .trig = FTRIG1_ZERO, .b = BUFFER_INIT(0, -1, 0, 0), .buf = "", .sa = STRALLOC_ZERO, .options = 0, .id = 0 }
+static ftrigio_t const fzero = FTRIGIO_ZERO ;
+
+static genalloc a = GENALLOC_ZERO ; /* array of ftrigio_t */
+
+static void ftrigio_deepfree (ftrigio_t_ref p)
+{
+ ftrig1_free(&p->trig) ;
+ stralloc_free(&p->sa) ;
+ regfree(&p->re) ;
+ *p = fzero ;
+}
+
+static void cleanup (void)
+{
+ register unsigned int i = genalloc_len(ftrigio_t, &a) ;
+ for (; i ; i--) ftrigio_deepfree(genalloc_s(ftrigio_t, &a) + i - 1) ;
+ genalloc_setlen(ftrigio_t, &a, 0) ;
+}
+
+static void trig (uint16 id, char what, char info)
+{
+ char pack[4] ;
+ unixmessage_t m = { .s = pack, .len = 4, .fds = 0, .nfds = 0 } ;
+ uint16_pack_big(pack, id) ;
+ pack[2] = what ; pack[3] = info ;
+ if (!unixmessage_put(unixmessage_sender_x, &m))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "build answer") ;
+ }
+}
+
+static void answer (char c)
+{
+ unixmessage_t m = { &c, 1, 0, 0 } ;
+ if (!unixmessage_put(unixmessage_sender_1, &m))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "unixmessage_put") ;
+ }
+}
+
+static void remove (unsigned int i)
+{
+ register unsigned int n = genalloc_len(ftrigio_t, &a) - 1 ;
+ ftrigio_deepfree(genalloc_s(ftrigio_t, &a) + i) ;
+ genalloc_s(ftrigio_t, &a)[i] = genalloc_s(ftrigio_t, &a)[n] ;
+ genalloc_setlen(ftrigio_t, &a, n) ;
+}
+
+static inline int ftrigio_read (ftrigio_t *p)
+{
+ unsigned int n = FTRIGRD_MAXREADS ;
+ while (n--)
+ {
+ regmatch_t pmatch ;
+ unsigned int blen ;
+ register int r = sanitize_read(buffer_fill(&p->b)) ;
+ if (!r) break ;
+ if (r < 0) return (trig(p->id, 'd', errno), 0) ;
+ blen = buffer_len(&p->b) ;
+ if (!stralloc_readyplus(&p->sa, blen+1)) dienomem() ;
+ buffer_getnofill(&p->b, p->sa.s + p->sa.len, blen) ;
+ p->sa.len += blen ;
+ p->sa.s[p->sa.len] = 0 ;
+ while (!regexec(&p->re, p->sa.s, 1, &pmatch, REG_NOTBOL | REG_NOTEOL))
+ {
+ trig(p->id, '!', p->sa.s[pmatch.rm_eo - 1]) ;
+ if (!(p->options & FTRIGR_REPEAT)) return 0 ;
+ byte_copy(p->sa.s, p->sa.len + 1 - pmatch.rm_eo, p->sa.s + pmatch.rm_eo) ;
+ p->sa.len -= pmatch.rm_eo ;
+ }
+ }
+ return 1 ;
+}
+
+static int parse_protocol (unixmessage_t const *m, void *context)
+{
+ uint16 id ;
+ if (m->len < 3 || m->nfds)
+ {
+ cleanup() ;
+ strerr_dief1x(100, "invalid client request") ;
+ }
+ uint16_unpack_big(m->s, &id) ;
+ switch (m->s[2])
+ {
+ case 'U' : /* unsubscribe */
+ {
+ register unsigned int i = genalloc_len(ftrigio_t, &a) ;
+ for (; i ; i--) if (genalloc_s(ftrigio_t, &a)[i-1].id == id) break ;
+ if (i) remove(i-1) ;
+ answer(0) ;
+ break ;
+ }
+ case 'L' : /* subscribe to path and match re */
+ {
+ ftrigio_t f = FTRIGIO_ZERO ;
+ uint32 pathlen, relen ;
+ int r ;
+ if (m->len < 18)
+ {
+ answer(EPROTO) ;
+ break ;
+ }
+ uint32_unpack_big(m->s + 3, &f.options) ;
+ uint32_unpack_big(m->s + 7, &pathlen) ;
+ uint32_unpack_big(m->s + 11, &relen) ;
+ if (((pathlen + relen + 17) != m->len) || m->s[15 + pathlen] || m->s[m->len - 1])
+ {
+ answer(EPROTO) ;
+ break ;
+ }
+ f.id = id ;
+ r = regcomp(&f.re, m->s + 16 + pathlen, REG_EXTENDED) ;
+ if (r)
+ {
+ answer(r == REG_ESPACE ? ENOMEM : EINVAL) ;
+ break ;
+ }
+ if (!ftrig1_make(&f.trig, m->s + 15))
+ {
+ regfree(&f.re) ;
+ answer(errno) ;
+ break ;
+ }
+ if (!genalloc_append(ftrigio_t, &a, &f))
+ {
+ ftrigio_deepfree(&f) ;
+ answer(errno) ;
+ break ;
+ }
+ answer(0) ;
+ break ;
+ }
+ default :
+ {
+ cleanup() ;
+ strerr_dief1x(100, "invalid client request") ;
+ }
+ }
+ (void)context ;
+ return 1 ;
+}
+
+int main (void)
+{
+ PROG = "s6-ftrigrd" ;
+
+ if (ndelay_on(0) < 0) strerr_diefu2sys(111, "ndelay_on ", "0") ;
+ if (ndelay_on(1) < 0) strerr_diefu2sys(111, "ndelay_on ", "1") ;
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ;
+
+ {
+ tain_t deadline ;
+ tain_now_g() ;
+ tain_addsec_g(&deadline, 2) ;
+ if (!skaclient_server_01x_init_g(FTRIGR_BANNER1, FTRIGR_BANNER1_LEN, FTRIGR_BANNER2, FTRIGR_BANNER2_LEN, &deadline))
+ strerr_diefu1sys(111, "sync with client") ;
+ }
+
+ for (;;)
+ {
+ register unsigned int n = genalloc_len(ftrigio_t, &a) ;
+ iopause_fd x[3 + n] ;
+ unsigned int i = 0 ;
+
+ x[0].fd = 0 ; x[0].events = IOPAUSE_EXCEPT | IOPAUSE_READ ;
+ x[1].fd = 1 ; x[1].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_1) ? 0 : IOPAUSE_WRITE) ;
+ x[2].fd = unixmessage_sender_fd(unixmessage_sender_x) ;
+ x[2].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_x) ? 0 : IOPAUSE_WRITE) ;
+ for (; i < n ; i++)
+ {
+ register ftrigio_t_ref p = genalloc_s(ftrigio_t, &a) + i ;
+ p->xindex = 3 + i ;
+ x[3+i].fd = p->trig.fd ;
+ x[3+i].events = IOPAUSE_READ ;
+ }
+
+ if (iopause(x, 3 + n, 0, 0) < 0)
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "iopause") ;
+ }
+
+ /* client closed */
+ if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) break ;
+
+ /* client is reading */
+ if (x[1].revents & IOPAUSE_WRITE)
+ if ((unixmessage_sender_flush(unixmessage_sender_1) < 0) && !error_isagain(errno))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "flush stdout") ;
+ }
+ if (x[2].revents & IOPAUSE_WRITE)
+ if ((!unixmessage_sender_flush(unixmessage_sender_x) < 0) && !error_isagain(errno))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "flush asyncout") ;
+ }
+
+ /* scan listening ftrigs */
+ for (i = 0 ; i < genalloc_len(ftrigio_t, &a) ; i++)
+ {
+ register ftrigio_t_ref p = genalloc_s(ftrigio_t, &a) + i ;
+ if (x[p->xindex].revents & IOPAUSE_READ)
+ if (!ftrigio_read(p)) remove(i--) ;
+ }
+
+ /* client is writing */
+ if (!unixmessage_receiver_isempty(unixmessage_receiver_0) || x[0].revents & IOPAUSE_READ)
+ {
+ if (unixmessage_handle(unixmessage_receiver_0, &parse_protocol, 0) < 0)
+ {
+ if (errno == EPIPE) break ; /* normal exit */
+ cleanup() ;
+ strerr_diefu1sys(111, "handle messages from client") ;
+ }
+ }
+ }
+ cleanup() ;
+ return 0 ;
+}