diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2021-03-28 01:50:36 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2021-03-28 01:50:36 +0000 |
commit | e2959c22c7309836d6f5dba2f0db7b293b860ad8 (patch) | |
tree | a26cefd2a36f6682e302bed8840642da0c6c64a8 /src/server | |
parent | ba3bcbb86ea0177349bcd021559347248d6ab10a (diff) | |
download | s6-rc-e2959c22c7309836d6f5dba2f0db7b293b860ad8.tar.xz |
A few pieces of the future s6-rcd server
Diffstat (limited to 'src/server')
-rw-r--r-- | src/server/client.c | 161 | ||||
-rw-r--r-- | src/server/client.h | 50 | ||||
-rw-r--r-- | src/server/clientrules.c | 118 | ||||
-rw-r--r-- | src/server/clientrules.h | 15 | ||||
-rw-r--r-- | src/server/command.c | 43 | ||||
-rw-r--r-- | src/server/command.h | 14 | ||||
-rw-r--r-- | src/server/db.h | 11 | ||||
-rw-r--r-- | src/server/ep.c | 128 | ||||
-rw-r--r-- | src/server/ep.h | 21 | ||||
-rw-r--r-- | src/server/ev.c | 47 | ||||
-rw-r--r-- | src/server/ev.h | 19 | ||||
-rw-r--r-- | src/server/livedir.h | 11 | ||||
-rw-r--r-- | src/server/livedir_init.c | 124 | ||||
-rw-r--r-- | src/server/livesubdir_create.c | 35 | ||||
-rw-r--r-- | src/server/main.h | 18 | ||||
-rw-r--r-- | src/server/s6-rcd.c | 139 | ||||
-rw-r--r-- | src/server/s6-rcd.h | 17 | ||||
-rw-r--r-- | src/server/signals.c | 89 | ||||
-rw-r--r-- | src/server/signals.h | 11 | ||||
-rw-r--r-- | src/server/transition.h | 22 |
20 files changed, 1093 insertions, 0 deletions
diff --git a/src/server/client.c b/src/server/client.c new file mode 100644 index 0000000..3984553 --- /dev/null +++ b/src/server/client.c @@ -0,0 +1,161 @@ +/* ISC license. */ + +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> + +#include <skalibs/alloc.h> +#include <skalibs/error.h> +#include <skalibs/strerr2.h> +#include <skalibs/tai.h> +#include <skalibs/djbunix.h> +#include <skalibs/iopause.h> +#include <skalibs/textmessage.h> + +#include <s6-rc/connection-common.h> +#include "command.h" +#include "main.h" +#include "client.h" + + /* + We can't use gensetdyn for client storage, because + c->connection.in has a pointer to c->connection.inbuf + and gensetdyn can relocate the whole thing on realloc; + also, having a sentinel is too costly (client_t is big). + So we use an OG linked list, with all the boundary value + problems it brings. + */ + +tain_t client_answer_tto = TAIN_INFINITE ; +client_t *client_head = 0 ; +uint32_t client_connections = 0 ; +uint32_t client_monitors = 0 ; + +static inline int ismonitored (client_t *c) +{ + return textmessage_sender_fd(&c->monitor) >= 0 ; +} + +static inline void client_free (client_t *c) +{ + monitor_finish(c) ; + fd_close(textmessage_sender_fd(&c->connection.out)) ; + textmessage_sender_free(&c->connection.out) ; + textmessage_receiver_free(&c->connection.in) ; + alloc_free(c) ; +} + +void client_yoink (client_t **c) +{ + client_t *prev = (*c)->prev ; + if (prev) prev->next = (*c)->next ; else client_head = (*c)->next ; + if ((*c)->next) (*c)->next->prev = prev ; + if (ismonitored(*c)) nmonitors-- ; + client_free(*c) ; + *c = prev ; + client_connections-- ; +} + +void client_setdeadline (client_t *c) +{ + tain_t blah ; + tain_half(&blah, &tain_infinite_relative) ; + tain_add_g(&blah, &blah) ; + if (tain_less(&blah, &c->deadline)) + tain_add_g(&c->deadline, &client_answer_tto) ; +} + +int client_prepare_iopause (client_t *c, tain_t *deadline, iopause_fd *x, uint32_t *j) +{ + if (tain_less(&c->deadline, deadline)) *deadline = c->deadline ; + if (!textmessage_sender_isempty(&c->connection.out) || !textmessage_receiver_isempty(&c->connection.in) || (!flags.lameduck && !textmessage_receiver_isfull(&c->connection.in))) + { + x[*j].fd = textmessage_sender_fd(&c->connection.out) ; + x[*j].events = (!textmessage_receiver_isempty(&c->connection.in) || (!flags.lameduck && !textmessage_receiver_isfull(&c->connection.in)) ? IOPAUSE_READ : 0) | (!textmessage_sender_isempty(&c->connection.out) ? IOPAUSE_WRITE : 0) ; + c->xindex[0] = (*j)++ ; + } + else c->xindex[0] = 0 ; + if (ismonitored(c) && !textmessage_sender_isempty(&c->monitor)) + { + x[*j].fd = textmessage_sender_fd(&c->monitor) ; + x[*j].events = IOPAUSE_WRITE ; + c->xindex[1] = (*j)++ ; + } + else c->xindex[1] = 0 ; + return !!c->xindex[0] || !!c->xindex[1] ; +} + +int client_add (int fd, uint8_t perms) +{ + client_t *c = alloc(sizeof(client_t)) ; + if (!c) return 0 ; + c->xindex[0] = c->xindex[1] = 0 ; + tain_add_g(&c->deadline, &client_answer_tto) ; + c->perms = perms ; + c->monitor = textmessage_sender_zero ; + textmessage_sender_init(&c->connection.out, fd) ; + textmessage_receiver_init(&c->connection.in, fd, &c->connection.inbuf, S6RC_CONNECTION_BUFSIZE) ; + c->prev = 0 ; + c->next = client_head ; + if (c->next) c->next->prev = c ; + client_head = c ; + client_connections++ ; + return 1 ; +} + +int client_flush (client_t *c, iopause_fd const *x) +{ + int ok = 1 ; + int done = 1 ; + if (c->xindex[0] && (x[c->xindex[0]].revents & IOPAUSE_WRITE)) + { + if (!textmessage_sender_flush(&c->connection.out)) + { + done = 0 ; + if (!error_isagain(errno)) ok = 0 ; + } + } + if (ismonitored(c) && c->xindex[1] && (x[c->xindex[1]].revents & IOPAUSE_WRITE)) + { + if (!textmessage_sender_flush(&c->monitor)) + { + done = 0 ; + if (!error_isagain(errno)) ok = 0 ; + } + } + if (done) tain_add_g(&c->deadline, &tain_infinite_relative) ; + return ok ; +} + +int client_read (client_t *c, iopause_fd const *x) +{ + return !textmessage_receiver_isempty(&c->connection.in) || (c->xindex[0] && (x[c->xindex[0]].revents & IOPAUSE_READ)) ? + textmessage_handle(&c->connection.in, command_handle, c) > 0 : 1 ; +} + +int monitor_init (client_t *c, int fd, unsigned int v) +{ + if (ismonitored(c)) return (errno = EINVAL, 0) ; + textmessage_sender_init(&c->monitor, fd) ; + c->monitor_verbosity = v ; + client_monitors++ ; + return 1 ; +} + +void monitor_finish (client_t *c) +{ + if (!ismonitored(c)) return ; + fd_close(textmessage_sender_fd(&c->monitor)) ; + textmessage_sender_free(&c->monitor) ; + c->monitor = textmessage_sender_zero ; + client_monitors-- ; +} + +void monitor_put (unsigned int v, char const *s, size_t len) +{ + if (!client_monitors) return ; + for (client_t *c = client_head ; c ; c = c->next) + if (ismonitored(c) && v >= c->monitor_verbosity && !textmessage_put(&c->monitor, s, len)) + strerr_diefu1sys("queue message to monitor") ; +} diff --git a/src/server/client.h b/src/server/client.h new file mode 100644 index 0000000..4a6ad6c --- /dev/null +++ b/src/server/client.h @@ -0,0 +1,50 @@ +/* ISC license. */ + +#ifndef S6RCD_CLIENT_H +#define S6RCD_CLIENT_H + +#include <stdint.h> + +#include <skalibs/tai.h> +#include <skalibs/iopause.h> +#include <skalibs/textmessage.h> + +#include <s6-rc/connection.h> + + + /* Client connection */ + +typedef struct client_s client_t, *client_t_ref ; +struct client_s +{ + client_t *prev ; + client_t *next ; + uint32_t xindex[2] ; + tain_t deadline ; + s6rc_connection_t connection ; + textmessage_sender_t monitor ; + unsigned int monitor_verbosity ; + uint8_t perms ; +} ; +#define CLIENT_ZERO { .next = 0, .xindex = 0, .deadline = TAIN_ZERO, .connection = S6RC_CONNECTION_ZERO, .monitor = TEXTMESSAGE_SENDER_ZERO, .monitor_verbosity = 0, .perms = 0 } + +extern tain_t client_answer_tto ; +extern client_t *client_head ; +extern uint32_t client_connections ; + +extern void client_yoink (client_t **) ; +extern int client_prepare_iopause (client_t *, tain_t *, iopause_fd *, uint32_t *) ; +extern int client_flush (client_t *, iopause_fd const *) ; +extern int client_add (int, uint8_t) ; + +extern void client_setdeadline (client_t *) ; + + + /* Monitors */ + +extern uint32_t client_monitors ; +extern int monitor_init (client_t *, int) ; +extern int monitor_finish (client_t *) ; +extern int monitor_put (unsigned int, char const *, size_t) ; + +#endif diff --git a/src/server/clientrules.c b/src/server/clientrules.c new file mode 100644 index 0000000..4050b2f --- /dev/null +++ b/src/server/clientrules.c @@ -0,0 +1,118 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <stdint.h> + +#include <skalibs/posixplz.h> +#include <skalibs/bytestr.h> +#include <skalibs/types.h> +#include <skalibs/env.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <skalibs/cdb.h> + +#include <s6/accessrules.h> + +#include "clientrules.h" + +static unsigned int rulestype = 0 ; +static char const *rules = 0 ; +static int cdbfd = -1 ; +static struct cdb cdbmap = CDB_ZERO ; + +void clientrules_init (unsigned int type, char const *s) +{ + rulestype = type ; + rules = s ; + if (rulestype == 2) + { + cdbfd = open_readb(rules) ; + if (cdbfd < 0) strerr_diefu3sys(111, "open ", rules, " for reading") ; + if (cdb_init(&cdbmap, cdbfd) < 0) + strerr_diefu2sys(111, "cdb_init ", rules) ; + } +} + +void clientrules_reload () +{ + int fd ; + struct cdb c = CDB_ZERO ; + if (rulestype != 2) break ; + fd = open_readb(rules) ; + if (fd < 0) break ; + if (cdb_init(&c, fd) < 0) + { + fd_close(fd) ; + break ; + } + cdb_free(&cdbmap) ; + fd_close(cdbfd) ; + cdbfd = fd ; + cdbmap = c ; +} + +static inline uint8_t parse_env (char const *const *envp) +{ + uint8_t perms = 0 ; + for (; *envp ; envp++) + { + if (str_start(*envp, "query=")) perms |= 1 ; + if (str_start(*envp, "monitor=")) perms |= 2 ; + if (str_start(*envp, "change=")) perms |= 4 ; + if (str_start(*envp, "event=")) perms |= 8 ; + if (str_start(*envp, "admin=")) perms |= 16 ; + } + return perms ; +} + +int clientrules_check (int fd, uint8_t *perms) +{ + s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ; + s6_accessrules_result_t result = S6_ACCESSRULES_ERROR ; + uid_t uid ; + gid_t gid ; + + if (getpeereid(fd, &uid, &gid) < 0) + { + if (verbosity) strerr_warnwu1sys("getpeereid") ; + return 0 ; + } + + switch (rulestype) + { + case 1 : + result = s6_accessrules_uidgid_fs(uid, gid, rules, ¶ms) ; break ; + case 2 : + result = s6_accessrules_uidgid_cdb(uid, gid, &cdbmap, ¶ms) ; break ; + default : break ; + } + if (result != S6_ACCESSRULES_ALLOW) + { + if (verbosity && (result == S6_ACCESSRULES_ERROR)) + strerr_warnw1sys("error while checking rules") ; + return 0 ; + } + if (params.exec.len && verbosity) + { + char fmtuid[UID_FMT] ; + char fmtgid[GID_FMT] ; + fmtuid[uid_fmt(fmtuid, uid)] = 0 ; + fmtgid[gid_fmt(fmtgid, gid)] = 0 ; + strerr_warnw4x("unused exec string in rules for uid ", fmtuid, " gid ", fmtgid) ; + } + if (params.env.s) + { + size_t n = byte_count(params.env.s, params.env.len, '\0') ; + char const *envp[n+1] ; + if (!env_make(envp, n, params.env.s, params.env.len)) + { + if (verbosity) strerr_warnwu1sys("env_make") ; + s6_accessrules_params_free(¶ms) ; + return 0 ; + } + envp[n] = 0 ; + *perms = parse_env(envp, perms) ; + } + s6_accessrules_params_free(¶ms) ; + return !!perms ; +} diff --git a/src/server/clientrules.h b/src/server/clientrules.h new file mode 100644 index 0000000..1080d7a --- /dev/null +++ b/src/server/clientrules.h @@ -0,0 +1,15 @@ +/* ISC license. */ + +#ifndef S6RCD_CLIENTRULES_H +#define S6RCD_CLIENTRULES_H + +#include <stdint.h> + + + /* Client rules */ + +extern void clientrules_init (unsigned int, char const *) ; +extern void clientrules_reload (void) ; +extern int clientrules_check (int, uint8_t *) ; + +#endif diff --git a/src/server/command.c b/src/server/command.c new file mode 100644 index 0000000..c52dc8b --- /dev/null +++ b/src/server/command.c @@ -0,0 +1,43 @@ +/* ISC license. */ + +#include <errno.h> +#include <sys/uio.h> + +#include <skalibs/textmessage.h> +#include <skalibs/posixishard.h> + +#include "client.h" +#include "command.h" + +static int answer (client_t *c, char e) +{ + if (!textmessage_put(&c->connection.out, &e, 1)) return 0 ; + client_setdeadline(c) ; + return 1 ; +} + +static int do_query (client_t *c, char const *s, size_t len) +{ + if (!len--) return (errno = EPROTO, 0) ; +} + +static int do_monitor (client_t *c, char const *s, size_t len) +{ +} + +int command_handle (struct iovec const *v, void *aux) +{ + client_t *c = aux ; + char const *s = v->iov_base ; + size_t len = v->iov_len ; + if (!len--) return (errno = EPROTO, 0) ; + switch (*s++) + { + case 'Q' : return do_query(c, s, len) ; + case 'M' : return do_monitor(c, s, len) ; + case '?' : return do_change(c, s, len) ; + case '!' : return do_event(c, s, len) ; + case '#' : return do_admin(c, s, len) ; + default : return do_error(c) ; + } +} diff --git a/src/server/command.h b/src/server/command.h new file mode 100644 index 0000000..4edfb09 --- /dev/null +++ b/src/server/command.h @@ -0,0 +1,14 @@ +/* ISC license. */ + +#ifndef S6RCD_COMMAND_H +#define S6RCD_COMMAND_H + +#include <sys/uio.h> + +#include <skalibs/textmessage.h> + + /* Client commands */ + +int command_handle (struct iovec const *, void *) ; + +#endif diff --git a/src/server/db.h b/src/server/db.h new file mode 100644 index 0000000..efe799d --- /dev/null +++ b/src/server/db.h @@ -0,0 +1,11 @@ +/* ISC license. */ + +#ifndef S6RCD_DB_H +#define S6RCD_DB_H + + /* Service database */ + +extern int s6rcd_db_load (s6rc_db_t *, char const *) ; +extern int s6rcd_db_read_sizes (s6rc_db_sizes_t *, char const *) ; + +#endif diff --git a/src/server/ep.c b/src/server/ep.c new file mode 100644 index 0000000..2fa0c0e --- /dev/null +++ b/src/server/ep.c @@ -0,0 +1,128 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <skalibs/strerr2.h> +#include <skalibs/genalloc.h> +#include <skalibs/gensetdyn.h> +#include <skalibs/avltree.h> + +#include <s6-rc/event.h> +#include "ep.h" + +typedef struct epelem_s epelem_t, *epelem_t_ref ; +struct epelem_s +{ + uint32_t owner ; + ep_func_t_ref f ; + void *aux ; +} ; +#define EPELEM_ZERO { .owner = 0, .f = 0, .aux = 0 } + +typedef struct eplist_s eplist_t, *eplist_t_ref ; +struct eplist_s +{ + char const *name ; + genalloc list ; /* epelem_t */ +} ; +#define EPLIST_ZERO { .name = 0, .list = GENALLOC_ZERO } + +typedef struct epset_s epset_t, *epset_t_ref ; +struct epset_s +{ + gensetdyn data ; /* eplist_t */ + avltree map ; +} ; +#define EPSET_ZERO { .data = GENSETDYN_ZERO, .map = AVLTREE_ZERO } + +static void *epset_dtok (uint32_t d, void *x) +{ + return (void *)&GENSETDYN_P(eplist_t, (gensetdyn *)x, d)->name ; +} + +static int epset_cmp (void const *a, void const *b, void *x) +{ + (void)x ; + return strcmp((char const *)a, (char const *)b) ; +} + +static epset_t ep[EVENTTYPE_PHAIL] = +{ + { .data = GENSETDYN_ZERO, .map = AVLTREE_INIT(8, 3, 8, &epset_dtok, &epset_cmp, &ep[0].data) }, + { .data = GENSETDYN_ZERO, .map = AVLTREE_INIT(8, 3, 8, &epset_dtok, &epset_cmp, &ep[1].data) }, + { .data = GENSETDYN_ZERO, .map = AVLTREE_INIT(8, 3, 8, &epset_dtok, &epset_cmp, &ep[2].data) }, + { .data = GENSETDYN_ZERO, .map = AVLTREE_INIT(8, 3, 8, &epset_dtok, &epset_cmp, &ep[3].data) }, + { .data = GENSETDYN_ZERO, .map = AVLTREE_INIT(8, 3, 8, &epset_dtok, &epset_cmp, &ep[4].data) }, + { .data = GENSETDYN_ZERO, .map = AVLTREE_INIT(8, 3, 8, &epset_dtok, &epset_cmp, &ep[5].data) }, + { .data = GENSETDYN_ZERO, .map = AVLTREE_INIT(8, 3, 8, &epset_dtok, &epset_cmp, &ep[6].data) }, +} ; + +static void eplist_free (void *p) +{ + eplist_t *x = p ; + genalloc_free(epelem_t, &x->list) ; +} + +void ep_free () +{ + for (s6rc_eventtype_t i = 0 ; i < S6RC_EVENTTYPE_PHAIL ; i++) + { + avltree_free(&ep[i].map) ; + gensetdyn_deepfree(&ep[i].data, &eplist_free) ; + avltree_init(&ep[i].map, 8, 3, 8, &epset_dtok, &epset_cmp, &ep[i].data) ; + } +} + +int ep_add (uint8_t type, char const *name, uint32_t owner, ep_func_t_ref f, void *aux) +{ + epelem_t ee = { .owner = owner, .f = f, .aux = aux } ; + uint32_t d ; + if (type >= S6RC_EVENTTYPE_PHAIL) return (errno = EINVAL, 0) ; + if (!avltree_search(&ep[type].map, name, &d)) + { + eplist_t newlist = { .name = name, .list = GENALLOC_ZERO } ; + if (!gensetdyn_new(&ep[type].data, &d)) return 0 ; + *GENSETDYN_P(eplist_t, &ep[type].data, d) = newlist ; + if (!avltree_insert(&ep[type].map, d)) + { + gensetdyn_delete(&ep[type].data, d) ; + return 0 ; + } + } + return genalloc_catb(epelem_t, &GENSETDYN_P(eplist_t, &ep[type].data, d)->list, &ee) ; +} + +void ep_delete (uint8_t type, char const *name, uint32_t owner, ep_func_t_ref f, void *aux) +{ + uint32_t d ; + if (type >= S6RC_EVENTTYPE_PHAIL) return ; + if (!avltree_search(&ep[type].map, name, &d)) return ; + + epelem_t ee = { .owner = owner, .f = f, .aux = aux } ; + genalloc *g = &GENSETDYN_P(eplist_t, &ep[type].data, d)->list ; + epelem_t *list = genalloc_s(epelem_t, g) ; + size_t n = genalloc_len(epelem_t, g) ; + size_t i = 0 ; + for (; i < n ; i++) if (list == ee) break ; + if (i < n) list[i] = list[--n] ; + if (!n) + { + genalloc_free(epelem_t, g) ; + avltree_delete(&ep[type].map, name) ; + gensetdyn_delete(eplist_t, &ep[type].data, d) ; + } +} + +void ep_run (s6rc_event_t const *ev) +{ + uint32_t d ; + if (!avltree_search(&ep[ev->type].map, ev->name, &d)) return ; + + genalloc *g = &GENSETDYN_P(eplist_t, &ep[ev->type].data, d)->list ; + epelem_t const *list = genalloc_s(epelem_t, g) ; + size_t n = genalloc_len(epelem_t, g) ; + for (size_t i = 0 ; i < n ; i++) + (*list[i].f)(ev, list[i].owner, list[i].aux) ; +} diff --git a/src/server/ep.h b/src/server/ep.h new file mode 100644 index 0000000..96e6c59 --- /dev/null +++ b/src/server/ep.h @@ -0,0 +1,21 @@ +/* ISC license. */ + +#ifndef S6RCD_EP_H +#define S6RCD_EP_H + +#include <stdint.h> + +#include <s6-rc/event.h> + + + /* Event processor: the dynamic part */ + +typedef void ep_func_t (s6rc_event_t const *, uint32_t, void *) ; +typedef ep_func_t *ep_func_t_ref ; + +extern void ep_free (void) ; +extern int ep_add (uint8_t, char const *, uint32_t, ep_func_t_ref, void *) ; +extern void ep_delete (uint8_t, char const *, uint32_t, ep_func_t_ref, void *) ; +extern void ep_run (s6rc_event_t const *) ; + +#endif diff --git a/src/server/ev.c b/src/server/ev.c new file mode 100644 index 0000000..c201191 --- /dev/null +++ b/src/server/ev.c @@ -0,0 +1,47 @@ +/* ISC license. */ + +#include <skalibs/genqdyn.h> + +#include <s6-rc/event.h> +#include "ev.h" + + +/* Event queue */ + +static genqdyn evq = GENQDYN_INIT(s6rc_event_t, 0, 1) ; /* only clean when it's empty */ + +int ev_enqueue (s6rc_event_t const *ev) +{ + return genqdyn_push(&evq, ev) ; +} + +int ev_pop (s6rc_event_t *ev) +{ + if (!genqdyn_n(&evq)) return 0 ; + *ev = *GENQDYN_PEEK(s6rc_event_t, &evq) ; + return genqdyn_pop(&evq) ; +} + + + /* Event processing (builtins) */ + +void ev_handle (s6rc_event_t const *ev) +{ + switch (ev->type) + { + case S6RC_EVENTTYPE_WANTED_STATE_DOWN : + break ; + case S6RC_EVENTTYPE_WANTED_STATE_UP : + break ; + case S6RC_EVENTTYPE_CURRENT_STATE_DOWN : + break ; + case S6RC_EVENTTYPE_CURRENT_STATE_UP : + break ; + case S6RC_EVENTTYPE_TRANSITION_STOP : + break ; + case S6RC_EVENTTYPE_TRANSITION_START : + break ; + case S6RC_EVENTTYPE_CUSTOM : + break ; + } +} diff --git a/src/server/ev.h b/src/server/ev.h new file mode 100644 index 0000000..c267a40 --- /dev/null +++ b/src/server/ev.h @@ -0,0 +1,19 @@ +/* ISC license. */ + +#ifndef S6RCD_EV_H +#define S6RCD_EV_H + +#include <s6-rc/event.h> + + + /* Event queue */ + +extern int ev_enqueue (s6rc_event_t const *) ; +extern int ev_pop (s6rc_event_t *) ; + + + /* Event processor: the static part */ + +extern void ev_handle (s6rc_event_t const *) ; + +#endif diff --git a/src/server/livedir.h b/src/server/livedir.h new file mode 100644 index 0000000..1d6d106 --- /dev/null +++ b/src/server/livedir.h @@ -0,0 +1,11 @@ +/* ISC license. */ + +#ifndef S6RCD_LIVEDIR_H +#define S6RCD_LIVEDIR_H + + /* Live directory */ + +extern int s6rcd_livedir_init (char const *, char const *, char const *, char const *, int *) +extern int s6rcd_livesubdir_create (char *, char const *, char const *) ; + +#endif diff --git a/src/server/livedir_init.c b/src/server/livedir_init.c new file mode 100644 index 0000000..f252dba --- /dev/null +++ b/src/server/livedir_init.c @@ -0,0 +1,124 @@ +/* ISC license. */ + +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <limits.h> +#include <stdlib.h> +#include <sys/stat.h> + +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <skalibs/webipc.h> + +#include "s6rcd.h" + +#ifdef NAME_MAX +# define S6RC_NAME_MAX NAME_MAX +#else +# define S6RC_NAME_MAX 63 +#endif + +static inline int mksubdirs (char *s) +{ + size_t n = strlen(s) ; + size_t i = 0 ; + for (; i < n ; i++) + { + if (s[i] == '/') + { + int r ; + s[i] = 0 ; + r = mkdir(s, 0755) ; + s[i] = '/' ; + if (r < 0 && errno != EEXIST) break ; + } + } + return i >= n ; +} + +int s6rcd_livedir_init (char const *livedir, char const *scandir, char const *prefix, char const *compiled, int *sock) +{ + size_t plen = strlen(prefix) ; + size_t llen = strlen(livedir) ; + struct stat st ; + char ltmp[llen + 9] ; + if (plen >= S6RC_NAME_MAX) + strerr_dief1x(100, "prefix is too long") ; + + memcpy(ltmp, livedir, llen + 1) ; + if (!mksubdirs(ltmp, len)) + strerr_diefu2sys(111, "create subdirectories of ", s) ; + if (mkdir(ltmp, 0755) < 0 && errno != EEXIST) + strerr_diefu2sys(111, "mkdir ", ltmp) ; + + memcpy(ltmp + llen, "/s", 3) ; + *sock = ipc_stream() ; + if (*sock < 0) + strerr_diefu1sys(111, "create socket") ; + if (ipc_bind_reuse(*sock, ltmp) < 0) + strerr_diefu2sys(111, "bind to ", ltmp) ; + + memcpy(ltmp + llen + 1, "scandir", 8) ; + if (lstat(ltmp, &st) < 0) + { + if (errno != ENOENT) + strerr_diefu2sys(111, "lstat ", ltmp) ; + if (symlink(scandir, ltmp) < 0) + strerr_diefu4sys(111, "symlink ", ltmp, " to ", scandir) ; + } + else + { + size_t slen = strlen(scandir) ; + char stmp[slen + 1] ; + if (!S_ISLNK(st.st_mode)) + strerr_dief3x(100, "file ", ltmp, " exists and is not a symbolic link") ; + if (readlink(ltmp, stmp, slen + 1) < 0) + strerr_diefu2sys(111, "readlink ", ltmp) ; + if (strncmp(scandir, stmp, slen + 1)) + strerr_dief4x(100, "provided scandir ", scandir, " does not match the contents of existing ", ltmp) ; + } + + memcpy(ltmp + llen + 1, "prefix", 7) ; + if (stat(ltmp, &st) < 0) + { + if (errno != ENOENT) + strerr_diefu2sys(111, "stat ", ltmp) ; + if (!openwritenclose_unsafe(ltmp, prefix, strlen(prefix))) + strerr_diefu2sys(111, "write prefix to ", ltmp) ; + } + else + { + if (!S_IFREG(st.st_mode)) + strerr_dief3x(100, "file ", ltmp, " exists and is not a regular file") ; + if (st.st_size != plen) + strerr_dief4x(100, "provided prefix ", prefix, " does not match the contents of existing ", ltmp) ; + { + char stmp[plen] ; + ssize_t r = openreadnclose(ltmp, ptmp, plen) ; + if (r != plen) + strerr_diefu2sys(111, "read ", ltmp) ; + if (memcmp(ptmp, prefix, plen)) + strerr_dief4x(100, "provided prefix ", prefix, " does not match the contents of existing ", ltmp) ; + } + } + + memcpy(ltmp + llen + 1, "live", 5) ; + if (lstat(ltmp, &st) < 0) + { + char name[12] ; + if (errno != ENOENT) strerr_diefu2sys(111, "lstat ", ltmp) ; + if (!s6rcd_livesubdir_create(name, live, compiled)) + strerr_diefu2sys(111, "create live subdirectory in ", live) ; + if (symlink(name, ltmp) < 0) + strerr_diefu4sys(111, "symlink ", ltmp, " to ", name) ; + return 0 ; + } + if (!S_ISLNK(st.st_mode)) + strerr_dief3x(100, "livesubdir ", ltmp, " exists and is not a symbolic link") ; + if (stat(ltmp, &st) < 0) + strerr_diefu2sys(111, "stat ", ltmp) ; + if (!S_ISDIR(st.st_mode)) + strerr_dief4x(100, "livesubdir ", ltmp, " exists and is not a symbolic link", " to a directory") ; + return 1 ; +} diff --git a/src/server/livesubdir_create.c b/src/server/livesubdir_create.c new file mode 100644 index 0000000..ff989aa --- /dev/null +++ b/src/server/livesubdir_create.c @@ -0,0 +1,35 @@ +/* ISC license. */ + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/stat.h> + +#include <skalibs/djbunix.h> + +#include "s6rcd.h" + +int s6rcd_livesubdir_create (char *name, char const *live, char const *compiled) +{ + size_t llen = strlen(live) ; + size_t clen = strlen(compiled) ; + char cfn[clen + 13] ; + char lfn[llen + 25] ; + memcpy(lfn, live, llen) ; + memcpy(lfn + llen, "/live:XXXXXX", 13) ; + if (!mkdtemp(lfn)) return 0 ; + + memcpy(lfn + llen + 12, "/compiled", 10) ; + if (symlink(compiled, lfn) < 0) return 0 ; + strerr_diefu4sys(111, "symlink ", compiled, " to ", realfn + llen + 1) ; + + memcpy(cfn, compiled, clen) ; + memcpy(cfn + clen, "/servicedirs", 13) ; + memcpy(lfn + llen + 13, "servicedirs", 12) ; + if (!hiercopy(cfn, lfn)) return 0 ; + + lfn[llen + 12] = 0 ; + if (chmod(lfn, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) return 0 ; + memcpy(name, lfn + llen + 1, 12) ; + return 1 ; +} diff --git a/src/server/main.h b/src/server/main.h new file mode 100644 index 0000000..84d6688 --- /dev/null +++ b/src/server/main.h @@ -0,0 +1,18 @@ +/* ISC license. */ + +#ifndef S6RCD_MAIN_H +#define S6RCD_MAIN_H + + /* Exported by the main file, s6-rcd.c */ + +typedef struct globalflags_s globalflags_t, *globalflags_t_ref ; +struct globalflags_s +{ + uint8_t lameduck : 1 ; +} ; + +extern globalflags_t flags ; +extern tain_t lameduckdeadline ; +extern unsigned int verbosity ; + +#endif diff --git a/src/server/s6-rcd.c b/src/server/s6-rcd.c new file mode 100644 index 0000000..0fcfb77 --- /dev/null +++ b/src/server/s6-rcd.c @@ -0,0 +1,139 @@ +/* ISC license. */ + +#include <fcntl.h> + +#include <skalibs/types.h> +#include <skalibs/strerr2.h> +#include <skalibs/subgetopt.h> +#include <skalibs/tai.h> +#include <skalibs/djbunix.h> + +#include "s6-rcd.h" + +#define USAGE "s6-rcd [ -v verbosity ] [ -1 ] [ -t timeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ] [ -l livedir ]" +#define dieusage() strerr_dieusage(100, USAGE) ; + +unsigned int verbosity = 1 ; +globalflags_t flags = +{ + .lameduck = 0 ; +} ; + +tain_t lameduckdeadline = TAIN_INFINITE_RELATIVE ; + +int main (int argc, char const *const *argv) +{ + int spfd, sock ; + PROG = "s6-rcd" ; + + { + char const *rules = 0 ; + unsigned int rulestype = 0 ; + int flag1 = 0 ; + unsigned int t = 0, T = 0 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "v:1c:n:i:x:t:T:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case '1' : flag1 = 1 ; break ; + case 'i' : rules = l.arg ; rulestype = 1 ; break ; + case 'x' : rules = l.arg ; rulestype = 2 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&client_answer_tto, t) ; + if (T) tain_from_millisecs(&lameduckdeadline, T) ; + if (!rulestype) strerr_dief1x(100, "no access rights specified!") ; + if (flag1) + { + if (fcntl(1, F_GETFD) < 0) + strerr_dief1sys(100, "called with option -1 but stdout said") ; + } + else close(1) ; + + spfd = signals_init() ; + clientrules_init(rulestype, rules) ; + sock = livedir_init() ; + if (!tain_now_set_stopwatch_g()) + strerr_diefu1sys(111, "initialize clock") ; + if (flag1) + { + fd_write(1, "\n", 1) ; + fd_close(1) ; + } + } + + for (;;) + { + iopause_fd x[2 + client_connections + client_monitors] ; + tain_t deadline ; + uint32_t j = 2 ; + int r = 1 ; + + { + s6rc_event_t ev ; + while (ev_pop(&ev)) { ev_handle(&ev) ; ep_run(&ev) ; } + } + + tain_add_g(&deadline, &tain_infinite_relative) ; + if (flags.lameduck) deadline = lameduckdeadline ; + + x[0].fd = spfd ; + x[0].events = IOPAUSE_READ ; + x[1].fd = sock ; + x[1].events = !flags.lameduck ? IOPAUSE_READ : 0 ; + + for (client_t *c = client_head ; c ; c = c->next) + if (client_prepare_iopause(c, &deadline, x, &j)) r = 0 ; + + if (r) /* clean buffers */ + { + if (flags.lameduck) break ; + if (flags.dbupdate && db_update()) + { + flags.dbupdate = 0 ; + continue ; + } + } + + r = iopause_g(x, j, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + + if (!r) + { + if (flags.lameduck && !tain_future(&lameduckdeadline)) break ; + for (client_t *c = client_head ; c ; c = c ? c->next : client_head) + if (!tain_future(c->deadline)) client_yoink(&c) ; + continue ; + } + + if (x[0].revents & IOPAUSE_READ) signals_handle() ; + + for (client_t *c = client_head ; c ; c = c ? c->next : client_head) + if (!client_flush(c, x)) client_yoink(&c) ; + + for (client_t *c = client_head ; c ; c = c ? c->next : client_head) + if (!client_read(c, x)) client_yoink(&c) ; + + if (x[1].revents & IOPAUSE_READ) + { + uint8_t perms = 0 ; + int dummy ; + int fd = ipc_accept_nb(x[1].fd, 0, 0, &dummy) ; + if (fd < 0) + if (!error_isagain(errno)) strerr_diefu1sys(111, "accept connection") ; + else continue ; + else if (!clientrules_check(fd, &perms)) fd_close(fd) ; + else client_add(fd, perms) ; + } + } + + return 0 ; +} diff --git a/src/server/s6-rcd.h b/src/server/s6-rcd.h new file mode 100644 index 0000000..2436c54 --- /dev/null +++ b/src/server/s6-rcd.h @@ -0,0 +1,17 @@ +/* ISC license. */ + +#ifndef S6RCD_H +#define S6RCD_H + +#include "ep.h" +#include "ev.h" +#include "transition.h" +#include "clientrules.h" +#include "client.h" +#include "command.h" +#include "livedir.h" +#include "db.h" +#include "signals.h" +#include "main.h" + +#endif diff --git a/src/server/signals.c b/src/server/signals.c new file mode 100644 index 0000000..ce26bdb --- /dev/null +++ b/src/server/signals.c @@ -0,0 +1,89 @@ +/* ISC license. */ + +#include <errno.h> +#include <signal.h> +#include <sys/wait.h> + +#include <skalibs/strerr2.h> +#include <skalibs/sig.h> +#include <skalibs/selfpipe.h> +#include <skalibs/tai.h> + +#include "clientrules.h" +#include "transition.h" +#include "main.h" +#include "signals.h" + +static inline void activate_lameduck (void) +{ + if (!flags.lameduck) + { + flags.lameduck = 1 ; + tain_add_g(&lameduckdeadline, &lameduckdeadline) ; + } +} + +static inline void reload_config (void) +{ + clientrules_reload() ; +} + +static inline void wait_children (void) +{ + for (;;) + { + unsigned int j = 0 ; + int wstat ; + pid_t r = wait_nohang(&wstat) ; + if (r < 0) + if (errno == ECHILD) break ; + else strerr_diefu1sys(111, "wait for children") ; + else if (!r) break ; + for (; j < ntransitions ; j++) if (transitions[j].pid == r) break ; + if (r < ntransitions) + { + transition_t tr = transitions[j] ; + transitions[j] = transitions[--ntransitions] ; + if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) + transition_success(&tr) ; + else + transition_failure(&tr, !!WIFSIGNALED(wstat), WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat)) ; + } + } +} + +void signals_handle () +{ + for (;;) + { + switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "selfpipe_read") ; + case 0 : return ; + case SIGTERM : + activate_lameduck() ; + break ; + case SIGHUP : + reload_config() ; + break ; + case SIGCHLD : + wait_children() ; + break ; + } + } +} + +int signals_init () +{ + int fd = selfpipe_init() ; + if (fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGHUP) ; + if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ; + } + return fd ; +} diff --git a/src/server/signals.h b/src/server/signals.h new file mode 100644 index 0000000..b9c167f --- /dev/null +++ b/src/server/signals.h @@ -0,0 +1,11 @@ +/* ISC license. */ + +#ifndef S6RCD_SIGNALS_H +#define S6RCD_SIGNALS_H + + /* Signal management */ + +extern int signals_init (void) ; +extern void signals_handle (void) ; + +#endif diff --git a/src/server/transition.h b/src/server/transition.h new file mode 100644 index 0000000..fcddfa6 --- /dev/null +++ b/src/server/transition.h @@ -0,0 +1,22 @@ +/* ISC license. */ + +#ifndef S6RCD_TRANSITION_H +#define S6RCD_TRANSITION_H + +#include <sys/types.h> +#include <stdint.h> + + + /* Transitions */ + +typedef struct transition_s transition_t, *transition_t_ref ; +struct transition_s +{ + pid_t pid ; +} ; +#define TRANSITION_ZERO { .pid = 0 } + +extern uint32_t ntransitions ; +transitions_t *transitions ; + +#endif |