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 | |
parent | ba3bcbb86ea0177349bcd021559347248d6ab10a (diff) | |
download | s6-rc-e2959c22c7309836d6f5dba2f0db7b293b860ad8.tar.xz |
A few pieces of the future s6-rcd server
29 files changed, 973 insertions, 161 deletions
diff --git a/src/include/s6-rc/connection.h b/src/include/s6-rc/connection.h index f6d5eda..b02c96f 100644 --- a/src/include/s6-rc/connection.h +++ b/src/include/s6-rc/connection.h @@ -8,9 +8,20 @@ #include <s6-rc/connection-common.h> +#define S6RC_MONITOR_BUFSIZE S6RC_CONNECTION_BUFSIZE + +typedef struct s6rc_monitor_s s6rc_monitor_t, *s6rc_monitor_t_ref ; +struct s6rc_monitor_s +{ + textmessage_receiver_t in ; + char inbuf[S6RC_MONITOR_BUFSIZE] ; +} +#define S6RC_MONITOR_ZERO { .in = TEXTMESSAGE_RECEIVER_ZERO, .inbuf = "" } + +extern void s6rc_connection_end (s6rc_connection_t *) ; extern int s6rc_connection_start (s6rc_connection_t *, char const *, tain_t const *, tain_t *) ; #define s6rc_connection_start_g(path, deadline) s6rc_connection_start(path, (deadline), &STAMP) -extern void s6rc_connection_end (s6rc_connection_t *) ; +extern int s6rc_monitor (s6rc_connection_t *, s6rc_monitor_t *, tain_t const *, tain_t *) ; #endif diff --git a/src/include/s6-rc/event.h b/src/include/s6-rc/event.h new file mode 100644 index 0000000..fe944e7 --- /dev/null +++ b/src/include/s6-rc/event.h @@ -0,0 +1,31 @@ +/* ISC license. */ + +#ifndef S6RC_EVENT_H +#define S6RC_EVENT_H + +#include <stdint.h> + +typedef enum s6rc_eventtype_e s6rc_eventtype_t, *s6rc_eventtype_t_ref ; +enum s6rc_eventtype_e +{ + S6RC_EVENTTYPE_WANTED_STATE_DOWN, + S6RC_EVENTTYPE_WANTED_STATE_UP, + S6RC_EVENTTYPE_CURRENT_STATE_DOWN, + S6RC_EVENTTYPE_CURRENT_STATE_UP, + S6RC_EVENTTYPE_TRANSITION_STOP, + S6RC_EVENTTYPE_TRANSITION_START, + S6RC_EVENTTYPE_CUSTOM, /* for misc events from other processes */ + S6RC_EVENTTYPE_PHAIL +} ; + +typedef struct s6rc_event_s s6rc_event_t, *s6rc_event_t_ref ; +struct s6rc_event_s +{ + char const *name ; + uint32_t value ; + uint8_t type : 3 ; + uint8_t extra : 5 ; +} ; +#define S6RC_EVENT_ZERO { .name = 0, .value = 0, .type = S6RC_EVENTTYPE_PHAIL, .extra = 0 } + +#endif diff --git a/src/include/s6-rc/s6rc-constants.h b/src/include/s6-rc/s6rc-constants.h deleted file mode 100644 index 168dac8..0000000 --- a/src/include/s6-rc/s6rc-constants.h +++ /dev/null @@ -1,14 +0,0 @@ -/* ISC license. */ - -#ifndef S6RC_CONSTANTS_H -#define S6RC_CONSTANTS_H - -#define S6RC_COMPILED_BASE "/etc/s6-rc/compiled" - -#define S6RC_ONESHOT_RUNNER "s6rc-oneshot-runner" -#define S6RC_ONESHOT_RUNNER_LEN (sizeof S6RC_ONESHOT_RUNNER - 1) - -#define S6RC_FDHOLDER "s6rc-fdholder" -#define S6RC_FDHOLDER_LEN (sizeof S6RC_FDHOLDER - 1) - -#endif diff --git a/src/include/s6-rc/s6rc-db.h b/src/include/s6-rc/s6rc-db.h deleted file mode 100644 index 0f03967..0000000 --- a/src/include/s6-rc/s6rc-db.h +++ /dev/null @@ -1,76 +0,0 @@ -/* ISC license. */ - -#ifndef S6RC_DB_H -#define S6RC_DB_H - -#include <stdint.h> -#include <skalibs/diuint32.h> -#include <skalibs/buffer.h> - -#define S6RC_DB_BANNER_START "s6rc-db: start\n" -#define S6RC_DB_BANNER_START_LEN (sizeof(S6RC_DB_BANNER_START) - 1) -#define S6RC_DB_BANNER_END "\ns6rc-db: end\n" -#define S6RC_DB_BANNER_END_LEN (sizeof(S6RC_DB_BANNER_END) - 1) - -#define S6RC_DB_FLAG_ESSENTIAL 0x00000001U - - -typedef struct s6rc_oneshot_s s6rc_oneshot_t, *s6rc_oneshot_t_ref ; -struct s6rc_oneshot_s -{ - uint32_t argc[2] ; - uint32_t argv[2] ; -} ; - -typedef struct s6rc_longrun_s s6rc_longrun_t, *s6rc_longrun_t_ref ; -struct s6rc_longrun_s -{ - uint32_t consumer ; - uint32_t nproducers ; - uint32_t producers ; -} ; - -typedef union s6rc_longshot_u s6rc_longshot_t, *s6rc_longshot_t_ref ; -union s6rc_longshot_u -{ - s6rc_oneshot_t oneshot ; - s6rc_longrun_t longrun ; -} ; - -typedef struct s6rc_service_s s6rc_service_t, *s6rc_service_t_ref ; -struct s6rc_service_s -{ - uint32_t name ; - uint32_t flags ; - uint32_t deps[2] ; - uint32_t ndeps[2] ; - uint32_t timeout[2] ; - s6rc_longshot_t x ; -} ; - -typedef struct s6rc_db_s s6rc_db_t, *s6rc_db_t_ref ; -struct s6rc_db_s -{ - s6rc_service_t *services ; - unsigned int nshort ; - unsigned int nlong ; - unsigned int stringlen ; - unsigned int nargvs ; - unsigned int ndeps ; - unsigned int nproducers ; - char *string ; - char const **argvs ; - uint32_t *deps ; - uint32_t *producers ; -} ; - -extern int s6rc_db_read_uint32 (buffer *, uint32_t *) ; - -extern int s6rc_db_read_sizes (int, s6rc_db_t *) ; -extern int s6rc_db_read (int, s6rc_db_t *) ; - -extern int s6rc_db_check_pipelines (s6rc_db_t const *, diuint32 *) ; -extern int s6rc_db_check_depcycles (s6rc_db_t const *, int, diuint32 *) ; -extern int s6rc_db_check_revdeps (s6rc_db_t const *) ; - -#endif diff --git a/src/include/s6-rc/s6rc-servicedir.h b/src/include/s6-rc/s6rc-servicedir.h deleted file mode 100644 index 1648975..0000000 --- a/src/include/s6-rc/s6rc-servicedir.h +++ /dev/null @@ -1,18 +0,0 @@ -/* ISC license. */ - -#ifndef S6RC_SERVICEDIR_H -#define S6RC_SERVICEDIR_H - -#include <skalibs/tai.h> - -extern int s6rc_servicedir_block (char const *) ; -extern int s6rc_servicedir_unblock (char const *, int) ; -extern int s6rc_servicedir_copy_offline (char const *, char const *) ; -extern int s6rc_servicedir_copy_online (char const *, char const *) ; -#define s6rc_servicedir_copy(src, dst, h) ((h) ? s6rc_servicedir_copy_online(src, dst) : s6rc_servicedir_copy_offline(src, dst)) -extern void s6rc_servicedir_unsupervise (char const *, char const *, char const *, int) ; - -extern int s6rc_servicedir_manage (char const *, char const *, tain_t const *, tain_t *) ; -#define s6rc_servicedir_manage_g(live, suffix, deadline) s6rc_servicedir_manage(live, suffix, (deadline), &STAMP) - -#endif diff --git a/src/include/s6-rc/s6rc-utils.h b/src/include/s6-rc/s6rc-utils.h deleted file mode 100644 index 8f67920..0000000 --- a/src/include/s6-rc/s6rc-utils.h +++ /dev/null @@ -1,19 +0,0 @@ -/* ISC license. */ - -#ifndef S6RC_UTILS_H -#define S6RC_UTILS_H - -#include <sys/types.h> -#include <skalibs/stralloc.h> -#include <s6-rc/s6rc-db.h> - -extern void s6rc_graph_closure (s6rc_db_t const *, unsigned char *, unsigned int, int) ; -extern int s6rc_lock (char const *, int, int *, char const *, int, int *, int) ; -extern int s6rc_read_uint (char const *, unsigned int *) ; -extern int s6rc_sanitize_dir (stralloc *, char const *, size_t *) ; - -extern int s6rc_livedir_prefixsize (char const *, size_t *) ; -extern ssize_t s6rc_livedir_prefix (char const *, char *, size_t) ; -extern int s6rc_livedir_create (stralloc *, char const *, char const *, char const *, char const *, char const *, unsigned char const *, unsigned int, size_t *) ; - -#endif diff --git a/src/include/s6-rc/s6rc.h b/src/include/s6-rc/s6rc.h deleted file mode 100644 index 87a87ef..0000000 --- a/src/include/s6-rc/s6rc.h +++ /dev/null @@ -1,11 +0,0 @@ -/* ISC license. */ - -#ifndef S6RC_H -#define S6RC_H - -#include <s6-rc/s6rc-constants.h> -#include <s6-rc/s6rc-db.h> -#include <s6-rc/s6rc-utils.h> -#include <s6-rc/s6rc-servicedir.h> - -#endif diff --git a/src/libs6rc/s6rc_monitor.c b/src/libs6rc/s6rc_monitor.c index bd27e8b..4a84cac 100644 --- a/src/libs6rc/s6rc_monitor.c +++ b/src/libs6rc/s6rc_monitor.c @@ -7,9 +7,9 @@ #include <s6-rc/connection-common.h> #include <s6-rc/connection.h> -int s6rc_monitor (s6rc_connection_t *a, textmessage_receiver_t *asyncin, char *asyncbuf, size_t asyncbufsize, tain_t const *deadline, tain_t *stamp) +int s6rc_monitor (s6rc_connection_t *a, s6rc_monitor_t *mon, tain_t const *deadline, tain_t *stamp) { if (!textmessage_timed_send(&a->out, "M", 1, deadline, stamp)) return 0 ; - if (!textmessage_recv_channel(textmessage_receiver_fd(&a->in), asyncin, asyncbuf, asyncbufsize, S6RC_MONITOR_BANNER, S6RC_MONITOR_BANNERLEN, deadline, stamp)) return 0 ; + if (!textmessage_recv_channel(textmessage_receiver_fd(&a->in), &mon->in, mon->inbuf, S6RC_MONITOR_BUFSIZE, S6RC_MONITOR_BANNER, S6RC_MONITOR_BANNERLEN, deadline, stamp)) return 0 ; return 1 ; } diff --git a/src/libs6rcd/s6rcd_db_load.c b/src/libs6rcd/s6rcd_db_load.c deleted file mode 100644 index 68aa11e..0000000 --- a/src/libs6rcd/s6rcd_db_load.c +++ /dev/null @@ -1,17 +0,0 @@ -/* ISC license. */ - -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <limits.h> -#include <stdlib.h> -#include <sys/stat.h> - -#include <s6-rc/db.h> -#include "s6rcd.h" - -int s6rcd_db_load (s6rc_db_t *db, char const *compiled) -{ - if (!s6rcd_db_read_sizes(db, compiled)) return 0 ; - -} 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/libs6rcd/s6rcd.h b/src/server/livedir.h index a9326b7..1d6d106 100644 --- a/src/libs6rcd/s6rcd.h +++ b/src/server/livedir.h @@ -1,9 +1,11 @@ /* ISC license. */ -#include <s6-rc/db.h> +#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 *) ; -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/libs6rcd/s6rcd_livedir_init.c b/src/server/livedir_init.c index f252dba..f252dba 100644 --- a/src/libs6rcd/s6rcd_livedir_init.c +++ b/src/server/livedir_init.c diff --git a/src/libs6rcd/s6rcd_livesubdir_create.c b/src/server/livesubdir_create.c index ff989aa..ff989aa 100644 --- a/src/libs6rcd/s6rcd_livesubdir_create.c +++ b/src/server/livesubdir_create.c 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 |