diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/s6-rc/s6-rc-init.c | 157 |
1 files changed, 125 insertions, 32 deletions
diff --git a/src/s6-rc/s6-rc-init.c b/src/s6-rc/s6-rc-init.c index a83db2a..b1a9624 100644 --- a/src/s6-rc/s6-rc-init.c +++ b/src/s6-rc/s6-rc-init.c @@ -1,5 +1,7 @@ /* ISC license. */ +#include <skalibs/bsdsnowflake.h> + #include <string.h> #include <unistd.h> #include <errno.h> @@ -10,9 +12,11 @@ #include <skalibs/types.h> #include <skalibs/prog.h> #include <skalibs/gol.h> +#include <skalibs/stat.h> #include <skalibs/strerr.h> #include <skalibs/stralloc.h> #include <skalibs/tai.h> +#include <skalibs/direntry.h> #include <skalibs/djbunix.h> #include <skalibs/unix-transactional.h> @@ -22,6 +26,7 @@ #define USAGE "s6-rc-init [ -c bootdb ] [ -l livedir ] [ -p prefix ] [ -t timeout ] [ -d ] [ -b ] scandir" #define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefusys(111, "stralloc_catb") enum golb_e { @@ -45,7 +50,7 @@ static void cleanup (stralloc *sa) errno = e ; } -static void check_scandir (char const *sdir) +static inline void check_scandir (char const *sdir) { int r = s6_svc_writectl(sdir, S6_SVSCAN_CTLDIR, "", 0) ; if (r == -1) strerr_diefusys(111, "check ", sdir) ; @@ -53,28 +58,95 @@ static void check_scandir (char const *sdir) if (!r) strerr_dief(100, "s6-svscan is not running on ", sdir) ; } -static int check_existing_s6rc (char const *sdir, char const *prefix, stralloc *sa) +static int check_livedir (char const *live, char const *sdir, char const *prefix, char const *bootdb, stralloc *sa) { - size_t sdirlen = strlen(sdir) ; + size_t llen = strlen(live) ; size_t plen = strlen(prefix) ; - size_t start = sa->len ; + size_t w ; int r ; - char fn[sdirlen + plen + S6RC_FDHOLDER_LEN + 2] ; - memcpy(fn, sdir, sdirlen) ; - fn[sdirlen] = '/' ; - memcpy(fn + sdirlen + 1, prefix, plen) ; - memcpy(fn + sdirlen + 1 + plen, S6RC_FDHOLDER, S6RC_FDHOLDER_LEN + 1) ; + char fn[llen + 14 + S6RC_FDHOLDER_LEN] ; + char pr[plen + 1] ; + + memcpy(fn, live, llen) ; + memcpy(fn + llen, "/servicedirs/" S6RC_FDHOLDER, 14 + S6RC_FDHOLDER_LEN) ; r = s6_svc_ok(fn) ; - if (r == -1) strerr_diefusys(111, "check the existence of service ", fn) ; - if (!r) return 0 ; - if (sarealpath(sa, fn) == -1) strerr_diefusys(111, "realpath ", fn) ; - if (sa->len - start < 14 + S6RC_FDHOLDER_LEN || memcmp(sa->s + sa->len - S6RC_FDHOLDER_LEN - 14, "/servicedirs/" S6RC_FDHOLDER, 14 + S6RC_FDHOLDER_LEN)) - strerr_dief(111, "service ", fn, " exists but is not part of an s6-rc database") ; - sa->s[sa->len - S6RC_FDHOLDER_LEN - 14] = 0 ; + if (r == -1) + { + if (errno != ENOENT) strerr_diefusys(111, "check ", fn) ; + fn[llen] = 0 ; + strerr_warnw("directory ", fn, " looks like a live directory but has no ", "s6rc-fdholder service") ; + return 0 ; + } + else if (!r) + { + fn[llen] = 0 ; + strerr_warnw("directory ", fn, " looks like a live directory but has no ", "supervisors running") ; + return 0 ; + } + + memcpy(fn + llen, "/scandir", 9) ; + if (sareadlink(sa, fn) == -1 || !stralloc_0(sa)) strerr_diefusys(111, "readlink ", fn) ; + sa->len = 0 ; + if (strcmp(sa->s, sdir)) + { + fn[llen] = 0 ; + strerr_dief(102, "existing livedir at ", fn, " uses scandir at ", sa->s, " rather than ", sdir) ; + } + + memcpy(fn + llen, "/prefix", 8) ; + w = openreadnclose(fn, pr, plen+1) ; + if (w == -1) strerr_diefusys(111, "read ", fn) ; + if (w != plen || strncmp(pr, prefix, plen)) + { + fn[llen] = 0 ; + strerr_dief(102, "prefix mismatch with the existing livedir at ", fn) ; + } + + memcpy(fn + llen, "/compiled", 10) ; + if (sareadlink(sa, fn) == -1 || !stralloc_0(sa)) strerr_diefusys(111, "readlink ", fn) ; + sa->len = 0 ; + if (strcmp(sa->s, bootdb)) + { + fn[llen] = 0 ; + strerr_dief(102, "existing livedir at ", fn, " uses bootdb at ", sa->s, " rather than ", bootdb) ; + } return 1 ; } +static inline size_t find_livedir (char const *live, size_t llen, size_t lpos, char const *sdir, char const *prefix, char const *bootdb, stralloc *sa) +{ + DIR *dir ; + char fn[llen + 7] ; + memcpy(fn, live, llen) ; + memcpy(fn + llen, ":s6-rc-", 7) ; + fn[lpos] = 0 ; + dir = opendir(fn) ; + if (!dir) strerr_diefusys(111, "opendir ", fn) ; + for (;;) + { + direntry *d ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + if (strncmp(d->d_name, fn + lpos + 1, llen + 6 - lpos)) continue ; + size_t sublen = strlen(d->d_name) ; + char subfn[lpos + sublen + 2] ; + memcpy(subfn, live, lpos) ; + subfn[lpos] = '/' ; + memcpy(subfn + lpos + 1, d->d_name, sublen + 1) ; + if (check_livedir(subfn, sdir, prefix, bootdb, sa)) + { + dir_close(dir) ; + if (!stralloc_catb(sa, subfn, lpos + sublen + 2)) dienomem() ; + return 1 ; + } + } + if (errno) strerr_diefusys(111, "readdir ", sa->s) ; + dir_close(dir) ; + return 0 ; +} int main (int argc, char const *const *argv) { @@ -95,6 +167,7 @@ int main (int argc, char const *const *argv) tain deadline = TAIN_INFINITE_RELATIVE ; stralloc sa = STRALLOC_ZERO ; size_t dirlen ; + char *x ; uint64_t wgolb = 0 ; char const *wgola[GOLA_N] = { [GOLA_BOOTDB] = S6RC_BOOTDB, [GOLA_LIVEDIR] = S6RC_LIVEDIR, [GOLA_PREFIX] = "", [GOLA_TIMEOUT] = 0, } ; unsigned int golc ; @@ -104,6 +177,7 @@ int main (int argc, char const *const *argv) argc -= golc ; argv += golc ; if (!argc) dieusage() ; + if (argv[0][0] != '/') strerr_dief(100, "scandir", " must be an absolute path") ; if (wgola[GOLA_TIMEOUT]) { unsigned int t = 0 ; @@ -111,28 +185,47 @@ int main (int argc, char const *const *argv) strerr_dief(100, "timeout", " must be an unsigned integer") ; if (t) tain_from_millisecs(&deadline, t) ; } - - if (!(wgolb & GOLB_DEREF) && wgola[GOLA_BOOTDB][0] != '/') - strerr_dief(100, "bootdb", " must be an absolute path") ; - if (wgola[GOLA_LIVEDIR][0] != '/') - strerr_dief(100, "livedir", " must be an absolute path") ; - if (argv[0][0] != '/') - strerr_dief(100, "scandir", " must be an absolute path") ; + if (wgolb & GOLB_DEREF) + { + x = realpath(wgola[GOLA_BOOTDB], 0) ; + if (!x) strerr_diefusys(111, "realpath ", wgola[GOLA_BOOTDB]) ; + wgola[GOLA_BOOTDB] = x ; + } + else if (wgola[GOLA_BOOTDB][0] != '/') strerr_dief(100, "bootdb", " must be an absolute path") ; if (strchr(wgola[GOLA_PREFIX], '/') || strchr(wgola[GOLA_PREFIX], '\n')) strerr_dief(100, "prefix", " cannot contain a / or a newline") ; - - tain_now_set_stopwatch_g() ; - tain_add_g(&deadline, &deadline) ; - if (!s6rc_livedir_canon(&wgola[GOLA_LIVEDIR])) - strerr_diefu1sys(111, "canonicalize livedir") ; - if (wgolb & GOLB_DEREF) + strerr_diefusys(111, "canonicalize ", wgola[GOLA_LIVEDIR]) ; + + check_scandir(argv[0]) ; + x = realpath(wgola[GOLA_LIVEDIR], 0) ; + if (x) { - char *x = realpath(wgola[GOLA_BOOTDB], 0) ; - if (!x) strerr_diefu2sys(111, "realpath ", wgola[GOLA_BOOTDB]) ; - wgola[GOLA_BOOTDB] = x ; + if (check_livedir(x, argv[0], wgola[GOLA_PREFIX], wgola[GOLA_BOOTDB], &sa)) + { + strerr_warni("s6-rc already running on ", x) ; + _exit(0) ; + } + free(x) ; + } + else + { + size_t llen = strlen(wgola[GOLA_LIVEDIR]) ; + char *d = strrchr(wgola[GOLA_LIVEDIR], '/') ; + if (!d || d == wgola[GOLA_LIVEDIR]) strerr_dief(102, "invalid existing livedir: ", wgola[GOLA_LIVEDIR]) ; + dirlen = d - wgola[GOLA_LIVEDIR] ; + if (find_livedir(wgola[GOLA_LIVEDIR], llen, dirlen, argv[0], wgola[GOLA_PREFIX], wgola[GOLA_BOOTDB], &sa)) + { + strerr_warni("s6-rc already running on ", sa.s, " - recovering it") ; + if (!atomic_symlink4(sa.s + dirlen + 1, wgola[GOLA_LIVEDIR], 0, 0)) + strerr_diefusys(111, "symlink ", sa.s + dirlen + 1, " to ", wgola[GOLA_LIVEDIR]) ; + _exit(0) ; + } } + tain_now_set_stopwatch_g() ; + tain_add_g(&deadline, &deadline) ; + { s6rc_db_t db ; int r ; @@ -173,7 +266,7 @@ int main (int argc, char const *const *argv) strerr_diefusys(111, "symlink ", sa.s + dirlen, " to ", wgola[GOLA_LIVEDIR]) ; } - if (s6rc_servicedir_manage_g(wgola[GOLA_LIVEDIR], wgola[GOLA_PREFIX], &deadline) < 0) + if (s6rc_servicedir_manage_g(sa.s, wgola[GOLA_PREFIX], &deadline) < 0) { unlink_void(wgola[GOLA_LIVEDIR]) ; cleanup(&sa) ; |
