diff options
Diffstat (limited to 'src/serverlib')
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 ; + } +} |