From 8b435b76d68dd8f11808f0cff4d8998d2be48f4c Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Tue, 2 Jul 2024 18:54:34 +0000 Subject: Prepare for 0.0.2.0; add shibari-cache-config Signed-off-by: Laurent Bercot --- src/cache/shibari-cache.c | 282 +++++++++++++++++++++++++++++ src/config/PARSING-config.txt | 26 +++ src/config/conftree.c | 51 ++++++ src/config/defaults.c | 70 +++++++ src/config/deps-exe/shibari-cache-config | 8 + src/config/lexparse.c | 230 +++++++++++++++++++++++ src/config/node.c | 34 ++++ src/config/repo.c | 46 +++++ src/config/shibari-cache-config-internal.h | 91 ++++++++++ src/config/shibari-cache-config.c | 101 +++++++++++ src/config/util.c | 15 ++ 11 files changed, 954 insertions(+) create mode 100644 src/cache/shibari-cache.c create mode 100644 src/config/PARSING-config.txt create mode 100644 src/config/conftree.c create mode 100644 src/config/defaults.c create mode 100644 src/config/deps-exe/shibari-cache-config create mode 100644 src/config/lexparse.c create mode 100644 src/config/node.c create mode 100644 src/config/repo.c create mode 100644 src/config/shibari-cache-config-internal.h create mode 100644 src/config/shibari-cache-config.c create mode 100644 src/config/util.c (limited to 'src') diff --git a/src/cache/shibari-cache.c b/src/cache/shibari-cache.c new file mode 100644 index 0000000..1d88556 --- /dev/null +++ b/src/cache/shibari-cache.c @@ -0,0 +1,282 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define USAGE "shibari-cache [ -U ] [ -v verbosity ] [ -d notif ] [ -D cachedumpfile ] [ -w wtimeout ] [ -i rulesdir | -x rulesfile ] ip[_port]..." +#define dieusage() strerr_dieusage(100, USAGE) + + +static char const *dumpfile = 0 ; + + +typedef struct shibari_ip4_s shibari_ip4 ; +struct shibari_ip4_s +{ + int fd ; + char ip[4] ; +} ; + +typedef struct shibari_ip6_s shibari_ip6 ; +struct shibari_ip6_s +{ + int fd ; + char ip[16] ; +} ; + +static inline void argv_pass1 (char const *const *argv, unsigned int *n4, unsigned int *n6) +{ + char ip[16] ; + for (; *argv ; argv++) + if (ip6_scan(argv, ip)) +#ifdef SKALIBS_IPV6_ENABLED + n6++ ; +#else + strerr_dief1x(100, "IPv6 listening addresses unsupported on this system") ; +#endif + else n4++ ; +} + +static inline void argv_pass2 (char const *const *argv, shibari_ip4 *ip4, shibari_ip6 *ip6, uint16_t *ports) +{ + unsigned int i4 = 0, i6 = 0 ; + char ip[16] ; + size_t len ; + for (; *argv ; argv++) + { + len = ip6_scan(argv, ip) ; + if (len) + { + if (argv[0][len] == '_') + { + uint16_t port ; + if (!uint160_scan(*argv + len + 1)) + strerr_dief + } + } + } +} + +static inline void reload_cdbs (void) +{ + cdb newtdb = CDB_ZERO ; + if (!cdb_init(&newtdb, tdbfile)) + { + if (verbosity) strerr_warnwu2sys("reopen DNS data file ", tdbfile) ; + } + else + { + cdb_free(&tdb) ; + tdb = newtdb ; + } + if (rulestype == 2) + { + cdb newrules = CDB_ZERO ; + if (!cdb_init(&newrules, rulesfile)) + { + if (verbosity) strerr_warnwu2sys("reopen access rules file ", rulesfile) ; + } + else + { + cdb_free(&rules) ; + rules = newrules ; + } + } +} + +static int check_rules (ip46 const *remoteip, s6_accessrules_params_t *params, char const **loc) +{ + s6_accessrules_result_t r ; + params->env.len = 0 ; + params->exec.len = 0 ; + r = rulestype == 2 ? + s6_accessrules_ip46_cdb(remoteip, &rules, params) : + s6_accessrules_ip46_fs(remoteip, rulesfile, params) ; + if (r != S6_ACCESSRULES_ALLOW) return 0 ; + + if (params->env.len) + { + char const *p ; + if (params->env.s[params->env.len - 1]) + { + if (verbosity) + { + char fmt[IP46_FMT] ; + fmt[ip46_fmt(fmt, remoteip)] = 0 ; + strerr_warnw6x("invalid environment parameters in rules ", rulestype == 2 ? "cdb " : "directory ", rulesfile, " for ip ", fmt, " - denying connection") ; + } + return 0 ; + } + p = memmem(params->env.s, params->env.len - 1, VAR "=", sizeof(VAR)) ; + if (p && (p == params->env.s || !p[-1])) *loc = p + sizeof(VAR) ; + } + return 1 ; +} + +static inline void handle_signals (void) +{ + for (;;) switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "read selfpipe") ; + case 0 : return ; + case SIGTERM : cont = 0 ; break ; + case SIGHUP : reload_cdbs() ; break ; + case SIGALRM : dump_cache() ; break ; + default : break ; + } +} + +int main (int argc, char const *const *argv) +{ + unsigned int notif = 0 ; + unsigned int n4 = 0, n6 = 0 ; + uid_t uid = 0 ; + gid_t gid = 0 ; + PROG = "shibari-cache" ; + { + int flagdrop = 0 ; + subgetopt l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "Uv:d:D:w:i:x:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'U' : flagdrop = 1 ; break ; + case 'v' : if (!uint320_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'd' : if (!uint0_scan(l.arg, ¬if)) dieusage() ; break ; + case 'D' : dumpfile = l.arg ; break ; + case 'w' : if (!uint0_scan(l.arg, &wtimeout)) dieusage() ; break ; + case 'i' : rulesfile = l.arg ; rulestype = 1 ; break ; + case 'x' : rulesfile = l.arg ; rulestype = 2 ; break ; + default : strerr_dieusage(10, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + if (!argc) default_iplist ; + + if (!ip46_scan(argv[0], &localip)) dieusage() ; + if (flagdrop) + { + char const *x = getenv("UID") ; + if (!x) strerr_dienotset(100, "UID") ; + if (!uid0_scan(x, &uid)) strerr_dieinvalid(100, "UID") ; + x = getenv("GID") ; + if (!x) strerr_dienotset(100, "GID") ; + if (!uid0_scan(x, &gid)) strerr_dieinvalid(100, "GID") ; + } + if (wtimeout) tain_from_millisecs(&wtto, wtimeout) ; + } + + if (notif) + { + if (notif < 3) strerr_dief1x(100, "notification fd cannot be 0, 1 or 2") ; + if (fcntl(notif, F_GETFD) == -1) strerr_diefu1sys(111, "check notification fd") ; + } + + close(0) ; + close(1) ; + x[0].fd = selfpipe_init() ; + if (x[0].fd == -1) strerr_diefu1sys(111, "create selfpipe") ; + if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGHUP) ; + sigaddset(&set, SIGTERM) ; + if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ; + } + + if (!cdb_init(&tdb, tdbfile)) strerr_diefu2sys(111, "open cdb file ", tdbfile) ; + if (rulestype == 2 && !cdb_init(&rules, rulesfile)) strerr_diefu2sys(111, "open rules file ", rulesfile) ; + + x[1].fd = socket_udp46_nb(ip46_is6(&localip)) ; + if (x[1].fd == -1) strerr_diefu1sys(111, "create socket") ; + if (socket_bind46_reuse(x[1].fd, &localip, localport) == -1) strerr_diefu1sys(111, "bind socket") ; + + if (gid && setgid(gid) == -1) strerr_diefu1sys(111, "setgid") ; + if (uid && setuid(uid) == -1) strerr_diefu1sys(111, "setuid") ; + if (!tain_now_set_stopwatch_g()) strerr_diefu1sys(111, "initialize clock") ; + + shibari_log_start(verbosity, &localip, localport) ; + if (notif) + { + write(notif, "\n", 1) ; + close(notif) ; + } + + while (cont) + { + tain wstamp = TAIN_INFINITE ; + char const *loc = 0 ; + s6dns_message_header_t hdr ; + s6dns_message_counts_t counts ; + s6dns_domain_t name ; + unsigned int rcode ; + ssize_t r ; + uint16_t qtype ; + uint16_t remoteport ; + ip46 remoteip ; + + if (iopause_g(x, 2, &wstamp) == -1) strerr_diefu1sys(111, "iopause") ; + if (x[0].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with selfpipe") ; + if (x[0].revents & IOPAUSE_READ) { handle_signals() ; continue ; } + + r = sanitize_read(socket_recv46(x[1].fd, buf, 512, &remoteip, &remoteport, ip46_is6(&localip))) ; + if (!r) continue ; + if (r == -1) strerr_diefu1sys(111, "recv from socket") ; + if (rulestype && !check_rules(&remoteip, ¶ms, &loc)) continue ; + if (!s6dns_message_parse_init(&hdr, &counts, buf, r, &rcode)) continue ; + if (hdr.opcode) { rcode = 4 ; goto answer ; } + if (!s6dns_message_parse_question(&counts, &name, &qtype, buf, r, &rcode) || !s6dns_domain_encode(&name)) + { + rcode = errno == ENOTSUP ? 4 : 1 ; + goto answer ; + } + shibari_log_queryplus(verbosity, &name, qtype, &remoteip, remoteport) ; + tain_wallclock_read(&wstamp) ; + rcode = shibari_packet_tdb_answer_query(&pkt, &tdb, &hdr, &name, qtype, loc, &wstamp) ; + + answer: + if (rcode && rcode != 3) + { + shibari_packet_begin(&pkt, hdr.id, &name, qtype) ; + pkt.hdr.rcode = rcode ; + shibari_packet_end(&pkt) ; + } + shibari_log_answer(verbosity, &pkt.hdr, pkt.pos) ; + tain_add_g(&wstamp, &wtto) ; + if (socket_sendnb46_g(x[1].fd, buf, pkt.pos, &remoteip, remoteport, &wstamp) < pkt.pos && verbosity) + strerr_warnwu1sys("send answer") ; + } + + shibari_log_exit(verbosity, 0) ; + return 0 ; +} diff --git a/src/config/PARSING-config.txt b/src/config/PARSING-config.txt new file mode 100644 index 0000000..072a1fa --- /dev/null +++ b/src/config/PARSING-config.txt @@ -0,0 +1,26 @@ + +class | 0 1 2 3 4 +st\ev | \0 space # \n other + +START | P np +00 | END SPACE COMMENT START WORD + +COMMENT | P +01 | END COMMENT COMMENT START COMMENT + +SPACE | P P np +02 | END SPACE COMMENT START WORD + +WORD | 0P 0 p 0P p +03 | END SPACE WORD START WORD + +END: 04 +X: 05 + +states: 3 bits +actions: 4 bits + +0x10 n new word +0x20 p push cur +0x40 0 push \0 +0x80 P process line diff --git a/src/config/conftree.c b/src/config/conftree.c new file mode 100644 index 0000000..573646a --- /dev/null +++ b/src/config/conftree.c @@ -0,0 +1,51 @@ +/* ISC license. */ + +#include +#include +#include + +#include "shibari-cache-config-internal.h" + +static repo conftree = \ +{ \ + .ga = GENALLOC_ZERO, \ + .tree = AVLTREE_INIT(8, 3, 8, &node_dtok, &node_cmp, &conftree.ga), \ + .storage = &g.storage \ +} ; + +void confnode_start (node *node, char const *key, size_t filepos, uint32_t line) +{ + return node_start(&g.storage, node, key, filepos, line) ; +} + +void confnode_add (node *node, char const *s, size_t len) +{ + return node_add(&g.storage, node, s, len) ; +} + +node const *conftree_search (char const *key) +{ + return repo_search(&conftree, key) ; +} + +void conftree_add (node const *node) +{ + return repo_add(&conftree, node) ; +} + +void conftree_update (node const *node) +{ + return repo_update(&conftree, node) ; +} + +static int confnode_write (uint32_t d, unsigned int h, void *data) +{ + node *nod = genalloc_s(node, &conftree.ga) + d ; + (void)h ; + return cdbmake_add((cdbmaker *)data, conftree.storage->s + nod->key, nod->keylen, conftree.storage->s + nod->data, nod->datalen) ; +} + +int conftree_write (cdbmaker *cm) +{ + return avltree_iter(&conftree.tree, &confnode_write, cm) ; +} diff --git a/src/config/defaults.c b/src/config/defaults.c new file mode 100644 index 0000000..14fce76 --- /dev/null +++ b/src/config/defaults.c @@ -0,0 +1,70 @@ +/* ISC license. */ + +#include + +#include "shibari-cache-config-internal.h" + +struct defaults_s +{ + char const *key ; + char const *value ; + size_t vlen ; +} ; + +#define REC(k, v, n) { .key = (k), .value = (v), .vlen = (n) } +#define RECS(k, v) REC(k, v, sizeof(v)) +#define RECU32(k, u) { .key = (k), .value = (char const [4]){ (u) >> 24 & 0xffu, (u) >> 16 & 0xffu, (u) >> 8 & 0xffu, (u) & 0xffu }, .vlen = 4 } + +static struct defaults_s const defaults[] = +{ + RECU32("G:logv", 1), + REC("G:listen4", "\0\0\0\0\0\35", 6), + REC("G:listen6", "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\35", 18), + + REC("R4:", + "\0\306\51\0\4" + "\0\252\367\252\2" + "\0\300\41\4\14" + "\0\307\7\133\15" + "\0\300\313\346\12" + "\0\300\5\5\361" + "\0\300\160\44\4" + "\0\306\141\276\65" + "\0\300\44\224\21" + "\0\300\72\200\36" + "\0\301\0\16\201" + "\0\307\7\123\52" + "\0\312\14\33\41" + , 65), + + REC("R6:", + "\0\40\1\5\3\272\76\0\0\0\0\0\0\0\2\0\60" + "\0\50\1\1\270\0\20\0\0\0\0\0\0\0\0\0\13" + "\0\40\1\5\0\0\2\0\0\0\0\0\0\0\0\0\14" + "\0\40\1\5\0\0\55\0\0\0\0\0\0\0\0\0\15" + "\0\40\1\5\0\0\250\0\0\0\0\0\0\0\0\0\16" + "\0\40\1\5\0\0\57\0\0\0\0\0\0\0\0\0\17" + "\0\40\1\5\0\0\22\0\0\0\0\0\0\0\0\15\15" + "\0\40\1\5\0\0\1\0\0\0\0\0\0\0\0\0\123" + "\0\40\1\7\376\0\0\0\0\0\0\0\0\0\0\0\123" + "\0\40\1\5\3\14\47\0\0\0\0\0\0\0\2\0\60" + "\0\40\1\7\375\0\0\0\0\0\0\0\0\0\0\0\1" + "\0\40\1\5\0\0\237\0\0\0\0\0\0\0\0\0\102" + "\0\40\1\15\303\0\0\0\0\0\0\0\0\0\0\0\65" + , 221), + REC(0, 0, 0) +} ; + +void conf_defaults (void) +{ + for (struct defaults_s const *p = defaults ; p->key ; p++) + { + if (!conftree_search(p->key)) + { + node node ; + confnode_start(&node, p->key, 0, 0) ; + confnode_add(&node, p->value, p->vlen) ; + conftree_add(&node) ; + } + } +} diff --git a/src/config/deps-exe/shibari-cache-config b/src/config/deps-exe/shibari-cache-config new file mode 100644 index 0000000..1685f25 --- /dev/null +++ b/src/config/deps-exe/shibari-cache-config @@ -0,0 +1,8 @@ +util.o +node.o +repo.o +conftree.o +defaults.o +lexparse.o +-ls6dns +-lskarnet diff --git a/src/config/lexparse.c b/src/config/lexparse.c new file mode 100644 index 0000000..9987fbd --- /dev/null +++ b/src/config/lexparse.c @@ -0,0 +1,230 @@ +/* ISC license. */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "shibari-cache-config-internal.h" + +#define dietoobig() strerr_diefu1sys(100, "read configuration") + +typedef struct mdt_s mdt, *mdt_ref ; +struct mdt_s +{ + size_t filepos ; + uint32_t line ; + char linefmt[UINT32_FMT] ; +} ; +#define MDT_ZERO { .filepos = 0, .line = 0, .linefmt = "0" } + +struct namevalue_s +{ + char const *name ; + uint32_t value ; +} ; + +enum directivevalue_e +{ + T_VERBOSITY, + T_LISTEN, + T_SERVER, + T_FORWARD, +} ; + +static void conftree_checkunique (char const *key, mdt const *md) +{ + node const *node = conftree_search(key) ; + if (node) + { + char fmt[UINT32_FMT] ; + fmt[uint32_fmt(fmt, node->line)] = 0 ; + strerr_diefn(1, 12, "duplicate ", "key ", key, " in file ", g.storage.s + md->filepos, " line ", md->linefmt, ", previously defined", " in file ", g.storage.s + node->filepos, " line ", fmt) ; + } +} + +static void add_unique (char const *key, char const *value, size_t valuelen, mdt const *md) +{ + node node ; + conftree_checkunique(key, md) ; + confnode_start(&node, key, md->filepos, md->line) ; + confnode_add(&node, value, valuelen) ; + conftree_add(&node) ; +} + +static int ip40_scan (char const *s, char *ip) +{ + size_t len = ip4_scan(s, ip) ; + return len ? !s[len] : 0 ; +} + +static int ip60_scan (char const *s, char *ip) +{ + size_t len = ip6_scan(s, ip) ; + return len ? !s[len] : 0 ; +} + +static inline void parse_verbosity (char const *s, size_t const *word, size_t n, mdt const *md) +{ + uint32_t v ; + char pack[4] ; + if (n != 1) + strerr_dief8x(1, "too ", n ? "many" : "few", " arguments to directive ", "verbosity", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + if (!uint320_scan(s + word[0], &v)) + strerr_dief7x(1, " argument to directive ", "verbosity", " must be an integer ", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + uint32_pack_big(pack, v) ; + add_unique("G:logv", pack, 4, md) ; +} + +static inline void parse_listen (char const *s, size_t const *word, size_t n, mdt const *md) +{ + if (!n) + strerr_dief6x(1, "too few arguments to directive ", "listen", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + { + size_t n4 = 0, n6 = 0 ; + char ip6[n << 4] ; + char ip4[n << 2] ; + for (size_t i = 0 ; i < n ; i++) + { + if (ip60_scan(s + word[i], ip6 + (n6 << 4))) n6++ ; + else if (ip40_scan(s + word[i], ip4 + (n4 << 2))) n4++ ; + else strerr_dief6x(1, "arguments to directive ", "listen", " must be IPs in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + } + add_unique("G:listen4", ip4, n4 << 2, md) ; + add_unique("G:listen6", ip6, n6 << 4, md) ; + } +} + +static inline void parse_server (char const *s, size_t const *word, size_t n, mdt const *md, int forward) +{ + char const *x = forward ? "forward" : "server" ; + s6dns_domain_t domain ; + if (n-- < 2) + strerr_dief8x(1, "too ", "few", " arguments to directive ", x, " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + if (!s6dns_domain_fromstring(&domain, s + word[0], strlen(s + word[0])) + || !s6dns_domain_noqualify(&domain)) + strerr_dief7x(1, "first argument to directive ", x, " must be a zone ", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + word++ ; + { + size_t n4 = 0, n6 = 0 ; + char ip6[n * 17] ; + char ip4[n * 5] ; + char key[3 + domain.len] ; + for (size_t i = 0 ; i < n ; i++) + { + if (ip60_scan(s + word[i], ip6 + (n6 * 17) + 1)) ip6[n6++ * 17] = !!forward ; + else if (ip40_scan(s + word[i], ip4 + (n4 * 5) + 1)) ip4[n4++ * 5] = !!forward ; + else strerr_dief6x(1, "subsequent arguments to directive ", x, " must be IPs in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + } + memcpy(key, "R4:", 3) ; + memcpy(key + 3, domain.s + 1, domain.len - 1) ; + key[2 + domain.len] = 0 ; + add_unique(key, ip4, n4 * 5, md) ; + key[1] = '6' ; + add_unique(key, ip6, n6 * 17, md) ; + } +} + +static inline void process_line (char const *s, size_t const *word, size_t n, mdt *md) +{ + static struct namevalue_s const directives[] = + { + { .name = "forward", .value = T_FORWARD }, + { .name = "listen", .value = T_LISTEN }, + { .name = "server", .value = T_SERVER }, + { .name = "verbosity", .value = T_VERBOSITY }, + } ; + struct namevalue_s const *directive ; + char const *word0 ; + if (!n--) return ; + word0 = s + *word++ ; + directive = BSEARCH(struct namevalue_s, word0, directives) ; + if (!directive) + strerr_dief6x(1, "unrecognized word ", word0, " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + switch (directive->value) + { + case T_VERBOSITY : + parse_verbosity(s, word, n, md) ; + break ; + case T_LISTEN : + parse_listen(s, word, n, md) ; + break ; + case T_SERVER : + parse_server(s, word, n, md, 0) ; + break ; + case T_FORWARD : + parse_server(s, word, n, md, 1) ; + break ; + } +} + +static inline uint8_t cclass (char c) +{ + switch (c) + { + case 0 : return 0 ; + case ' ' : + case '\t' : + case '\f' : + case '\r' : return 1 ; + case '#' : return 2 ; + case '\n' : return 3 ; + default : return 4 ; + } +} + +static inline char next (buffer *b, mdt const *md) +{ + char c ; + ssize_t r = buffer_get(b, &c, 1) ; + if (r == -1) strerr_diefu1sys(111, "read from preprocessor") ; + if (!r) return 0 ; + if (!c) strerr_dief5x(1, "null character", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + return c ; +} + +void conf_lexparse (buffer *b, char const *ifile) +{ + static uint8_t const table[4][5] = /* see PARSING-config.txt */ + { + { 0x04, 0x02, 0x01, 0x80, 0x33 }, + { 0x04, 0x01, 0x01, 0x80, 0x01 }, + { 0x84, 0x02, 0x01, 0x80, 0x33 }, + { 0xc4, 0x42, 0x23, 0xc0, 0x23 } + } ; + stralloc sa = STRALLOC_ZERO ; + genalloc words = GENALLOC_ZERO ; /* size_t */ + mdt md = MDT_ZERO ; + uint8_t state = 0 ; + if (!stralloc_catb(&g.storage, ifile, strlen(ifile) + 1)) dienomem() ; + while (state < 0x04) + { + char c = next(b, &md) ; + uint8_t what = table[state][cclass(c)] ; + state = what & 0x07 ; + if (what & 0x10) if (!genalloc_catb(size_t, &words, &sa.len, 1)) dienomem() ; + if (what & 0x20) if (!stralloc_catb(&sa, &c, 1)) dienomem() ; + if (what & 0x40) if (!stralloc_0(&sa)) dienomem() ; + if (what & 0x80) + { + process_line(sa.s, genalloc_s(size_t, &words), genalloc_len(size_t, &words), &md) ; + genalloc_setlen(size_t, &words, 0) ; + sa.len = 0 ; + md.line++ ; + md.linefmt[uint32_fmt(md.linefmt, md.line)] = 0 ; + } + } + genalloc_free(size_t, &words) ; + stralloc_free(&sa) ; +} diff --git a/src/config/node.c b/src/config/node.c new file mode 100644 index 0000000..7e6cd4b --- /dev/null +++ b/src/config/node.c @@ -0,0 +1,34 @@ +/* ISC license. */ + +#include +#include + +#include +#include + +#include "shibari-cache-config-internal.h" + +#define diestorage() strerr_diefu2x(100, "add node to configuration tree", ": too much data") +#define diefilepos() strerr_diefu2x(100, "add node to configuration tree", ": file too large") + +void node_start (stralloc *storage, node *node, char const *key, size_t filepos, uint32_t line) +{ + size_t l = strlen(key) ; + size_t k = storage->len ; + if (!stralloc_catb(storage, key, l + 1)) dienomem() ; + if (storage->len >= UINT32_MAX) diestorage() ; + if (filepos > UINT32_MAX) diefilepos() ; + node->key = k ; + node->keylen = l ; + node->data = storage->len ; + node->datalen = 0 ; + node->filepos = filepos ; + node->line = line ; +} + +void node_add (stralloc *storage, node *node, char const *s, size_t len) +{ + if (!stralloc_catb(storage, s, len)) dienomem() ; + if (storage->len >= UINT32_MAX) diestorage() ; + node->datalen += len ; +} diff --git a/src/config/repo.c b/src/config/repo.c new file mode 100644 index 0000000..8ed8c51 --- /dev/null +++ b/src/config/repo.c @@ -0,0 +1,46 @@ +/* ISC license. */ + +#include +#include + +#include +#include + +#include "shibari-cache-config-internal.h" + +void *node_dtok (uint32_t d, void *data) +{ + repo *r = data ; + return r->storage->s + genalloc_s(node, &r->ga)[d].key ; +} + +int node_cmp (void const *a, void const *b, void *data) +{ + (void)data ; + return strcmp((char const *)a, (char const *)b) ; +} + +node const *repo_search (repo const *r, char const *key) +{ + uint32_t i ; + return avltree_search(&r->tree, key, &i) ? genalloc_s(node const, &r->ga) + i : 0 ; +} + +void repo_add (repo *r, node const *nod) +{ + uint32_t i = genalloc_len(node, &r->ga) ; + if (!genalloc_append(node, &r->ga, nod)) dienomem() ; + if (!avltree_insert(&r->tree, i)) dienomem() ; +} + +void repo_update (repo *r, node const *nod) +{ + uint32_t i ; + if (avltree_search(&r->tree, r->storage->s + nod->key, &i)) + { + if (!avltree_delete(&r->tree, r->storage->s + nod->key)) dienomem() ; + genalloc_s(node, &r->ga)[i] = *nod ; + if (!avltree_insert(&r->tree, i)) dienomem() ; + } + else repo_add(r, nod) ; +} diff --git a/src/config/shibari-cache-config-internal.h b/src/config/shibari-cache-config-internal.h new file mode 100644 index 0000000..c7b0197 --- /dev/null +++ b/src/config/shibari-cache-config-internal.h @@ -0,0 +1,91 @@ +/* ISC license. */ + +#ifndef SHIBARI_CACHE_CONFIG_INTERNAL_H +#define SHIBARI_CACHE_CONFIG_INTERNAL_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") + +typedef struct node_s node, *node_ref ; +struct node_s +{ + uint32_t key ; + uint32_t keylen ; + uint32_t data ; + uint32_t datalen ; + uint32_t filepos ; + uint32_t line ; +} ; +#define NODE_ZERO { .key = 0, .keylen = 0, .data = 0, .datalen = 0 } + +typedef struct repo_s repo, *repo_ref ; +struct repo_s +{ + genalloc ga ; + avltree tree ; + stralloc *storage ; +} ; +#define REPO_ZERO { .ga = GENALLOC_ZERO, .tree = AVLTREE_ZERO, .storage = 0 } + +struct global_s +{ + stralloc storage ; +} ; +#define GLOBAL_ZERO { .storage = STRALLOC_ZERO } + +extern struct global_s g ; + + + /* util */ + +extern int keycmp (void const *, void const *) ; /* for any struct starting with a string key */ +#define BSEARCH(type, key, array) bsearch(key, (array), sizeof(array)/sizeof(type), sizeof(type), &keycmp) + + + /* node */ + +extern void node_start (stralloc *, node *, char const *, size_t, uint32_t) ; +extern void node_add (stralloc *, node *, char const *, size_t) ; + + + /* repo */ + +extern void *node_dtok (uint32_t, void *) ; +extern int node_cmp (void const *, void const *, void *) ; +extern node const *repo_search (repo const *, char const *) ; +extern void repo_add (repo *, node const *) ; +extern void repo_update (repo *, node const *) ; + + + /* conftree */ + +extern void confnode_start (node *, char const *, size_t, uint32_t) ; +extern void confnode_add (node *, char const *, size_t) ; + +extern node const *conftree_search (char const *) ; +extern void conftree_add (node const *) ; +extern void conftree_update (node const *) ; + +extern int conftree_write (cdbmaker *) ; + + + /* lexparse */ + +extern void conf_lexparse (buffer *, char const *) ; + + + /* defaults */ + +extern void conf_defaults (void) ; + +#endif diff --git a/src/config/shibari-cache-config.c b/src/config/shibari-cache-config.c new file mode 100644 index 0000000..0aef2ce --- /dev/null +++ b/src/config/shibari-cache-config.c @@ -0,0 +1,101 @@ +/* ISC license. */ + +#include +#include +#include +#include /* rename() */ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "shibari-cache-config-internal.h" + +#define USAGE "shibari-cache-config [ -i textfile ] [ -o cdbfile ] [ -m mode ]" +#define dieusage() strerr_dieusage(100, USAGE) + +struct global_s g = GLOBAL_ZERO ; + +static inline void conf_output (char const *ofile, unsigned int omode) +{ + int fdw ; + cdbmaker cm = CDBMAKER_ZERO ; + size_t olen = strlen(ofile) ; + char otmp[olen + 8] ; + memcpy(otmp, ofile, olen) ; + memcpy(otmp + olen, ":XXXXXX", 8) ; + fdw = mkstemp(otmp) ; + if (fdw == -1) strerr_diefu3sys(111, "open ", otmp, " for writing") ; + if (!cdbmake_start(&cm, fdw)) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "cdmake_start ", otmp) ; + } + if (!conftree_write(&cm)) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "write config tree into ", otmp) ; + } + if (!cdbmake_finish(&cm)) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "cdbmake_finish ", otmp) ; + } + if (fsync(fdw) == -1) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "fsync ", otmp) ; + } + if (fchmod(fdw, omode & 0777) == -1) + { + unlink_void(otmp) ; + strerr_diefu2sys(111, "fchmod ", otmp) ; + } + if (rename(otmp, ofile) == -1) + { + unlink_void(otmp) ; + strerr_diefu4sys(111, "rename ", otmp, " to ", ofile) ; + } +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + char const *ifile = SHIBARI_SYSCONFPREFIX "shibari-cache.conf" ; + char const *ofile = SHIBARI_SYSCONFPREFIX "shibari-cache.conf.cdb" ; + unsigned int omode = 0644 ; + + PROG = "shibari-cache-config" ; + { + subgetopt l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "i:o:m:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'i' : ifile = l.arg ; break ; + case 'o' : ofile = l.arg ; break ; + case 'm' : if (!uint0_oscan(l.arg, &omode)) dieusage() ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + { + int fdr = openc_readb(ifile) ; + char buf[4096] ; + buffer b = BUFFER_INIT(&buffer_read, fdr, buf, 4096) ; + if (fdr == -1) strerr_diefu2sys(111, "open ", ifile) ; + conf_lexparse(&b, ifile) ; + } + conf_defaults() ; + conf_output(ofile, omode) ; + return 0 ; +} diff --git a/src/config/util.c b/src/config/util.c new file mode 100644 index 0000000..f3da287 --- /dev/null +++ b/src/config/util.c @@ -0,0 +1,15 @@ +/* ISC license. */ + +#include + +#include "shibari-cache-config-internal.h" + +struct starts_with_a_string_key_s +{ + char const *s ; +} ; + +int keycmp (void const *a, void const *b) +{ + return strcmp((char const *)a, ((struct starts_with_a_string_key_s const *)b)->s) ; +} -- cgit v1.2.3