summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/include/s6-rc/connection.h13
-rw-r--r--src/include/s6-rc/event.h31
-rw-r--r--src/include/s6-rc/s6rc-constants.h14
-rw-r--r--src/include/s6-rc/s6rc-db.h76
-rw-r--r--src/include/s6-rc/s6rc-servicedir.h18
-rw-r--r--src/include/s6-rc/s6rc-utils.h19
-rw-r--r--src/include/s6-rc/s6rc.h11
-rw-r--r--src/libs6rc/s6rc_monitor.c4
-rw-r--r--src/libs6rcd/s6rcd_db_load.c17
-rw-r--r--src/server/client.c161
-rw-r--r--src/server/client.h50
-rw-r--r--src/server/clientrules.c118
-rw-r--r--src/server/clientrules.h15
-rw-r--r--src/server/command.c43
-rw-r--r--src/server/command.h14
-rw-r--r--src/server/db.h11
-rw-r--r--src/server/ep.c128
-rw-r--r--src/server/ep.h21
-rw-r--r--src/server/ev.c47
-rw-r--r--src/server/ev.h19
-rw-r--r--src/server/livedir.h (renamed from src/libs6rcd/s6rcd.h)8
-rw-r--r--src/server/livedir_init.c (renamed from src/libs6rcd/s6rcd_livedir_init.c)0
-rw-r--r--src/server/livesubdir_create.c (renamed from src/libs6rcd/s6rcd_livesubdir_create.c)0
-rw-r--r--src/server/main.h18
-rw-r--r--src/server/s6-rcd.c139
-rw-r--r--src/server/s6-rcd.h17
-rw-r--r--src/server/signals.c89
-rw-r--r--src/server/signals.h11
-rw-r--r--src/server/transition.h22
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, &params) ; break ;
+ case 2 :
+ result = s6_accessrules_uidgid_cdb(uid, gid, &cdbmap, &params) ; 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(&params) ;
+ return 0 ;
+ }
+ envp[n] = 0 ;
+ *perms = parse_env(envp, perms) ;
+ }
+ s6_accessrules_params_free(&params) ;
+ 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