summaryrefslogtreecommitdiff
path: root/src/serverlib
diff options
context:
space:
mode:
Diffstat (limited to 'src/serverlib')
-rw-r--r--src/serverlib/deps-lib/s6rc22
-rw-r--r--src/serverlib/s6rc-servicedir-internal.h34
-rw-r--r--src/serverlib/s6rc_db_check_depcycles.c55
-rw-r--r--src/serverlib/s6rc_db_check_pipelines.c61
-rw-r--r--src/serverlib/s6rc_db_check_revdeps.c29
-rw-r--r--src/serverlib/s6rc_db_read.c173
-rw-r--r--src/serverlib/s6rc_db_read_sizes.c46
-rw-r--r--src/serverlib/s6rc_db_read_uint32.c14
-rw-r--r--src/serverlib/s6rc_graph_closure.c40
-rw-r--r--src/serverlib/s6rc_livedir_create.c52
-rw-r--r--src/serverlib/s6rc_livedir_prefix.c19
-rw-r--r--src/serverlib/s6rc_livedir_prefixsize.c26
-rw-r--r--src/serverlib/s6rc_lock.c60
-rw-r--r--src/serverlib/s6rc_read_uint.c18
-rw-r--r--src/serverlib/s6rc_sanitize_dir.c26
-rw-r--r--src/serverlib/s6rc_servicedir_block.c25
-rw-r--r--src/serverlib/s6rc_servicedir_copy_offline.c18
-rw-r--r--src/serverlib/s6rc_servicedir_copy_online.c80
-rw-r--r--src/serverlib/s6rc_servicedir_internal.c86
-rw-r--r--src/serverlib/s6rc_servicedir_manage.c123
-rw-r--r--src/serverlib/s6rc_servicedir_unblock.c21
-rw-r--r--src/serverlib/s6rc_servicedir_unsupervise.c29
22 files changed, 1057 insertions, 0 deletions
diff --git a/src/serverlib/deps-lib/s6rc b/src/serverlib/deps-lib/s6rc
new file mode 100644
index 0000000..52c220f
--- /dev/null
+++ b/src/serverlib/deps-lib/s6rc
@@ -0,0 +1,22 @@
+s6rc_db_check_depcycles.o
+s6rc_db_check_pipelines.o
+s6rc_db_check_revdeps.o
+s6rc_db_read.o
+s6rc_db_read_sizes.o
+s6rc_db_read_uint32.o
+s6rc_graph_closure.o
+s6rc_livedir_create.o
+s6rc_livedir_prefix.o
+s6rc_livedir_prefixsize.o
+s6rc_lock.o
+s6rc_read_uint.o
+s6rc_sanitize_dir.o
+s6rc_servicedir_internal.o
+s6rc_servicedir_block.o
+s6rc_servicedir_unblock.o
+s6rc_servicedir_copy_offline.o
+s6rc_servicedir_copy_online.o
+s6rc_servicedir_manage.o
+s6rc_servicedir_unsupervise.o
+-ls6
+-lskarnet
diff --git a/src/serverlib/s6rc-servicedir-internal.h b/src/serverlib/s6rc-servicedir-internal.h
new file mode 100644
index 0000000..a4af0b9
--- /dev/null
+++ b/src/serverlib/s6rc-servicedir-internal.h
@@ -0,0 +1,34 @@
+/* ISC license. */
+
+#ifndef S6RC_SERVICEDIR_INTERNAL_H
+#define S6RC_SERVICEDIR_INTERNAL_H
+
+#include <sys/types.h>
+
+typedef enum s6rc_filetype_e s6rc_filetype_t, *s6rc_filetype_t_ref ;
+enum s6rc_filetype_e
+{
+ FILETYPE_NORMAL,
+ FILETYPE_EMPTY,
+ FILETYPE_UINT,
+ FILETYPE_DIR
+} ;
+
+#define SVFILE_EXECUTABLE 0x01
+#define SVFILE_MANDATORY 0x02
+#define SVFILE_ATOMIC 0x04
+
+typedef struct s6rc_servicedir_desc_s s6rc_servicedir_desc_t, *s6rc_servicedir_desc_t_ref ;
+struct s6rc_servicedir_desc_s
+{
+ char const *name ;
+ s6rc_filetype_t type ;
+ unsigned char options ;
+} ;
+
+extern s6rc_servicedir_desc_t const *s6rc_servicedir_file_list ;
+extern size_t const s6rc_servicedir_file_maxlen ;
+
+extern int s6rc_servicedir_copy_one (char const *, char const *, s6rc_servicedir_desc_t const *) ;
+
+#endif
diff --git a/src/serverlib/s6rc_db_check_depcycles.c b/src/serverlib/s6rc_db_check_depcycles.c
new file mode 100644
index 0000000..5f8339e
--- /dev/null
+++ b/src/serverlib/s6rc_db_check_depcycles.c
@@ -0,0 +1,55 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdint.h>
+#include <skalibs/bitarray.h>
+#include <s6-rc/s6rc-db.h>
+
+typedef struct recinfo_s recinfo_t, *recinfo_t_ref ;
+struct recinfo_s
+{
+ s6rc_db_t const *db ;
+ uint32_t n ;
+ unsigned char *gray ;
+ unsigned char *black ;
+ unsigned char h : 1 ;
+} ;
+
+static uint32_t s6rc_db_checknocycle_rec (recinfo_t *recinfo, uint32_t i)
+{
+ if (!bitarray_peek(recinfo->black, i))
+ {
+ uint32_t j = recinfo->db->services[i].ndeps[recinfo->h] ;
+ if (bitarray_peek(recinfo->gray, i)) return i ;
+ bitarray_set(recinfo->gray, i) ;
+ while (j--)
+ {
+ uint32_t r = s6rc_db_checknocycle_rec(recinfo, recinfo->db->deps[recinfo->h * recinfo->db->ndeps + recinfo->db->services[i].deps[recinfo->h] + j]) ;
+ if (r < recinfo->n) return r ;
+ }
+ bitarray_set(recinfo->black, i) ;
+ }
+ return recinfo->n ;
+}
+
+int s6rc_db_check_depcycles (s6rc_db_t const *db, int h, diuint32 *problem)
+{
+ uint32_t n = db->nshort + db->nlong ;
+ uint32_t i = n ;
+ unsigned char gray[bitarray_div8(n)] ;
+ unsigned char black[bitarray_div8(n)] ;
+ recinfo_t info = { .db = db, .n = n, .gray = gray, .black = black, .h = !!h } ;
+ memset(gray, 0, bitarray_div8(n)) ;
+ memset(black, 0, bitarray_div8(n)) ;
+ while (i--)
+ {
+ uint32_t r = s6rc_db_checknocycle_rec(&info, i) ;
+ if (r < n)
+ {
+ problem->left = i ;
+ problem->right = r ;
+ return 1 ;
+ }
+ }
+ return 0 ;
+}
diff --git a/src/serverlib/s6rc_db_check_pipelines.c b/src/serverlib/s6rc_db_check_pipelines.c
new file mode 100644
index 0000000..68121e2
--- /dev/null
+++ b/src/serverlib/s6rc_db_check_pipelines.c
@@ -0,0 +1,61 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdint.h>
+#include <skalibs/diuint32.h>
+#include <s6-rc/s6rc-db.h>
+
+struct recinfo_s
+{
+ s6rc_db_t const *db ;
+ unsigned char *mark ;
+} ;
+
+static uint32_t check_prod_rec (struct recinfo_s *recinfo, uint32_t n)
+{
+ uint32_t i = 0 ;
+ if (recinfo->mark[n] & 3) return n ;
+ recinfo->mark[n] |= 1 ;
+ for (; i < recinfo->db->services[n].x.longrun.nproducers ; i++)
+ {
+ uint32_t j = recinfo->db->producers[recinfo->db->services[n].x.longrun.producers + i] ;
+ if (j >= recinfo->db->nlong) return (recinfo->mark[n] |= 4, n) ;
+ if (recinfo->db->services[j].x.longrun.consumer != n) return (recinfo->mark[j] |= 4, j) ;
+ j = check_prod_rec(recinfo, j) ;
+ if (j < recinfo->db->nlong) return j ;
+ }
+ recinfo->mark[n] |= 2 ;
+ return recinfo->db->nlong ;
+}
+
+int s6rc_db_check_pipelines (s6rc_db_t const *db, diuint32 *problem)
+{
+ uint32_t i = db->nlong ;
+ unsigned char mark[db->nlong] ;
+ struct recinfo_s recinfo = { .db = db, .mark = mark } ;
+ memset(mark, 0, db->nlong) ;
+ while (i--)
+ {
+ if (db->services[i].x.longrun.consumer >= db->nlong && db->services[i].x.longrun.nproducers)
+ {
+ uint32_t j = check_prod_rec(&recinfo, i) ;
+ if (j < db->nlong)
+ {
+ problem->left = i ;
+ problem->right = j ;
+ return mark[j] & 4 ? 3 : mark[j] & 2 ? 2 : 1 ;
+ }
+ }
+ }
+ i = db->nlong ;
+ while (i--)
+ {
+ if (!mark[i] && db->services[i].x.longrun.nproducers)
+ {
+ problem->left = db->services[i].x.longrun.consumer ;
+ problem->right = i ;
+ return 1 ;
+ }
+ }
+ return 0 ;
+}
diff --git a/src/serverlib/s6rc_db_check_revdeps.c b/src/serverlib/s6rc_db_check_revdeps.c
new file mode 100644
index 0000000..0097d06
--- /dev/null
+++ b/src/serverlib/s6rc_db_check_revdeps.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <string.h>
+#include <skalibs/bitarray.h>
+#include <s6-rc/s6rc-db.h>
+
+int s6rc_db_check_revdeps (s6rc_db_t const *db)
+{
+ size_t n = db->nshort + db->nlong ;
+ size_t m = bitarray_div8(n) ;
+ unsigned char matrix[n * m] ;
+ unsigned int i = n ;
+ unsigned char const *p = matrix ;
+ memset(matrix, 0, n * m) ;
+ while (i--)
+ {
+ unsigned int j = db->services[i].ndeps[1] ;
+ while (j--) bitarray_not(matrix + m * i, db->deps[db->ndeps + db->services[i].deps[1] + j], 1) ;
+ }
+ i = n ;
+ while (i--)
+ {
+ unsigned int j = db->services[i].ndeps[0] ;
+ while (j--) bitarray_not(matrix + m * db->deps[db->services[i].deps[0] + j], i, 1) ;
+ }
+ n *= m ;
+ while (n--) if (*p++) return 1 ;
+ return 0 ;
+}
diff --git a/src/serverlib/s6rc_db_read.c b/src/serverlib/s6rc_db_read.c
new file mode 100644
index 0000000..3991b4f
--- /dev/null
+++ b/src/serverlib/s6rc_db_read.c
@@ -0,0 +1,173 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdint.h>
+
+#include <skalibs/buffer.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+#include <skalibs/posixishard.h>
+#include <s6-rc/s6rc-db.h>
+
+#ifdef DEBUG
+#include <skalibs/lolstdio.h>
+#define DBG(...) LOLDEBUG(__VA_ARGS__)
+#else
+#define DBG(...)
+#endif
+
+static int s6rc_db_check_valid_string (char const *string, size_t stringlen, size_t pos)
+{
+ if (pos >= stringlen) return 0 ;
+ if (strnlen(string + pos, stringlen - pos) == stringlen - pos) return 0 ;
+ return 1 ;
+}
+
+static inline int s6rc_db_check_valid_strings (char const *string, size_t stringlen, size_t pos, unsigned int n)
+{
+ while (n--)
+ {
+ if (!s6rc_db_check_valid_string(string, stringlen, pos)) return 0 ;
+ pos += strlen(string + pos) + 1 ;
+ }
+ return 1 ;
+}
+
+static inline int s6rc_db_read_uints (buffer *b, unsigned int max, uint32_t *p, unsigned int n)
+{
+ uint32_t x ;
+ while (n--)
+ {
+ if (!s6rc_db_read_uint32(b, &x)) return -1 ;
+ if (x >= max) return 0 ;
+ *p++ = x ;
+ }
+ return 1 ;
+}
+
+
+static inline int s6rc_db_read_services (buffer *b, s6rc_db_t *db)
+{
+ unsigned int n = db->nshort + db->nlong ;
+ unsigned int nargvs = db->nargvs ;
+ unsigned int argvpos = 0 ;
+ unsigned int i = 0 ;
+ for (; i < n ; i++)
+ {
+ s6rc_service_t *sv = db->services + i ;
+ DBG("service %u/%u", i, n) ;
+ if (!s6rc_db_read_uint32(b, &sv->name)) return -1 ;
+ DBG(" name is %u: %s", sv->name, db->string + sv->name) ;
+ if (sv->name >= db->stringlen) return 0 ;
+ if (!s6rc_db_check_valid_string(db->string, db->stringlen, sv->name)) return 0 ;
+ if (!s6rc_db_read_uint32(b, &sv->flags)) return -1 ;
+ DBG(" flags is %x", sv->flags) ;
+ if (!s6rc_db_read_uint32(b, &sv->timeout[0])) return -1 ;
+ DBG(" timeout0 is %u", sv->timeout[0]) ;
+ if (!s6rc_db_read_uint32(b, &sv->timeout[1])) return -1 ;
+ DBG(" timeout1 is %u", sv->timeout[1]) ;
+ if (!s6rc_db_read_uint32(b, &sv->ndeps[0])) return -1 ;
+ DBG(" ndeps0 is %u", sv->ndeps[0]) ;
+ if (!s6rc_db_read_uint32(b, &sv->ndeps[1])) return -1 ;
+ DBG(" ndeps1 is %u", sv->ndeps[1]) ;
+ if (!s6rc_db_read_uint32(b, &sv->deps[0])) return -1 ;
+ DBG(" deps0 is %u", sv->deps[0]) ;
+ if (sv->deps[0] > db->ndeps || sv->deps[0] + sv->ndeps[0] > db->ndeps)
+ return 0 ;
+ if (!s6rc_db_read_uint32(b, &sv->deps[1])) return -1 ;
+ DBG(" deps1 is %u", sv->deps[1]) ;
+ if (sv->deps[1] > db->ndeps || sv->deps[1] + sv->ndeps[1] > db->ndeps)
+ return 0 ;
+#ifdef DEBUG
+ {
+ unsigned int k = 0 ;
+ for (; k < sv->ndeps[0] ; k++)
+ DBG(" rev dep on %u", db->deps[sv->deps[0] + k]) ;
+ for (k = 0 ; k < sv->ndeps[1] ; k++)
+ DBG(" dep on %u", db->deps[db->ndeps + sv->deps[1] + k]) ;
+ }
+#endif
+ if (i < db->nlong)
+ {
+ if (!s6rc_db_read_uint32(b, &sv->x.longrun.consumer)) return -1 ;
+ if (!s6rc_db_read_uint32(b, &sv->x.longrun.nproducers)) return -1 ;
+ if (!s6rc_db_read_uint32(b, &sv->x.longrun.producers)) return -1 ;
+ }
+ else
+ {
+ unsigned int j = 0 ;
+ DBG(" oneshot") ;
+ for (; j < 2 ; j++)
+ {
+ uint32_t pos, argc ;
+ if (!s6rc_db_read_uint32(b, &argc)) return -1 ;
+ DBG(" argc[%u] is %u, nargvs is %u", j, argc, nargvs) ;
+ if (argc > nargvs) return 0 ;
+ if (!s6rc_db_read_uint32(b, &pos)) return -1 ;
+ DBG(" pos[%u] is %u", j, pos) ;
+ if (!s6rc_db_check_valid_strings(db->string, db->stringlen, pos, argc)) return 0 ;
+ if (!env_make((char const **)db->argvs + argvpos, argc, db->string + pos, db->stringlen - pos)) return -1 ;
+ DBG(" first arg is %s", db->argvs[argvpos]) ;
+ sv->x.oneshot.argv[j] = argvpos ;
+ sv->x.oneshot.argc[j] = argc ;
+ argvpos += argc ; nargvs -= argc ;
+ if (!nargvs--) return 0 ;
+ db->argvs[argvpos++] = 0 ;
+ }
+ }
+ {
+ char c ;
+ if (buffer_get(b, &c, 1) < 1) return -1 ;
+ if (c != '\376') return 0 ;
+ }
+ }
+ if (nargvs) return 0 ;
+ return 1 ;
+}
+
+static inline int s6rc_db_read_string (buffer *b, char *string, size_t len)
+{
+ if (buffer_get(b, string, len) < (ssize_t)len) return -1 ;
+ return 1 ;
+}
+
+static inline int s6rc_db_read_buffer (buffer *b, s6rc_db_t *db)
+{
+ {
+ char banner[S6RC_DB_BANNER_START_LEN] ;
+ if (buffer_get(b, banner, S6RC_DB_BANNER_START_LEN) < S6RC_DB_BANNER_START_LEN) return -1 ;
+ if (memcmp(banner, S6RC_DB_BANNER_START, S6RC_DB_BANNER_START_LEN)) return 0 ;
+ }
+
+ {
+ int r = s6rc_db_read_string(b, db->string, db->stringlen) ;
+ if (r < 1) return r ;
+ r = s6rc_db_read_uints(b, db->nshort + db->nlong, db->deps, db->ndeps << 1) ;
+ if (r < 1) return r ;
+ r = s6rc_db_read_uints(b, db->nlong, db->producers, db->nproducers) ;
+ if (r < 1) return r ;
+ r = s6rc_db_read_services(b, db) ;
+ if (r < 1) return r ;
+ }
+
+ {
+ char banner[S6RC_DB_BANNER_END_LEN] ;
+ if (buffer_get(b, banner, S6RC_DB_BANNER_END_LEN) < S6RC_DB_BANNER_END_LEN) return -1 ;
+ if (memcmp(banner, S6RC_DB_BANNER_END, S6RC_DB_BANNER_END_LEN)) return 0 ;
+ }
+ return 1 ;
+}
+
+int s6rc_db_read (int fdcompiled, s6rc_db_t *db)
+{
+ int r ;
+ buffer b ;
+ char buf[BUFFER_INSIZE] ;
+ int fd = open_readatb(fdcompiled, "db") ;
+ if (fd < 0) return -1 ;
+ buffer_init(&b, &buffer_read, fd, buf, BUFFER_INSIZE) ;
+ r = s6rc_db_read_buffer(&b, db) ;
+ fd_close(fd) ;
+ return r ;
+}
diff --git a/src/serverlib/s6rc_db_read_sizes.c b/src/serverlib/s6rc_db_read_sizes.c
new file mode 100644
index 0000000..433af2e
--- /dev/null
+++ b/src/serverlib/s6rc_db_read_sizes.c
@@ -0,0 +1,46 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <skalibs/buffer.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+#include <s6-rc/s6rc-db.h>
+
+static inline int s6rc_db_read_sizes_buffer (buffer *b, s6rc_db_t *db)
+{
+ uint32_t x ;
+ if (!s6rc_db_read_uint32(b, &x)) return 0 ;
+ db->nshort = x ;
+ if (!s6rc_db_read_uint32(b, &x)) return 0 ;
+ db->nlong = x ;
+ if (!s6rc_db_read_uint32(b, &x)) return 0 ;
+ db->stringlen = x ;
+ if (!s6rc_db_read_uint32(b, &x)) return 0 ;
+ db->nargvs = x ;
+ if (!s6rc_db_read_uint32(b, &x)) return 0 ;
+ db->ndeps = x ;
+ if (!s6rc_db_read_uint32(b, &x)) return 0 ;
+ db->nproducers = x ;
+ return 1 ;
+}
+
+int s6rc_db_read_sizes (int fdcompiled, s6rc_db_t *db)
+{
+ char buf[24] ;
+ buffer b ;
+ int fd = open_readatb(fdcompiled, "n") ;
+ if (fd < 0) return 0 ;
+ buffer_init(&b, &buffer_read, fd, buf, 24) ;
+ if (!s6rc_db_read_sizes_buffer(&b, db))
+ {
+ fd_close(fd) ;
+ return 0 ;
+ }
+ {
+ char c ;
+ ssize_t r = buffer_get(&b, &c, 1) ;
+ fd_close(fd) ;
+ return !r ;
+ }
+}
diff --git a/src/serverlib/s6rc_db_read_uint32.c b/src/serverlib/s6rc_db_read_uint32.c
new file mode 100644
index 0000000..b26bf46
--- /dev/null
+++ b/src/serverlib/s6rc_db_read_uint32.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <skalibs/uint32.h>
+#include <skalibs/buffer.h>
+#include <s6-rc/s6rc-db.h>
+
+int s6rc_db_read_uint32 (buffer *b, uint32_t *x)
+{
+ size_t w = 0 ;
+ char pack[4] ;
+ if (buffer_getall(b, pack, 4, &w) <= 0) return 0 ;
+ uint32_unpack_big(pack, x) ;
+ return 1 ;
+}
diff --git a/src/serverlib/s6rc_graph_closure.c b/src/serverlib/s6rc_graph_closure.c
new file mode 100644
index 0000000..7c3a8f9
--- /dev/null
+++ b/src/serverlib/s6rc_graph_closure.c
@@ -0,0 +1,40 @@
+/* ISC license. */
+
+#include <string.h>
+#include <skalibs/bitarray.h>
+#include <s6-rc/s6rc-db.h>
+#include <s6-rc/s6rc-utils.h>
+
+typedef struct recinfo_s recinfo_t, *recinfo_t_ref ;
+struct recinfo_s
+{
+ s6rc_db_t const *db ;
+ unsigned int n ;
+ unsigned char *bits ;
+ unsigned char *mark ;
+ unsigned char mask ;
+ unsigned char h : 1 ;
+} ;
+
+static void s6rc_graph_closure_rec (recinfo_t *recinfo, unsigned int i)
+{
+ if (!bitarray_peek(recinfo->mark, i))
+ {
+ unsigned int j = recinfo->db->services[i].ndeps[recinfo->h] ;
+ bitarray_set(recinfo->mark, i) ;
+ while (j--) s6rc_graph_closure_rec(recinfo, recinfo->db->deps[recinfo->h * recinfo->db->ndeps + recinfo->db->services[i].deps[recinfo->h] + j]) ;
+ recinfo->bits[i] |= recinfo->mask ;
+ }
+}
+
+void s6rc_graph_closure (s6rc_db_t const *db, unsigned char *bits, unsigned int bitno, int h)
+{
+ unsigned int n = db->nshort + db->nlong ;
+ unsigned int m = bitarray_div8(n) ;
+ unsigned char mark[m] ;
+ recinfo_t info = { .db = db, .n = n, .bits = bits, .mark = mark, .mask = 1 << (bitno & 7), .h = !!h } ;
+ unsigned int i = n ;
+ memset(mark, 0, m) ;
+ while (i--)
+ if (bits[i] & info.mask) s6rc_graph_closure_rec(&info, i) ;
+}
diff --git a/src/serverlib/s6rc_livedir_create.c b/src/serverlib/s6rc_livedir_create.c
new file mode 100644
index 0000000..fd23a07
--- /dev/null
+++ b/src/serverlib/s6rc_livedir_create.c
@@ -0,0 +1,52 @@
+/* ISC license. */
+
+#include <skalibs/nonposix.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <s6-rc/s6rc-utils.h>
+
+int s6rc_livedir_create (stralloc *sa, char const *live, char const *suffix, char const *scandir, char const *prefix, char const *compiled, unsigned char const *state, unsigned int statelen, size_t *dirlen)
+{
+ size_t newlen, ddirlen ;
+ size_t sabase = sa->len ;
+ int wasnull = !sa->s ;
+ if (!s6rc_sanitize_dir(sa, live, &ddirlen)) return 0 ;
+ if (!stralloc_cats(sa, ":")) goto err ;
+ if (!stralloc_cats(sa, suffix)) goto err ;
+ if (!stralloc_cats(sa, ":XXXXXX")) goto err ;
+ if (!stralloc_0(sa)) goto err ;
+ if (!mkdtemp(sa->s + sabase)) goto err ;
+ newlen = sa->len-- ;
+ if (chmod(sa->s + sabase, 0755) < 0) goto delerr ;
+ if (!stralloc_catb(sa, "/servicedirs", 13)) goto delerr ; /* allocates enough for the next strcpys */
+ if (mkdir(sa->s + sabase, 0755) < 0) goto delerr ;
+ strcpy(sa->s + newlen, "compiled") ;
+ if (symlink(compiled, sa->s + sabase) < 0) goto delerr ;
+ strcpy(sa->s + newlen, "scandir") ;
+ if (symlink(scandir, sa->s + sabase) < 0) goto delerr ;
+ strcpy(sa->s + newlen, "prefix") ;
+ if (!openwritenclose_unsafe(sa->s + sabase, prefix, strlen(prefix))) goto delerr ;
+ strcpy(sa->s + newlen, "state") ;
+ if (!openwritenclose_unsafe(sa->s + sabase, (char const *)state, statelen)) goto delerr ;
+ sa->len = newlen-1 ;
+ sa->s[newlen-1] = 0 ;
+ *dirlen = ddirlen ;
+ return 1 ;
+
+ delerr:
+ {
+ int e = errno ;
+ sa->s[newlen] = 0 ;
+ rm_rf_in_tmp(sa, sabase) ;
+ errno = e ;
+ }
+ err:
+ if (wasnull) stralloc_free(sa) ;
+ else sa->len = sabase ;
+ return 0 ;
+}
diff --git a/src/serverlib/s6rc_livedir_prefix.c b/src/serverlib/s6rc_livedir_prefix.c
new file mode 100644
index 0000000..09fe263
--- /dev/null
+++ b/src/serverlib/s6rc_livedir_prefix.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+#include <skalibs/djbunix.h>
+#include <s6-rc/s6rc-utils.h>
+
+ssize_t s6rc_livedir_prefix (char const *live, char *s, size_t n)
+{
+ size_t llen = strlen(live) ;
+ ssize_t r ;
+ char sfn[llen + 8] ;
+ memcpy(sfn, live, llen) ;
+ memcpy(sfn + llen, "/prefix", 8) ;
+ r = openreadnclose(sfn, s, n) ;
+ if (r < 0) return errno == ENOENT ? 0 : r ;
+ if (memchr(s, '/', r) || memchr(s, '\n', r)) return (errno = EINVAL, -1) ;
+ return r ;
+}
diff --git a/src/serverlib/s6rc_livedir_prefixsize.c b/src/serverlib/s6rc_livedir_prefixsize.c
new file mode 100644
index 0000000..d897fe6
--- /dev/null
+++ b/src/serverlib/s6rc_livedir_prefixsize.c
@@ -0,0 +1,26 @@
+/* ISC license. */
+
+#include <limits.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <s6-rc/s6rc-utils.h>
+
+int s6rc_livedir_prefixsize (char const *live, size_t *n)
+{
+ struct stat st ;
+ size_t llen = strlen(live) ;
+ char sfn[llen + 8] ;
+ memcpy(sfn, live, llen) ;
+ memcpy(sfn + llen, "/prefix", 8) ;
+ if (stat(sfn, &st) < 0)
+ {
+ if (errno != ENOENT) return 0 ;
+ *n = 0 ;
+ return 1 ;
+ }
+ if (!S_ISREG(st.st_mode)) return (errno = EINVAL, 0) ;
+ if (st.st_size > PATH_MAX) return (errno = ENAMETOOLONG, 0) ;
+ *n = st.st_size ;
+ return 1 ;
+}
diff --git a/src/serverlib/s6rc_lock.c b/src/serverlib/s6rc_lock.c
new file mode 100644
index 0000000..d3ae2b5
--- /dev/null
+++ b/src/serverlib/s6rc_lock.c
@@ -0,0 +1,60 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+#include <skalibs/djbunix.h>
+#include <s6-rc/s6rc-utils.h>
+
+static inline int lockex (int fd, int blocking)
+{
+ return blocking ? lock_ex(fd) : lock_exnb(fd) ;
+}
+
+static inline int locksh (int fd, int blocking)
+{
+ return blocking ? lock_sh(fd) : lock_shnb(fd) ;
+}
+
+int s6rc_lock (char const *live, int lwhat, int *llfd, char const *compiled, int cwhat, int *ccfd, int blocking)
+{
+ int lfd = -1, cfd = -1 ;
+
+ if (lwhat)
+ {
+ size_t llen = strlen(live) ;
+ char lfn[llen + 6] ;
+ memcpy(lfn, live, llen) ;
+ memcpy(lfn + llen, "/lock", 6) ;
+ lfd = open_create(lfn) ;
+ if (lfd < 0) return 0 ;
+ if (coe(lfd) < 0) goto lerr ;
+ if ((lwhat > 1 ? lockex(lfd, blocking) : locksh(lfd, blocking)) < 0) goto lerr ;
+ }
+
+ if (cwhat)
+ {
+ size_t clen = strlen(compiled) ;
+ char cfn[clen + 6] ;
+ memcpy(cfn, compiled, clen) ;
+ memcpy(cfn + clen, "/lock", 6) ;
+ cfd = open_create(cfn) ;
+ if (cfd < 0)
+ if (cwhat > 1 || errno != EROFS) goto lerr ;
+ else cfd = -errno ;
+ else
+ {
+ if (coe(cfd) < 0) goto cerr ;
+ if ((cwhat > 1 ? lockex(cfd, blocking) : locksh(cfd, blocking)) < 0) goto cerr ;
+ }
+ }
+
+ if (lwhat) *llfd = lfd ;
+ if (cwhat) *ccfd = cfd ;
+ return 1 ;
+
+ cerr:
+ fd_close(cfd) ;
+ lerr:
+ if (lwhat) fd_close(lfd) ;
+ return 0 ;
+}
diff --git a/src/serverlib/s6rc_read_uint.c b/src/serverlib/s6rc_read_uint.c
new file mode 100644
index 0000000..1f148ac
--- /dev/null
+++ b/src/serverlib/s6rc_read_uint.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <skalibs/types.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/djbunix.h>
+#include <s6-rc/s6rc-utils.h>
+
+int s6rc_read_uint (char const *file, unsigned int *u)
+{
+ char buf[UINT_FMT + 1] ;
+ ssize_t r = openreadnclose(file, buf, UINT_FMT) ;
+ if (r < 0) return (errno == ENOENT) ? 0 : -1 ;
+ buf[byte_chr(buf, r, '\n')] = 0 ;
+ if (!uint0_scan(buf, u)) return (errno = EINVAL, -1) ;
+ return 1 ;
+}
diff --git a/src/serverlib/s6rc_sanitize_dir.c b/src/serverlib/s6rc_sanitize_dir.c
new file mode 100644
index 0000000..9f647a9
--- /dev/null
+++ b/src/serverlib/s6rc_sanitize_dir.c
@@ -0,0 +1,26 @@
+/* ISC license. */
+
+#include <string.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <s6-rc/s6rc-utils.h>
+
+int s6rc_sanitize_dir (stralloc *sa, char const *fn, size_t *dirlen)
+{
+ size_t base = sa->len ;
+ size_t fnlen = strlen(fn) ;
+ size_t ddirlen ;
+ int wasnull = !sa->s ;
+ if (!sadirname(sa, fn, fnlen)) return 0 ;
+ if (sa->len != base + 1 || sa->s[base] != '/')
+ if (!stralloc_catb(sa, "/", 1)) goto err ;
+ ddirlen = sa->len ;
+ if (!sabasename(sa, fn, fnlen)) goto err ;
+ *dirlen = ddirlen ;
+ return 1 ;
+
+ err:
+ if (wasnull) stralloc_free(sa) ;
+ else sa->len = base ;
+ return 0 ;
+}
diff --git a/src/serverlib/s6rc_servicedir_block.c b/src/serverlib/s6rc_servicedir_block.c
new file mode 100644
index 0000000..bfa6440
--- /dev/null
+++ b/src/serverlib/s6rc_servicedir_block.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <string.h>
+#include <unistd.h>
+#include <skalibs/posixplz.h>
+#include <skalibs/djbunix.h>
+#include <s6/s6-supervise.h>
+#include <s6-rc/s6rc-servicedir.h>
+
+int s6rc_servicedir_block (char const *dir)
+{
+ size_t dirlen = strlen(dir) ;
+ s6_svstatus_t status ;
+ char fn[dirlen + 6] ;
+ if (!s6_svstatus_read(dir, &status)) return -1 ;
+ memcpy(fn, dir, dirlen) ;
+ memcpy(fn + dirlen, "/down", 6) ;
+ if (!touch(fn)) return -1 ;
+ if (s6_svc_writectl(dir, S6_SUPERVISE_CTLDIR, "O", 1) < 0)
+ {
+ unlink_void(fn) ;
+ return -1 ;
+ }
+ return status.flagwantup ;
+}
diff --git a/src/serverlib/s6rc_servicedir_copy_offline.c b/src/serverlib/s6rc_servicedir_copy_offline.c
new file mode 100644
index 0000000..6581a4f
--- /dev/null
+++ b/src/serverlib/s6rc_servicedir_copy_offline.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <sys/stat.h>
+#include <errno.h>
+#include "s6rc-servicedir-internal.h"
+#include <s6-rc/s6rc-servicedir.h>
+
+int s6rc_servicedir_copy_offline (char const *src, char const *dst)
+{
+ s6rc_servicedir_desc_t const *p = s6rc_servicedir_file_list ;
+ if (mkdir(dst, 0755) < 0)
+ {
+ if (errno != EEXIST) return 0 ;
+ }
+ for (; p->name ; p++)
+ if (!s6rc_servicedir_copy_one(src, dst, p)) return 0 ;
+ return 1 ;
+}
diff --git a/src/serverlib/s6rc_servicedir_copy_online.c b/src/serverlib/s6rc_servicedir_copy_online.c
new file mode 100644
index 0000000..e676d5a
--- /dev/null
+++ b/src/serverlib/s6rc_servicedir_copy_online.c
@@ -0,0 +1,80 @@
+/* ISC license. */
+
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <skalibs/djbunix.h>
+#include <s6-rc/s6rc-utils.h>
+#include "s6rc-servicedir-internal.h"
+#include <s6-rc/s6rc-servicedir.h>
+
+int s6rc_servicedir_copy_online (char const *src, char const *dst)
+{
+ size_t srclen = strlen(src) ;
+ size_t dstlen = strlen(dst) ;
+ unsigned int n ;
+ unsigned int i = 0 ;
+ int wantup = 0 ;
+ int e ;
+ char srcfn[srclen + s6rc_servicedir_file_maxlen + 6] ;
+ char dstfn[dstlen + s6rc_servicedir_file_maxlen + 6] ;
+ char oldfn[dstlen + s6rc_servicedir_file_maxlen + 6] ;
+ memcpy(srcfn, src, srclen) ;
+ srcfn[srclen] = '/' ;
+ memcpy(dstfn, dst, dstlen + 1) ;
+ memcpy(oldfn, dst, dstlen) ;
+ memcpy(oldfn + dstlen, "/old", 5) ;
+ if (rm_rf(oldfn) < 0 && errno != ENOENT) return 0 ;
+ if (mkdir(oldfn, 0755) < 0) return 0 ;
+ dstfn[dstlen] = '/' ;
+ oldfn[dstlen + 4] = '/' ;
+ wantup = s6rc_servicedir_block(dst) ;
+ if (wantup < 0) { e = errno ; goto errdir ; }
+
+ for (; s6rc_servicedir_file_list[i].name ; i++)
+ {
+ strcpy(dstfn + dstlen + 1, s6rc_servicedir_file_list[i].name) ;
+ strcpy(oldfn + dstlen + 5, s6rc_servicedir_file_list[i].name) ;
+ if (rename(dstfn, oldfn) < 0
+ && (errno != ENOENT || s6rc_servicedir_file_list[i].options & SVFILE_MANDATORY))
+ {
+ e = errno ;
+ goto errrename ;
+ }
+ }
+ n = i ;
+ while (i--)
+ {
+ if (!s6rc_servicedir_copy_one(src, dst, s6rc_servicedir_file_list + i))
+ {
+ e = errno ;
+ goto errremove ;
+ }
+ }
+ oldfn[dstlen + 4] = 0 ;
+ rm_rf(oldfn) ;
+ s6rc_servicedir_unblock(dst, wantup) ;
+ return 1 ;
+
+ errremove:
+ for (; i < n ; i++)
+ {
+ strcpy(dstfn + dstlen + 1, s6rc_servicedir_file_list[i].name) ;
+ rm_rf(dstfn) ;
+ }
+ i = n ;
+ errrename:
+ while (i--)
+ {
+ strcpy(dstfn + dstlen + 1, s6rc_servicedir_file_list[i].name) ;
+ strcpy(oldfn + dstlen + 5, s6rc_servicedir_file_list[i].name) ;
+ rename(oldfn, dstfn) ;
+ }
+ errdir:
+ oldfn[dstlen + 4] = 0 ;
+ rmdir(oldfn) ;
+ errno = e ;
+ return 0 ;
+}
diff --git a/src/serverlib/s6rc_servicedir_internal.c b/src/serverlib/s6rc_servicedir_internal.c
new file mode 100644
index 0000000..b70061f
--- /dev/null
+++ b/src/serverlib/s6rc_servicedir_internal.c
@@ -0,0 +1,86 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <skalibs/types.h>
+#include <skalibs/djbunix.h>
+#include <s6-rc/s6rc-utils.h>
+#include "s6rc-servicedir-internal.h"
+
+static s6rc_servicedir_desc_t const svdir_file_list[] =
+{
+ { .name = "finish", .type = FILETYPE_NORMAL, .options = SVFILE_EXECUTABLE | SVFILE_ATOMIC },
+ { .name = "finish.user", .type = FILETYPE_NORMAL, .options = SVFILE_EXECUTABLE },
+ { .name = "run", .type = FILETYPE_NORMAL, .options = SVFILE_EXECUTABLE | SVFILE_MANDATORY | SVFILE_ATOMIC },
+ { .name = "run.user", .type = FILETYPE_NORMAL, .options = SVFILE_EXECUTABLE },
+ { .name = "nosetsid", .type = FILETYPE_EMPTY, .options = 0 },
+ { .name = "notification-fd", .type = FILETYPE_UINT, .options = 0 },
+ { .name = "timeout-kill", .type = FILETYPE_UINT, .options = 0 },
+ { .name = "timeout-finish", .type = FILETYPE_UINT, .options = 0 },
+ { .name = "max-death-tally", .type = FILETYPE_UINT, .options = 0 },
+ { .name = "down-signal", .type = FILETYPE_NORMAL, .options = 0 },
+ { .name = "data", .type = FILETYPE_DIR, .options = 0 },
+ { .name = "env", .type = FILETYPE_DIR, .options = 0 },
+ { .name = 0, .options = 0 }
+} ;
+
+s6rc_servicedir_desc_t const *s6rc_servicedir_file_list = svdir_file_list ;
+size_t const s6rc_servicedir_file_maxlen = 16 ;
+
+int s6rc_servicedir_copy_one (char const *src, char const *dst, s6rc_servicedir_desc_t const *p)
+{
+ size_t srclen = strlen(src) ;
+ size_t dstlen = strlen(dst) ;
+ size_t plen = strlen(p->name) ;
+ char srcfn[srclen + plen + 2] ;
+ char dstfn[dstlen + plen + 2] ;
+ memcpy(srcfn, src, srclen) ;
+ srcfn[srclen] = '/' ;
+ memcpy(srcfn + srclen + 1, p->name, plen + 1) ;
+ memcpy(dstfn, dst, dstlen) ;
+ dstfn[dstlen] = '/' ;
+ memcpy(dstfn + dstlen + 1, p->name, plen + 1) ;
+
+ switch (p->type)
+ {
+ case FILETYPE_NORMAL :
+ {
+ unsigned int mode = p->options & SVFILE_EXECUTABLE ? 0755 : 0644 ;
+ if (!(p->options & SVFILE_ATOMIC ? filecopy_suffix(srcfn, dstfn, mode, ".new") : filecopy_unsafe(srcfn, dstfn, mode)))
+ {
+ if (errno != ENOENT || p->options & SVFILE_MANDATORY) return 0 ;
+ }
+ break ;
+ }
+ case FILETYPE_EMPTY :
+ if (access(srcfn, F_OK) < 0)
+ {
+ if (errno != ENOENT || p->options & SVFILE_MANDATORY) return 0 ;
+ }
+ else if (!touch(dstfn)) return 0 ;
+ break ;
+ case FILETYPE_UINT :
+ {
+ unsigned int u ;
+ int r = s6rc_read_uint(srcfn, &u) ;
+ if (r < 0 || (!r && p->options & SVFILE_MANDATORY)) return 0 ;
+ if (r)
+ {
+ char fmt[UINT_FMT] ;
+ r = uint_fmt(fmt, u) ;
+ fmt[r++] = '\n' ;
+ if (!openwritenclose_unsafe(dstfn, fmt, r)) return 0 ;
+ }
+ break ;
+ }
+ case FILETYPE_DIR :
+ if (!hiercopy(srcfn, dstfn))
+ {
+ if (errno != ENOENT || p->options & SVFILE_MANDATORY) return 0 ;
+ }
+ break ;
+ default : return (errno = EDOM, 0) ;
+ }
+ return 1 ;
+}
diff --git a/src/serverlib/s6rc_servicedir_manage.c b/src/serverlib/s6rc_servicedir_manage.c
new file mode 100644
index 0000000..5449f35
--- /dev/null
+++ b/src/serverlib/s6rc_servicedir_manage.c
@@ -0,0 +1,123 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <skalibs/direntry.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <s6/s6-supervise.h>
+#include <s6/ftrigr.h>
+#include <s6/ftrigw.h>
+#include <s6-rc/s6rc-servicedir.h>
+
+static inline void rollback (char const *live, char const *prefix, char const *s, size_t len)
+{
+ while (len)
+ {
+ size_t n = strlen(s) + 1 ;
+ s6rc_servicedir_unsupervise(live, prefix, s, 0) ;
+ s += n ; len -= n ;
+ }
+}
+
+int s6rc_servicedir_manage (char const *live, char const *prefix, tain_t const *deadline, tain_t *stamp)
+{
+ ftrigr_t a = FTRIGR_ZERO ;
+ stralloc newnames = STRALLOC_ZERO ;
+ genalloc ids = GENALLOC_ZERO ; /* uint16_t */
+ gid_t gid = getgid() ;
+ size_t livelen = strlen(live) ;
+ size_t prefixlen = strlen(prefix) ;
+ int fdlock ;
+ int ok = 1 ;
+ DIR *dir ;
+ char dirfn[livelen + 13] ;
+ if (!ftrigr_startf(&a, deadline, stamp)) return 0 ;
+ memcpy(dirfn, live, livelen) ;
+ memcpy(dirfn + livelen, "/servicedirs", 13) ;
+ dir = opendir(dirfn) ;
+ if (!dir) goto closederr ;
+ for (;;)
+ {
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ {
+ size_t len = strlen(d->d_name) ;
+ int r ;
+ uint16_t id ;
+ char srcfn[livelen + 20 + len] ;
+ char dstfn[livelen + 10 + prefixlen + len] ;
+ memcpy(srcfn, dirfn, livelen + 12) ;
+ srcfn[livelen + 12] = '/' ;
+ memcpy(srcfn + livelen + 13, d->d_name, len + 1) ;
+ fdlock = s6_svc_lock_take(srcfn) ;
+ if (fdlock < 0) goto err ;
+ r = s6_svc_ok(srcfn) ;
+ if (r < 0) goto erru ;
+ if (!r)
+ {
+ memcpy(srcfn + livelen + 13 + len, "/down", 6) ;
+ if (!touch(srcfn)) goto erru ;
+ memcpy(srcfn + livelen + 14 + len, "event", 6) ;
+ if (!ftrigw_fifodir_make(srcfn, gid, 0)) goto erru ;
+ id = ftrigr_subscribe(&a, srcfn, "s", 0, deadline, stamp) ;
+ if (!id) goto erru ;
+ s6_svc_lock_release(fdlock) ;
+ if (!genalloc_append(uint16_t, &ids, &id)) goto err ;
+ srcfn[livelen + 13 + len] = 0 ;
+ }
+ else s6_svc_lock_release(fdlock) ;
+ memcpy(dstfn, live, livelen) ;
+ memcpy(dstfn + livelen, "/scandir/", 9) ;
+ memcpy(dstfn + livelen + 9, prefix, prefixlen) ;
+ memcpy(dstfn + livelen + 9 + prefixlen, d->d_name, len + 1) ;
+ if (symlink(srcfn, dstfn) < 0)
+ {
+ if (!r || errno != EEXIST) goto err ;
+ }
+ else if (!r)
+ {
+ if (!stralloc_catb(&newnames, d->d_name, len + 1))
+ {
+ s6rc_servicedir_unsupervise(live, prefix, d->d_name, 0) ;
+ goto err ;
+ }
+ }
+ }
+ }
+ if (errno) goto err ;
+ dir_close(dir) ;
+ {
+ char scanfn[livelen + 9] ;
+ int r ;
+ memcpy(scanfn, live, livelen) ;
+ memcpy(scanfn + livelen, "/scandir", 9) ;
+ r = s6_svc_writectl(scanfn, S6_SVSCAN_CTLDIR, "a", 1) ;
+ if (r < 0) goto closederr ;
+ if (!r) ok = 3 ;
+ else if (ftrigr_wait_and(&a, genalloc_s(uint16_t, &ids), genalloc_len(uint16_t, &ids), deadline, stamp) < 0)
+ goto closederr ;
+ }
+
+ ftrigr_end(&a) ;
+ genalloc_free(uint16_t, &ids) ;
+ stralloc_free(&newnames) ;
+ return ok ;
+
+ erru:
+ s6_svc_lock_release(fdlock) ;
+ err:
+ dir_close(dir) ;
+ closederr:
+ ftrigr_end(&a) ;
+ genalloc_free(uint16_t, &ids) ;
+ rollback(live, prefix, newnames.s, newnames.len) ;
+ stralloc_free(&newnames) ;
+ return 0 ;
+}
diff --git a/src/serverlib/s6rc_servicedir_unblock.c b/src/serverlib/s6rc_servicedir_unblock.c
new file mode 100644
index 0000000..2a10bfd
--- /dev/null
+++ b/src/serverlib/s6rc_servicedir_unblock.c
@@ -0,0 +1,21 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <s6/s6-supervise.h>
+#include <s6-rc/s6rc-servicedir.h>
+
+int s6rc_servicedir_unblock (char const *dir, int h)
+{
+ if (h)
+ {
+ size_t dirlen = strlen(dir) ;
+ char fn[dirlen + 6] ;
+ memcpy(fn, dir, dirlen) ;
+ memcpy(fn + dirlen, "/down", 6) ;
+ if (unlink(fn) < 0 && errno != ENOENT) return -1 ;
+ if (s6_svc_writectl(dir, S6_SUPERVISE_CTLDIR, "u", 1) < 0) return -1 ;
+ }
+ return 0 ;
+}
diff --git a/src/serverlib/s6rc_servicedir_unsupervise.c b/src/serverlib/s6rc_servicedir_unsupervise.c
new file mode 100644
index 0000000..2d15a70
--- /dev/null
+++ b/src/serverlib/s6rc_servicedir_unsupervise.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <skalibs/posixplz.h>
+#include <s6/s6-supervise.h>
+#include <s6-rc/s6rc-servicedir.h>
+
+void s6rc_servicedir_unsupervise (char const *live, char const *prefix, char const *name, int keepsupervisor)
+{
+ size_t livelen = strlen(live) ;
+ size_t prefixlen = strlen(prefix) ;
+ size_t namelen = strlen(name) ;
+ char fn[livelen + 14 + prefixlen + namelen] ;
+ memcpy(fn, live, livelen) ;
+ memcpy(fn + livelen, "/scandir/", 9) ;
+ memcpy(fn + livelen + 9, prefix, prefixlen) ;
+ memcpy(fn + livelen + 9 + prefixlen, name, namelen + 1) ;
+ unlink_void(fn) ;
+ if (!keepsupervisor)
+ {
+ int e = errno ;
+ memcpy(fn + livelen + 1, "servicedirs/", 12) ;
+ memcpy(fn + livelen + 13, name, namelen + 1) ;
+ s6_svc_writectl(fn, S6_SUPERVISE_CTLDIR, "x", 1) ;
+ errno = e ;
+ }
+}