aboutsummaryrefslogtreecommitdiffstats
path: root/src/repo
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2025-10-22 22:05:56 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2025-10-22 22:05:56 +0000
commite0bcc0ef3466b7b5fc776d2e78d7e7ec027810c0 (patch)
treed9686a3c25525760ef9ce5ed5594ec382b3fb6e7 /src/repo
parent3b9836248ed7f5164140d63e96ece21973fc43f7 (diff)
downloads6-rc-e0bcc0ef3466b7b5fc776d2e78d7e7ec027810c0.tar.gz
Separate libs6rcrepo
Diffstat (limited to 'src/repo')
-rw-r--r--src/repo/deps-exe/s6-rc-repo-init4
-rw-r--r--src/repo/deps-exe/s6-rc-repo-sync4
-rw-r--r--src/repo/deps-exe/s6-rc-set-change4
-rw-r--r--src/repo/deps-exe/s6-rc-set-commit4
-rw-r--r--src/repo/deps-exe/s6-rc-set-copy4
-rw-r--r--src/repo/deps-exe/s6-rc-set-delete4
-rw-r--r--src/repo/deps-exe/s6-rc-set-install4
-rw-r--r--src/repo/deps-exe/s6-rc-set-new4
-rw-r--r--src/repo/deps-lib/s6rcrepo36
-rw-r--r--src/repo/s6rc_repo_badsub.c69
-rw-r--r--src/repo/s6rc_repo_checkset.c39
-rw-r--r--src/repo/s6rc_repo_cleanup.c117
-rw-r--r--src/repo/s6rc_repo_compile.c106
-rw-r--r--src/repo/s6rc_repo_fillset.c73
-rw-r--r--src/repo/s6rc_repo_fix.c51
-rw-r--r--src/repo/s6rc_repo_fixset.c96
-rw-r--r--src/repo/s6rc_repo_flattenservices.c72
-rw-r--r--src/repo/s6rc_repo_getserviceflags.c70
-rw-r--r--src/repo/s6rc_repo_list_sets.c14
-rw-r--r--src/repo/s6rc_repo_listalldeps.c8
-rw-r--r--src/repo/s6rc_repo_listcontents.c67
-rw-r--r--src/repo/s6rc_repo_listdeps.c8
-rw-r--r--src/repo/s6rc_repo_listdeps_internal.c73
-rw-r--r--src/repo/s6rc_repo_listsub.c19
-rw-r--r--src/repo/s6rc_repo_lock.c20
-rw-r--r--src/repo/s6rc_repo_ls.c46
-rw-r--r--src/repo/s6rc_repo_makedefbundle.c121
-rw-r--r--src/repo/s6rc_repo_makesetbundles.c137
-rw-r--r--src/repo/s6rc_repo_makestores.c65
-rw-r--r--src/repo/s6rc_repo_makesvlist.c66
-rw-r--r--src/repo/s6rc_repo_makesvlist_byname.c14
-rw-r--r--src/repo/s6rc_repo_moveservices.c60
-rw-r--r--src/repo/s6rc_repo_nlto0.c33
-rw-r--r--src/repo/s6rc_repo_setcompile.c33
-rw-r--r--src/repo/s6rc_repo_setuptodate.c44
-rw-r--r--src/repo/s6rc_repo_subnames.c5
-rw-r--r--src/repo/s6rc_repo_sv_bcmpr.c13
-rw-r--r--src/repo/s6rc_repo_sv_cmpr.c11
-rw-r--r--src/repo/s6rc_repo_sync.c230
-rw-r--r--src/repo/s6rc_repo_syncset.c16
-rw-r--r--src/repo/s6rc_repo_syncset_tmp.c101
-rw-r--r--src/repo/s6rc_repo_touch.c45
-rw-r--r--src/repo/s6rc_repo_touchset.c25
-rw-r--r--src/repo/s6rc_repo_type_check.c29
44 files changed, 2048 insertions, 16 deletions
diff --git a/src/repo/deps-exe/s6-rc-repo-init b/src/repo/deps-exe/s6-rc-repo-init
index 6dd7af7..9fe69bb 100644
--- a/src/repo/deps-exe/s6-rc-repo-init
+++ b/src/repo/deps-exe/s6-rc-repo-init
@@ -1,3 +1,3 @@
-${LIBS6RC}
+${LIBS6RCREPO}
-lskarnet
-${SYSCLOCK_LIB}
+${SPAWN_LIB}
diff --git a/src/repo/deps-exe/s6-rc-repo-sync b/src/repo/deps-exe/s6-rc-repo-sync
index 6dd7af7..9fe69bb 100644
--- a/src/repo/deps-exe/s6-rc-repo-sync
+++ b/src/repo/deps-exe/s6-rc-repo-sync
@@ -1,3 +1,3 @@
-${LIBS6RC}
+${LIBS6RCREPO}
-lskarnet
-${SYSCLOCK_LIB}
+${SPAWN_LIB}
diff --git a/src/repo/deps-exe/s6-rc-set-change b/src/repo/deps-exe/s6-rc-set-change
index 6dd7af7..9fe69bb 100644
--- a/src/repo/deps-exe/s6-rc-set-change
+++ b/src/repo/deps-exe/s6-rc-set-change
@@ -1,3 +1,3 @@
-${LIBS6RC}
+${LIBS6RCREPO}
-lskarnet
-${SYSCLOCK_LIB}
+${SPAWN_LIB}
diff --git a/src/repo/deps-exe/s6-rc-set-commit b/src/repo/deps-exe/s6-rc-set-commit
index 6dd7af7..9fe69bb 100644
--- a/src/repo/deps-exe/s6-rc-set-commit
+++ b/src/repo/deps-exe/s6-rc-set-commit
@@ -1,3 +1,3 @@
-${LIBS6RC}
+${LIBS6RCREPO}
-lskarnet
-${SYSCLOCK_LIB}
+${SPAWN_LIB}
diff --git a/src/repo/deps-exe/s6-rc-set-copy b/src/repo/deps-exe/s6-rc-set-copy
index 6dd7af7..9fe69bb 100644
--- a/src/repo/deps-exe/s6-rc-set-copy
+++ b/src/repo/deps-exe/s6-rc-set-copy
@@ -1,3 +1,3 @@
-${LIBS6RC}
+${LIBS6RCREPO}
-lskarnet
-${SYSCLOCK_LIB}
+${SPAWN_LIB}
diff --git a/src/repo/deps-exe/s6-rc-set-delete b/src/repo/deps-exe/s6-rc-set-delete
index 6dd7af7..9fe69bb 100644
--- a/src/repo/deps-exe/s6-rc-set-delete
+++ b/src/repo/deps-exe/s6-rc-set-delete
@@ -1,3 +1,3 @@
-${LIBS6RC}
+${LIBS6RCREPO}
-lskarnet
-${SYSCLOCK_LIB}
+${SPAWN_LIB}
diff --git a/src/repo/deps-exe/s6-rc-set-install b/src/repo/deps-exe/s6-rc-set-install
index 6dd7af7..9fe69bb 100644
--- a/src/repo/deps-exe/s6-rc-set-install
+++ b/src/repo/deps-exe/s6-rc-set-install
@@ -1,3 +1,3 @@
-${LIBS6RC}
+${LIBS6RCREPO}
-lskarnet
-${SYSCLOCK_LIB}
+${SPAWN_LIB}
diff --git a/src/repo/deps-exe/s6-rc-set-new b/src/repo/deps-exe/s6-rc-set-new
index 6dd7af7..9fe69bb 100644
--- a/src/repo/deps-exe/s6-rc-set-new
+++ b/src/repo/deps-exe/s6-rc-set-new
@@ -1,3 +1,3 @@
-${LIBS6RC}
+${LIBS6RCREPO}
-lskarnet
-${SYSCLOCK_LIB}
+${SPAWN_LIB}
diff --git a/src/repo/deps-lib/s6rcrepo b/src/repo/deps-lib/s6rcrepo
new file mode 100644
index 0000000..a3afeb5
--- /dev/null
+++ b/src/repo/deps-lib/s6rcrepo
@@ -0,0 +1,36 @@
+s6rc_repo_badsub.o
+s6rc_repo_checkset.o
+s6rc_repo_cleanup.o
+s6rc_repo_compile.o
+s6rc_repo_fillset.o
+s6rc_repo_fixset.o
+s6rc_repo_flattenservices.o
+s6rc_repo_getserviceflags.o
+s6rc_repo_list_sets.o
+s6rc_repo_listalldeps.o
+s6rc_repo_listcontents.o
+s6rc_repo_listdeps.o
+s6rc_repo_listdeps_internal.o
+s6rc_repo_listsub.o
+s6rc_repo_lock.o
+s6rc_repo_ls.o
+s6rc_repo_makedefbundle.o
+s6rc_repo_makesetbundles.o
+s6rc_repo_makestores.o
+s6rc_repo_makesvlist.o
+s6rc_repo_makesvlist_byname.o
+s6rc_repo_moveservices.o
+s6rc_repo_nlto0.o
+s6rc_repo_setcompile.o
+s6rc_repo_setuptodate.o
+s6rc_repo_subnames.o
+s6rc_repo_sv_bcmpr.o
+s6rc_repo_sv_cmpr.o
+s6rc_repo_sync.o
+s6rc_repo_syncset.o
+s6rc_repo_syncset_tmp.o
+s6rc_repo_touch.o
+s6rc_repo_touchset.o
+s6rc_repo_type_check.o
+-lskarnet
+${SPAWN_LIB}
diff --git a/src/repo/s6rc_repo_badsub.c b/src/repo/s6rc_repo_badsub.c
new file mode 100644
index 0000000..69697eb
--- /dev/null
+++ b/src/repo/s6rc_repo_badsub.c
@@ -0,0 +1,69 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/direntry.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+
+#include <s6-rc/repo.h>
+
+static int strqcmp (void const *a, void const *b)
+{
+ return strcmp(*(char const *const *)a, *(char const *const *)b) ;
+}
+
+int s6rc_repo_badsub (char const *repo, char const *set, char const **services, uint32_t n, uint8_t newsub, uint8_t what, s6rc_repo_sv const *svlist, uint32_t ntot, stralloc *sa, genalloc *badga)
+{
+ int sawasnull = !!sa->s ;
+ int gawasnull = !!genalloc_s(uint32_t, badga) ;
+ size_t sabase = sa->len ;
+ size_t gabase = genalloc_len(uint32_t, badga) ;
+ genalloc fulldeps = GENALLOC_ZERO ;
+ size_t fulln ;
+ size_t const *ind ;
+ uint32_t mid ;
+
+ if (newsub < 3 && (what & 1) && s6rc_repo_listalldeps(repo, services, n, sa, &fulldeps, 0)) return 0 ;
+ mid = genalloc_len(size_t, &fulldeps) ;
+ if (newsub > 0 && (what & 2) && s6rc_repo_listalldeps(repo, services, n, sa, &fulldeps, 1)) goto err ;
+
+ qsort(services, n, sizeof(char const *), &strqcmp) ;
+ fulln = genalloc_len(size_t, &fulldeps) ;
+ ind = genalloc_s(size_t, &fulldeps) ;
+
+ for (uint32_t i = 0 ; i < fulln ; i++)
+ {
+ s6rc_repo_sv *p ;
+ char const *cur = sa->s + ind[i] ;
+ if (bsearch(&cur, services, n, sizeof(char const *), &strqcmp)) continue ;
+ p = bsearchr(cur, svlist, ntot, sizeof(s6rc_repo_sv), &s6rc_repo_sv_bcmpr, sa->s) ;
+ if (!p)
+ {
+ strerr_warnfu6x("find service ", cur, " in set ", set, " of repository ", repo) ;
+ goto err ;
+ }
+ if (i >= mid ? p->sub < newsub : p->sub > newsub)
+ {
+ uint32_t k = p - svlist ;
+ if (!genalloc_append(uint32_t, badga, &k))
+ {
+ strerr_warnfu1sys("make bad sub list") ;
+ goto err ;
+ }
+ }
+ }
+ genalloc_free(size_t, &fulldeps) ;
+ return 1 ;
+
+ err:
+ genalloc_free(size_t, &fulldeps) ;
+ if (sawasnull) stralloc_free(sa) ; else sa->len = sabase ;
+ if (gawasnull) genalloc_free(uint32_t, badga) ; else genalloc_setlen(uint32_t, badga, gabase) ;
+ return 0 ;
+}
diff --git a/src/repo/s6rc_repo_checkset.c b/src/repo/s6rc_repo_checkset.c
new file mode 100644
index 0000000..a5b994e
--- /dev/null
+++ b/src/repo/s6rc_repo_checkset.c
@@ -0,0 +1,39 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+
+#include <skalibs/stat.h>
+#include <skalibs/strerr.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_checkset (char const *repo, char const *set)
+{
+ struct stat st ;
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ char fn[repolen + 10 + setlen] ;
+ memcpy(fn, repo, repolen) ;
+ memcpy(fn + repolen, "/sources/", 9) ;
+ memcpy(fn + repolen + 9, set, setlen + 1) ;
+ if (stat(fn, &st) == -1)
+ {
+ if (errno == ENOENT)
+ {
+ strerr_warnf4x("set ", set, " does not exist in repository ", repo) ;
+ return 3 ;
+ }
+ else
+ {
+ strerr_warnfu2sys("stat ", fn) ;
+ return 111 ;
+ }
+ }
+ if (!S_ISDIR(st.st_mode))
+ {
+ strerr_warnf3x("file ", fn, " is not a directory") ;
+ return 102 ;
+ }
+ return 0 ;
+}
diff --git a/src/repo/s6rc_repo_cleanup.c b/src/repo/s6rc_repo_cleanup.c
new file mode 100644
index 0000000..05881cd
--- /dev/null
+++ b/src/repo/s6rc_repo_cleanup.c
@@ -0,0 +1,117 @@
+/* ISC license. */
+
+#include <skalibs/bsdsnowflake.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <skalibs/stat.h>
+#include <skalibs/direntry.h>
+#include <skalibs/posixplz.h>
+#include <skalibs/strerr.h>
+#include <skalibs/djbunix.h>
+
+#include <s6-rc/repo.h>
+
+static inline int sub_cleanup (char const *sub)
+{
+ size_t sublen = strlen(sub) ;
+ DIR *dir = opendir(sub) ;
+ if (!dir) return 0 ;
+ for (;;)
+ {
+ size_t len ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ len = strlen(d->d_name) ;
+ {
+ char fn[sublen + len + 2] ;
+ memcpy(fn, sub, sublen) ;
+ fn[sublen] = '/' ;
+ memcpy(fn + sublen + 1, d->d_name, len + 1) ;
+ if (access(fn, F_OK) == 0)
+ {
+ struct stat st ;
+ if (lstat(fn, &st) == -1) break ;
+ if (!S_ISLNK(st.st_mode)) rm_rf(fn) ;
+ }
+ else if (errno != ENOENT)
+ {
+ strerr_warnfu2sys("access ", fn) ;
+ break ;
+ }
+ else unlink_void(fn) ;
+ }
+ }
+ dir_close(dir) ;
+ return !errno ;
+}
+
+static inline int set_cleanup (char const *set)
+{
+ size_t setlen = strlen(set) ;
+ DIR *dir ;
+ {
+ char bundle[setlen + 8] ;
+ memcpy(bundle, set, setlen) ;
+ memcpy(bundle + setlen, "/bundle", 8) ;
+ rm_rf(bundle) ;
+ }
+ dir = opendir(set) ;
+ if (!dir) return 0 ;
+ for (;;)
+ {
+ size_t len ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ len = strlen(d->d_name) ;
+ {
+ char fn[setlen + len + 2] ;
+ memcpy(fn, set, setlen) ;
+ fn[setlen] = '/' ;
+ memcpy(fn + setlen + 1, d->d_name, len + 1) ;
+ if (!sub_cleanup(fn)) break ;
+ }
+ }
+ dir_close(dir) ;
+ return !errno ;
+}
+
+int s6rc_repo_sources_cleanup (char const *repo)
+{
+ DIR *dir ;
+ size_t repolen = strlen(repo) ;
+ unsigned int nsets = 0 ;
+ char sets[repolen + 9] ;
+ memcpy(sets, repo, repolen) ;
+ memcpy(sets + repolen, "/sources", 9) ;
+ dir = opendir(sets) ;
+ if (!dir) return 0 ;
+ for (;;)
+ {
+ size_t len ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ len = strlen(d->d_name) ;
+ {
+ char fn[repolen + len + 10] ;
+ memcpy(fn, sets, repolen + 8) ;
+ fn[repolen + 8] = '/' ;
+ memcpy(fn + repolen + 9, d->d_name, len + 1) ;
+ if (!set_cleanup(fn)) break ;
+ nsets++ ;
+ }
+ }
+ dir_close(dir) ;
+ return errno ? -1 : nsets ;
+}
diff --git a/src/repo/s6rc_repo_compile.c b/src/repo/s6rc_repo_compile.c
new file mode 100644
index 0000000..ac4207d
--- /dev/null
+++ b/src/repo/s6rc_repo_compile.c
@@ -0,0 +1,106 @@
+/* ISC license. */
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/types.h>
+#include <skalibs/tai.h>
+#include <skalibs/cspawn.h>
+#include <skalibs/strerr.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+#include <s6-rc/config.h>
+#include <s6-rc/repo.h>
+
+int s6rc_repo_compile (char const *repo, char const *set, char const *const *subs, uint8_t nsubs, char *oldc, unsigned int verbosity, char const *fdhuser)
+{
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ size_t totsublen = 0 ;
+ int needprefix = strcmp(set, ".ref") ;
+ char newc[repolen + setlen + 45] ;
+ memcpy(newc, repo, repolen) ;
+ memcpy(newc + repolen, "/compiled/.", 11) ;
+ memcpy(newc + repolen + 11, set, setlen) ;
+ newc[repolen + 11 + setlen] = ':' ;
+ timestamp_g(newc + repolen + 12 + setlen) ;
+ memcpy(newc + repolen + 37 + setlen, ":XXXXXX", 8) ;
+ memcpy(oldc, newc, repolen + 10) ;
+ if (mkntemp(newc) == -1) { strerr_warnfu2sys("mkntemp ", newc) ; return -1 ; }
+ for (uint8_t i = 0 ; i < nsubs ; i++) totsublen += strlen(subs[i]) + 1 ;
+
+ {
+ pid_t pid ;
+ size_t m = 0 ;
+ int wstat ;
+ char const *argv[9 + nsubs] ;
+ char fmtv[UINT_FMT] ;
+ char src[nsubs * (repolen + 10 + (needprefix ? setlen + 1 : 0)) + totsublen] ;
+ char *w = src ;
+ fmtv[uint_fmt(fmtv, verbosity)] = 0 ;
+ argv[m++] = S6RC_BINPREFIX "s6-rc-compile" ;
+ argv[m++] = "-v";
+ argv[m++] = fmtv ;
+ argv[m++] = "-b" ;
+ if (fdhuser)
+ {
+ argv[m++] = "-h" ;
+ argv[m++] = fdhuser ;
+ }
+ argv[m++] = "--" ;
+ argv[m++] = newc ;
+ for (uint8_t i = 0 ; i < nsubs ; i++)
+ {
+ size_t sublen = strlen(subs[i]) ;
+ argv[m++] = w ;
+ memcpy(w, repo, repolen) ; w += repolen ;
+ memcpy(w, "/sources/", 9) ; w += 9 ;
+ if (needprefix)
+ {
+ memcpy(w, set, setlen) ; w += setlen ;
+ *w++ = '/' ;
+ }
+ memcpy(w, subs[i], sublen+1) ; w += sublen + 1 ;
+ }
+ argv[m++] = 0 ;
+ pid = cspawn(argv[0], argv, (char const *const *)environ, 0, 0, 0) ;
+ if (!pid) { strerr_warnfu2sys("spawn ", argv[0]) ; return -1 ; }
+ if (wait_pid(pid, &wstat) == -1)
+ {
+ strerr_warnfu2sys("wait for ", argv[0]) ;
+ return -1 ;
+ }
+ if (WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_warnf3x(argv[0], " crashed with signal ", fmt) ;
+ return -1 ;
+ }
+ if (WEXITSTATUS(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WEXITSTATUS(wstat))] = 0 ;
+ strerr_warnf3x(argv[0], " exited with code ", fmt) ;
+ return (WEXITSTATUS(wstat) == 1) - 1 ;
+ }
+ }
+ {
+ char fn[repolen + setlen + 11] ;
+ memcpy(fn, newc, repolen + 10) ;
+ memcpy(fn + repolen + 10, set, setlen + 1) ;
+ if (!atomic_symlink4(newc + repolen + 10, fn, oldc + repolen + 10, setlen + 35))
+ {
+ int e = errno ;
+ rm_rf(newc) ;
+ errno = e ;
+ strerr_warnfu4sys("atomically make a ", fn, " symlink pointing to ", newc + repolen + 10) ;
+ return -1 ;
+ }
+ }
+ return 1 + !!oldc[repolen + 10] ;
+}
diff --git a/src/repo/s6rc_repo_fillset.c b/src/repo/s6rc_repo_fillset.c
new file mode 100644
index 0000000..15832c3
--- /dev/null
+++ b/src/repo/s6rc_repo_fillset.c
@@ -0,0 +1,73 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/bytestr.h>
+#include <skalibs/direntry.h>
+#include <skalibs/strerr.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_fillset (char const *repo, char const *set, char const *const *existing, uint32_t n)
+{
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ DIR *dir ;
+ char atomics[repolen + 18] ;
+ char setfn[repolen + 10 + setlen] ;
+ memcpy(atomics, repo, repolen) ;
+ memcpy(atomics + repolen, "/sources/.atomics", 18) ;
+ memcpy(setfn, repo, repolen) ;
+ memcpy(setfn + repolen, "/sources/", 9) ;
+ memcpy(setfn + repolen + 9, set, setlen+1) ;
+
+ dir = opendir(atomics) ;
+ if (!dir)
+ {
+ strerr_warnfu2sys("opendir ", atomics) ;
+ return 0 ;
+ }
+ for (;;)
+ {
+ size_t len ;
+ uint32_t flags ;
+ direntry *d ;
+ uint8_t subi ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+
+ len = strlen(d->d_name) ;
+ char src[len + 13] ;
+ char dst[repolen + 18 + setlen + len] ;
+ memcpy(src, "../.atomics/", 12) ;
+ memcpy(src + 12, d->d_name, len+1) ;
+ if (n && bsearch(d->d_name, existing, n, sizeof(char const *), &str_bcmp)) continue ;
+ if (s6rc_repo_getserviceflags(repo, d->d_name, &flags) <= 0) return 0 ;
+ if (flags & 1) subi = 3 ;
+ else if (flags & 2) subi = 2 ;
+ else subi = 1 ;
+ memcpy(dst, setfn, repolen + 10 + setlen) ;
+ memcpy(dst + repolen + 10 + setlen, s6rc_repo_subnames[subi], 6) ;
+ dst[repolen + setlen + 16] = '/' ;
+ memcpy(dst + repolen + setlen + 17, d->d_name, len+1) ;
+ if (symlink(src, dst) == -1)
+ {
+ strerr_warnfu4sys("make a symlink named ", dst, " pointing to ", src) ;
+ return 0 ;
+ }
+ }
+ dir_close(dir) ;
+ if (errno)
+ {
+ strerr_warnfu2sys("readdir ", atomics) ;
+ return 0 ;
+ }
+
+ return 1 ;
+}
diff --git a/src/repo/s6rc_repo_fix.c b/src/repo/s6rc_repo_fix.c
new file mode 100644
index 0000000..b379d30
--- /dev/null
+++ b/src/repo/s6rc_repo_fix.c
@@ -0,0 +1,51 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/direntry.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_fix (char const *repo, uint32_t options, unsigned int verbosity)
+{
+ stralloc sa = STRALLOC_ZERO ;
+ genalloc svlist = GENALLOC_ZERO ; /* s6rc_repo_sv */
+ genalloc badga = GENALLOC_ZERO ; /* uint32_t */
+ genalloc setind = GENALLOC_ZERO ; /* size_t */
+ int n ;
+
+ if (verbosity >= 3)
+ strerr_warni2x("checking set consistency in repository ", repo) ;
+
+ n = s6rc_repo_list_sets(repo, &sa, &setind) ;
+ if (n < 0)
+ {
+ strerr_warnfu1sys("list sets") ;
+ return 0 ;
+ }
+ for (uint32_t i = 0 ; i < nsets ; i++)
+ {
+ if (verbosity >= 3)
+ strerr_warni2x("checking set ", sa.s + genalloc_s(size_t, &setind)[i]) ;
+ if (!s6rc_repo_fixset(repo, sa.s + genalloc_s(size_t, &setind)[i], options, verbosity, &sa, &svlist, &badga)) goto err ;
+ }
+
+ genalloc_free(s6rc_repo_sv, &svlist) ;
+ genalloc_free(uint32_t, &badga) ;
+ genalloc_free(size_t, &setind) ;
+ stralloc_free(&sa) ;
+ return 1 ;
+
+ err:
+ genalloc_free(s6rc_repo_sv, &svlist) ;
+ genalloc_free(uint32_t, &badga) ;
+ genalloc_free(size_t, &setind) ;
+ stralloc_free(&sa) ;
+ return 0 ;
+}
diff --git a/src/repo/s6rc_repo_fixset.c b/src/repo/s6rc_repo_fixset.c
new file mode 100644
index 0000000..8ad64bc
--- /dev/null
+++ b/src/repo/s6rc_repo_fixset.c
@@ -0,0 +1,96 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+
+#include <s6-rc/repo.h>
+
+static inline int s6rc_repo_fixsub (char const *repo, char const *set, uint8_t sub, s6rc_repo_sv *byname, char const **bysub, uint32_t ntot, uint32_t options, uint32_t const *subind, uint32_t const *subn, unsigned int verbosity, stralloc *sa, genalloc *badga)
+{
+ size_t sabase = sa->len ;
+ uint32_t gabase = genalloc_len(uint32_t, badga) ;
+ uint32_t const *bads ;
+ uint32_t badn ;
+ if (!s6rc_repo_badsub(repo, set, bysub + subind[sub], subn[sub], sub, options & 1 ? 2 : 1, byname, ntot, sa, badga)) return 0 ;
+ bads = genalloc_s(uint32_t, badga) ;
+ badn = genalloc_len(uint32_t, badga) ;
+ if (!badn) return 1 ;
+
+ s6rc_repo_sv tomove[badn] ;
+ for (uint32_t j = 0 ; j < badn ; j++)
+ {
+ tomove[j] = byname[bads[j]] ;
+ if (verbosity >= 2)
+ strerr_warni("in set ", set, " of repository ", repo, ": ", options & 1 ? "up" : "down", "fixing service ", sa->s + tomove[j].pos, " from ", s6rc_repo_subnames[tomove[j].sub], " to ", s6rc_repo_subnames[sub]) ;
+ if (tomove[j].sub == 0 && verbosity)
+ strerr_warnw("service ", sa->s + tomove[j].pos, " is being automatically unmasked by an upfix to ", s6rc_repo_subnames[sub]) ;
+ if (tomove[j].sub == 3)
+ {
+ if (!(options & 2))
+ {
+ strerr_warnf("in set ", set, " of repository ", repo, ": service ", sa->s + tomove[j].pos, " is marked as essential and cannot be downfixed. If you are sure of yourself, try --force-essential") ;
+ goto err ;
+ }
+ if (verbosity)
+ strerr_warnw("service ", sa->s + tomove[j].pos, " is being automatically marked non-essential by a downfix to ", s6rc_repo_subnames[sub]) ;
+ }
+ }
+ if (!s6rc_repo_moveservices(repo, set, tomove, badn, sub, sa->s, verbosity)) goto err ;
+ for (uint32_t j = 0 ; j < badn ; j++) byname[bads[j]].sub = sub ;
+ genalloc_setlen(uint32_t, badga, gabase) ;
+ sa->len = sabase ;
+ return 1 ;
+
+ err:
+ genalloc_setlen(uint32_t, badga, gabase) ;
+ sa->len = sabase ;
+ return 0 ;
+}
+
+int s6rc_repo_fixset (char const *repo, char const *set, uint32_t options, unsigned int verbosity, stralloc *sa, genalloc *svlist, genalloc *badga)
+{
+ int sawasnull = !sa->s ;
+ int svwasnull = !genalloc_s(s6rc_repo_sv, svlist) ;
+ int gawasnull = !genalloc_s(uint32_t, badga) ;
+ size_t sabase = sa->len ;
+ uint32_t svbase = genalloc_len(s6rc_repo_sv, svlist) ;
+ uint32_t gabase = genalloc_len(uint32_t, badga) ;
+ uint32_t ntot ;
+ uint32_t subind[4] ;
+ uint32_t sublen[4] ;
+ s6rc_repo_sv *byname ;
+ if (!s6rc_repo_makesvlist(repo, set, sa, svlist, subind)) return 0 ;
+ byname = genalloc_s(s6rc_repo_sv, svlist) ;
+ ntot = genalloc_len(s6rc_repo_sv, svlist) ;
+ if (!ntot) return 1 ;
+ for (uint8_t i = 0 ; i < 4 ; i++) sublen[i] = (i == 3 ? ntot : subind[i+1]) - subind[i] ;
+
+ char const *bysub[ntot] ;
+ char bysub_storage[sa->len - sabase] ;
+ memcpy(bysub_storage, sa->s + sabase, sa->len - sabase) ;
+ for (uint32_t i = 0 ; i < ntot ; i++) bysub[i] = bysub_storage + byname[i].pos - sabase ;
+ qsortr(byname, ntot, sizeof(s6rc_repo_sv), &s6rc_repo_sv_cmpr, sa->s) ;
+
+ for (uint8_t i = 0 ; i < 4 ; i++) if (sublen[i])
+ {
+ if (!s6rc_repo_fixsub(repo, set, i, byname, bysub, ntot, options, subind, sublen, verbosity, sa, badga))
+ goto err ;
+ }
+
+ genalloc_setlen(uint32_t, badga, gabase) ;
+ genalloc_setlen(s6rc_repo_sv, svlist, svbase) ;
+ sa->len = sabase ;
+ return 1 ;
+
+ err:
+ if (gawasnull) genalloc_free(uint32_t, badga) ; else genalloc_setlen(uint32_t, badga, gabase) ;
+ if (svwasnull) genalloc_free(s6rc_repo_sv, svlist) ; else genalloc_setlen(s6rc_repo_sv, svlist, svbase) ;
+ if (sawasnull) stralloc_free(sa) ; else sa->len = sabase ;
+ return 0 ;
+}
diff --git a/src/repo/s6rc_repo_flattenservices.c b/src/repo/s6rc_repo_flattenservices.c
new file mode 100644
index 0000000..5693129
--- /dev/null
+++ b/src/repo/s6rc_repo_flattenservices.c
@@ -0,0 +1,72 @@
+/* ISC license. */
+
+#include <sys/wait.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/types.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/cspawn.h>
+#include <skalibs/djbunix.h>
+
+#include <s6-rc/config.h>
+#include <s6-rc/repo.h>
+
+int s6rc_repo_flattenservices (char const *repo, char const *const *services, uint32_t n, stralloc *storage, genalloc *indices)
+{
+ int swasnull = !storage->s ;
+ int gwasnull = !indices->s ;
+ size_t sabase = storage->len ;
+ size_t gabase = genalloc_len(size_t, indices) ;
+
+ {
+ size_t m = 0 ;
+ size_t repolen = strlen(repo) ;
+ pid_t pid ;
+ int fd ;
+ int wstat ;
+ char const *argv[7 + n] ;
+ char refdb[repolen + 15] ;
+ memcpy(refdb, repo, repolen) ;
+ memcpy(refdb + repolen, "/compiled/.ref", 15) ;
+ argv[m++] = S6RC_BINPREFIX "s6-rc-db" ;
+ argv[m++] = "-c";
+ argv[m++] = refdb ;
+ argv[m++] = "-b" ;
+ argv[m++] = "--" ;
+ argv[m++] = "atomics" ;
+ for (size_t i = 0 ; i < n ; i++) argv[m++] = services[i] ;
+ argv[m++] = 0 ;
+ pid = child_spawn1_pipe(argv[0], argv, (char const *const *)environ, &fd, 1) ;
+ if (!pid) { strerr_warnfu2sys("spawn ", argv[0]) ; return 111 ; }
+ if (!slurpn(fd, storage, 0)) { strerr_warnfu2sys("read output from ", argv[0]) ; return 111 ; }
+ fd_close(fd) ;
+ if (wait_pid(pid, &wstat) == -1)
+ {
+ strerr_warnfu2sys("wait for ", argv[0]) ;
+ goto err ;
+ }
+ if (WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_warnf3x(argv[0], " crashed with signal ", fmt) ;
+ goto err ;
+ }
+ if (WEXITSTATUS(wstat)) return WEXITSTATUS(wstat) ;
+ }
+
+ if (!s6rc_repo_nlto0(storage->s + sabase, sabase, storage->len, indices)) goto err ;
+ return 0 ;
+
+ err:
+ if (gwasnull) genalloc_free(size_t, indices) ;
+ else genalloc_setlen(size_t, indices, gabase) ;
+ if (swasnull) stralloc_free(storage) ;
+ else storage->len = sabase ;
+ return 111 ;
+}
diff --git a/src/repo/s6rc_repo_getserviceflags.c b/src/repo/s6rc_repo_getserviceflags.c
new file mode 100644
index 0000000..0ad68e7
--- /dev/null
+++ b/src/repo/s6rc_repo_getserviceflags.c
@@ -0,0 +1,70 @@
+/* ISC license. */
+
+#include <sys/wait.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/uint32.h>
+#include <skalibs/types.h>
+#include <skalibs/strerr.h>
+#include <skalibs/cspawn.h>
+#include <skalibs/djbunix.h>
+
+#include <s6-rc/config.h>
+#include <s6-rc/repo.h>
+
+#include <skalibs/posixishard.h>
+
+int s6rc_repo_getserviceflags (char const *repo, char const *name, uint32_t *flags)
+{
+ size_t repolen = strlen(repo) ;
+ ssize_t r ;
+ pid_t pid ;
+ int fd ;
+ int wstat ;
+ char flagstr[10] ;
+ char refdb[repolen + 15] ;
+ char const *argv[8] = { S6RC_BINPREFIX "s6-rc-db", "-c", refdb, "-b", "--", "flags", name, 0 } ;
+
+ memcpy(refdb, repo, repolen) ;
+ memcpy(refdb + repolen, "/compiled/.ref", 15) ;
+ pid = child_spawn1_pipe(argv[0], argv, (char const *const *)environ, &fd, 1) ;
+ if (!pid) { strerr_warnfu2sys("spawn ", argv[0]) ; return -1 ; }
+ r = readnclose(fd, flagstr, 10) ;
+ if (r != 9)
+ {
+ if (r == 10) goto err ;
+ else if (r >= 0) errno = EPIPE ;
+ strerr_warnfu2sys("read output from ", argv[0]) ;
+ return -1 ;
+ }
+ if (wait_pid(pid, &wstat) == -1)
+ {
+ strerr_warnfu2sys("wait for ", argv[0]) ;
+ return -1 ;
+ }
+ if (WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_warnf3x(argv[0], " crashed with signal ", fmt) ;
+ return -1 ;
+ }
+ if (WEXITSTATUS(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WEXITSTATUS(wstat))] = 0 ;
+ strerr_warnf3x(argv[0], " exited with code ", fmt) ;
+ return (WEXITSTATUS(wstat) < 99) - 1 ;
+ }
+ flagstr[8] = 0 ;
+ if (!uint320_xscan(flagstr, flags)) goto err ;
+ return 1 ;
+
+ err:
+ strerr_warnf2x("invalid output from ", S6RC_BINPREFIX "s6-rc-db") ;
+ errno = EPROTO ;
+ return -1 ;
+}
diff --git a/src/repo/s6rc_repo_list_sets.c b/src/repo/s6rc_repo_list_sets.c
new file mode 100644
index 0000000..1698460
--- /dev/null
+++ b/src/repo/s6rc_repo_list_sets.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <string.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_list_sets (char const *repo, stralloc *sa, genalloc *ga)
+{
+ size_t repolen = strlen(repo) ;
+ char fn[repolen + 9] ;
+ memcpy(fn, repo, repolen) ;
+ memcpy(fn + repolen, "/sources", 9) ;
+ return s6rc_repo_ls(fn, sa, ga) ;
+}
diff --git a/src/repo/s6rc_repo_listalldeps.c b/src/repo/s6rc_repo_listalldeps.c
new file mode 100644
index 0000000..7e9aca6
--- /dev/null
+++ b/src/repo/s6rc_repo_listalldeps.c
@@ -0,0 +1,8 @@
+/* ISC license. */
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_listalldeps (char const *repo, char const *const *services, uint32_t n, stralloc *storage, genalloc *indices, int up)
+{
+ return s6rc_repo_listdeps_internal(repo, services, n, storage, indices, 2 | !!up) ;
+}
diff --git a/src/repo/s6rc_repo_listcontents.c b/src/repo/s6rc_repo_listcontents.c
new file mode 100644
index 0000000..e3ffc84
--- /dev/null
+++ b/src/repo/s6rc_repo_listcontents.c
@@ -0,0 +1,67 @@
+/* ISC license. */
+
+#include <sys/wait.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/types.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/cspawn.h>
+#include <skalibs/djbunix.h>
+
+#include <s6-rc/config.h>
+#include <s6-rc/repo.h>
+
+int s6rc_repo_listcontents (char const *repo, char const *bundle, stralloc *storage, genalloc *indices)
+{
+ int swasnull = !storage->s ;
+ int gwasnull = !indices->s ;
+ size_t sabase = storage->len ;
+ size_t gabase = genalloc_len(size_t, indices) ;
+ size_t repolen = strlen(repo) ;
+ pid_t pid ;
+ int fd ;
+ int wstat ;
+ char refdb[repolen + 15] ;
+ char const *argv[8] = { S6RC_BINPREFIX "s6-rc-db", "-c", refdb, "-b", "--", "contents", bundle, 0 } ;
+
+ memcpy(refdb, repo, repolen) ;
+ memcpy(refdb + repolen, "/compiled/.ref", 15) ;
+ pid = child_spawn1_pipe(argv[0], argv, (char const *const *)environ, &fd, 1) ;
+ if (!pid) { strerr_warnfu2sys("spawn ", argv[0]) ; return -1 ; }
+ if (!slurpn(fd, storage, 0)) { strerr_warnfu2sys("read output from ", argv[0]) ; return -1 ; }
+ fd_close(fd) ;
+ if (wait_pid(pid, &wstat) == -1)
+ {
+ strerr_warnfu2sys("wait for ", argv[0]) ;
+ return -1 ;
+ }
+ if (WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_warnf3x(argv[0], " crashed with signal ", fmt) ;
+ return -1 ;
+ }
+ if (WEXITSTATUS(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WEXITSTATUS(wstat))] = 0 ;
+ strerr_warnf3x(argv[0], " exited with code ", fmt) ;
+ return (WEXITSTATUS(wstat) < 99) - 1 ;
+ }
+
+ if (!s6rc_repo_nlto0(storage->s + sabase, sabase, storage->len, indices)) goto err ;
+ return 1 ;
+
+ err:
+ if (gwasnull) genalloc_free(size_t, indices) ;
+ else genalloc_setlen(size_t, indices, gabase) ;
+ if (swasnull) stralloc_free(storage) ;
+ else storage->len = sabase ;
+ return -1 ;
+}
diff --git a/src/repo/s6rc_repo_listdeps.c b/src/repo/s6rc_repo_listdeps.c
new file mode 100644
index 0000000..4dfb495
--- /dev/null
+++ b/src/repo/s6rc_repo_listdeps.c
@@ -0,0 +1,8 @@
+/* ISC license. */
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_listdeps (char const *repo, char const *service, stralloc *storage, genalloc *indices, int up)
+{
+ return s6rc_repo_listdeps_internal(repo, &service, 1, storage, indices, !!up) ;
+}
diff --git a/src/repo/s6rc_repo_listdeps_internal.c b/src/repo/s6rc_repo_listdeps_internal.c
new file mode 100644
index 0000000..6d66796
--- /dev/null
+++ b/src/repo/s6rc_repo_listdeps_internal.c
@@ -0,0 +1,73 @@
+/* ISC license. */
+
+#include <sys/wait.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/types.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/cspawn.h>
+#include <skalibs/djbunix.h>
+
+#include <s6-rc/config.h>
+#include <s6-rc/repo.h>
+
+int s6rc_repo_listdeps_internal (char const *repo, char const *const *services, uint32_t n, stralloc *storage, genalloc *indices, uint32_t options)
+{
+ int swasnull = !storage->s ;
+ int gwasnull = !indices->s ;
+ size_t sabase = storage->len ;
+ size_t gabase = genalloc_len(size_t, indices) ;
+
+ {
+ size_t m = 0 ;
+ size_t repolen = strlen(repo) ;
+ pid_t pid ;
+ int fd ;
+ int wstat ;
+ char const *argv[8 + n] ;
+ char refdb[repolen + 15] ;
+ memcpy(refdb, repo, repolen) ;
+ memcpy(refdb + repolen, "/compiled/.ref", 15) ;
+ argv[m++] = S6RC_BINPREFIX "s6-rc-db" ;
+ argv[m++] = "-c";
+ argv[m++] = refdb ;
+ argv[m++] = "-b" ;
+ argv[m++] = options & 1 ? "-u" : "-d" ;
+ argv[m++] = "--" ;
+ argv[m++] = options & 2 ? "all-dependencies" : "dependencies" ;
+ for (uint32_t i = 0 ; i < n ; i++) argv[m++] = services[i] ;
+ argv[m++] = 0 ;
+ pid = child_spawn1_pipe(argv[0], argv, (char const *const *)environ, &fd, 1) ;
+ if (!pid) { strerr_warnfu2sys("spawn ", argv[0]) ; return 111 ; }
+ if (!slurpn(fd, storage, 0)) { strerr_warnfu2sys("read output from ", argv[0]) ; return 111 ; }
+ fd_close(fd) ;
+ if (wait_pid(pid, &wstat) == -1)
+ {
+ strerr_warnfu2sys("wait for ", argv[0]) ;
+ goto err ;
+ }
+ if (WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_warnf3x(argv[0], " crashed with signal ", fmt) ;
+ goto err ;
+ }
+ if (WEXITSTATUS(wstat)) return wait_estatus(wstat) ;
+ }
+
+ if (!s6rc_repo_nlto0(storage->s + sabase, sabase, storage->len, indices)) goto err ;
+ return 0 ;
+
+ err:
+ if (gwasnull) genalloc_free(size_t, indices) ;
+ else genalloc_setlen(size_t, indices, gabase) ;
+ if (swasnull) stralloc_free(storage) ;
+ else storage->len = sabase ;
+ return 111 ;
+}
diff --git a/src/repo/s6rc_repo_listsub.c b/src/repo/s6rc_repo_listsub.c
new file mode 100644
index 0000000..1ee23bf
--- /dev/null
+++ b/src/repo/s6rc_repo_listsub.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <string.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_listsub (char const *repo, char const *set, char const *sub, stralloc *sa, genalloc *ga)
+{
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ size_t sublen = strlen(sub) ;
+ char fn[repolen + setlen + 10 + sublen] ;
+ memcpy(fn, repo, repolen) ;
+ memcpy(fn + repolen, "/sources/", 9) ;
+ memcpy(fn + repolen + 9, set, setlen) ;
+ memcpy(fn + repolen + 9 + setlen, sub, sublen + 1) ;
+
+ return s6rc_repo_ls(fn, sa, ga) ;
+}
diff --git a/src/repo/s6rc_repo_lock.c b/src/repo/s6rc_repo_lock.c
new file mode 100644
index 0000000..472ff15
--- /dev/null
+++ b/src/repo/s6rc_repo_lock.c
@@ -0,0 +1,20 @@
+/* ISC license. */
+
+#include <string.h>
+
+#include <skalibs/djbunix.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_lock (char const *repo, int w)
+{
+ int fd ;
+ size_t repolen = strlen(repo) ;
+ char fn[repolen + 13] ;
+ memcpy(fn, repo, repolen) ;
+ memcpy(fn + repolen, "/lock", 6) ;
+ fd = openc_write(fn) ;
+ if (fd == -1) return -1 ;
+ if (fd_lock(fd, w, 0) < 1) return -1 ;
+ return fd ;
+}
diff --git a/src/repo/s6rc_repo_ls.c b/src/repo/s6rc_repo_ls.c
new file mode 100644
index 0000000..1294f48
--- /dev/null
+++ b/src/repo/s6rc_repo_ls.c
@@ -0,0 +1,46 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+
+#include <skalibs/direntry.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_ls (char const *fn, stralloc *sa, genalloc *ga)
+{
+ size_t sabase = sa->len ;
+ size_t gabase = genalloc_len(size_t, ga) ;
+ int sawasnull = !sa->s ;
+ int gawasnull = !genalloc_s(size_t, ga) ;
+ int n = 0 ;
+
+ DIR *dir = opendir(fn) ;
+ if (!dir) return -1 ;
+ for (;;)
+ {
+ direntry *d ;
+ size_t len ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ len = strlen(d->d_name) ;
+ if (!genalloc_catb(size_t, ga, &sa->len, 1)) goto err ;
+ if (!stralloc_catb(sa, d->d_name, len+1)) goto err ;
+ n++ ;
+ }
+ if (errno) goto err ;
+ dir_close(dir) ;
+ return n ;
+
+ err:
+ dir_close(dir) ;
+ if (gawasnull) genalloc_free(size_t, ga) ;
+ else genalloc_setlen(size_t, ga, gabase) ;
+ if (sawasnull) stralloc_free(sa) ;
+ else sa->len = sabase ;
+ return -1 ;
+}
diff --git a/src/repo/s6rc_repo_makedefbundle.c b/src/repo/s6rc_repo_makedefbundle.c
new file mode 100644
index 0000000..5c67917
--- /dev/null
+++ b/src/repo/s6rc_repo_makedefbundle.c
@@ -0,0 +1,121 @@
+/* ISC license. */
+
+#include <skalibs/bsdsnowflake.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <skalibs/stat.h>
+#include <skalibs/direntry.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_makedefbundle (char const *repo, char const *set, char const *bundle)
+{
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ size_t bundlelen = strlen(bundle) ;
+ size_t bfnlen = repolen + setlen + bundlelen + 28 ;
+ DIR *dir ;
+ char bfn[bfnlen + 1] ;
+ char subfn[repolen + setlen + 17] ;
+
+ memcpy(bfn, repo, repolen) ;
+ memcpy(bfn + repolen, "/sources/.atomics/", 18) ;
+ memcpy(bfn + repolen + 18, bundle, bundlelen + 1) ;
+
+ if (access(bfn, F_OK) == 0) goto errexists ;
+ if (errno != ENOENT)
+ {
+ strerr_warnfu2sys("check existence of ", bfn) ;
+ return 0 ;
+ }
+ memcpy(bfn + repolen + 10, "bundle", 6) ;
+ if (access(bfn, F_OK) == 0) goto errexists ;
+ if (errno != ENOENT)
+ {
+ strerr_warnfu2sys("check existence of ", bfn) ;
+ return 0 ;
+ }
+
+ memcpy(bfn + repolen + 9, set, setlen) ;
+ memcpy(bfn + repolen + 9 + setlen, "/bundle/", 8) ;
+ memcpy(bfn + repolen + 17 + setlen, bundle, bundlelen + 1) ;
+ if (mkdir(bfn, 02755) == -1)
+ {
+ if (errno != EEXIST) strerr_warnfu2sys("mkdir ", bfn) ;
+ else strerr_warnf6x("bundle ", bundle, " is already defined in set ", set, " of repository ", repo) ;
+ return 0 ;
+ }
+ memcpy(bfn + repolen + setlen + 17 + bundlelen, "/type", 6) ;
+ if (!openwritenclose_unsafe(bfn, "bundle\n", 7))
+ {
+ strerr_warnfu2sys("write to ", bfn) ;
+ return 0 ;
+ }
+ memcpy(bfn + repolen + setlen + 18 + bundlelen, "contents.d", 11) ;
+ if (mkdir(bfn, 02755) == -1)
+ {
+ strerr_warnfu2sys("mkdir ", bfn) ;
+ return 0 ;
+ }
+ bfn[bfnlen] = '/' ;
+
+ memcpy(subfn, bfn, repolen + 10 + setlen) ;
+ memcpy(subfn + repolen + 10 + setlen, "onboot", 7) ;
+ dir = opendir(subfn) ;
+ if (!dir)
+ {
+ strerr_warnfu2sys("opendir ", subfn) ;
+ return 0 ;
+ }
+
+ for (;;)
+ {
+ size_t len ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ len = strlen(d->d_name) ;
+ char fn[bfnlen + len + 2] ;
+ memcpy(fn, bfn, bfnlen + 1) ;
+ memcpy(fn + bfnlen + 1, d->d_name, len + 1) ;
+ if (!openwritenclose_unsafe(fn, "", 0))
+ {
+ strerr_warnfu2sys("write to ", fn) ;
+ dir_close(dir) ;
+ return 0 ;
+ }
+ }
+ dir_close(dir) ;
+ if (errno)
+ {
+ strerr_warnfu2sys("readdir ", subfn) ;
+ return 0 ;
+ }
+
+ return 1 ;
+
+ errexists:
+ if (sareadlink(&satmp, bfn) == -1)
+ {
+ strerr_warnfu2sys("readlink ", bfn) ;
+ return 0 ;
+ }
+ if (!stralloc_0(&satmp))
+ {
+ strerr_warnfu1sys("stralloc_catb ") ;
+ satmp.len = 0 ;
+ return 0 ;
+ }
+ strerr_warnf4x("bundle ", bundle, " is already defined at ", satmp.s) ;
+ satmp.len = 0 ;
+ return (errno = EINVAL, 0) ;
+}
diff --git a/src/repo/s6rc_repo_makesetbundles.c b/src/repo/s6rc_repo_makesetbundles.c
new file mode 100644
index 0000000..81267b9
--- /dev/null
+++ b/src/repo/s6rc_repo_makesetbundles.c
@@ -0,0 +1,137 @@
+/* ISC license. */
+
+#include <skalibs/bsdsnowflake.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/stat.h>
+#include <skalibs/direntry.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/djbunix.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_makesetbundles (char const *repo, char const *set, unsigned int verbosity)
+{
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ stralloc sa = STRALLOC_ZERO ;
+ genalloc ga = GENALLOC_ZERO ;
+ int n ;
+ char src[repolen + 19] ;
+ char dst[repolen + 17 + setlen] ;
+ memcpy(dst, repo, repolen) ;
+ memcpy(dst + repolen, "/sources/", 9) ;
+ memcpy(dst + repolen + 9, set, setlen) ;
+ memcpy(dst + repolen + 9 + setlen, "/bundle", 8) ;
+ memcpy(src, dst, repolen + 10) ;
+ memcpy(src + repolen + 10, ".bundles", 9) ;
+ rm_rf_tmp(dst, &sa) ;
+
+ n = s6rc_repo_listsub(repo, set, s6rc_repo_subnames[0], &sa, &ga) ;
+ if (n < 0)
+ {
+ strerr_warnfu6sys("list sub ", s6rc_repo_subnames[0], " of set ", set, " in repository ", repo) ;
+ goto err ;
+ }
+ if (n)
+ {
+ DIR *dir ;
+ char const *masked[n] ;
+ char maskedstorage[sa.len] ;
+ memcpy(maskedstorage, sa.s, sa.len) ;
+ for (size_t i = 0 ; i < n ; i++) masked[i] = maskedstorage + genalloc_s(size_t, &ga)[i] ;
+ sa.len = 0 ;
+ genalloc_setlen(size_t, &ga, 0) ;
+ qsort(masked, n, sizeof(char const *), &str_cmp) ;
+
+ if (mkdir(dst, 02755) == -1)
+ {
+ strerr_warnfu2sys("mkdir ", dst) ;
+ goto errga ;
+ }
+ dir = opendir(src) ;
+ if (!dir)
+ {
+ strerr_warnfu2sys("opendir ", src) ;
+ goto err0 ;
+ }
+ for (;;)
+ {
+ int m, i = 0 ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+
+ sa.len = 0 ;
+ genalloc_setlen(size_t, &ga, 0) ;
+ m = s6rc_repo_listcontents(repo, d->d_name, &sa, &ga) ;
+ if (m < 0)
+ {
+ dir_close(dir) ;
+ goto err0 ;
+ }
+
+ for (; i < m ; i++)
+ {
+ char const *x = sa.s + genalloc_s(size_t, &ga)[i] ;
+ if (bsearch(x, masked, n, sizeof(char const *), &str_bcmp))
+ {
+ if (verbosity >= 3)
+ strerr_warni4x("skipping bundle ", d->d_name, " containing e.g. masked service ", x) ;
+ break ;
+ }
+ }
+ if (i < m) continue ;
+
+ size_t len = strlen(d->d_name) ;
+ char target[16 + len] ;
+ char fn[repolen + setlen + 18 + len] ;
+ memcpy(target, "../../.bundles/", 15) ;
+ memcpy(target + 15, d->d_name, len+1) ;
+ memcpy(fn, dst, repolen + setlen + 16) ;
+ fn[repolen + setlen + 16] = '/' ;
+ memcpy(fn + repolen + setlen + 17, d->d_name, len+1) ;
+ if (symlink(target, fn) == -1)
+ {
+ strerr_warnfu4sys("make a symlink named ", fn, " pointing to ", target) ;
+ dir_close(dir) ;
+ goto err0 ;
+ }
+ }
+ dir_close(dir) ;
+ if (errno)
+ {
+ strerr_warnfu2sys("readdir ", src) ;
+ goto err0 ;
+ }
+ }
+ else if (!hiercopy_tmp(src, dst, &sa))
+ {
+ strerr_warnfu4sys("recursively copy ", src, " to ", dst) ;
+ goto err0 ;
+ }
+
+ stralloc_free(&sa) ;
+ return 1 ;
+
+ err0:
+ {
+ int e = errno ;
+ rm_rf_tmp(dst, &sa) ;
+ errno = e ;
+ }
+ errga:
+ genalloc_free(size_t, &ga) ;
+ err:
+ stralloc_free(&sa) ;
+ return 0 ;
+}
diff --git a/src/repo/s6rc_repo_makestores.c b/src/repo/s6rc_repo_makestores.c
new file mode 100644
index 0000000..f61f94c
--- /dev/null
+++ b/src/repo/s6rc_repo_makestores.c
@@ -0,0 +1,65 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/uint16.h>
+#include <skalibs/posixplz.h>
+#include <skalibs/stat.h>
+#include <skalibs/strerr.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_makestores (char const *repo, char const *const *stores, uint16_t n, char *sold)
+{
+ size_t repolen = strlen(repo) ;
+ mode_t m = umask(0) ;
+ char slink[repolen + 8] ;
+ char sdir[repolen + 21] ;
+
+ memcpy(slink, repo, repolen) ;
+ memcpy(slink + repolen, "/stores", 8) ;
+ memcpy(sdir, slink, repolen + 1) ;
+ memcpy(sdir + repolen + 1, ".stores:XXXXXX", 15) ;
+
+ if (!mkdtemp(sdir))
+ {
+ umask(m) ;
+ strerr_warnfu2sys("mkdtemp ", sdir) ;
+ return 0 ;
+ }
+ umask(m) ;
+ sdir[repolen + 15] = '/' ;
+ sdir[repolen + 20] = 0 ;
+ for (uint16_t i = 0 ; i < n ; i++)
+ {
+ uint160_xfmt(sdir + repolen + 16, i, 4) ;
+ if (symlink(stores[i], sdir) == -1)
+ {
+ strerr_warnfu4sys("symlink ", stores[i], " to ", sdir) ;
+ sdir[repolen + 15] = 0 ;
+ goto err ;
+ }
+ }
+ sdir[repolen + 15] = 0 ;
+ if (chmod(sdir, 02755) == -1)
+ {
+ strerr_warnfu2sys("chmod ", sdir) ;
+ goto err ;
+ }
+ if (!atomic_symlink4(sdir + repolen + 1, slink, sold, 15))
+ {
+ strerr_warnfu4sys("atomically symlink ", sdir + repolen + 1, " to ", slink) ;
+ goto err ;
+ }
+ return 1 ;
+
+ err:
+ rm_rf(sdir) ;
+ return 0 ;
+}
diff --git a/src/repo/s6rc_repo_makesvlist.c b/src/repo/s6rc_repo_makesvlist.c
new file mode 100644
index 0000000..99a174e
--- /dev/null
+++ b/src/repo/s6rc_repo_makesvlist.c
@@ -0,0 +1,66 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/direntry.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+
+#include <s6-rc/repo.h>
+
+static inline int s6rc_repo_addsub (char const *sub, uint8_t i, stralloc *sa, genalloc *ga)
+{
+ DIR *dir = opendir(sub) ;
+ if (!dir) { strerr_warnfu2sys("opendir ", sub) ; return 0 ; }
+ for (;;)
+ {
+ s6rc_repo_sv sv = { .pos = sa->len, .sub = i } ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+
+ if (!stralloc_cats(sa, d->d_name) || !stralloc_0(sa)
+ || !genalloc_append(s6rc_repo_sv, ga, &sv)) goto nomem ;
+ }
+ dir_close(dir) ;
+ if (errno) { strerr_warnfu2sys("readdir ", sub) ; return 0 ; }
+ return 1 ;
+
+ nomem:
+ strerr_warnfu1sys("stralloc_catb") ;
+ return 0 ;
+}
+
+int s6rc_repo_makesvlist (char const *repo, char const *set, stralloc *sa, genalloc *ga, uint32_t *tab)
+{
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ size_t sabase = sa->len ;
+ size_t gabase = genalloc_len(s6rc_repo_sv, ga) ;
+ int sawasnull = !sa->s ;
+ int gawasnull = !genalloc_s(s6rc_repo_sv, ga) ;
+ char subfn[repolen + setlen + 17] ;
+
+ memcpy(subfn, repo, repolen) ;
+ memcpy(subfn + repolen, "/sources/", 9) ;
+ memcpy(subfn + repolen + 9, set, setlen) ;
+ subfn[repolen + 9 + setlen] = '/' ;
+ for (uint8_t i = 0 ; i < 4 ; i++)
+ {
+ if (tab) tab[i] = genalloc_len(s6rc_repo_sv, ga) ;
+ memcpy(subfn + repolen + setlen + 10, s6rc_repo_subnames[i], 7) ;
+ if (!s6rc_repo_addsub(subfn, i, sa, ga)) goto err ;
+ }
+ return 1 ;
+
+ err:
+ if (sawasnull) stralloc_free(sa) ; else sa->len = sabase ;
+ if (gawasnull) genalloc_free(s6rc_repo_sv, ga) ; else genalloc_setlen(s6rc_repo_sv, ga, gabase) ;
+ return 0 ;
+}
diff --git a/src/repo/s6rc_repo_makesvlist_byname.c b/src/repo/s6rc_repo_makesvlist_byname.c
new file mode 100644
index 0000000..c017be6
--- /dev/null
+++ b/src/repo/s6rc_repo_makesvlist_byname.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <skalibs/posixplz.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_makesvlist_byname (char const *repo, char const *set, stralloc *sa, genalloc *ga)
+{
+ if (!s6rc_repo_makesvlist(repo, set, sa, ga, 0)) return 0 ;
+ qsortr(genalloc_s(s6rc_repo_sv, ga), genalloc_len(s6rc_repo_sv, ga), sizeof(s6rc_repo_sv), &s6rc_repo_sv_cmpr, sa->s) ;
+ return 1 ;
+}
diff --git a/src/repo/s6rc_repo_moveservices.c b/src/repo/s6rc_repo_moveservices.c
new file mode 100644
index 0000000..3cc3b92
--- /dev/null
+++ b/src/repo/s6rc_repo_moveservices.c
@@ -0,0 +1,60 @@
+/* ISC license. */
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <skalibs/strerr.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_moveservices (char const *repo, char const *set, s6rc_repo_sv const *services, uint32_t n, uint8_t newsub, char const *storage, unsigned int verbosity)
+{
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ size_t maxserviceslen = 0 ;
+ for (uint32_t i = 0 ; i < n ; i++)
+ {
+ size_t len = strlen(storage + services[i].pos) ;
+ if (len > maxserviceslen) maxserviceslen = len ;
+ }
+ char oldfn[repolen + setlen + maxserviceslen + 18] ;
+ char newfn[repolen + setlen + maxserviceslen + 18] ;
+ memcpy(oldfn, repo, repolen) ;
+ memcpy(oldfn + repolen, "/sources/", 9) ;
+ memcpy(oldfn + repolen + 9, set, setlen) ;
+ oldfn[repolen + 9 + setlen] = '/' ;
+ oldfn[repolen + 16 + setlen] = '/' ;
+ memcpy(newfn, oldfn, repolen + 10 + setlen) ;
+ memcpy(newfn + repolen + 10 + setlen, s6rc_repo_subnames[newsub], 6) ;
+ newfn[repolen + 16 + setlen] = '/' ;
+
+ for (uint32_t i = 0 ; i < n ; i++)
+ {
+ size_t len = strlen(storage + services[i].pos) ;
+ memcpy(oldfn + repolen + 10, s6rc_repo_subnames[services[i].sub], 6) ;
+ memcpy(oldfn + repolen + setlen + 17, storage + services[i].pos, len + 1) ;
+ memcpy(newfn + repolen + setlen + 17, storage + services[i].pos, len + 1) ;
+ if (access(newfn, F_OK) == 0)
+ {
+ if (verbosity >= 2)
+ strerr_warni8x("service ", storage + services[i].pos, " already existed in subset ", s6rc_repo_subnames[newsub], " of set ", set, " in repository ", repo) ;
+ }
+ else if (errno != ENOENT)
+ {
+ strerr_warnfu2sys("access ", newfn) ;
+ return 0 ;
+ }
+ if (rename(oldfn, newfn) == -1)
+ {
+ strerr_warnfu4sys("rename ", oldfn, " to ", newfn) ;
+ return 0 ;
+ }
+ if (verbosity >= 4)
+ {
+ strerr_warnt10x("repository ", repo, ", set ", set, ", from subset ", s6rc_repo_subnames[services[i].sub], " to subset ", s6rc_repo_subnames[newsub], ": successfully moved service ", storage + services[i].pos) ;
+ }
+ }
+ return 1 ;
+}
diff --git a/src/repo/s6rc_repo_nlto0.c b/src/repo/s6rc_repo_nlto0.c
new file mode 100644
index 0000000..9076bd3
--- /dev/null
+++ b/src/repo/s6rc_repo_nlto0.c
@@ -0,0 +1,33 @@
+/* ISC license. */
+
+#include <stdint.h>
+
+#include <skalibs/genalloc.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_nlto0 (char *s, size_t start, size_t len, genalloc *indices)
+{
+ size_t origlen = genalloc_len(size_t, indices) ;
+ size_t pos = start ;
+ int wasnull = !indices->s ;
+ int inword = 0 ;
+
+ for (size_t i = start ; i < len ; i++)
+ {
+ if (s[i] == '\n')
+ {
+ s[i] = 0 ;
+ if (!genalloc_append(size_t, indices, &pos)) goto err ;
+ inword = 0 ;
+ }
+ else if (!inword) { pos = i ; inword = 1 ; }
+ }
+ if (inword) goto err ;
+ return 1 ;
+
+ err:
+ if (wasnull) genalloc_free(size_t, indices) ;
+ else genalloc_setlen(size_t, indices, origlen) ;
+ return 0 ;
+}
diff --git a/src/repo/s6rc_repo_setcompile.c b/src/repo/s6rc_repo_setcompile.c
new file mode 100644
index 0000000..5da136a
--- /dev/null
+++ b/src/repo/s6rc_repo_setcompile.c
@@ -0,0 +1,33 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+
+#include <skalibs/djbunix.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_setcompile (char const *repo, char const *set, char const *defbundle, char *oldc, unsigned int verbosity, char const *fdhuser)
+{
+ static char const *const subs[4] = { "bundle", s6rc_repo_subnames[1], s6rc_repo_subnames[2], s6rc_repo_subnames[3] } ;
+ int r ;
+ if (!s6rc_repo_makesetbundles(repo, set, verbosity)) return -1 ;
+ if (!s6rc_repo_makedefbundle(repo, set, defbundle)) return -1 ;
+ r = s6rc_repo_compile(repo, set, subs, 4, oldc, verbosity, fdhuser) ;
+
+ {
+ int e = errno ;
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ size_t sublen = strlen(subs[0]) ;
+ char fn[repolen + setlen + 11 + sublen] ;
+ memcpy(fn, repo, repolen) ;
+ memcpy(fn + repolen, "/sources/", 9) ;
+ memcpy(fn + repolen + 9, set, setlen) ;
+ fn[repolen + 9 + setlen] = '/' ;
+ memcpy(fn + repolen + setlen + 10, subs[0], sublen + 1) ;
+ rm_rf(fn) ;
+ errno = e ;
+ }
+ return r ;
+}
diff --git a/src/repo/s6rc_repo_setuptodate.c b/src/repo/s6rc_repo_setuptodate.c
new file mode 100644
index 0000000..3dded26
--- /dev/null
+++ b/src/repo/s6rc_repo_setuptodate.c
@@ -0,0 +1,44 @@
+/* ISC license. */
+
+#include <skalibs/bsdsnowflake.h>
+
+#include <string.h>
+#include <errno.h>
+
+#include <skalibs/stat.h>
+#include <skalibs/strerr.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_setuptodate (char const *repo, char const *set)
+{
+ struct stat stsource ;
+ struct stat stcompiled ;
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ char srcfn[repolen + 17 + setlen] ;
+ char dstfn[repolen + 11 + setlen] ;
+ memcpy(srcfn, repo, repolen) ;
+ memcpy(srcfn + repolen, "/sources/", 9) ;
+ memcpy(srcfn + repolen + 9, set, setlen) ;
+ memcpy(srcfn + repolen + 9 + setlen, "/.stamp", 8) ;
+ memcpy(dstfn, repo, repolen) ;
+ memcpy(dstfn + repolen, "/compiled/", 10) ;
+ memcpy(dstfn + repolen + 10, set, setlen + 1) ;
+ if (stat(srcfn, &stsource) == -1)
+ {
+ strerr_warnfu2sys("stat ", srcfn) ;
+ return -1 ;
+ }
+ if (stat(dstfn, &stcompiled) == -1)
+ {
+ if (errno == ENOENT) return 0 ;
+ strerr_warnfu2sys("stat ", dstfn) ;
+ return -1 ;
+ }
+ return
+ stsource.st_atim.tv_sec < stcompiled.st_atim.tv_sec ? 1 :
+ stsource.st_atim.tv_sec > stcompiled.st_atim.tv_sec ? 0 :
+ stsource.st_atim.tv_nsec < stcompiled.st_atim.tv_nsec ;
+}
+
diff --git a/src/repo/s6rc_repo_subnames.c b/src/repo/s6rc_repo_subnames.c
new file mode 100644
index 0000000..2653f01
--- /dev/null
+++ b/src/repo/s6rc_repo_subnames.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <s6-rc/repo.h>
+
+char const s6rc_repo_subnames[4][7] = { "masked", "usable", "active", "always" } ;
diff --git a/src/repo/s6rc_repo_sv_bcmpr.c b/src/repo/s6rc_repo_sv_bcmpr.c
new file mode 100644
index 0000000..bcc4d80
--- /dev/null
+++ b/src/repo/s6rc_repo_sv_bcmpr.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <string.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_sv_bcmpr (void const *a, void const *b, void *aux)
+{
+ char const *key = a ;
+ s6rc_repo_sv const *elem = b ;
+ char const *s = aux ;
+ return strcmp(key, s + elem->pos) ;
+}
diff --git a/src/repo/s6rc_repo_sv_cmpr.c b/src/repo/s6rc_repo_sv_cmpr.c
new file mode 100644
index 0000000..80b07c9
--- /dev/null
+++ b/src/repo/s6rc_repo_sv_cmpr.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_sv_cmpr (void const *a, void const *b, void *aux)
+{
+ s6rc_repo_sv const *aa = a ;
+ s6rc_repo_sv const *bb = b ;
+ char const *s = aux ;
+ return strcmp(s + aa->pos, s + bb->pos) ;
+}
diff --git a/src/repo/s6rc_repo_sync.c b/src/repo/s6rc_repo_sync.c
new file mode 100644
index 0000000..6a17876
--- /dev/null
+++ b/src/repo/s6rc_repo_sync.c
@@ -0,0 +1,230 @@
+/* ISC license. */
+
+#include <skalibs/nonposix.h> /* mkdtemp */
+
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/uint16.h>
+#include <skalibs/stat.h>
+#include <skalibs/direntry.h>
+#include <skalibs/posixplz.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/unix-transactional.h>
+
+#include <s6-rc/repo.h>
+
+static inline void cleanup (char const *ato, char const *bun)
+{
+ int e = errno ;
+ rm_rf(ato) ;
+ rm_rf(bun) ;
+ errno = e ;
+}
+
+int s6rc_repo_sync (char const *repo, unsigned int verbosity, char const *fdhuser)
+{
+ size_t repolen = strlen(repo) ;
+ mode_t m ;
+ char store[repolen + 13] ;
+ char ato[repolen + 26] ;
+ char bun[repolen + 26] ;
+
+ memcpy(store, repo, repolen) ;
+ memcpy(store + repolen, "/stores/", 8) ;
+ store[repolen + 12] = 0 ;
+
+
+ /* Fill new .atomics/ and .bundles/ with symlinks to real stores */
+
+ memcpy(ato, repo, repolen) ;
+ memcpy(ato + repolen, "/sources/..atomics:XXXXXX", 26) ;
+ memcpy(bun, ato, repolen + 26) ;
+ memcpy(bun + repolen + 11, "bundle", 6) ;
+ m = umask(0) ;
+ if (!mkdtemp(ato))
+ {
+ strerr_warnfu2sys("mkdtemp ", ato) ;
+ umask(m) ;
+ return -1 ;
+ }
+ if (chmod(ato, 02755) == -1)
+ {
+ strerr_warnfu2sys("chmod ", ato) ;
+ umask(m) ;
+ return -1 ;
+ }
+
+ if (!mkdtemp(bun))
+ {
+ strerr_warnfu2sys("mkdtemp ", bun) ;
+ rmdir(ato) ;
+ umask(m) ;
+ return -1 ;
+ }
+ umask(m) ;
+ if (chmod(bun, 02755) == -1)
+ {
+ strerr_warnfu2sys("chmod ", bun) ;
+ return -1 ;
+ }
+
+ for (uint16_t istore = 0 ;; istore++)
+ {
+ DIR *dir ;
+ uint160_xfmt(store + repolen + 8, istore, 4) ;
+ dir = opendir(store) ;
+ if (!dir)
+ {
+ if (errno == ENOENT) break ;
+ strerr_warnfu2sys("opendir ", store) ;
+ goto err ;
+ }
+ for (;;)
+ {
+ size_t len ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ len = strlen(d->d_name) ;
+ {
+ char const *x ;
+ char dst[repolen + 28 + len] ;
+ char src[22 + len] ;
+ memcpy(src, "../../../stores/", 16) ;
+ memcpy(src + 16, store + repolen + 8, 4) ;
+ src[20] = '/' ;
+ memcpy(src + 21, d->d_name, len+1) ;
+ switch (s6rc_repo_type_check(src))
+ {
+ case 1 :
+ case 2 : x = ato ; break ;
+ case 3 : x = bun ; break ;
+ case 0 :
+ strerr_warnf2x("invalid service type for ", src) ;
+ dir_close(dir) ;
+ goto err ;
+ default :
+ strerr_warnfu2sys("check service type of ", src) ;
+ dir_close(dir) ;
+ goto err ;
+ }
+ memcpy(dst, x, repolen + 26) ;
+ dst[repolen + 26] = '/' ;
+ memcpy(dst + repolen + 27, d->d_name, len+1) ;
+ if (symlink(src, dst) == -1)
+ {
+ if (errno != EEXIST)
+ {
+ strerr_warnfu4sys("symlink ", src, " to ", dst) ;
+ dir_close(dir) ;
+ goto err ;
+ }
+ if (verbosity)
+ {
+ if (!sareadlink(&satmp, dst) || !stralloc_0(&satmp))
+ {
+ strerr_warnwu2sys("readlink ", dst) ;
+ errno = EEXIST ;
+ strerr_warnwu4sys("symlink ", src, " to ", dst) ;
+ }
+ else
+ strerr_warnwu6x("symlink ", src, " to ", dst, ": already provided by ", satmp.s) ;
+ satmp.len = 0 ;
+ }
+ }
+ }
+ }
+ dir_close(dir) ;
+ if (errno)
+ {
+ strerr_warnfu2sys("readdir ", store) ;
+ goto err ;
+ }
+ }
+
+
+ /* Compile the reference db - also checks consistency of new stores */
+
+ {
+ char const *subs[2] = { ato + repolen + 9, bun + repolen + 9 } ;
+ char oldc[repolen + 49] ;
+ int r = s6rc_repo_compile(repo, ".ref", subs, 2, oldc, verbosity, fdhuser) ;
+ if (r < 0) goto err ;
+ if (!r) { cleanup(ato, bun) ; return 0 ; }
+ if (r == 2) rm_rf(oldc) ;
+ }
+
+
+ /* Switch, delete links to old stores */
+
+ {
+ char fn[repolen + 18] ;
+ char oldato[17] ;
+ char oldbun[17] ;
+ memcpy(fn, repo, repolen) ;
+ memcpy(fn + repolen, "/sources/.atomics", 18) ;
+ if (!atomic_symlink4(ato + repolen + 9, fn, oldato, 17)) goto err ;
+ memcpy(fn + repolen + 10, "bundle", 6) ;
+ if (!atomic_symlink4(bun + repolen + 9, fn, oldbun, 17))
+ {
+ strerr_warnfu4sys("symlink ", bun + repolen + 9, " to ", fn) ;
+ memcpy(fn + repolen + 10, "atomic", 6) ;
+ if (!atomic_symlink4(oldato, fn, 0, 0))
+ {
+ strerr_warnfu6sys("symlink ", oldato, " back to ", fn, " - weird race, manually check ", repo) ;
+ goto err ;
+ }
+ goto err ;
+ }
+ memcpy(ato + repolen + 9, oldato, 16) ;
+ memcpy(bun + repolen + 9, oldbun, 16) ;
+ cleanup(ato, bun) ;
+ }
+
+
+ /* Update the existing sets */
+
+ ato[repolen + 8] = 0 ;
+ {
+ stralloc sa = STRALLOC_ZERO ;
+ genalloc ga = GENALLOC_ZERO ; /* size_t */
+ DIR *dir = opendir(ato) ;
+ if (!dir)
+ {
+ strerr_warnfu2sys("opendir ", ato) ;
+ return -1 ;
+ }
+ for (;;)
+ {
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ if (!s6rc_repo_syncset_tmp(repo, d->d_name, &sa, &ga, verbosity)) break ;
+ }
+ dir_close(dir) ;
+ genalloc_free(size_t, &ga) ;
+ stralloc_free(&sa) ;
+ if (errno)
+ {
+ strerr_warnfu2sys("readdir ", ato) ;
+ return -1 ;
+ }
+ }
+
+ return 1 ;
+
+ err:
+ cleanup(ato, bun) ;
+ return -1 ;
+}
diff --git a/src/repo/s6rc_repo_syncset.c b/src/repo/s6rc_repo_syncset.c
new file mode 100644
index 0000000..929d9c4
--- /dev/null
+++ b/src/repo/s6rc_repo_syncset.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_syncset (char const *repo, char const *set, unsigned int verbosity)
+{
+ stralloc sa = STRALLOC_ZERO ;
+ genalloc ga = GENALLOC_ZERO ; /* size_t */
+ int r = s6rc_repo_syncset_tmp(repo, set, &sa, &ga, verbosity) ;
+ genalloc_free(&size_t, &ga) ;
+ stralloc_free(&sa) ;
+ return r ;
+}
diff --git a/src/repo/s6rc_repo_syncset_tmp.c b/src/repo/s6rc_repo_syncset_tmp.c
new file mode 100644
index 0000000..44e5b52
--- /dev/null
+++ b/src/repo/s6rc_repo_syncset_tmp.c
@@ -0,0 +1,101 @@
+/* ISC license. */
+
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <skalibs/direntry.h>
+#include <skalibs/posixplz.h>
+#include <skalibs/strerr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+
+#include <s6-rc/repo.h>
+
+static int unlink_stales_in_sub (char const *repo, size_t repolen, char const *set, char const *sub, uint32_t where, stralloc *sa, genalloc *ga, unsigned int verbosity)
+{
+ size_t setlen = strlen(set) ;
+ size_t sublen = strlen(sub) ;
+ size_t subfnlen = repolen + setlen + sublen + 10 ;
+ char subfn[subfnlen + 1] ;
+ memcpy(subfn, repo, repolen) ;
+ memcpy(subfn + repolen, "/sources/", 9) ;
+ memcpy(subfn + repolen + 9, set, setlen) ;
+ subfn[repolen + 9 + setlen] = '/' ;
+ memcpy(subfn + repolen + 10 + setlen, sub, sublen + 1) ;
+ DIR *dir = opendir(subfn) ;
+ if (!dir)
+ {
+ strerr_warnfu2sys("opendir ", subfn) ;
+ return 0 ;
+ }
+ for (;;)
+ {
+ size_t len ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ len = strlen(d->d_name) ;
+ char fn[subfnlen + len + 2] ;
+ memcpy(fn, subfn, sublen) ;
+ fn[sublen] = '/' ;
+ memcpy(fn + sublen + 1, d->d_name, len+1) ;
+ if (access(fn, F_OK) == -1)
+ {
+ if (errno != ENOENT)
+ {
+ strerr_warnfu2sys("access ", fn) ;
+ goto err ;
+ }
+ unlink_void(fn) ;
+ if (verbosity >= 3)
+ strerr_warni6x("service ", d->d_name, " does not exist anymore, removed from sub ", sub, " of set ", set) ;
+ }
+ else
+ {
+ if (!genalloc_append(size_t, ga, &sa->len)) goto err ;
+ if (!stralloc_catb(sa, d->d_name, len+1)) goto err ;
+ }
+ }
+ if (errno)
+ {
+ strerr_warnfu2sys("readdir ", subfn) ;
+ goto err ;
+ }
+
+ dir_close(dir) ;
+ return 1 ;
+
+ err:
+ dir_close(dir) ;
+ return 0 ;
+}
+
+int s6rc_repo_syncset_tmp (char const *repo, char const *set, stralloc *sa, genalloc *ga, unsigned int verbosity)
+{
+ size_t repolen = strlen(repo) ;
+ size_t sabase = sa->len ;
+ size_t gabase = genalloc_len(size_t, ga) ;
+
+ for (unsigned int i = 0 ; i < 4 ; i++)
+ if (unlink_stales_in_sub(repo, repolen, set, s6rc_repo_subnames[i], i, sa, ga, verbosity)) goto err ;
+
+ {
+ uint32_t n = genalloc_len(size_t, ga) ;
+ char const *stillhere[n + !n] ;
+ for (uint32_t i = 0 ; i < n ; i++) stillhere[i] = sa->s + genalloc_s(size_t, ga)[i] ;
+ if (!s6rc_repo_fillset(repo, set, stillhere, n)) goto err ;
+ }
+
+ sa->len = sabase ;
+ genalloc_setlen(size_t, ga, gabase) ;
+ return 1 ;
+
+ err:
+ sa->len = sabase ;
+ genalloc_setlen(size_t, ga, gabase) ;
+ return 0 ;
+}
diff --git a/src/repo/s6rc_repo_touch.c b/src/repo/s6rc_repo_touch.c
new file mode 100644
index 0000000..069ed78
--- /dev/null
+++ b/src/repo/s6rc_repo_touch.c
@@ -0,0 +1,45 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+
+#include <skalibs/direntry.h>
+#include <skalibs/strerr.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_touch (char const *repo)
+{
+ DIR *dir ;
+ size_t repolen = strlen(repo) ;
+ char sources[repolen + 9] ;
+ memcpy(sources, repo, repolen) ;
+ memcpy(sources + repolen, "/sources", 9) ;
+
+ dir = opendir(sources) ;
+ if (!dir)
+ {
+ strerr_warnfu2sys("opendir ", sources) ;
+ return 0 ;
+ }
+ for (;;)
+ {
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ if (!s6rc_repo_touchset(repo, d->d_name))
+ {
+ dir_close(dir) ;
+ return 0 ;
+ }
+ }
+ dir_close(dir) ;
+ if (errno)
+ {
+ strerr_warnfu2sys("readdir ", sources) ;
+ return 0 ;
+ }
+ return 1 ;
+}
diff --git a/src/repo/s6rc_repo_touchset.c b/src/repo/s6rc_repo_touchset.c
new file mode 100644
index 0000000..684bfc4
--- /dev/null
+++ b/src/repo/s6rc_repo_touchset.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <string.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/strerr.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_touchset (char const *repo, char const *set)
+{
+ size_t repolen = strlen(repo) ;
+ size_t setlen = strlen(set) ;
+ char fn[repolen + 17 + setlen] ;
+ memcpy(fn, repo, repolen) ;
+ memcpy(fn + repolen, "/sources/", 9) ;
+ memcpy(fn + repolen + 9, set, setlen) ;
+ memcpy(fn + repolen + 9 + setlen, "/.stamp", 8) ;
+ if (!touch(fn))
+ {
+ strerr_warnfu2sys("touch ", fn) ;
+ return 0 ;
+ }
+ return 1 ;
+}
diff --git a/src/repo/s6rc_repo_type_check.c b/src/repo/s6rc_repo_type_check.c
new file mode 100644
index 0000000..8793819
--- /dev/null
+++ b/src/repo/s6rc_repo_type_check.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-transactional.h>
+
+#include <s6-rc/repo.h>
+
+int s6rc_repo_type_check (char const *src)
+{
+ ssize_t r ;
+ char type[8] ;
+ size_t srclen = strlen(src) ;
+ char fn[srclen + 6] ;
+ memcpy(fn, src, srclen) ;
+ memcpy(fn + srclen, "/type", 6) ;
+ r = openreadnclose(fn, type, 8) ;
+ if (r == -1) return -1 ;
+ if (r < 6) return 0 ;
+ if (type[r-1] == '\n') r-- ;
+ if (r == 8) return 0 ;
+ type[r++] = 0 ;
+ return
+ !strcmp(type, "longrun") ? 1 :
+ !strcmp(type, "oneshot") ? 2 :
+ !strcmp(type, "bundle") ? 3 : 0 ;
+}