aboutsummaryrefslogtreecommitdiffstats
path: root/src/repo/s6-rc-set-install.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/repo/s6-rc-set-install.c')
-rw-r--r--src/repo/s6-rc-set-install.c323
1 files changed, 233 insertions, 90 deletions
diff --git a/src/repo/s6-rc-set-install.c b/src/repo/s6-rc-set-install.c
index e2ecd6f..3940aba 100644
--- a/src/repo/s6-rc-set-install.c
+++ b/src/repo/s6-rc-set-install.c
@@ -36,6 +36,7 @@ enum golb_e
GOLB_KEEPOLD = 0x02,
GOLB_NOUPDATE = 0x04,
GOLB_NOFORCEESSENTIALS = 0x08,
+ GOLB_NOSNAP = 0x10,
} ;
enum gola_e
@@ -48,6 +49,104 @@ enum gola_e
GOLA_N
} ;
+#define DSTFNLEN(bootdir, set) (strlen(bootdir) + strlen(set) + 36)
+
+static int install_needs_upgrade (char const *livedir)
+{
+ size_t llen = strlen(livedir) ;
+ char livedb[llen + 10] ;
+ memcpy(livedb, livedir, llen) ;
+ memcpy(livedb + llen, "/compiled", 10) ;
+ return s6rc_repo_read_major(livedb) < s6rc_get_major(S6RC_VERSION) ;
+}
+
+static void snapcompile (char const *livedir, char const *repo, unsigned int verbosity, stralloc *sa)
+{
+ int r ;
+ size_t llen = strlen(livedir) ;
+ size_t snappos = sa->len, fdhuserpos, catchallpos, defbundlepos ;
+ char fn[llen + 28] ;
+ memcpy(fn, livedir, llen) ;
+ memcpy(fn + llen, "/compiled", 10) ;
+ if (!stralloc_catb(sa, ".snapshot.", 10)) strerr_diefusys(111, "stralloc_catb") ;
+ if (sareadlink(sa, fn) == -1 || !stralloc_0(sa)) strerr_diefusys(111, "readlink ", fn) ;
+ fdhuserpos = sa->len ;
+ memcpy(fn + llen + 9, "metainfo/fdhuser", 17) ;
+ if (!openslurpnclose(fn, sa, 4096) || !stralloc_0(sa)) strerr_diefusys(111, "read ", fn) ;
+ catchallpos = sa->len ;
+ memcpy(fn + llen + 18, "catchall", 9) ;
+ if (!openslurpnclose(fn, sa, 4096) || !stralloc_0(sa)) strerr_diefusys(111, "read ", fn) ;
+ defbundlepos = sa->len ;
+ memcpy(fn + llen + 18, "defbundle", 10) ;
+ if (!openslurpnclose(fn, sa, 4096) || !stralloc_0(sa)) strerr_diefusys(111, "read ", fn) ;
+
+ size_t oldclen = S6RC_REPO_COMPILE_BUFLEN(strlen(repo), fdhuserpos - 1) ;
+ char oldc[oldclen] ;
+
+ r = s6rc_repo_setcompile(repo, sa->s + snappos, sa->s + defbundlepos, oldc, verbosity, sa->s + fdhuserpos, sa->s + catchallpos, 1) ;
+ if (r == -1) _exit(111) ;
+ if (!r) _exit(1) ;
+ if (r == 2) rm_rf(oldc) ;
+ sa->len = fdhuserpos ;
+}
+
+static void copy_compiled_to_bootdir (char *dstfn, char const *repo, char const *set, char const *bootdir, char const *livedir, stralloc *olddbsa)
+{
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ size_t bootdirlen = strlen(bootdir) ;
+ size_t clen = S6RC_REPO_COMPILE_BUFLEN(repolen, setlen) ;
+ ssize_t l ;
+ char clink[repolen + setlen + 11] ;
+ char cfull[clen] ;
+ memcpy(clink, repo, repolen) ;
+ memcpy(clink + repolen, "/compiled/", 10) ;
+ memcpy(clink + repolen + 10, set, setlen + 1) ;
+ memcpy(cfull, clink, repolen + 10) ;
+ memcpy(dstfn, bootdir, bootdirlen) ;
+ dstfn[bootdirlen] = '/' ;
+ l = readlink(clink, cfull + repolen + 10, setlen + 35) ;
+ if (l == -1) strerr_diefusys(111, "readlink ", clink) ;
+ if (l >= setlen + 35) strerr_dief(102, "incorrect/unexpected link contents for ", clink) ;
+ cfull[repolen + 10 + l] = 0 ;
+ memcpy(dstfn + bootdirlen + 1, cfull + repolen + 10, l+1) ;
+ l = 0 ;
+
+ {
+ struct stat stsource, stdest ;
+ if (stat(cfull, &stsource) == -1) strerr_diefusys(111, "stat ", cfull) ;
+ if (stat(dstfn, &stdest) == -1)
+ {
+ if (errno != ENOENT)
+ strerr_diefusys(111, "stat ", dstfn) ;
+ }
+ else if (stdest.st_mtim.tv_sec > stsource.st_mtim.tv_sec || (stdest.st_mtim.tv_sec == stsource.st_mtim.tv_sec && stdest.st_mtim.tv_nsec >= stsource.st_mtim.tv_nsec))
+ {
+ strerr_warni("set ", set, " is already installed as ", dstfn) ;
+ _exit(0) ;
+ }
+ else strerr_dief(102, "huh? ", dstfn, " already exists") ;
+ }
+
+ {
+ size_t llen = strlen(livedir) ;
+ char cfn[llen + 10] ;
+ memcpy(cfn, livedir, llen) ;
+ memcpy(cfn + llen, "/compiled", 10) ;
+ if (sarealpath(olddbsa, cfn) == -1 && errno != ENOENT)
+ strerr_diefusys(111, "realpath ", cfn) ;
+ if (!stralloc_0(olddbsa)) strerr_diefusys(111, "stralloc_catb") ;
+ }
+
+ if (!hiercopy(cfull, dstfn))
+ {
+ int e = errno ;
+ rm_rf(dstfn) ;
+ errno = e ;
+ strerr_diefusys(111, "recursively copy ", cfull, " to ", dstfn) ;
+ }
+}
+
int main (int argc, char const *const *argv)
{
static gol_bool const rgolb[] =
@@ -57,6 +156,8 @@ int main (int argc, char const *const *argv)
{ .so = 0, .lo = "no-update", .set = GOLB_NOUPDATE },
{ .so = 'e', .lo = "force-essentials", .clear = GOLB_NOFORCEESSENTIALS, .set = 0 },
{ .so = 'E', .lo = "no-force-essentials", .clear = 0, .set = GOLB_NOFORCEESSENTIALS },
+ { .so = 0, .lo = "snapshot", .clear = GOLB_NOSNAP, .set = 0 },
+ { .so = 0, .lo = "no-snapshot", .clear = 0, .set = GOLB_NOSNAP },
} ;
static gol_arg const rgola[] =
{
@@ -73,7 +174,7 @@ int main (int argc, char const *const *argv)
uint64_t wgolb = 0 ;
unsigned int golc ;
int r ;
- stralloc sa = STRALLOC_ZERO ;
+ stralloc bootdirsa = STRALLOC_ZERO ;
PROG = "s6-rc-set-install" ;
wgola[GOLA_BOOTDB] = S6RC_BOOTDB ;
@@ -97,139 +198,181 @@ int main (int argc, char const *const *argv)
r = s6rc_repo_checkset(wgola[GOLA_REPODIR], argv[0]) ;
if (r) _exit(r) ;
- r = s6rc_repo_setuptodate(wgola[GOLA_REPODIR], argv[0]) ;
+ r = s6rc_repo_setuptodate(wgola[GOLA_REPODIR], argv[0], !!(wgolb & GOLB_NOSNAP)) ;
if (r == -1) _exit(111) ;
if (!r) strerr_dief3x(7, "set ", argv[0], " is not up-to-date; commit it first") ;
- if (!sadirname(&sa, wgola[GOLA_BOOTDB], strlen(wgola[GOLA_BOOTDB]))
- || !stralloc_0(&sa)) strerr_diefu1sys(111, "sadirname") ;
- sa.len-- ;
+ if (!sadirname(&bootdirsa, wgola[GOLA_BOOTDB], strlen(wgola[GOLA_BOOTDB]))
+ || !stralloc_0(&bootdirsa)) strerr_diefu1sys(111, "sadirname") ;
+ bootdirsa.len-- ;
{
- size_t repolen = strlen(wgola[GOLA_REPODIR]) ;
- size_t setlen = strlen(argv[0]) ;
- size_t clen = S6RC_REPO_COMPILE_BUFLEN(repolen, setlen) ;
- ssize_t l ;
- pid_t pid ;
- int wstat ;
- char *olddb = 0 ;
- char const *uargv[12] ;
+ int needupgrade = install_needs_upgrade(wgola[GOLA_LIVEDIR]) ;
+ stralloc olddbsa = STRALLOC_ZERO ;
+ char dstfn[DSTFNLEN(bootdirsa.s, argv[0])] ;
char fmtv[UINT_FMT] ;
- char clink[repolen + setlen + 11] ;
- char cfull[clen] ;
- char dstfn[sa.len + setlen + 36] ;
fmtv[uint_fmt(fmtv, verbosity)] = 0 ;
- memcpy(clink, wgola[GOLA_REPODIR], repolen) ;
- memcpy(clink + repolen, "/compiled/", 10) ;
- memcpy(clink + repolen + 10, argv[0], setlen + 1) ;
- memcpy(cfull, clink, repolen + 10) ;
- memcpy(dstfn, sa.s, sa.len) ;
- dstfn[sa.len] = '/' ;
- l = readlink(clink, cfull + repolen + 10, setlen + 35) ;
- if (l == -1) strerr_diefu2sys(111, "readlink ", clink) ;
- if (l >= setlen + 35) strerr_diefu2x(102, "incorrect/unexpected link contents for ", clink) ;
- cfull[repolen + 10 + l] = 0 ;
- memcpy(dstfn + sa.len + 1, cfull + repolen + 10, l+1) ;
- l = 0 ;
- {
- struct stat stsource, stdest ;
- if (stat(cfull, &stsource) == -1) strerr_diefu2sys(111, "stat ", cfull) ;
- if (stat(dstfn, &stdest) == -1)
- {
- if (errno != ENOENT)
- strerr_diefu2sys(111, "stat ", dstfn) ;
- }
- else if (stdest.st_mtim.tv_sec > stsource.st_mtim.tv_sec || (stdest.st_mtim.tv_sec == stsource.st_mtim.tv_sec && stdest.st_mtim.tv_nsec >= stsource.st_mtim.tv_nsec))
- {
- strerr_warni(0, "set ", argv[0], " is already installed as ", dstfn) ;
- _exit(0) ;
- }
- else strerr_dief(102, "huh? ", dstfn, " already exists") ;
- }
- {
- size_t llen = strlen(wgola[GOLA_LIVEDIR]) ;
- char cfn[llen + 10] ;
- memcpy(cfn, wgola[GOLA_LIVEDIR], llen) ;
- memcpy(cfn + llen, "/compiled", 10) ;
- olddb = realpath(cfn, 0) ;
- if (!olddb && errno != ENOENT) strerr_diefu2sys(111, "realpath ", cfn) ;
- }
- if (!hiercopy(cfull, dstfn))
- {
- int e = errno ;
- rm_rf(dstfn) ;
- errno = e ;
- strerr_diefu4sys(111, "recursively copy ", cfull, " to ", dstfn) ;
- }
- if (!(wgolb & GOLB_NOUPDATE))
+ /* If we need a format-upgrade and the user isn't opposed to it */
+ if (needupgrade && !(wgolb & GOLB_NOUPDATE) && !(wgolb & GOLB_NOSNAP))
{
- uargv[l++] = S6RC_BINPREFIX "s6-rc-update" ;
- if (wgolb & GOLB_BLOCK) uargv[l++] = "-b" ;
- uargv[l++] = wgolb & GOLB_NOFORCEESSENTIALS ? "-E" : "-e" ;
+ pid_t pid ;
+ int wstat ;
+ char const *uargv[9] ;
+ unsigned int l = 0 ;
+ char snapfn[DSTFNLEN(bootdirsa.s, olddbsa.s)] ;
+
+ /* We find the snapshot corresponding to the current live db and we compile it */
+ snapcompile(wgola[GOLA_LIVEDIR], wgola[GOLA_REPODIR], verbosity, &olddbsa) ;
+ olddbsa.len = 0 ;
+
+ /* We copy the compiled snapshot to the bootdir. It has the correct major. */
+ copy_compiled_to_bootdir(snapfn, wgola[GOLA_REPODIR], olddbsa.s, bootdirsa.s, wgola[GOLA_LIVEDIR], &olddbsa) ;
+
+ /* We perform the format-upgrade */
+ uargv[l++] = S6RC_BINPREFIX "s6-rc-format-upgrade" ;
uargv[l++] = "-v" ;
uargv[l++] = fmtv ;
uargv[l++] = "-l" ;
uargv[l++] = wgola[GOLA_LIVEDIR] ;
- if (wgola[GOLA_CONVFILE])
- {
- uargv[l++] = "-f" ;
- uargv[l++] = wgola[GOLA_CONVFILE] ;
- }
+ if (wgolb & GOLB_BLOCK) uargv[l++] = "-b" ;
uargv[l++] = "--" ;
- uargv[l++] = dstfn ;
+ uargv[l++] = snapfn ;
uargv[l++] = 0 ;
-
pid = cspawn(uargv[0], uargv, (char const *const *)environ, 0, 0, 0) ;
if (!pid)
{
int e = errno ;
- rm_rf(dstfn) ;
+ rm_rf(snapfn) ;
errno = e ;
- strerr_diefu2sys(111, "spawn ", uargv[0]) ;
+ strerr_diefusys(111, "spawn ", uargv[0]) ;
}
r = wait_pid(pid, &wstat) ;
- if (r == -1) strerr_diefu3sys(111, "wait for the ", uargv[0], " process (updating may yet succeed)") ;
+ if (r == -1) strerr_diefusys(111, "wait for the ", uargv[0], " process") ;
if (WIFSIGNALED(wstat))
{
char fmt[INT_FMT] ;
fmt[int_fmt(fmt, WTERMSIG(wstat))] = 0 ;
- strerr_dief3x(wait_estatus(wstat), uargv[0], " crashed with signal ", fmt) ;
+ strerr_dief(wait_estatus(wstat), uargv[0], " crashed with signal ", fmt) ;
}
- r = WEXITSTATUS(wstat) ;
- if (r == 111)
- strerr_dief2x(111, uargv[0], " exited 111, unable to know the state of the live db or clean up") ;
- if ((r >= 3 && r <= 10) || r == 100)
+ if (WEXITSTATUS(wstat))
{
- int e = errno ;
char fmt[INT_FMT] ;
- fmt[int_fmt(fmt, r)] = 0 ;
+ fmt[int_fmt(fmt, WEXITSTATUS(wstat))] = 0 ;
+ strerr_dief(wait_estatus(wstat), uargv[0], " exited with signal ", fmt) ;
+ }
+
+ /* Now the live db has the same major version number as the new db we want. */
+
+ if (!atomic_symlink4(snapfn + bootdirsa.len + 1, wgola[GOLA_BOOTDB], 0, 0))
+ strerr_diefusys(111, "symlink ", snapfn + bootdirsa.len + 1, " to ", wgola[GOLA_BOOTDB], " (live database switch was performed to snapshot, not to new set)", " (update that link manually then retry the command)") ;
+ if (olddbsa.s[0]) rm_rf(olddbsa.s) ; /* we don't keep the db with the obsolete major */
+ olddbsa.len = 0 ;
+
+ /* The compiled snapshot is now in place and serves as the db to update from. */
+ }
+
+ /* We copy the compiled committed set to the bootdir */
+ copy_compiled_to_bootdir(dstfn, wgola[GOLA_REPODIR], argv[0], bootdirsa.s, wgola[GOLA_LIVEDIR], &olddbsa) ;
+
+ if (!(wgolb & GOLB_NOUPDATE))
+ {
+ pid_t pid ;
+ int wstat ;
+ char const *uargv[12] ;
+ unsigned int l = 0 ;
+
+ /* If --no-snapshot AND we need an upgrade, we assume the user knows what they're doing
+ and the dbs represent the same state: so we just format-upgrade directly to the new compiled db */
+ if (needupgrade && wgolb & GOLB_NOSNAP)
+ {
+ uargv[l++] = S6RC_BINPREFIX "s6-rc-format-upgrade" ;
+ uargv[l++] = "-v" ;
+ uargv[l++] = fmtv ;
+ uargv[l++] = "-l" ;
+ uargv[l++] = wgola[GOLA_LIVEDIR] ;
+ if (wgolb & GOLB_BLOCK) uargv[l++] = "-b" ;
+ uargv[l++] = "--" ;
+ uargv[l++] = dstfn ;
+ uargv[l++] = 0 ;
+ if (verbosity)
+ strerr_warnw("installing set without a snapshot after a major version update; assuming the same service state") ;
+ }
+ else
+ /* We know the new db has the same major as the live db (either naturally or after a format-upgrade
+ via a snapshot), but we don't know if they represent the same state. This is the common case.
+ So we s6-rc-update, to switch live dbs. */
+ {
+ uargv[l++] = S6RC_BINPREFIX "s6-rc-update" ;
+ if (wgolb & GOLB_BLOCK) uargv[l++] = "-b" ;
+ uargv[l++] = wgolb & GOLB_NOFORCEESSENTIALS ? "-E" : "-e" ;
+ uargv[l++] = "-v" ;
+ uargv[l++] = fmtv ;
+ uargv[l++] = "-l" ;
+ uargv[l++] = wgola[GOLA_LIVEDIR] ;
+ if (wgola[GOLA_CONVFILE])
+ {
+ uargv[l++] = "-f" ;
+ uargv[l++] = wgola[GOLA_CONVFILE] ;
+ }
+ uargv[l++] = "--" ;
+ uargv[l++] = dstfn ;
+ uargv[l++] = 0 ;
+ }
+
+ pid = cspawn(uargv[0], uargv, (char const *const *)environ, 0, 0, 0) ;
+ if (!pid)
+ {
+ int e = errno ;
rm_rf(dstfn) ;
errno = e ;
- strerr_dief4x(r, uargv[0], " exited with code ", fmt, " (live database switch was NOT performed)") ;
+ strerr_diefusys(111, "spawn ", uargv[0]) ;
+ }
+ r = wait_pid(pid, &wstat) ;
+ if (r == -1) strerr_diefusys(111, "wait for the ", uargv[0], " process (updating may yet succeed)") ;
+ if (WIFSIGNALED(wstat))
+ {
+ char fmt[INT_FMT] ;
+ fmt[int_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_dief(wait_estatus(wstat), uargv[0], " crashed with signal ", fmt) ;
}
- if (olddb)
+ r = WEXITSTATUS(wstat) ;
+ if (!needupgrade || !(wgolb & GOLB_NOSNAP))
{
- if (wgolb & GOLB_KEEPOLD)
+ if (r == 111)
+ strerr_dief(111, uargv[0], " exited 111, unable to know the state of the live db or clean up") ;
+ if ((r >= 3 && r <= 10) || r == 100)
{
- if (buffer_puts(buffer_1small, olddb) == -1
- || !buffer_putflush(buffer_1small, "\n", 1))
- strerr_diefu2sys(111, "write to stdout", " (live database switch was performed)") ;
+ int e = errno ;
+ char fmt[INT_FMT] ;
+ fmt[int_fmt(fmt, r)] = 0 ;
+ rm_rf(dstfn) ;
+ errno = e ;
+ strerr_dief(r, uargv[0], " exited with code ", fmt, " (live database switch was NOT performed)") ;
}
- else rm_rf(olddb) ;
}
if (r)
{
char fmt[INT_FMT] ;
fmt[int_fmt(fmt, r)] = 0 ;
- strerr_dief4x(r, uargv[0], " exited with code ", fmt, " (live database switch was performed") ;
+ strerr_dief(r, uargv[0], " exited with code ", fmt, " (live database switch was performed") ;
}
}
- if (!atomic_symlink4(dstfn + sa.len + 1, wgola[GOLA_BOOTDB], 0, 0))
- strerr_diefu6sys(111, "symlink ", dstfn + sa.len + 1, " to ", wgola[GOLA_BOOTDB], " (live database switch was performed)", " (update that link manually or next boot might fail)") ;
+ if (!atomic_symlink4(dstfn + bootdirsa.len + 1, wgola[GOLA_BOOTDB], 0, 0))
+ strerr_diefusys(111, "symlink ", dstfn + bootdirsa.len + 1, " to ", wgola[GOLA_BOOTDB], " (live database switch was performed)", " (update that link manually or next boot might fail)") ;
+
+ if (olddbsa.s[0])
+ {
+ if (wgolb & GOLB_KEEPOLD)
+ {
+ if (buffer_puts(buffer_1small, olddbsa.s) == -1
+ || !buffer_putflush(buffer_1small, "\n", 1))
+ strerr_diefusys(111, "write to stdout", " (live database switch was performed)") ;
+ }
+ else rm_rf(olddbsa.s) ;
+ }
}
- // stralloc_free(&sa) ;
_exit(0) ;
}