aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/s6-rc/s6-rc-init.c157
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) ;