From 4b31caa9cdaef67c70bc7fb43963ba68b7bf5bb1 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Thu, 4 Jun 2015 20:48:10 +0000 Subject: Initial commit --- .gitignore | 5 + AUTHORS | 6 + COPYING | 13 + INSTALL | 148 +++++ Makefile | 131 ++++ README | 30 + README.macosx | 4 + README.solaris | 12 + configure | 409 ++++++++++++ doc/s6-rc.html | 163 +++++ package/deps-build | 3 + package/deps.mak | 30 + package/info | 4 + package/modes | 5 + package/targets.mak | 25 + patch-for-solaris | 21 + src/include/s6-rc/s6rc-constants.h | 14 + src/include/s6-rc/s6rc-db.h | 69 ++ src/include/s6-rc/s6rc-utils.h | 10 + src/include/s6-rc/s6rc.h | 10 + src/libs6rc/deps-lib/s6rc | 6 + src/libs6rc/s6rc_db_check_depcycles.c | 50 ++ src/libs6rc/s6rc_db_check_revdeps.c | 30 + src/libs6rc/s6rc_db_read.c | 176 +++++ src/libs6rc/s6rc_db_read_sizes.c | 53 ++ src/libs6rc/s6rc_db_read_uint32.c | 14 + src/libs6rc/s6rc_graph_closure.c | 39 ++ src/s6-rc/deps-exe/s6-rc | 3 + src/s6-rc/deps-exe/s6-rc-compile | 3 + src/s6-rc/deps-exe/s6-rc-db | 2 + src/s6-rc/deps-exe/s6-rc-dryrun | 2 + src/s6-rc/deps-exe/s6-rc-init | 4 + src/s6-rc/s6-rc-compile.c | 1163 +++++++++++++++++++++++++++++++++ src/s6-rc/s6-rc-compile.h | 21 + src/s6-rc/s6-rc-db.c | 484 ++++++++++++++ src/s6-rc/s6-rc-dryrun.c | 46 ++ src/s6-rc/s6-rc-init.c | 282 ++++++++ src/s6-rc/s6-rc.c | 545 +++++++++++++++ tools/gen-deps.sh | 83 +++ tools/install.sh | 64 ++ 40 files changed, 4182 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 README create mode 100644 README.macosx create mode 100644 README.solaris create mode 100755 configure create mode 100644 doc/s6-rc.html create mode 100644 package/deps-build create mode 100644 package/deps.mak create mode 100644 package/info create mode 100644 package/modes create mode 100644 package/targets.mak create mode 100755 patch-for-solaris create mode 100644 src/include/s6-rc/s6rc-constants.h create mode 100644 src/include/s6-rc/s6rc-db.h create mode 100644 src/include/s6-rc/s6rc-utils.h create mode 100644 src/include/s6-rc/s6rc.h create mode 100644 src/libs6rc/deps-lib/s6rc create mode 100644 src/libs6rc/s6rc_db_check_depcycles.c create mode 100644 src/libs6rc/s6rc_db_check_revdeps.c create mode 100644 src/libs6rc/s6rc_db_read.c create mode 100644 src/libs6rc/s6rc_db_read_sizes.c create mode 100644 src/libs6rc/s6rc_db_read_uint32.c create mode 100644 src/libs6rc/s6rc_graph_closure.c create mode 100644 src/s6-rc/deps-exe/s6-rc create mode 100644 src/s6-rc/deps-exe/s6-rc-compile create mode 100644 src/s6-rc/deps-exe/s6-rc-db create mode 100644 src/s6-rc/deps-exe/s6-rc-dryrun create mode 100644 src/s6-rc/deps-exe/s6-rc-init create mode 100644 src/s6-rc/s6-rc-compile.c create mode 100644 src/s6-rc/s6-rc-compile.h create mode 100644 src/s6-rc/s6-rc-db.c create mode 100644 src/s6-rc/s6-rc-dryrun.c create mode 100644 src/s6-rc/s6-rc-init.c create mode 100644 src/s6-rc/s6-rc.c create mode 100755 tools/gen-deps.sh create mode 100755 tools/install.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c6415e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.a +*.lo +*.so +*.so.* diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..0335ad0 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Main author: + Laurent Bercot + +Thanks to: + Avery Payne + Olivier Brunel diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..463bc49 --- /dev/null +++ b/COPYING @@ -0,0 +1,13 @@ +Copyright (c) 2015 Laurent Bercot + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..4d92439 --- /dev/null +++ b/INSTALL @@ -0,0 +1,148 @@ +Build Instructions +------------------ + +* Requirements + ------------ + + - A POSIX-compliant C development environment + - GNU make version 4.0 or later + - skalibs version 2.3.3.0 or later: http://skarnet.org/software/skalibs/ + - execline version 2.1.2.0 or later: http://skarnet.org/software/execline/ + - s6 version 2.1.3.0 or later: http://skarnet.org/software/s6/ + + This software will run on any operating system that implements +POSIX.1-2008, available at: + http://pubs.opengroup.org/onlinepubs/9699919799/ + + +* Standard usage + -------------- + + ./configure && make && sudo make install + + will work for most users. + It will install the binaries in /bin and the static libraries in +/usr/lib/s6-rc. + + You can strip the binaries and libraries of their extra symbols via +"make strip" before the "make install" phase. It will shave a few bytes +off them. + + +* Customization + ------------- + + You can customize paths via flags given to configure. + See ./configure --help for a list of all available configure options. + + +* Environment variables + --------------------- + + Controlling a build process via environment variables is a big and +dangerous hammer. You should try and pass flags to configure instead; +nevertheless, the standard environment variables are recognized. + + The value of the CROSS_COMPILE environment variable will prefix the +building tools' names. The --enable-cross option is preferred, see +"Cross-compilation" below. + + If the CC environment variable is set, its value will override compiler +detection by configure. + + The values of CFLAGS, CPPFLAGS and LDFLAGS will be appended to flags +auto-detected by configure. To entirely override the flags set by +configure, use make -e. + + The value of LDLIBS will be appended by make to command lines that link +an executable, even without the -e option. + + The Makefile supports the DESTDIR convention for staging. + + +* Shared libraries + ---------------- + + Software from skarnet.org is small enough that shared libraries are +generally not worth using. Static linking is simpler and incurs less +runtime overhead and less points of failure: so by default, shared +libraries are not built and binaries are linked against the static +versions of the skarnet.org libraries. Nevertheless, you can: + * build shared libraries: --enable-shared + * link binaries against shared libraries: --disable-allstatic + + +* Static binaries + --------------- + + By default, binaries are linked against static versions of all the +libraries they depend on, except for the libc. You can enforce +linking against the static libc with --enable-static-libc. + + (If you are using a GNU/Linux system, be aware that the GNU libc +behaves badly with static linking and produces huge executables, +which is why it is not the default. Other libcs are better suited +to static linking, for instance musl: http://musl-libc.org/) + + +* Cross-compilation + ----------------- + + skarnet.org packages centralize all the difficulty of +cross-compilation in one place: skalibs. Once you have +cross-compiled skalibs, the rest is easy. + + Use the --enable-cross=PREFIX option to configure, or simply +--enable-cross if your default toolchain is a cross-compiling +toolchain. And make sure to use the correct version of skalibs +for your target, and the correct sysdeps directory, making use +of the --with-include, --with-lib, --with-dynlib and --with-sysdeps +options as necessary. + + +* The slashpackage convention + --------------------------- + + The slashpackage convention (http://cr.yp.to/slashpackage.html) +is a package installation scheme that provides a few guarantees +over other conventions such as the FHS, for instance fixed +absolute pathnames. skarnet.org packages support it: use the +--enable-slashpackage option to configure, or +--enable-slashpackage=DIR for a prefixed DIR/package tree. +This option will activate slashpackage support during the build +and set slashpackage-compatible installation directories. +If $package_home is the home of the package, defined as +DIR/package/$category/$package-$version with the variables +read from the package/info file, then: + + --dynlibdir is set to $package_home/library.so + --bindir is set to $package_home/command + --sbindir is also set to $package_home/command (slashpackage +differentiates root-only binaries by their Unix rights, not their +location in the filesystem) + --libexecdir is also set to $package_home/command (slashpackage +does not need a specific directory for internal binaries) + --libdir is set to $package_home/library + --includedir is set to $package_home/include + + --prefix is pretty much ignored when you use --enable-slashpackage. +You should probably not use both --enable-slashpackage and --prefix. + + When using slashpackage, two additional Makefile targets are +available after "make install": + - "make update" changes the default version of the software to the +freshly installed one. (This is useful when you have several installed +versions of the same software, which slashpackage supports.) + - "make -L global-links" adds links from /command and /library.so to the +default version of the binaries and shared libraries. The "-L" option to +make is necessary because targets are symbolic links, and the default make +behaviour is to check the pointed file's timestamp and not the symlink's +timestamp. + + +* Out-of-tree builds + ------------------ + + skarnet.org packages do not support out-of-tree builds. They +are small, so it does not cost much to duplicate the entire +source tree if parallel builds are needed. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b861d25 --- /dev/null +++ b/Makefile @@ -0,0 +1,131 @@ +# +# This Makefile requires GNU make. +# +# Do not make changes here. +# Use the included .mak files. +# + +it: all + +CC = $(error Please use ./configure first) + +STATIC_LIBS := +SHARED_LIBS := +INTERNAL_LIBS := +EXTRA_TARGETS := + +-include config.mak +include package/targets.mak +include package/deps.mak + +version_m := $(basename $(version)) +version_M := $(basename $(version_m)) +version_l := $(basename $(version_M)) +CPPFLAGS_ALL := -iquote src/include-local -Isrc/include $(CPPFLAGS) +CFLAGS_ALL := $(CFLAGS) -pipe -Wall +CFLAGS_SHARED := -fPIC +LDFLAGS_ALL := $(LDFLAGS) +LDFLAGS_SHARED := -shared +LDLIBS_ALL := $(LDLIBS) +REALCC = $(CROSS_COMPILE)$(CC) +AR := $(CROSS_COMPILE)ar +RANLIB := $(CROSS_COMPILE)ranlib +STRIP := $(CROSS_COMPILE)strip +INSTALL := ./tools/install.sh + +ALL_BINS := $(LIBEXEC_TARGETS) $(BIN_TARGETS) $(SBIN_TARGETS) +ALL_LIBS := $(SHARED_LIBS) $(STATIC_LIBS) $(INTERNAL_LIBS) +ALL_INCLUDES := $(wildcard src/include/$(package)/*.h) + +all: $(ALL_LIBS) $(ALL_BINS) $(ALL_INCLUDES) + +clean: + @exec rm -f $(ALL_LIBS) $(ALL_BINS) $(wildcard src/*/*.o src/*/*.lo) $(EXTRA_TARGETS) + +distclean: clean + @exec rm -f config.mak src/include/${package}/config.h + +tgz: distclean + @. package/info && \ + rm -rf /tmp/$$package-$$version && \ + cp -a . /tmp/$$package-$$version && \ + cd /tmp && \ + tar -zpcv --owner=0 --group=0 --numeric-owner --exclude=.git* -f /tmp/$$package-$$version.tar.gz $$package-$$version && \ + exec rm -rf /tmp/$$package-$$version + +strip: $(ALL_LIBS) $(ALL_BINS) +ifneq ($(strip $(ALL_LIBS)),) + exec ${STRIP} -x -R .note -R .comment -R .note.GNU-stack $(ALL_LIBS) +endif +ifneq ($(strip $(ALL_BINS)),) + exec ${STRIP} -R .note -R .comment -R .note.GNU-stack $(ALL_BINS) +endif + +install: install-dynlib install-libexec install-bin install-sbin install-lib install-include +install-dynlib: $(SHARED_LIBS:lib%.so=$(DESTDIR)$(dynlibdir)/lib%.so) +install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%) +install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%) +install-sbin: $(SBIN_TARGETS:%=$(DESTDIR)$(sbindir)/%) +install-lib: $(STATIC_LIBS:lib%.a=$(DESTDIR)$(libdir)/lib%.a) +install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h) +install-data: $(ALL_DATA:src/etc/%=$(DESTDIR)$(datadir)/%) + +ifneq ($(exthome),) + +update: + exec $(INSTALL) -l $(notdir $(home)) $(DESTDIR)$(exthome) + +global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so=$(DESTDIR)$(sproot)/library.so/lib%.so) $(BIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) $(SBIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) + +$(DESTDIR)$(sproot)/command/%: $(DESTDIR)$(home)/command/% + exec $(INSTALL) -D -l ..$(subst $(sproot),,$(exthome))/command/$( + + Please use the mailing-list for +questions about s6-rc. diff --git a/README.macosx b/README.macosx new file mode 100644 index 0000000..d71a096 --- /dev/null +++ b/README.macosx @@ -0,0 +1,4 @@ + + This package will compile and run on Darwin (MacOS X), but the building of +shared libraries is not supported. + Make sure you use the --disable-shared option to configure. diff --git a/README.solaris b/README.solaris new file mode 100644 index 0000000..91a5b26 --- /dev/null +++ b/README.solaris @@ -0,0 +1,12 @@ + + This package assumes the existence of a POSIX shell in /bin/sh. + On Solaris, /bin/sh is not POSIX. Most versions of Solaris provide +a POSIX shell in /usr/xpg4/bin/sh. + + To compile this package on Solaris, you will need to run + + ./patch-for-solaris + + before you run ./configure. This script will change the #! invocation +of the configure script and various tools so that a POSIX shell is used +for the compilation process. diff --git a/configure b/configure new file mode 100755 index 0000000..5244a37 --- /dev/null +++ b/configure @@ -0,0 +1,409 @@ +#!/bin/sh + +. package/info + +usage () { +cat </dev/null 2>&1 && { echo "$1" ; return 0 ; } +$1 +EOF + echo "$1" | sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/" -e "s#^'\([-[:alnum:]_,./:]*\)=\(.*\)\$#\1='\2#" -e "s|\*/|* /|g" +} + +fail () { + echo "$*" + exit 1 +} + +fnmatch () { + eval "case \"\$2\" in $1) return 0 ;; *) return 1 ;; esac" +} + +cmdexists () { + type "$1" >/dev/null 2>&1 +} + +trycc () { + test -z "$CC_AUTO" && cmdexists "$1" && CC_AUTO=$1 +} + +stripdir () { + while eval "fnmatch '*/' \"\${$1}\"" ; do + eval "$1=\${$1%/}" + done +} + +tryflag () { + echo "checking whether compiler accepts $2 ..." + echo "typedef int x;" > "$tmpc" + if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO "$2" -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then + echo " ... yes" + eval "$1=\"\${$1} \$2\"" + eval "$1=\${$1# }" + return 0 + else + echo " ... no" + return 1 + fi +} + +tryldflag () { + echo "checking whether linker accepts $2 ..." + echo "typedef int x;" > "$tmpc" + if $CC_AUTO $CFLAGS_AUTO $LDFLAGS_AUTO -nostdlib "$2" -o /dev/null "$tmpc" >/dev/null 2>&1 ; then + echo " ... yes" + eval "$1=\"\${$1} \$2\"" + eval "$1=\${$1# }" + return 0 + else + echo " ... no" + return 1 + fi +} + + +# Actual script + +CC_AUTO="$CC" +CFLAGS_AUTO="$CFLAGS" +CPPFLAGS_AUTO="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O2 $CPPFLAGS" +LDFLAGS_AUTO="$LDFLAGS" +LDFLAGS_NOSHARED= +prefix= +exec_prefix='$prefix' +dynlibdir='$prefix/lib' +libexecdir='$exec_prefix/libexec' +bindir='$exec_prefix/bin' +sbindir='$exec_prefix/sbin' +libdir='$prefix/lib/$package' +includedir='$prefix/include' +datadir='$prefix/etc' +sysdeps='$prefix/lib/skalibs/sysdeps' +manualsysdeps=false +shared=false +static=true +slashpackage=false +sproot= +home= +exthome= +allstatic=true +evenmorestatic=false +addincpath='' +addlibspath='' +addlibdpath='' +vpaths='' +vpathd='' +cross="$CROSS_COMPILE" + +for arg ; do + case "$arg" in + --help) usage ;; + --prefix=*) prefix=${arg#*=} ;; + --exec-prefix=*) exec_prefix=${arg#*=} ;; + --dynlibdir=*) dynlibdir=${arg#*=} ;; + --libexecdir=*) libexecdir=${arg#*=} ;; + --bindir=*) bindir=${arg#*=} ;; + --sbindir=*) sbindir=${arg#*=} ;; + --libdir=*) libdir=${arg#*=} ;; + --includedir=*) includedir=${arg#*=} ;; + --datadir=*) datadir=${arg#*=} ;; + --with-sysdeps=*) sysdeps=${arg#*=} manualsysdeps=true ;; + --with-include=*) var=${arg#*=} ; stripdir var ; addincpath="$addincpath -I$var" ;; + --with-lib=*) var=${arg#*=} ; stripdir var ; addlibspath="$addlibspath -L$var" ; vpaths="$vpaths $var" ;; + --with-dynlib=*) var=${arg#*=} ; stripdir var ; addlibdpath="$addlibdpath -L$var" ; vpathd="$vpathd $var" ;; + --enable-shared|--enable-shared=yes) shared=true ;; + --disable-shared|--enable-shared=no) shared=false ;; + --enable-static|--enable-static=yes) static=true ;; + --disable-static|--enable-static=no) static=false ;; + --enable-allstatic|--enable-allstatic=yes) allstatic=true ;; + --disable-allstatic|--enable-allstatic=no) allstatic=false ; evenmorestatic=false ;; + --enable-static-libc|--enable-static-libc=yes) allstatic=true ; evenmorestatic=true ;; + --disable-static-libc|--enable-static-libc=no) evenmorestatic=false ;; + --enable-slashpackage=*) sproot=${arg#*=} ; slashpackage=true ; ;; + --enable-slashpackage) sproot= ; slashpackage=true ;; + --disable-slashpackage) sproot= ; slashpackage=false ;; + --enable-cross=*) cross=${arg#*=} ;; + --enable-cross) cross= ;; + --disable-cross) cross= ;; + --enable-*|--disable-*|--with-*|--without-*|--*dir=*|--build=*) ;; + --host=*|--target=*) target=${arg#*=} ;; + -* ) echo "$0: unknown option $arg" ;; + *=*) ;; + *) target=$arg ;; + esac +done + +# Add /usr in the default default case +if test -z "$prefix" ; then + if test "$libdir" = '$prefix/lib/$package' ; then + libdir=/usr/lib/$package + fi + if test "$includedir" = '$prefix/include' ; then + includedir=/usr/include + fi + if test "$sysdeps" = '$prefix/lib/skalibs/sysdeps' ; then + sysdeps=/usr/lib/skalibs/sysdeps + fi +fi + +# Expand installation directories +stripdir prefix +for i in exec_prefix dynlibdir libexecdir bindir sbindir libdir includedir datadir sysdeps sproot skalibs ; do + eval tmp=\${$i} + eval $i=$tmp + stripdir $i +done + +# Get usable temp filenames +i=0 +set -C +while : ; do + i=$(($i+1)) + tmpc="./tmp-configure-$$-$PPID-$i.c" + tmpe="./tmp-configure-$$-$PPID-$i.tmp" + 2>|/dev/null > "$tmpc" && break + 2>|/dev/null > "$tmpe" && break + test "$i" -gt 50 && fail "$0: cannot create temporary files" +done +set +C +trap 'rm -f "$tmpc" "$tmpe"' EXIT ABRT INT QUIT TERM HUP + +# Set slashpackage values +if $slashpackage ; then + home=${sproot}/package/${category}/${package}-${version} + exthome=${sproot}/package/${category}/${package} + if $manualsysdeps ; then + : + else + sysdeps=${sproot}/package/prog/skalibs/sysdeps + fi + binprefix=${home}/command + extbinprefix=${exthome}/command + dynlibdir=${home}/library.so + libexecdir=$binprefix + bindir=$binprefix + sbindir=$binprefix + libdir=${home}/library + includedir=${home}/include + while read dep ; do + addincpath="$addincpath -I${sproot}${dep}/include" + vpaths="$vpaths ${sproot}${dep}/library" + addlibspath="$addlibspath -L${sproot}${dep}/library" + if $allstatic ; then : ; else + vpathd="$vpathd ${sproot}${dep}/library.so" + addlibdpath="$addlibdpath -L${sproot}${dep}/library.so" + fi + done < package/deps-build +fi + +# Find a C compiler to use +echo "checking for C compiler..." +trycc ${cross}gcc +trycc ${cross}c99 +trycc ${cross}cc +test -n "$CC_AUTO" || { echo "$0: cannot find a C compiler" ; exit 1 ; } +echo " ... $CC_AUTO" +echo "checking whether C compiler works... " +echo "typedef int x;" > "$tmpc" +if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO -c -o /dev/null "$tmpc" 2>"$tmpe" ; then + echo " ... yes" +else + echo " ... no. Compiler output follows:" + cat < "$tmpe" + exit 1 +fi + +echo "checking target system type..." +test -n "$target" || target=$($CC_AUTO -dumpmachine 2>/dev/null) || target=unknown +echo " ... $target" +if test ! -d $sysdeps || test ! -f $sysdeps/target ; then + echo "$0: error: $sysdeps is not a valid sysdeps directory" + exit 1 +fi +if [ "x$target" != "x$(cat $sysdeps/target)" ] ; then + echo "$0: error: target $target does not match the contents of $sysdeps/target" + exit 1 +fi + +rt_lib=$(cat $sysdeps/rt.lib) +socket_lib=$(cat $sysdeps/socket.lib) +sysclock_lib=$(cat $sysdeps/sysclock.lib) +tainnow_lib=$(cat $sysdeps/tainnow.lib) +util_lib=$(cat $sysdeps/util.lib) + +tryflag CFLAGS_AUTO -std=c99 +tryflag CFLAGS_AUTO -fomit-frame-pointer +tryflag CFLAGS_AUTO -fno-exceptions +tryflag CFLAGS_AUTO -fno-unwind-tables +tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables +tryflag CFLAGS_AUTO -Wa,--noexecstack +tryflag CFLAGS_AUTO -fno-stack-protector +tryflag CPPFLAGS_AUTO -Werror=implicit-function-declaration +tryflag CPPFLAGS_AUTO -Werror=implicit-int +tryflag CPPFLAGS_AUTO -Werror=pointer-sign +tryflag CPPFLAGS_AUTO -Werror=pointer-arith + +if $evenmorestatic ; then + LDFLAGS_NOSHARED=-static +fi + +if $shared ; then + tryldflag LDFLAGS_AUTO -Wl,--hash-style=both +fi + +if test -z "$vpaths" ; then + while read dep ; do + base=$(basename $dep) ; + vpaths="$vpaths /usr/lib/$base" + addlibspath="$addlibspath -L/usr/lib/$base" + done < package/deps-build +fi + +CPPFLAGS_AUTO="$CPPFLAGS_AUTO $addincpath" +LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibspath" +$allstatic || LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibdpath" + +echo "creating config.mak..." +cmdline=$(quote "$0") +for i ; do cmdline="$cmdline $(quote "$i")" ; done +exec 3>&1 1>config.mak +cat << EOF +# This file was generated by: +# $cmdline +# Any changes made here will be lost if configure is re-run. + +target := $target +package := $package +prefix := $prefix +exec_prefix := $exec_prefix +dynlibdir := $dynlibdir +libexecdir := $libexecdir +bindir := $bindir +sbindir := $sbindir +libdir := $libdir +includedir := $includedir +datadir := $datadir +sysdeps := $sysdeps +slashpackage := $slashpackage +sproot := $sproot +version := $version +home := $home +exthome := $exthome +RT_LIB := ${rt_lib} +SOCKET_LIB := ${socket_lib} +SYSCLOCK_LIB := ${sysclock_lib} +TAINNOW_LIB := ${tainnow_lib} +UTIL_LIB := ${util_lib} + +CC := $CC_AUTO +CFLAGS := $CFLAGS_AUTO +CPPFLAGS := $CPPFLAGS_AUTO +LDFLAGS := $LDFLAGS_AUTO +LDFLAGS_NOSHARED := $LDFLAGS_NOSHARED +CROSS_COMPILE := $cross + +vpath lib%a$vpaths +EOF +if $allstatic ; then + echo ".LIBPATTERNS := lib%.a" + echo "DO_ALLSTATIC := 1" + vpathd= +fi + echo "vpath lib%.so$vpathd" +if $static ; then + echo "DO_STATIC := 1" +else + echo "DO_STATIC :=" +fi +if $shared ; then + echo "DO_SHARED := 1" +else + echo "DO_SHARED :=" +fi + +exec 1>&3 3>&- +echo " ... done." + +echo "creating src/include/${package}/config.h..." +mkdir -p -m 0755 src/include/${package} +exec 3>&1 1> src/include/${package}/config.h +cat <&3 3>&- +echo " ... done." diff --git a/doc/s6-rc.html b/doc/s6-rc.html new file mode 100644 index 0000000..e9a712c --- /dev/null +++ b/doc/s6-rc.html @@ -0,0 +1,163 @@ + + + + + + s6: the s6-rc program + + + + + + +

+s6
+Software
+skarnet.org +

+ +

The s6-rc program

+ +

+ s6-rc is a machine state manager: it brings the machine to a +desired state, by starting or stopping services as needed. +

+ +

Interface

+ +
+     s6-rc servicenames...
+
+ +
    +
  • s6-rc expects to find a compiled service database +in /etc/s6-rc/compiled and a live state in +/s6/s6-rc. If it cannot find that data, it complains and +exits. +
      +
    • The compiled service database is built offline +via the s6-rc-compile tool.
    • +
    • The live state should be initialized at boot time +via the s6-rc-init tool. It is then +maintained by s6-rc itself.
    • +
  • +
  • The command line arguments servicenames... define a set +of selected services the user wants to act on.
  • +
  • s6-rc computes the necessary transitions to bring the machine +to the desired state - by default a state where all the +services listed on the command line are up. If asked to, it performs +those transitions.
  • +
  • s6-rc processes services as soon as they can be processed. It +will wait until a service is up to start a dependent service, but it +will start two independent services in parallel.
  • +
  • If every state transition completes successfully, s6-rc exits 0.
  • +
  • If a state transition fails, s6-rc will not perform the transitions +that depend on it. It will wait until all the other independent transitions +are done, then exit 1.
  • +
+ +

Options

+ +

s6-rc control

+ +
    +
  • -v verbosity : be more or less +verbose. Default is 1: warning and error messages will be printed to +stderr. 0 silences warnings. 2 writes information messages whenever +s6-rc performs a transition. 3 or more is debug info.
  • +
  • -n dryruntimeout : dry run. +s6-rc will pretend to perform transitions, but will replace all its +program invocations by a call to +s6-rc-dryrun, which will do nothing but +print the command line s6-rc would have executed, then sleep for +dryruntimeout milliseconds before reporting success.
  • +
  • -t timeout : timeout. If s6-rc +isn't done after timeout milliseconds, it will exit, leaving +the live state as it is at exit time. It does not kill its children, so +a child may successfully complete afterwards and the live state will +not be updated; in that case, subsequent s6-rc invocations will notice +and correctly update it.
  • +
  • -c compiled : look for the +compiled service database in compiled. Default is +/etc/s6-rc/compiled
  • +
  • -l live : look for the +live state in live. Default is +/s6/s6-rc
  • +
+ +

Up or down

+ +
    +
  • -u : selected services are interpreted +as to be brought up. This is the default.
  • +
  • -d : selected services are interpreted +as to be brought down.
  • +
+ +

Service selection

+ +
    +
  • -p : prune. The state will be brought to +exactly servicenames..., plus their dependencies, and +the other services will be brought down. With the -d option, +the meaning is reversed: the state will be brought to the maximum +possible set that does not include servicenames....
  • +
  • -a : all. Add the current set of active services to +the selected set. This is useful to ensure consistency of the machine +state, for instance, and also at shutdown time: s6-rc -da +will stop all the currently active services.
  • +
+ +

Actions

+ +
    +
  • -C : check. s6-rc will check the consistency of the +database, and exit with an error message if it finds errors.
  • +
  • -L : list. s6-rc will resolve the given names, then +print the list of corresponding atomic services to stdout, without taking their +dependencies into account. It will print an empty line afterwards.
  • +
  • -A : list all. s6-rc will print the list of selected +atomic services to stdout, after computing dependencies. Note that with +the -d option, it computes reverse dependencies instead.
  • +
  • -S : state change. A state change will be performed +for the selected services. +This is the default if no other action option has been given.
  • +
+ +

Usage examples

+ +
 s6-rc myservicebundle 
+

+ Brings up all the services represented by myservicebundle, +dependencies first. +

+ +
 s6-rc -Sad 
+

+ Brings down all the services in an orderly manner. This is typically +run at shutdown time. +

+ +
 s6-rc -Au myservicebundle 
+

+ Prints the names of all atomic services represented by +myservicebundle, as well as everything they depend on. +

+ +
 s6-rc -Ad myservicebundle 
+

+ Prints the names of all atomic services represented by +myservicebundle, as well as everything that depends on them. +

+ +
 s6-rc -pun0 myservicebundle 
+

+ Prints what s6-rc would do to bring the state to just +myservicebundle and its dependencies. +

+ + +

Internals

+ + + diff --git a/package/deps-build b/package/deps-build new file mode 100644 index 0000000..d63229b --- /dev/null +++ b/package/deps-build @@ -0,0 +1,3 @@ +/package/prog/skalibs +/package/admin/execline +/package/admin/s6 diff --git a/package/deps.mak b/package/deps.mak new file mode 100644 index 0000000..f6158dc --- /dev/null +++ b/package/deps.mak @@ -0,0 +1,30 @@ +# +# This file has been generated by tools/gen-deps.sh +# + +src/include/s6-rc/s6rc-utils.h: src/include/s6-rc/s6rc-db.h +src/include/s6-rc/s6rc.h: src/include/s6-rc/s6rc-constants.h src/include/s6-rc/s6rc-db.h src/include/s6-rc/s6rc-utils.h +src/libs6rc/s6rc_db_check_depcycles.o src/libs6rc/s6rc_db_check_depcycles.lo: src/libs6rc/s6rc_db_check_depcycles.c src/include/s6-rc/s6rc-db.h +src/libs6rc/s6rc_db_check_revdeps.o src/libs6rc/s6rc_db_check_revdeps.lo: src/libs6rc/s6rc_db_check_revdeps.c src/include/s6-rc/s6rc-db.h +src/libs6rc/s6rc_db_read.o src/libs6rc/s6rc_db_read.lo: src/libs6rc/s6rc_db_read.c src/include/s6-rc/s6rc-db.h +src/libs6rc/s6rc_db_read_sizes.o src/libs6rc/s6rc_db_read_sizes.lo: src/libs6rc/s6rc_db_read_sizes.c src/include/s6-rc/s6rc-db.h +src/libs6rc/s6rc_db_read_uint32.o src/libs6rc/s6rc_db_read_uint32.lo: src/libs6rc/s6rc_db_read_uint32.c src/include/s6-rc/s6rc-db.h +src/libs6rc/s6rc_graph_closure.o src/libs6rc/s6rc_graph_closure.lo: src/libs6rc/s6rc_graph_closure.c src/include/s6-rc/s6rc-db.h src/include/s6-rc/s6rc-utils.h +src/s6-rc/s6-rc-compile.o src/s6-rc/s6-rc-compile.lo: src/s6-rc/s6-rc-compile.c src/s6-rc/s6-rc-compile.h src/include/s6-rc/s6rc.h +src/s6-rc/s6-rc-db.o src/s6-rc/s6-rc-db.lo: src/s6-rc/s6-rc-db.c src/include/s6-rc/s6rc-db.h +src/s6-rc/s6-rc-dryrun.o src/s6-rc/s6-rc-dryrun.lo: src/s6-rc/s6-rc-dryrun.c +src/s6-rc/s6-rc-init.o src/s6-rc/s6-rc-init.lo: src/s6-rc/s6-rc-init.c src/include/s6-rc/s6rc.h +src/s6-rc/s6-rc.o src/s6-rc/s6-rc.lo: src/s6-rc/s6-rc.c src/include/s6-rc/s6rc.h + +libs6rc.a: src/libs6rc/s6rc_db_check_depcycles.o src/libs6rc/s6rc_db_read.o src/libs6rc/s6rc_db_read_sizes.o src/libs6rc/s6rc_db_read_uint32.o src/libs6rc/s6rc_db_check_revdeps.o src/libs6rc/s6rc_graph_closure.o +libs6rc.so: src/libs6rc/s6rc_db_check_depcycles.lo src/libs6rc/s6rc_db_read.lo src/libs6rc/s6rc_db_read_sizes.lo src/libs6rc/s6rc_db_read_uint32.lo src/libs6rc/s6rc_db_check_revdeps.lo src/libs6rc/s6rc_graph_closure.lo +s6-rc: private EXTRA_LIBS := ${TAINNOW_LIB} +s6-rc: src/s6-rc/s6-rc.o ${LIBS6RC} -lskarnet +s6-rc-compile: private EXTRA_LIBS := +s6-rc-compile: src/s6-rc/s6-rc-compile.o ${LIBS6RC} -lexecline -lskarnet +s6-rc-db: private EXTRA_LIBS := +s6-rc-db: src/s6-rc/s6-rc-db.o ${LIBS6RC} -lskarnet +s6-rc-dryrun: private EXTRA_LIBS := ${TAINNOW_LIB} +s6-rc-dryrun: src/s6-rc/s6-rc-dryrun.o -lskarnet +s6-rc-init: private EXTRA_LIBS := ${TAINNOW_LIB} +s6-rc-init: src/s6-rc/s6-rc-init.o ${LIBS6RC} -ls6 -lskarnet diff --git a/package/info b/package/info new file mode 100644 index 0000000..1a67009 --- /dev/null +++ b/package/info @@ -0,0 +1,4 @@ +package=s6-rc +version=0.0.1.0 +category=admin +package_macro_name=S6_RC diff --git a/package/modes b/package/modes new file mode 100644 index 0000000..7956243 --- /dev/null +++ b/package/modes @@ -0,0 +1,5 @@ +s6-rc-compile 0755 +s6-rc-db 0755 +s6-rc-dryrun 0755 +s6-rc-init 0700 +s6-rc 0700 diff --git a/package/targets.mak b/package/targets.mak new file mode 100644 index 0000000..2f19728 --- /dev/null +++ b/package/targets.mak @@ -0,0 +1,25 @@ +BIN_TARGETS := \ +s6-rc-compile \ +s6-rc-dryrun \ +s6-rc-db + +SBIN_TARGETS := \ +s6-rc-init \ +s6-rc + + +LIBEXEC_TARGETS := + +ifdef DO_ALLSTATIC +LIBS6RC := libs6rc.a +else +LIBS6RC := libs6rc.so +endif + +ifdef DO_SHARED +SHARED_LIBS := libs6rc.so +endif + +ifdef DO_STATIC +STATIC_LIBS := libs6rc.a +endif diff --git a/patch-for-solaris b/patch-for-solaris new file mode 100755 index 0000000..2d1296b --- /dev/null +++ b/patch-for-solaris @@ -0,0 +1,21 @@ +#!/usr/xpg4/bin/sh + +patchit () { + echo '#!/usr/xpg4/bin/sh' > $1.tmp + tail -n +2 $1 >> $1.tmp + mv -f $1.tmp $1 + chmod 755 $1 +} + +# Solaris doesn't understand POSIX.1-2008 either. +sed -e 's/XOPEN_SOURCE=700/XOPEN_SOURCE=600/' < configure > configure.tmp +mv -f configure.tmp configure + +patchit ./configure +patchit ./tools/install.sh +patchit ./tools/gen-deps.sh + +echo 'SHELL := /usr/xpg4/bin/sh' > Makefile.tmp +echo >> Makefile.tmp +cat Makefile >> Makefile.tmp +mv -f Makefile.tmp Makefile diff --git a/src/include/s6-rc/s6rc-constants.h b/src/include/s6-rc/s6rc-constants.h new file mode 100644 index 0000000..50217c9 --- /dev/null +++ b/src/include/s6-rc/s6rc-constants.h @@ -0,0 +1,14 @@ +/* ISC license. */ + +#ifndef S6RC_CONSTANTS_H +#define S6RC_CONSTANTS_H + +#define S6RC_COMPILED_BASE "/etc/s6-rc/compiled" +#define S6RC_LIVE_BASE "/s6/s6-rc" + +#define S6RC_COMPILED_DIR_LEN 32 + +#define S6RC_ONESHOT_RUNNER "s6rc-oneshot-runner" +#define S6RC_ONESHOT_RUNNER_LEN (sizeof S6RC_ONESHOT_RUNNER - 1) + +#endif diff --git a/src/include/s6-rc/s6rc-db.h b/src/include/s6-rc/s6rc-db.h new file mode 100644 index 0000000..bc475c7 --- /dev/null +++ b/src/include/s6-rc/s6rc-db.h @@ -0,0 +1,69 @@ +/* ISC license. */ + +#ifndef S6RC_DB_H +#define S6RC_DB_H + +#include +#include + +#define S6RC_DB_BANNER_START "s6rc-db: start\n" +#define S6RC_DB_BANNER_START_LEN (sizeof(S6RC_DB_BANNER_START) - 1) +#define S6RC_DB_BANNER_END "\ns6rc-db: end\n" +#define S6RC_DB_BANNER_END_LEN (sizeof(S6RC_DB_BANNER_END) - 1) + + +typedef struct s6rc_oneshot_s s6rc_oneshot_t, *s6rc_oneshot_t_ref ; +struct s6rc_oneshot_s +{ + uint32 argc[2] ; + uint32 argv[2] ; +} ; + +typedef struct s6rc_longrun_s s6rc_longrun_t, *s6rc_longrun_t_ref ; +struct s6rc_longrun_s +{ + uint32 servicedir ; +} ; + +typedef union s6rc_longshot_u s6rc_longshot_t, *s6rc_longshot_t_ref ; +union s6rc_longshot_u +{ + s6rc_oneshot_t oneshot ; + s6rc_longrun_t longrun ; +} ; + +typedef struct s6rc_service_s s6rc_service_t, *s6rc_service_t_ref ; +struct s6rc_service_s +{ + uint32 name ; + uint32 flags ; + uint32 deps[2] ; + uint32 ndeps[2] ; + uint32 timeout[2] ; + s6rc_longshot_t x ; + unsigned char type : 1 ; +} ; + +typedef struct s6rc_db_s s6rc_db_t, *s6rc_db_t_ref ; +struct s6rc_db_s +{ + s6rc_service_t *services ; + unsigned int nshort ; + unsigned int nlong ; + unsigned int stringlen ; + unsigned int nargvs ; + unsigned int ndeps ; + char *string ; + char **argvs ; + uint32 *deps ; +} ; + +extern int s6rc_db_read_uint32 (buffer *, uint32 *) ; + +extern int s6rc_db_read_sizes (int, s6rc_db_t *) ; +extern int s6rc_db_read (int, s6rc_db_t *) ; + +extern unsigned int s6rc_db_check_depcycles (s6rc_db_t const *, int, unsigned int *) ; +extern int s6rc_db_check_revdeps (s6rc_db_t const *) ; + +#endif diff --git a/src/include/s6-rc/s6rc-utils.h b/src/include/s6-rc/s6rc-utils.h new file mode 100644 index 0000000..992d865 --- /dev/null +++ b/src/include/s6-rc/s6rc-utils.h @@ -0,0 +1,10 @@ +/* ISC license. */ + +#ifndef S6RC_UTILS_H +#define S6RC_UTILS_H + +#include + +extern void s6rc_graph_closure (s6rc_db_t const *, unsigned char *, unsigned int, int) ; + +#endif diff --git a/src/include/s6-rc/s6rc.h b/src/include/s6-rc/s6rc.h new file mode 100644 index 0000000..098f38b --- /dev/null +++ b/src/include/s6-rc/s6rc.h @@ -0,0 +1,10 @@ +/* ISC license. */ + +#ifndef S6RC_H +#define S6RC_H + +#include +#include +#include + +#endif diff --git a/src/libs6rc/deps-lib/s6rc b/src/libs6rc/deps-lib/s6rc new file mode 100644 index 0000000..65b562e --- /dev/null +++ b/src/libs6rc/deps-lib/s6rc @@ -0,0 +1,6 @@ +s6rc_db_check_depcycles.o +s6rc_db_read.o +s6rc_db_read_sizes.o +s6rc_db_read_uint32.o +s6rc_db_check_revdeps.o +s6rc_graph_closure.o diff --git a/src/libs6rc/s6rc_db_check_depcycles.c b/src/libs6rc/s6rc_db_check_depcycles.c new file mode 100644 index 0000000..13dcf7b --- /dev/null +++ b/src/libs6rc/s6rc_db_check_depcycles.c @@ -0,0 +1,50 @@ +/* ISC license. */ + +#include +#include +#include +#include + +typedef struct recinfo_s recinfo_t, *recinfo_t_ref ; +struct recinfo_s +{ + s6rc_db_t const *db ; + unsigned int n ; + unsigned char *gray ; + unsigned char *black ; + unsigned char h : 1 ; +} ; + +static unsigned int s6rc_db_checknocycle_rec (recinfo_t *recinfo, unsigned int i) +{ + if (!bitarray_peek(recinfo->black, i)) + { + unsigned int j = recinfo->db->services[i].ndeps[recinfo->h] ; + if (bitarray_peek(recinfo->gray, i)) return i ; + bitarray_set(recinfo->gray, i) ; + while (j--) + { + register unsigned int r = s6rc_db_checknocycle_rec(recinfo, recinfo->db->deps[recinfo->h * recinfo->db->ndeps + recinfo->db->services[i].deps[recinfo->h] + j]) ; + if (r < recinfo->n) return r ; + } + bitarray_set(recinfo->black, i) ; + } + return recinfo->n ; +} + +unsigned int s6rc_db_check_depcycles (s6rc_db_t const *db, int h, unsigned int *problem) +{ + unsigned int n = db->nshort + db->nlong ; + unsigned char gray[bitarray_div8(n)] ; + unsigned char black[bitarray_div8(n)] ; + recinfo_t info = { .db = db, .n = n, .gray = gray, .black = black, .h = !!h } ; + unsigned int i = n ; + byte_zero(gray, bitarray_div8(n)) ; + byte_zero(black, bitarray_div8(n)) ; + while (i--) + { + register unsigned int r = s6rc_db_checknocycle_rec(&info, i) ; + if (r < n) return (*problem = r, i) ; + } + return n ; +} diff --git a/src/libs6rc/s6rc_db_check_revdeps.c b/src/libs6rc/s6rc_db_check_revdeps.c new file mode 100644 index 0000000..0cb2845 --- /dev/null +++ b/src/libs6rc/s6rc_db_check_revdeps.c @@ -0,0 +1,30 @@ +/* ISC license. */ + +#include +#include +#include +#include + +int s6rc_db_check_revdeps (s6rc_db_t const *db) +{ + unsigned int n = db->nshort + db->nlong ; + unsigned int m = bitarray_div8(n) ; + unsigned char matrix[n * m] ; + register unsigned int i = n ; + register unsigned char const *p = matrix ; + byte_zero(matrix, n * m) ; + while (i--) + { + register unsigned int j = db->services[i].ndeps[1] ; + while (j--) bitarray_not(matrix + m * i, db->deps[db->ndeps + db->services[i].deps[1] + j], 1) ; + } + i = n ; + while (i--) + { + register unsigned int j = db->services[i].ndeps[0] ; + while (j--) bitarray_not(matrix + m * db->deps[db->services[i].deps[0] + j], i, 1) ; + } + i = n * m ; + while (i--) if (*p++) return 0 ; + return 1 ; +} diff --git a/src/libs6rc/s6rc_db_read.c b/src/libs6rc/s6rc_db_read.c new file mode 100644 index 0000000..f0c7909 --- /dev/null +++ b/src/libs6rc/s6rc_db_read.c @@ -0,0 +1,176 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#include +#define DBG(...) do { bprintf(buffer_2, "debug: ") ; bprintf(buffer_2, __VA_ARGS__) ; bprintf(buffer_2, "\n") ; buffer_flush(buffer_2) ; } while(0) +#else +#define DBG(...) +#endif + +static int s6rc_db_check_valid_string (char const *string, unsigned int stringlen, unsigned int pos) +{ + if (pos >= stringlen) return 0 ; + if (str_nlen(string + pos, stringlen - pos) == stringlen - pos) return 0 ; + return 1 ; +} + +static inline int s6rc_db_check_valid_strings (char const *string, unsigned int stringlen, unsigned int pos, unsigned int n) +{ + while (n--) + { + if (!s6rc_db_check_valid_string(string, stringlen, pos)) return 0 ; + pos += str_len(string + pos) + 1 ; + } + return 1 ; +} + +static inline int s6rc_db_read_deps (buffer *b, unsigned int max, uint32 *deps, unsigned int ndeps) +{ + uint32 x ; + ndeps <<= 1 ; + while (ndeps--) + { + if (!s6rc_db_read_uint32(b, &x)) return -1 ; + if (x >= max) return 0 ; + *deps++ = x ; + } + return 1 ; +} + +static inline int s6rc_db_read_services (buffer *b, s6rc_db_t *db) +{ + unsigned int n = db->nshort + db->nlong ; + s6rc_service_t *sv = db->services ; + unsigned int nargvs = db->nargvs ; + char **argvs = db->argvs ; + char type ; +#ifdef DEBUG + register unsigned int i = 0 ; +#endif + while (n--) + { + DBG("iteration %u - %u remaining", i++, n) ; + if (!s6rc_db_read_uint32(b, &sv->name)) return -1 ; + DBG(" name is %u: %s", sv->name, db->string + sv->name) ; + if (sv->name >= db->stringlen) return 0 ; + if (!s6rc_db_check_valid_string(db->string, db->stringlen, sv->name)) return 0 ; + if (!s6rc_db_read_uint32(b, &sv->flags)) return -1 ; + DBG(" flags is %x", sv->flags) ; + if (!s6rc_db_read_uint32(b, &sv->timeout[0])) return -1 ; + DBG(" timeout0 is %u", sv->timeout[0]) ; + if (!s6rc_db_read_uint32(b, &sv->timeout[1])) return -1 ; + DBG(" timeout1 is %u", sv->timeout[1]) ; + if (!s6rc_db_read_uint32(b, &sv->ndeps[0])) return -1 ; + DBG(" ndeps0 is %u", sv->ndeps[0]) ; + if (!s6rc_db_read_uint32(b, &sv->ndeps[1])) return -1 ; + DBG(" ndeps1 is %u", sv->ndeps[1]) ; + if (!s6rc_db_read_uint32(b, &sv->deps[0])) return -1 ; + DBG(" deps0 is %u", sv->deps[0]) ; + if (sv->deps[0] > db->ndeps || sv->deps[0] + sv->ndeps[0] > db->ndeps) + return 0 ; + if (!s6rc_db_read_uint32(b, &sv->deps[1])) return -1 ; + DBG(" deps1 is %u", sv->deps[1]) ; + if (sv->deps[1] > db->ndeps || sv->deps[1] + sv->ndeps[1] > db->ndeps) + return 0 ; +#ifdef DEBUG + { + unsigned int k = 0 ; + for (; k < sv->ndeps[0] ; k++) + DBG(" rev dep on %u", db->deps[sv->deps[0] + k]) ; + for (k = 0 ; k < sv->ndeps[1] ; k++) + DBG(" dep on %u", db->deps[db->ndeps + sv->deps[1] + k]) ; + } +#endif + if (buffer_get(b, &type, 1) < 1) return -1 ; + if (type) + { + sv->type = 1 ; + if (!s6rc_db_read_uint32(b, &sv->x.longrun.servicedir)) return -1 ; + DBG(" longrun - servicedir is %u: %s", sv->x.longrun.servicedir, db->string + sv->x.longrun.servicedir) ; + if (!s6rc_db_check_valid_string(db->string, db->stringlen, sv->x.longrun.servicedir)) return 0 ; + } + else + { + unsigned int i = 0 ; + DBG(" oneshot") ; + sv->type = 0 ; + for (; i < 2 ; i++) + { + uint32 argvpos, argc ; + if (!s6rc_db_read_uint32(b, &argc)) return -1 ; + DBG(" argc[%u] is %u, nargvs is %u", i, argc, nargvs) ; + if (argc > nargvs) return 0 ; + if (!s6rc_db_read_uint32(b, &argvpos)) return -1 ; + DBG(" argvpos[%u] is %u", i, argvpos) ; + if (!s6rc_db_check_valid_strings(db->string, db->stringlen, argvpos, argc)) return 0 ; + if (!env_make((char const **)argvs, argc, db->string + argvpos, db->stringlen - argvpos)) return -1 ; + DBG(" first arg is %s", argvs[0]) ; + sv->x.oneshot.argv[i] = argvpos ; + sv->x.oneshot.argc[i] = argc ; + argvs += argc ; nargvs -= argc ; + if (!nargvs--) return 0 ; *argvs++ = 0 ; + } + } + if (buffer_get(b, &type, 1) < 1) return -1 ; + if (type != '\376') return 0 ; + sv++ ; + } + if (nargvs) return 0 ; + return 1 ; +} + +static inline int s6rc_db_read_string (buffer *b, char *string, unsigned int len) +{ + if (buffer_get(b, string, len) < (int)len) return -1 ; + return 1 ; +} + +static inline int s6rc_db_read_buffer (buffer *b, s6rc_db_t *db) +{ + { + char banner[S6RC_DB_BANNER_START_LEN] ; + if (buffer_get(b, banner, S6RC_DB_BANNER_START_LEN) < S6RC_DB_BANNER_START_LEN) return -1 ; + if (byte_diff(banner, S6RC_DB_BANNER_START_LEN, S6RC_DB_BANNER_START)) return 0 ; + } + + { + register int r = s6rc_db_read_string(b, db->string, db->stringlen) ; + if (r < 1) return r ; + r = s6rc_db_read_deps(b, db->nshort + db->nlong, db->deps, db->ndeps) ; + if (r < 1) return r ; + r = s6rc_db_read_services(b, db) ; + if (r < 1) return r ; + } + + { + char banner[S6RC_DB_BANNER_END_LEN] ; + if (buffer_get(b, banner, S6RC_DB_BANNER_END_LEN) < S6RC_DB_BANNER_END_LEN) return -1 ; + if (byte_diff(banner, S6RC_DB_BANNER_END_LEN, S6RC_DB_BANNER_END)) return 0 ; + } + return 1 ; +} + +int s6rc_db_read (int fdcompiled, s6rc_db_t *db) +{ + int r, e ; + buffer b ; + char buf[BUFFER_INSIZE] ; + int fd = open_readatb(fdcompiled, "db") ; + if (fd < 0) return -1 ; + buffer_init(&b, &fd_readsv, fd, buf, BUFFER_INSIZE) ; + r = s6rc_db_read_buffer(&b, db) ; + e = errno ; + fd_close(fd) ; + errno = e ; + return r ; +} diff --git a/src/libs6rc/s6rc_db_read_sizes.c b/src/libs6rc/s6rc_db_read_sizes.c new file mode 100644 index 0000000..4d1537f --- /dev/null +++ b/src/libs6rc/s6rc_db_read_sizes.c @@ -0,0 +1,53 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +static inline int s6rc_db_read_sizes_buffer (buffer *b, s6rc_db_t *db) +{ + uint32 x ; + if (!s6rc_db_read_uint32(b, &x)) return 0 ; + db->nshort = x ; + if (!s6rc_db_read_uint32(b, &x)) return 0 ; + db->nlong = x ; + if (!s6rc_db_read_uint32(b, &x)) return 0 ; + db->stringlen = x ; + if (!s6rc_db_read_uint32(b, &x)) return 0 ; + db->nargvs = x ; + if (!s6rc_db_read_uint32(b, &x)) return 0 ; + db->ndeps = x ; + return 1 ; +} + +int s6rc_db_read_sizes (int fdcompiled, s6rc_db_t *db) +{ + char buf[64] ; + buffer b ; + int fd = open_readatb(fdcompiled, "n") ; + if (fd < 0) return 0 ; + buffer_init(&b, &fd_readsv, fd, buf, 64) ; + if (!s6rc_db_read_sizes_buffer(&b, db)) + { + fd_close(fd) ; + return 0 ; + } + { + char c ; + register int r = buffer_get(&b, &c, 1) ; + if (r < 0) + { + r = errno ; + fd_close(fd) ; + errno = r ; + return 0 ; + } + fd_close(fd) ; + if (r) return 0 ; + } + return 1 ; +} diff --git a/src/libs6rc/s6rc_db_read_uint32.c b/src/libs6rc/s6rc_db_read_uint32.c new file mode 100644 index 0000000..05d1f9e --- /dev/null +++ b/src/libs6rc/s6rc_db_read_uint32.c @@ -0,0 +1,14 @@ +/* ISC license. */ + +#include +#include +#include + +int s6rc_db_read_uint32 (buffer *b, uint32 *x) +{ + unsigned int w = 0 ; + char pack[4] ; + if (buffer_getall(b, pack, 4, &w) <= 0) return 0 ; + uint32_unpack_big(pack, x) ; + return 1 ; +} diff --git a/src/libs6rc/s6rc_graph_closure.c b/src/libs6rc/s6rc_graph_closure.c new file mode 100644 index 0000000..0ec2def --- /dev/null +++ b/src/libs6rc/s6rc_graph_closure.c @@ -0,0 +1,39 @@ +/* ISC license. */ + +#include +#include +#include + +typedef struct recinfo_s recinfo_t, *recinfo_t_ref ; +struct recinfo_s +{ + s6rc_db_t const *db ; + unsigned int n ; + unsigned char *bits ; + unsigned char *mark ; + unsigned char mask ; + unsigned char h : 1 ; +} ; + +static void s6rc_graph_closure_rec (recinfo_t *recinfo, unsigned int i) +{ + if (!bitarray_peek(recinfo->mark, i)) + { + unsigned int j = recinfo->db->services[i].ndeps[recinfo->h] ; + bitarray_set(recinfo->mark, i) ; + while (j--) s6rc_graph_closure_rec(recinfo, recinfo->db->deps[recinfo->h * recinfo->db->ndeps + recinfo->db->services[i].deps[recinfo->h] + j]) ; + recinfo->bits[i] |= recinfo->mask ; + } +} + +void s6rc_graph_closure (s6rc_db_t const *db, unsigned char *bits, unsigned int bitno, int h) +{ + unsigned int n = db->nshort + db->nlong ; + unsigned int m = bitarray_div8(n) ; + unsigned char mark[m] ; + recinfo_t info = { .db = db, .n = n, .bits = bits, .mark = mark, .mask = 1 << (bitno & 7), .h = !!h } ; + register unsigned int i = n ; + byte_zero(mark, m) ; + while (i--) + if (bits[i] & info.mask) s6rc_graph_closure_rec(&info, i) ; +} diff --git a/src/s6-rc/deps-exe/s6-rc b/src/s6-rc/deps-exe/s6-rc new file mode 100644 index 0000000..f95b7ab --- /dev/null +++ b/src/s6-rc/deps-exe/s6-rc @@ -0,0 +1,3 @@ +${LIBS6RC} +-lskarnet +${TAINNOW_LIB} diff --git a/src/s6-rc/deps-exe/s6-rc-compile b/src/s6-rc/deps-exe/s6-rc-compile new file mode 100644 index 0000000..9dbd823 --- /dev/null +++ b/src/s6-rc/deps-exe/s6-rc-compile @@ -0,0 +1,3 @@ +${LIBS6RC} +-lexecline +-lskarnet diff --git a/src/s6-rc/deps-exe/s6-rc-db b/src/s6-rc/deps-exe/s6-rc-db new file mode 100644 index 0000000..152b051 --- /dev/null +++ b/src/s6-rc/deps-exe/s6-rc-db @@ -0,0 +1,2 @@ +${LIBS6RC} +-lskarnet diff --git a/src/s6-rc/deps-exe/s6-rc-dryrun b/src/s6-rc/deps-exe/s6-rc-dryrun new file mode 100644 index 0000000..1840bc1 --- /dev/null +++ b/src/s6-rc/deps-exe/s6-rc-dryrun @@ -0,0 +1,2 @@ +-lskarnet +${TAINNOW_LIB} diff --git a/src/s6-rc/deps-exe/s6-rc-init b/src/s6-rc/deps-exe/s6-rc-init new file mode 100644 index 0000000..b76070e --- /dev/null +++ b/src/s6-rc/deps-exe/s6-rc-init @@ -0,0 +1,4 @@ +${LIBS6RC} +-ls6 +-lskarnet +${TAINNOW_LIB} diff --git a/src/s6-rc/s6-rc-compile.c b/src/s6-rc/s6-rc-compile.c new file mode 100644 index 0000000..e0ee4b5 --- /dev/null +++ b/src/s6-rc/s6-rc-compile.c @@ -0,0 +1,1163 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s6-rc-compile.h" + +#ifdef DEBUG +# include +# define DBG(...) do { buffer_puts(buffer_2, PROG) ; buffer_puts(buffer_2, ": debug: ") ; bprintf(buffer_2, __VA_ARGS__) ; buffer_putflush(buffer_2, "\n", 1) ; } while (0) +#else +# define DBG(...) +#endif + +#define USAGE "s6-rc-compile [ -v verbosity ] destdir sources..." +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_dief1x(111, "out of memory") ; + +static unsigned int verbosity = 1 ; +static stralloc keep = STRALLOC_ZERO ; +static stralloc data = STRALLOC_ZERO ; + +typedef enum servicetype_e servicetype_t, *servicetype_t_ref ; +enum servicetype_e +{ + SVTYPE_UNDEFINED = 0, + SVTYPE_ONESHOT = 1, + SVTYPE_LONGRUN = 2, + SVTYPE_BUNDLE = 4 +} ; + + + + /* The names tree */ + + +typedef struct nameinfo_s nameinfo_t, *nameinfo_t_ref ; +struct nameinfo_s +{ + unsigned int pos ; + unsigned int kpos ; + unsigned int i ; + servicetype_t type ; +} ; + +static void *names_dtok (unsigned int d, void *x) +{ + return (void *)(data.s + genalloc_s(nameinfo_t, (genalloc *)x)[d].pos) ; +} + +static int names_cmp (void const *a, void const *b, void *x) +{ + (void)x ; + return str_diff((char const *)a, (char const *)b) ; +} + +static genalloc nameinfo = GENALLOC_ZERO ; /* nameinfo_t */ +static avltree names_map = AVLTREE_INIT(8, 3, 8, &names_dtok, &names_cmp, &nameinfo) ; + + + + /* Services before resolution */ + + +typedef struct common_s common_t, *common_t_ref ; +struct common_s +{ + unsigned int name ; /* pos in data */ + unsigned int kname ; /* pos in keep */ + unsigned int ndeps ; + unsigned int depindex ; /* pos in indices */ + uint32 annotation_flags ; + uint32 timeout[2] ; +} ; + +typedef struct oneshot_s oneshot_t, *oneshot_t_ref ; +struct oneshot_s +{ + common_t common ; + unsigned int argvindex[2] ; /* pos in data */ + unsigned int argc[2] ; +} ; + +typedef struct longrun_s longrun_t, *longrun_t_ref ; +struct longrun_s +{ + common_t common ; + char const *srcdir ; + unsigned int servicedirname ; + unsigned int logrelated ; + unsigned char logtype ; +} ; + +typedef struct bundle_s bundle_t, *bundle_t_ref ; +struct bundle_s +{ + unsigned int name ; /* pos in data */ + unsigned int n ; + unsigned int listindex ; /* pos in indices */ +} ; + +typedef struct before_s before_t, *before_t_ref ; +struct before_s +{ + genalloc indices ; /* uint32 */ + genalloc oneshots ; /* oneshot_t */ + genalloc longruns ; /* longrun_t */ + genalloc bundles ; /* bundle_t */ + unsigned int nargvs ; + unsigned int maxnamelen ; +} ; + +#define BEFORE_ZERO { .indices = GENALLOC_ZERO, .oneshots = GENALLOC_ZERO, .longruns = GENALLOC_ZERO, .bundles = GENALLOC_ZERO, .nargvs = 0 } ; + + + + /* Read all the sources, populate the map and string data */ + + +static char const *typestr (servicetype_t type) +{ + return (type == SVTYPE_ONESHOT) ? "oneshot" : + (type == SVTYPE_LONGRUN) ? "longrun" : + (type == SVTYPE_BUNDLE) ? "bundle" : + "unknown" ; +} + +static int add_name (before_t *be, char const *srcdir, char const *name, servicetype_t type, unsigned int *pos, unsigned int *kpos) +{ + unsigned int id ; + + if (verbosity >= 4) + strerr_warnt6x("from ", srcdir, ": adding identifier ", name, " of type ", typestr(type)) ; + + if (avltree_search(&names_map, name, &id)) + { + nameinfo_t *info = genalloc_s(nameinfo_t, &nameinfo) + id ; + if (type == SVTYPE_UNDEFINED) + { + if (verbosity >= 4) + strerr_warnt4x("identifier ", name, " was already declared with type ", typestr(info->type)) ; + switch (info->type) + { + case SVTYPE_ONESHOT : + case SVTYPE_LONGRUN : *kpos = info->kpos ; + default : break ; + } + } + else if (info->type == SVTYPE_UNDEFINED) + { + info->type = type ; + if (verbosity >= 4) + strerr_warnt4x("previously encountered identifier ", name, " has now type ", typestr(type)) ; + switch (type) + { + case SVTYPE_UNDEFINED : break ; + case SVTYPE_ONESHOT : + info->i = genalloc_len(oneshot_t, &be->oneshots) ; + info->kpos = keep.len ; + if (!stralloc_cats(&keep, name) || !stralloc_0(&keep)) dienomem() ; + break ; + case SVTYPE_LONGRUN : + info->i = genalloc_len(longrun_t, &be->longruns) ; + info->kpos = keep.len ; + if (!stralloc_cats(&keep, name) || !stralloc_0(&keep)) dienomem() ; + break ; + case SVTYPE_BUNDLE : + info->i = genalloc_len(bundle_t, &be->bundles) ; + break ; + } + } + else strerr_dief6x(1, "duplicate service definition: ", name, " defined in ", srcdir, " is already defined and has type ", typestr(info->type)) ; + *pos = info->pos ; + *kpos = info->kpos ; + return 1 ; + } + else + { + nameinfo_t info = + { + .pos = data.len, + .kpos = keep.len, + .i = type == SVTYPE_ONESHOT ? genalloc_len(oneshot_t, &be->oneshots) : SVTYPE_LONGRUN ? genalloc_len(longrun_t, &be->longruns) : SVTYPE_BUNDLE ? genalloc_len(bundle_t, &be->bundles) : 0, + .type = type + } ; + unsigned int namelen = str_len(name) ; + unsigned int i = genalloc_len(nameinfo_t, &nameinfo) ; + if (!stralloc_catb(&data, name, namelen + 1)) dienomem() ; + if (type == SVTYPE_ONESHOT || type == SVTYPE_LONGRUN) + if (!stralloc_catb(&keep, name, namelen + 1)) dienomem() ; + if (!genalloc_append(nameinfo_t, &nameinfo, &info)) dienomem() ; + if (!avltree_insert(&names_map, i)) dienomem() ; + *pos = info.pos ; + *kpos = info.kpos ; + if (namelen > be->maxnamelen) be->maxnamelen = namelen ; + return 0 ; + } +} + +static inline void add_specials (before_t *be) +{ + longrun_t service = + { + .common = + { + .ndeps = 0, + .depindex = genalloc_len(unsigned int, &be->indices), + .annotation_flags = 0, + .timeout = { 0, 0 } + }, + .srcdir = 0, + .servicedirname = keep.len, + .logrelated = 0, + .logtype = 0 + } ; + add_name(be, "(s6-rc-compile internals)", SPECIAL_NAME, SVTYPE_LONGRUN, &service.common.name, &service.common.kname) ; + if (!stralloc_cats(&keep, data.s + service.common.name) + || !stralloc_0(&keep)) dienomem() ; + if (!genalloc_append(longrun_t, &be->longruns, &service)) dienomem() ; +} + +static int uint_uniq (unsigned int const *list, unsigned int n, unsigned int pos) +{ + while (n--) if (pos == list[n]) return 0 ; + return 1 ; +} + +static int add_namelist (before_t *be, int dirfd, char const *srcdir, char const *name, char const *list, unsigned int *listindex, unsigned int *n) +{ + buffer b ; + unsigned int start = satmp.len ; + int cont = 1 ; + char buf[2048] ; + int fd = open_readatb(dirfd, list) ; + if (fd < 0) return 0 ; + buffer_init(&b, &fd_readsv, fd, buf, 2048) ; + *listindex = genalloc_len(unsigned int, &be->indices) ; + DBG("add_namelist %s %s: *listindex is %u", name, list, *listindex) ; + while (cont) + { + register int r = skagetln(&b, &satmp, '\n') ; + if (!r) cont = 0 ; + else + { + unsigned int i = start ; + if (r < 0) + { + if (errno != EPIPE) strerr_diefu6sys(111, "read ", srcdir, "/", name, "/", list) ; + if (!stralloc_0(&satmp)) dienomem() ; + cont = 0 ; + } + else satmp.s[satmp.len-1] = 0 ; + while (satmp.s[i] == ' ' || satmp.s[i] == '\t') i++ ; + if (satmp.s[i] && satmp.s[i] != '#') + { + unsigned int pos, kpos ; + add_name(be, name, satmp.s + i, SVTYPE_UNDEFINED, &pos, &kpos) ; + if (uint_uniq(genalloc_s(unsigned int, &be->indices) + *listindex, genalloc_len(unsigned int, &be->indices) - *listindex, pos)) + { + if (!genalloc_append(unsigned int, &be->indices, &pos)) + dienomem() ; + } + else if (verbosity >= 2) + strerr_warnw8x("in ", srcdir, "/", name, "/", list, ": name listed more than once: ", satmp.s + i) ; + } + satmp.len = start ; + } + } + close(fd) ; + *n = genalloc_len(unsigned int, &be->indices) - *listindex ; + DBG("add_namelist %s %s: *n is %u", name, list, *n) ; + return 1 ; +} + +static void read_script (before_t *be, int dirfd, char const *srcdir, char const *name, char const *script, unsigned int *argvindex, unsigned int *argc) +{ + buffer b ; + int r ; + char buf[4096] ; + int fd = open_readatb(dirfd, script) ; + if (fd < 0) strerr_diefu6sys(111, "open ", srcdir, "/", name, "/", script) ; + buffer_init(&b, &fd_readsv, fd, buf, 4096) ; + *argvindex = keep.len ; + r = el_parse_from_buffer(&keep, &b) ; + switch (r) + { + case -3 : strerr_dief7x(1, "syntax error in ", srcdir, "/", name, "/", script, ": missing }"); + case -2 : strerr_dief6x(1, "syntax error in ", srcdir, "/", name, "/", script) ; + case -1 : strerr_diefu6sys(111, "parse ", srcdir, "/", name, "/", script) ; + } + close(fd) ; + *argc = r ; + be->nargvs += r+1 ; +} + +static uint32 read_timeout (int dirfd, char const *srcdir, char const *name, char const *tname) +{ + char buf[64] ; + uint32 timeout = 0 ; + unsigned int r = openreadnclose_at(dirfd, tname, buf, 63) ; + if (!r) + { + if (errno && errno != ENOENT) + strerr_diefu6sys(111, "read ", srcdir, "/", name, "/", tname) ; + } + else + { + while (r && buf[r-1] == '\n') r-- ; + buf[r] = 0 ; + if (!uint320_scan(buf, &timeout)) + strerr_dief6x(1, "invalid ", srcdir, "/", name, "/", tname) ; + } + return timeout ; +} + +static void add_common (before_t *be, int dirfd, char const *srcdir, char const *name, common_t *common, servicetype_t svtype) +{ + common->annotation_flags = 0 ; + add_name(be, srcdir, name, svtype, &common->name, &common->kname) ; + if (!add_namelist(be, dirfd, srcdir, name, "dependencies", &common->depindex, &common->ndeps)) + { + if (errno != ENOENT) + strerr_diefu5sys(111, "open ", srcdir, "/", name, "/dependencies") ; + common->depindex = genalloc_len(unsigned int, &be->indices) ; + common->ndeps = 0 ; + } + common->timeout[0] = read_timeout(dirfd, srcdir, name, "timeout-down") ; + common->timeout[1] = read_timeout(dirfd, srcdir, name, "timeout-up") ; +} + +static inline void add_oneshot (before_t *be, int dirfd, char const *srcdir, char const *name) +{ + static unsigned int const special_dep = 0 ; + oneshot_t service ; + if (verbosity >= 3) strerr_warni3x(name, " has type ", "oneshot") ; + add_common(be, dirfd, srcdir, name, &service.common, SVTYPE_ONESHOT) ; + read_script(be, dirfd, srcdir, name, "up", &service.argvindex[1], &service.argc[1]) ; + read_script(be, dirfd, srcdir, name, "down", &service.argvindex[0], &service.argc[0]) ; + if (uint_uniq(genalloc_s(unsigned int, &be->indices) + service.common.depindex, service.common.ndeps, 0)) + { + if (!genalloc_append(unsigned int, &be->indices, &special_dep)) dienomem() ; + service.common.ndeps++ ; + } + else if (verbosity) + strerr_warnw6x(srcdir, "/", name, "/dependencies", " explicitly lists ", SPECIAL_NAME) ; + if (verbosity >= 4) + { + unsigned int i = service.common.ndeps ; + unsigned int const *p = genalloc_s(unsigned int const, &be->indices) + service.common.depindex ; + char fmt[UINT_FMT] ; + while (i--) + { + fmt[uint_fmt(fmt, *p)] = 0 ; + strerr_warnt7x("dependency from ", name, " to ", data.s + *p, " (", fmt, ")") ; + p++ ; + } + + } + if (!genalloc_append(oneshot_t, &be->oneshots, &service)) dienomem() ; +} + +static inline void add_longrun (before_t *be, int dirfd, char const *srcdir, char const *name) +{ + longrun_t service = { .srcdir = srcdir } ; + int fd ; + unsigned int logindex, n ; + if (verbosity >= 3) strerr_warni3x(name, " has type ", "longrun") ; + add_common(be, dirfd, srcdir, name, &service.common, SVTYPE_LONGRUN) ; + fd = open_readat(dirfd, "run") ; + if (fd < 0) + { + if (errno == ENOENT) + strerr_diefu5x(1, "find a run script in ", srcdir, "/", name, "/run") ; + else strerr_diefu5sys(111, "open ", srcdir, "/", name, "/run") ; + } + else + { + struct stat st ; + if (fstat(fd, &st) < 0) + strerr_diefu5sys(111, "stat ", srcdir, "/", name, "/run") ; + if (!S_ISREG(st.st_mode)) + strerr_dief4x(1, srcdir, "/", name, "/run is not a regular file") ; + } + close(fd) ; + if (add_namelist(be, dirfd, srcdir, name, "logger", &logindex, &n)) + { + register unsigned int const *deps = genalloc_s(unsigned int, &be->indices) + service.common.depindex ; + register unsigned int i = 0 ; + if (n != 1) + strerr_dief5x(1, srcdir, "/", name, "/logger", " should only contain one service name") ; + service.logtype = 1 ; + service.logrelated = genalloc_s(unsigned int, &be->indices)[logindex] ; + service.servicedirname = service.common.kname ; + for (; i < service.common.ndeps ; i++) if (service.logrelated == deps[i]) break ; + if (i < service.common.ndeps) + genalloc_setlen(unsigned int, &be->indices, logindex) ; + else service.common.ndeps++ ; + if (verbosity >= 3) + strerr_warni3x(name, " is a producer for ", data.s + service.logrelated) ; + } + else if (add_namelist(be, dirfd, srcdir, name, "producer", &logindex, &n)) + { + if (n != 1) + strerr_dief5x(1, srcdir, "/", name, "/producer", " should only contain one service name") ; + service.logtype = 2 ; + service.logrelated = genalloc_s(unsigned int, &be->indices)[logindex] ; + genalloc_setlen(unsigned int, &be->indices, logindex) ; + service.servicedirname = keep.len ; + if (!stralloc_cats(&keep, data.s + service.logrelated) + || !stralloc_catb(&keep, "/log", 5)) dienomem() ; + if (verbosity >= 3) + strerr_warni3x(name, " is a logger for ", data.s + service.logrelated) ; + } + else + { + service.logtype = 0 ; + service.servicedirname = service.common.kname ; + if (verbosity >= 3) strerr_warni2x(name, " has no logger or producer") ; + } + if (!genalloc_append(longrun_t, &be->longruns, &service)) dienomem() ; +} + +static inline void add_bundle (before_t *be, int dirfd, char const *srcdir, char const *name) +{ + bundle_t bundle ; + unsigned int dummy ; + if (verbosity >= 3) strerr_warni3x(name, " has type ", "bundle") ; + add_name(be, srcdir, name, SVTYPE_BUNDLE, &bundle.name, &dummy) ; + if (!add_namelist(be, dirfd, srcdir, name, "contents", &bundle.listindex, &bundle.n)) + strerr_diefu5sys(111, "open ", srcdir, "/", name, "/contents") ; + if (!genalloc_append(bundle_t, &be->bundles, &bundle)) dienomem() ; +} + +static inline void add_source (before_t *be, int dirfd, char const *srcdir, char const *name) +{ + char typestr[8] = "" ; + register unsigned int r ; + if (verbosity >= 2) strerr_warni4x("parsing ", srcdir, "/", name) ; + r = openreadnclose_at(dirfd, "type", typestr, 8) ; + if (!r) + { + if (!errno) errno = EINVAL ; + strerr_diefu5sys(111, "read ", srcdir, "/", name, "/type") ; + } + if (typestr[r-1] == '\n') r-- ; + typestr[r++] = 0 ; + if (!str_diff(typestr, "oneshot")) add_oneshot(be, dirfd, srcdir, name) ; + else if (!str_diff(typestr, "longrun")) add_longrun(be, dirfd, srcdir, name) ; + else if (!str_diff(typestr, "bundle")) add_bundle(be, dirfd, srcdir, name) ; + else strerr_dief6x(1, "invalid ", srcdir, "/", name, "/type", ": must be oneshot, longrun, or bundle") ; +} + +static inline void add_sources (before_t *be, char const *srcdir) +{ + DIR *dir ; + int fddir = open_readb(srcdir) ; + if (fddir < 0) strerr_diefu2sys(111, "open ", srcdir) ; + dir = fdopendir(fddir) ; + if (!dir) strerr_diefu2sys(111, "fdopendir ", srcdir) ; + for (;;) + { + struct stat st ; + int fd ; + direntry *d ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + if (lstat_at(fddir, d->d_name, &st) < 0) + strerr_diefu4sys(111, "lstat ", srcdir, "/", d->d_name) ; + if (!S_ISDIR(st.st_mode)) continue ; + if (d->d_name[str_chr(d->d_name, '\n')]) + strerr_dief3x(2, "subdirectory of ", srcdir, " contains a newline character") ; + fd = open_readatb(fddir, d->d_name) ; + if (fd < 0) strerr_diefu4sys(111, "open ", srcdir, "/", d->d_name) ; + add_source(be, fd, srcdir, d->d_name) ; + close(fd) ; + } + if (errno) strerr_diefu2sys(111, "readdir ", srcdir) ; + dir_close(dir) ; + close(fddir) ; +} + + + + /* Resolve all names and dependencies */ + + +typedef struct bundle_recinfo_s bundle_recinfo_t, *bundle_recinfo_t_ref ; +struct bundle_recinfo_s +{ + bundle_t const *oldb ; + bundle_t *newb ; + unsigned int n ; + unsigned int nlong ; + unsigned int nbits ; + uint32 const *indices ; + unsigned char *barray ; + unsigned char *mark ; + unsigned int source ; +} ; + +static void resolve_bundle_rec (bundle_recinfo_t *recinfo, unsigned int i) +{ + if (!(recinfo->mark[i] & 2)) + { + bundle_t const *me = recinfo->oldb + i ; + uint32 const *listindex = recinfo->indices + me->listindex ; + unsigned int j = 0 ; + if (recinfo->mark[i] & 1) + strerr_dief4x(1, "cyclic bundle definition: resolution of ", data.s + recinfo->oldb[recinfo->source].name, " encountered a cycle involving ", data.s + me->name) ; + recinfo->mark[i] |= 1 ; + for (; j < me->n ; j++) + { + unsigned int id ; + register nameinfo_t const *p ; + avltree_search(&names_map, data.s + listindex[j], &id) ; + p = genalloc_s(nameinfo_t, &nameinfo) + id ; + switch (p->type) + { + case SVTYPE_ONESHOT : + bitarray_set(recinfo->barray + i * recinfo->nbits, recinfo->nlong + p->i) ; + break ; + case SVTYPE_LONGRUN : + bitarray_set(recinfo->barray + i * recinfo->nbits, p->i) ; + break ; + case SVTYPE_BUNDLE : + resolve_bundle_rec(recinfo, p->i) ; + bitarray_or(recinfo->barray + i * recinfo->nbits, recinfo->barray + i * recinfo->nbits, recinfo->barray + p->i * recinfo->nbits, recinfo->n) ; + break ; + default : + strerr_dief4x(1, "during resolution of bundle ", data.s + me->name, ": undefined service name ", data.s + p->pos) ; + } + } + recinfo->mark[i] |= 2 ; + } +} + +static inline unsigned int resolve_bundles (bundle_t const *oldb, bundle_t *newb, unsigned int nshort, unsigned int nlong, unsigned int nbundles, uint32 const *indices, unsigned char *barray) +{ + unsigned int total = 0, i = 0 ; + unsigned char mark[nbundles] ; + bundle_recinfo_t recinfo = { .oldb = oldb, .newb = newb, .n = nshort + nlong, .nlong = nlong, .nbits = bitarray_div8(nshort + nlong), .indices = indices, .barray = barray, .mark = mark } ; + if (verbosity >= 2) strerr_warni1x("resolving bundle names") ; + byte_zero(barray, recinfo.nbits * nbundles) ; + byte_zero(mark, nbundles) ; + for (; i < nbundles ; i++) + { + newb[i].name = oldb[i].name ; + recinfo.source = i ; + resolve_bundle_rec(&recinfo, i) ; + } + for (i = 0 ; i < nbundles ; i++) + { + newb[i].listindex = total ; + newb[i].n = bitarray_countones(barray + i * recinfo.nbits, recinfo.n) ; + total += newb[i].n ; + } + return total ; +} + +static inline void flatlist_bundles (bundle_t *bundles, unsigned int nbundles, unsigned int nbits, unsigned char const *barray, uint32 *bdeps) +{ + unsigned int i = 0 ; + if (verbosity >= 3) strerr_warni1x("converting bundle array") ; + for (; i < nbundles ; i++) + { + unsigned char const *mybits = barray + i * nbits ; + uint32 *mydeps = bdeps + bundles[i].listindex ; + unsigned int j = 0, k = 0 ; + for (; k < bundles[i].n ; j++) + if (bitarray_peek(mybits, j)) mydeps[k++] = j ; + } +} + +static void resolve_deps (common_t const *me, unsigned int nlong, unsigned int n, unsigned int nbits, uint32 const *indices, unsigned char *sarray, unsigned char const *barray) +{ + unsigned int j = 0 ; + for (; j < me->ndeps ; j++) + { + unsigned int id ; + register nameinfo_t const *p ; + avltree_search(&names_map, data.s + indices[me->depindex + j], &id) ; + p = genalloc_s(nameinfo_t, &nameinfo) + id ; + switch (p->type) + { + case SVTYPE_ONESHOT : + bitarray_set(sarray, nlong + p->i) ; + if (verbosity >= 4) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, nlong + p->i)] = 0 ; + strerr_warnt7x("atomic ", data.s + me->name, " depends on oneshot ", data.s + p->pos, " (", fmt, ")") ; + } + break ; + case SVTYPE_LONGRUN : + bitarray_set(sarray, p->i) ; + if (verbosity >= 4) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, p->i)] = 0 ; + strerr_warnt7x("atomic ", data.s + me->name, " depends on longrun ", data.s + p->pos, " (", fmt, ")") ; + } + break ; + case SVTYPE_BUNDLE : + bitarray_or(sarray, sarray, barray + p->i * nbits, n) ; + if (verbosity >= 4) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, nlong + p->i)] = 0 ; + strerr_warnt4x("atomic ", data.s + me->name, " depends on bundle ", data.s + p->pos) ; + } + break ; + default : + strerr_dief4x(1, "during dependency resolution for service ", data.s + me->name, ": undefined service name ", data.s + p->pos) ; + } + } +} + +static inline void check_logger (longrun_t const *longruns, unsigned int i) +{ + unsigned int j ; + register nameinfo_t const *p ; + if (!longruns[i].logtype) return ; + avltree_search(&names_map, data.s + longruns[i].logrelated, &j) ; + p = genalloc_s(nameinfo_t, &nameinfo) + j ; + switch (p->type) + { + case SVTYPE_LONGRUN : + { + unsigned int k ; + register nameinfo_t const *q ; + if (longruns[p->i].logtype != 3 - longruns[i].logtype) goto err ; + avltree_search(&names_map, data.s + longruns[p->i].logrelated, &k) ; + q = genalloc_s(nameinfo_t, &nameinfo) + k ; + if (q->type != SVTYPE_LONGRUN) goto err ; + if (q->i != i) goto err ; + break ; + err: + strerr_dief7x(1, "longrun service ", data.s + longruns[i].common.name, " declares a ", longruns[i].logtype == 2 ? "producer" : "logger", " named ", data.s + p->pos, " that does not declare it back ") ; + } + case SVTYPE_ONESHOT : + case SVTYPE_BUNDLE : + strerr_dief8x(1, "longrun service ", data.s + longruns[i].common.name, " declares a ", longruns[i].logtype == 2 ? "producer" : "logger", " named ", data.s + p->pos, " of type ", p->type == SVTYPE_BUNDLE ? "bundle" : "oneshot") ; + default : + strerr_dief7x(1, "longrun service ", data.s + longruns[i].common.name, " declares a ", longruns[i].logtype == 2 ? "producer" : "logger", " named ", data.s + p->pos, " that is not defined") ; + } +} + +static inline unsigned int ugly_bitarray_vertical_countones (unsigned char const *sarray, unsigned int n, unsigned int i) +{ + unsigned int m = 0, j = 0, nbits = bitarray_div8(n) ; + for (; j < n ; j++) + if (bitarray_peek(sarray + j * nbits, i)) m++ ; + return m ; +} + +static inline unsigned int resolve_services (s6rc_db_t *db, before_t const *be, char const **srcdirs, unsigned char *sarray, unsigned char const *barray) +{ + oneshot_t const *oneshots = genalloc_s(oneshot_t const, &be->oneshots) ; + longrun_t const *longruns = genalloc_s(longrun_t const, &be->longruns) ; + uint32 const *indices = genalloc_s(uint32 const, &be->indices) ; + unsigned int n = db->nshort + db->nlong ; + unsigned int nbits = bitarray_div8(n) ; + unsigned int total[2] = { 0, 0 } ; + unsigned int i = 0 ; + if (verbosity >= 2) strerr_warni1x("resolving service names") ; + byte_zero(sarray, nbits * n) ; + for (; i < db->nlong ; i++) + { + db->services[i].type = 1 ; + db->services[i].name = longruns[i].common.kname ; + db->services[i].flags = longruns[i].common.annotation_flags ; + db->services[i].timeout[0] = longruns[i].common.timeout[0] ; + db->services[i].timeout[1] = longruns[i].common.timeout[1] ; + db->services[i].x.longrun.servicedir = longruns[i].servicedirname ; + srcdirs[i] = longruns[i].srcdir ; + resolve_deps(&longruns[i].common, db->nlong, n, nbits, indices, sarray + i * nbits, barray) ; + check_logger(longruns, i) ; + } + for (i = 0 ; i < db->nshort ; i++) + { + db->services[db->nlong + i].type = 0 ; + db->services[db->nlong + i].name = oneshots[i].common.kname ; + db->services[db->nlong + i].flags = oneshots[i].common.annotation_flags ; + db->services[db->nlong + i].timeout[0] = oneshots[i].common.timeout[0] ; + db->services[db->nlong + i].timeout[1] = oneshots[i].common.timeout[1] ; + db->services[db->nlong + i].x.oneshot.argc[0] = oneshots[i].argc[0] ; + db->services[db->nlong + i].x.oneshot.argv[0] = oneshots[i].argvindex[0] ; + db->services[db->nlong + i].x.oneshot.argc[1] = oneshots[i].argc[1] ; + db->services[db->nlong + i].x.oneshot.argv[1] = oneshots[i].argvindex[1] ; + resolve_deps(&oneshots[i].common, db->nlong, n, nbits, indices, sarray + (db->nlong + i) * nbits, barray) ; + } + + for (i = 0 ; i < n ; i++) + { + db->services[i].deps[0] = total[0] ; + db->services[i].ndeps[0] = ugly_bitarray_vertical_countones(sarray, n, i) ; + total[0] += db->services[i].ndeps[0] ; + db->services[i].deps[1] = total[1] ; + db->services[i].ndeps[1] = bitarray_countones(sarray + i * nbits, n) ; + total[1] += db->services[i].ndeps[1] ; + } + return total[1] ; +} + +static inline void flatlist_services (s6rc_db_t *db, unsigned char const *sarray) +{ + unsigned int n = db->nshort + db->nlong ; + unsigned int nbits = bitarray_div8(n) ; + unsigned int i = 0 ; + if (verbosity >= 3) strerr_warni1x("converting service dependency array") ; + for (; i < n ; i++) + { + uint32 *mydeps = db->deps + db->services[i].deps[0] ; + unsigned int j = 0, k = 0 ; + for (; k < db->services[i].ndeps[0] ; j++) + if (bitarray_peek(sarray + j * nbits, i)) mydeps[k++] = j ; + mydeps = db->deps + db->ndeps + db->services[i].deps[1] ; + j = 0 ; k = 0 ; + for (; k < db->services[i].ndeps[1] ; j++) + if (bitarray_peek(sarray + i * nbits, j)) mydeps[k++] = j ; + } + if (verbosity >= 3) strerr_warni1x("checking for dependency cycles") ; + nbits = s6rc_db_check_depcycles(db, 1, &i) ; + if (nbits < n) + strerr_dief4x(1, "cyclic service definition: resolution of ", db->string + db->services[nbits].name, " encountered a cycle involving ", db->string + db->services[i].name) ; +} + + + + /* Write the compiled database */ + + +static void cleanup (char const *compiled) +{ + int e = errno ; + rm_rf(compiled) ; + errno = e ; +} + +static void auto_dir (char const *compiled, char const *dir) +{ + unsigned int clen = str_len(compiled) ; + unsigned int dlen = str_len(dir) ; + char fn[clen + dlen + 2] ; + byte_copy(fn, clen, compiled) ; + fn[clen] = dlen ? '/' : 0 ; + byte_copy(fn + clen + 1, dlen + 1, dir) ; + if (mkdir(fn, 0755) < 0) + { + cleanup(compiled) ; + strerr_diefu2sys(111, "mkdir ", fn) ; + } +} + +static void auto_file (char const *compiled, char const *file, char const *s, unsigned int n) +{ + unsigned int clen = str_len(compiled) ; + unsigned int flen = str_len(file) ; + char fn[clen + flen + 2] ; + byte_copy(fn, clen, compiled) ; + fn[clen] = '/' ; + byte_copy(fn + clen + 1, flen + 1, file) ; + if (!openwritenclose_unsafe(fn, s, n)) + { + cleanup(compiled) ; + strerr_diefu2sys(111, "write to ", fn) ; + } +} + +static void auto_rights (char const *compiled, char const *file, mode_t mode) +{ + unsigned int clen = str_len(compiled) ; + unsigned int flen = str_len(file) ; + char fn[clen + flen + 2] ; + byte_copy(fn, clen, compiled) ; + fn[clen] = '/' ; + byte_copy(fn + clen + 1, flen + 1, file) ; + if (chmod(fn, mode) < 0) + { + cleanup(compiled) ; + strerr_diefu2sys(111, "chmod ", fn) ; + } +} + +static inline void init_compiled (char const *compiled) +{ + if (mkdir(compiled, 0755) < 0) + strerr_diefu2sys(111, "mkdir ", compiled) ; + auto_dir(compiled, "servicedirs") ; +} + +static inline void write_sizes (char const *compiled, s6rc_db_t const *db) +{ + char pack[20] ; + if (verbosity >= 3) strerr_warni3x("writing ", compiled, "/n") ; + uint32_pack_big(pack, (uint32)db->nshort) ; + uint32_pack_big(pack + 4, (uint32)db->nlong) ; + uint32_pack_big(pack + 8, (uint32)db->stringlen) ; + uint32_pack_big(pack + 12, (uint32)db->nargvs) ; + uint32_pack_big(pack + 16, (uint32)db->ndeps) ; + auto_file(compiled, "n", pack, 20) ; +} + +static inline void write_specials (char const *compiled) +{ + auto_dir(compiled, "servicedirs/" SPECIAL_NAME) ; + auto_dir(compiled, "servicedirs/" SPECIAL_NAME "/data") ; + auto_dir(compiled, "servicedirs/" SPECIAL_NAME "/data/rules") ; + auto_dir(compiled, "servicedirs/" SPECIAL_NAME "/data/rules/uid") ; + auto_dir(compiled, "servicedirs/" SPECIAL_NAME "/data/rules/uid/default") ; + auto_file(compiled, "servicedirs/" SPECIAL_NAME "/data/rules/uid/default/deny", "", 0) ; + auto_dir(compiled, "servicedirs/" SPECIAL_NAME "/data/rules/uid/0") ; + auto_file(compiled, "servicedirs/" SPECIAL_NAME "/data/rules/uid/0/allow", "", 0) ; + auto_file(compiled, "servicedirs/" SPECIAL_NAME "/run", SPECIAL_RUNSCRIPT, sizeof(SPECIAL_RUNSCRIPT) - 1) ; + auto_rights(compiled, "servicedirs/" SPECIAL_NAME "/run", 0755) ; +} + +static inline void write_resolve (char const *compiled, s6rc_db_t const *db, bundle_t const *bundles, unsigned int nbundles, uint32 const *bdeps) +{ + unsigned int clen = str_len(compiled) ; + int fd ; + struct cdb_make c = CDB_MAKE_ZERO ; + unsigned int i = db->nshort + db->nlong ; + char fn[clen + 13] ; + if (verbosity >= 3) strerr_warni3x("writing ", compiled, "/resolve.cdb") ; + byte_copy(fn, clen, compiled) ; + byte_copy(fn + clen, 13, "/resolve.cdb") ; + fd = open_trunc(fn) ; + if (fd < 0 || ndelay_off(fd) < 0) + { + cleanup(compiled) ; + strerr_diefu2sys(111, "open_trunc ", fn) ; + } + if (cdb_make_start(&c, fd) < 0) + { + cleanup(compiled) ; + strerr_diefu2sys(111, "cdb_make_start on ", fn) ; + } + + /* atomic services */ + while (i--) + { + char pack[4] ; + uint32_pack_big(pack, (uint32)i) ; + if (cdb_make_add(&c, db->string + db->services[i].name, str_len(db->string + db->services[i].name), pack, 4) < 0) + { + cleanup(compiled) ; + strerr_diefu1sys(111, "cdb_make_add") ; + } + } + + /* bundles */ + i = nbundles ; + while (i--) + { + unsigned int j = 0 ; + char pack[(bundles[i].n << 2) + 1] ; /* +1 because braindead C standard */ + for (; j < bundles[i].n ; j++) + uint32_pack_big(pack + (j << 2), bdeps[bundles[i].listindex + j]) ; + if (cdb_make_add(&c, data.s + bundles[i].name, str_len(data.s + bundles[i].name), pack, bundles[i].n << 2) < 0) + { + cleanup(compiled) ; + strerr_diefu1sys(111, "cdb_make_add") ; + } + } + + if (cdb_make_finish(&c) < 0 || fsync(fd) < 0) + { + cleanup(compiled) ; + strerr_diefu2sys(111, "write to ", fn) ; + } + close(fd) ; +} + +static int filecopy (char const *src, char const *dst, mode_t mode) +{ + int d ; + int s = open_readb(src) ; + if (s < 0) return 0 ; + d = open3(dst, O_WRONLY | O_CREAT | O_TRUNC, mode) ; + if (d < 0) + { + close(s) ; + return 0 ; + } + if (fd_cat(s, d) < 0) goto err ; + close(s) ; + close(d) ; + return 1 ; + +err: + { + register int e = errno ; + close(s) ; + close(d) ; + errno = e ; + } + return 0 ; +} + +static void dircopy (char const *compiled, char const *srcfn, char const *dstfn) +{ + struct stat st ; + if (stat(srcfn, &st) < 0) + { + if (errno == ENOENT) return ; + cleanup(compiled) ; + strerr_diefu2sys(111, "stat ", srcfn) ; + } + if (!S_ISDIR(st.st_mode)) + { + cleanup(compiled) ; + strerr_dief2x(1, srcfn, " is not a directory") ; + } + if (!hiercopy(srcfn, dstfn)) + { + cleanup(compiled) ; + strerr_diefu4sys(111, "recursively copy ", srcfn, " to ", dstfn) ; + } +} + +static void write_servicedir (char const *compiled, char const *srcdir, char const *src, char const *dst) +{ + unsigned int clen = str_len(compiled) ; + unsigned int srcdirlen = str_len(srcdir) ; + unsigned int srclen = str_len(src) ; + unsigned int dstlen = str_len(dst) ; + char dstfn[clen + 19 + dstlen] ; + char srcfn[srcdirlen + srclen + 8] ; + byte_copy(dstfn, clen, compiled) ; + byte_copy(dstfn + clen, 13, "/servicedirs/") ; + byte_copy(dstfn + clen + 13, dstlen + 1, dst) ; + if (mkdir(dstfn, 0755) < 0) + { + cleanup(compiled) ; + strerr_diefu2sys(111, "mkdir ", dstfn) ; + } + byte_copy(dstfn + clen + 13 + dstlen, 5, "/run") ; + byte_copy(srcfn, srcdirlen, srcdir) ; + srcfn[srcdirlen] = '/' ; + byte_copy(srcfn + srcdirlen + 1, srclen, src) ; + srcfn[srcdirlen + 1 + srclen] = '/' ; + byte_copy(srcfn + srcdirlen + srclen + 2, 5, "/run") ; + if (!filecopy(srcfn, dstfn, 0755)) + { + cleanup(compiled) ; + strerr_diefu4sys(111, "copy ", srcfn, " to ", dstfn) ; + } + byte_copy(dstfn + clen + 14 + dstlen, 5, "data") ; + byte_copy(srcfn + srcdirlen + srclen + 3, 5, "data") ; + dircopy(compiled, srcfn, dstfn) ; + byte_copy(dstfn + clen + 14 + dstlen, 4, "env") ; + byte_copy(srcfn + srcdirlen + srclen + 3, 4, "env") ; + dircopy(compiled, srcfn, dstfn) ; +} + +static inline void write_servicedirs (char const *compiled, s6rc_db_t const *db, char const *const *srcdirs, unsigned int maxnamelen) +{ + unsigned int clen = str_len(compiled) ; + unsigned int i = 1 ; + char fn[clen + 23 + maxnamelen] ; + char islogger[db->nlong] ; + if (verbosity >= 3) strerr_warni3x("writing ", compiled, "/servicedirs") ; + byte_copy(fn, clen, compiled) ; + byte_copy(fn + clen, 12, "/servicedirs") ; + fn[clen+12] = '/' ; + byte_zero(islogger, db->nlong) ; + for (; i < db->nlong ; i++) + { + char const *servicedirname = db->string + db->services[i].x.longrun.servicedir ; + islogger[i] = servicedirname[str_chr(servicedirname, '/')] ; + if (!islogger[i]) + write_servicedir(compiled, srcdirs[i], db->string + db->services[i].name, servicedirname) ; + } + for (i = 1 ; i < db->nlong ; i++) + { + if (islogger[i]) + write_servicedir(compiled, srcdirs[i], db->string + db->services[i].name, db->string + db->services[i].x.longrun.servicedir) ; + } +} + +static inline int write_service (buffer *b, s6rc_service_t const *sv) +{ + char pack[50] ; + unsigned int m ; + uint32_pack_big(pack, sv->name) ; + uint32_pack_big(pack + 4, sv->flags) ; + uint32_pack_big(pack + 8, sv->timeout[0]) ; + uint32_pack_big(pack + 12, sv->timeout[1]) ; + uint32_pack_big(pack + 16, sv->ndeps[0]) ; + uint32_pack_big(pack + 20, sv->ndeps[1]) ; + uint32_pack_big(pack + 24, sv->deps[0]) ; + uint32_pack_big(pack + 28, sv->deps[1]) ; + pack[32] = sv->type ; + if (sv->type) + { + uint32_pack_big(pack + 33, sv->x.longrun.servicedir) ; + m = 37 ; + } + else + { + uint32_pack_big(pack + 33, sv->x.oneshot.argc[0]) ; + uint32_pack_big(pack + 37, sv->x.oneshot.argv[0]) ; + uint32_pack_big(pack + 41, sv->x.oneshot.argc[1]) ; + uint32_pack_big(pack + 45, sv->x.oneshot.argv[1]) ; + m = 49 ; + } + pack[m++] = '\376' ; + return (buffer_put(b, pack, m) == m) ; +} + +static inline void write_db (char const *compiled, s6rc_db_t const *db) +{ + unsigned int clen = str_len(compiled) ; + buffer b ; + int fd ; + char buf[4096] ; + char dbfn[clen + 4] ; + byte_copy(dbfn, clen, compiled) ; + byte_copy(dbfn + clen, 4, "/db") ; + if (verbosity >= 3) strerr_warni2x("writing ", dbfn) ; + fd = open_trunc(dbfn) ; + if (fd < 0) + { + cleanup(compiled) ; + strerr_diefu3sys(111, "open ", dbfn, " for writing") ; + } + if (ndelay_off(fd) < 0) + { + cleanup(compiled) ; + strerr_diefu2sys(111, "ndelay_off ", dbfn) ; + } + buffer_init(&b, &fd_writesv, fd, buf, 4096) ; + + if (buffer_put(&b, S6RC_DB_BANNER_START, S6RC_DB_BANNER_START_LEN) < 0) + goto err ; + + if (buffer_put(&b, db->string, db->stringlen) < 0) goto err ; + + { + unsigned int i = db->ndeps << 1 ; + uint32 const *deps = db->deps ; + while (i--) + { + char pack[4] ; + uint32_pack_big(pack, *deps++) ; + if (buffer_put(&b, pack, 4) < 0) goto err ; + } + } + + { + unsigned int i = db->nshort + db->nlong ; + s6rc_service_t const *sv = db->services ; + while (i--) if (!write_service(&b, sv++)) goto err ; + } + + if (buffer_putflush(&b, S6RC_DB_BANNER_END, S6RC_DB_BANNER_END_LEN) < 0) + goto err ; + + if (fsync(fd) < 0) goto err ; + close(fd) ; + return ; + err: + cleanup(compiled) ; + strerr_diefu2sys(111, "write to ", dbfn) ; +} + +static inline void write_compiled (char const *compiled, s6rc_db_t const *db, char const *const *srcdirs, bundle_t const *bundles, unsigned int nbundles, uint32 const *bdeps, unsigned int maxnamelen) +{ + if (verbosity >= 2) strerr_warni2x("writing compiled information to ", compiled) ; + init_compiled(compiled) ; + write_sizes(compiled, db) ; + write_resolve(compiled, db, bundles, nbundles, bdeps) ; + write_db(compiled, db) ; + write_specials(compiled) ; + write_servicedirs(compiled, db, srcdirs, maxnamelen) ; +} + +int main (int argc, char const *const *argv) +{ + before_t before = BEFORE_ZERO ; + char const *compiled ; + PROG = "s6-rc-compile" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "v:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 2) dieusage() ; + compiled = *argv++ ; + add_specials(&before) ; + for (; *argv ; argv++) add_sources(&before, *argv) ; + + { + unsigned int n = genalloc_len(oneshot_t, &before.oneshots) + genalloc_len(longrun_t, &before.longruns) ; + unsigned int nbits = bitarray_div8(n) ; + unsigned int nbundles = genalloc_len(bundle_t, &before.bundles) ; + unsigned int maxnamelen = before.maxnamelen ; + s6rc_service_t servicesblob[n] ; + s6rc_db_t db = + { + .services = servicesblob, + .nshort = genalloc_len(oneshot_t, &before.oneshots), + .nlong = genalloc_len(longrun_t, &before.longruns), + .stringlen = keep.len, + .nargvs = before.nargvs, + .string = keep.s + } ; + char const *srcdirs[db.nlong] ; + unsigned char sarray[nbits * n] ; + bundle_t bundles[nbundles] ; + unsigned char barray[nbits * nbundles] ; + unsigned int nbdeps = resolve_bundles(genalloc_s(bundle_t, &before.bundles), bundles, db.nshort, db.nlong, nbundles, genalloc_s(uint32, &before.indices), barray) ; + uint32 bdeps[nbdeps] ; + + genalloc_free(bundle_t, &before.bundles) ; + flatlist_bundles(bundles, nbundles, nbits, barray, bdeps) ; + db.ndeps = resolve_services(&db, &before, srcdirs, sarray, barray) ; + genalloc_free(uint32, &before.indices) ; + genalloc_free(oneshot_t, &before.oneshots) ; + genalloc_free(longrun_t, &before.longruns) ; + genalloc_free(nameinfo_t, &nameinfo) ; + avltree_free(&names_map) ; + { + uint32 deps[db.ndeps << 1] ; + db.deps = deps ; + flatlist_services(&db, sarray) ; + write_compiled(compiled, &db, srcdirs, bundles, nbundles, bdeps, maxnamelen) ; + } + } + + return 0 ; +} diff --git a/src/s6-rc/s6-rc-compile.h b/src/s6-rc/s6-rc-compile.h new file mode 100644 index 0000000..7d2bc19 --- /dev/null +++ b/src/s6-rc/s6-rc-compile.h @@ -0,0 +1,21 @@ +/* ISC license. */ + +#ifndef S6_RC_COMPILE_H +#define S6_RC_COMPILE_H + +#include +#include + +#define SPECIAL_NAME "s6-oneshot-runner" + +#define SPECIAL_RUNSCRIPT \ +"#!" EXECLINE_EXTBINPREFIX "execlineb -P\n" \ +EXECLINE_EXTBINPREFIX "fdmove -c 2 1\n" \ +S6_EXTBINPREFIX "s6-ipcserver-socketbinder -- s\n" \ +S6_EXTBINPREFIX "s6-notifywhenup -f --\n" \ +S6_EXTBINPREFIX "s6-ipcserverd -1 --\n" \ +S6_EXTBINPREFIX "s6-ipcserver-access -E -l0 -i data/rules --\n" \ +S6_EXTBINPREFIX "s6-sudod -t 2000 --\n" + +#endif + diff --git a/src/s6-rc/s6-rc-db.c b/src/s6-rc/s6-rc-db.c new file mode 100644 index 0000000..d0bd845 --- /dev/null +++ b/src/s6-rc/s6-rc-db.c @@ -0,0 +1,484 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-rc-db [ -u | -d ] [ -l live ] [ -c compiled ] command... (use s6-rc-db help for more information)" +#define dieusage() strerr_dieusage(100, USAGE) + +static char const *compiled = 0 ; +static int fdcompiled = -1 ; +static s6rc_db_t *db ; + +static void print_bundle_contents (char const *name) +{ + cdb_t c = CDB_ZERO ; + int fd = open_readatb(fdcompiled, "resolve.cdb") ; + register int r ; + if (fd < 0) strerr_diefu3sys(111, "open ", compiled, "/resolve.cdb") ; + if (!cdb_init_map(&c, fd, 1)) + strerr_diefu3sys(111, "cdb_init ", compiled, "/resolve.cdb") ; + r = cdb_find(&c, name, str_len(name)) ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/resolve.cdb") ; + if (!r) strerr_dief3x(1, name, " is not a valid identifier in ", compiled) ; + if (cdb_datalen(&c) == 4) + { + uint32 x ; + char pack[4] ; + if (cdb_read(&c, pack, 4, cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + uint32_unpack_big(pack, &x) ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (!str_diff(name, db->string + db->services[x].name)) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents an atomic service") ; + if (buffer_puts(buffer_1, db->string + db->services[x].name) < 0) + strerr_diefu1sys(111, "write to stdout") ; + } + else + { + unsigned int len = cdb_datalen(&c) >> 2 ; + char pack[cdb_datalen(&c) + 1] ; + register char const *p = pack ; + if (cdb_datalen(&c) & 3) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (cdb_read(&c, pack, cdb_datalen(&c), cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + while (len--) + { + uint32 x ; + uint32_unpack_big(p, &x) ; p += 4 ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (buffer_puts(buffer_1, db->string + db->services[x].name) < 0 + || buffer_put(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + } + } + cdb_free(&c) ; + close(fd) ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_services (unsigned int from, unsigned int to) +{ + for (; from < to ; from++) + { + if (buffer_puts(buffer_1, db->string + db->services[from].name) < 0 + || buffer_put(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + } + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_all (int bundlesonly) +{ + cdb_t c = CDB_ZERO ; + uint32 kpos ; + int fd = open_readatb(fdcompiled, "resolve.cdb") ; + if (fd < 0) strerr_diefu3sys(111, "open ", compiled, "/resolve.cdb") ; + if (!cdb_init_map(&c, fd, 1)) + strerr_diefu3sys(111, "cdb_init ", compiled, "/resolve.cdb") ; + cdb_traverse_init(&c, &kpos) ; + for (;;) + { + register int r = cdb_nextkey(&c, &kpos) ; + char name[cdb_keylen(&c) + 1] ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/resolve.cdb") ; + if (!r) break ; + if (cdb_read(&c, name, cdb_keylen(&c), cdb_keypos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + if (bundlesonly && cdb_datalen(&c) == 4) + { + uint32 x ; + char pack[4] ; + if (cdb_read(&c, pack, 4, cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + uint32_unpack_big(pack, &x) ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (!byte_diff(name, cdb_keylen(&c), db->string + db->services[x].name) + && !db->string[db->services[x].name + cdb_keylen(&c)]) + continue ; + } + name[cdb_keylen(&c)] = '\n' ; + if (buffer_put(buffer_1, name, cdb_keylen(&c) + 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + } + cdb_free(&c) ; + close(fd) ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static unsigned int resolve_service (char const *name) +{ + cdb_t c = CDB_ZERO ; + int fd = open_readatb(fdcompiled, "resolve.cdb") ; + uint32 x ; + char pack[4] ; + unsigned int len ; + register int r ; + if (fd < 0) strerr_diefu3sys(111, "open ", compiled, "/resolve.cdb") ; + if (!cdb_init_map(&c, fd, 1)) + strerr_diefu3sys(111, "cdb_init ", compiled, "/resolve.cdb") ; + r = cdb_find(&c, name, str_len(name)) ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/resolve.cdb") ; + if (!r) strerr_dief3x(1, name, " is not a valid identifier in ", compiled) ; + len = cdb_datalen(&c) >> 2 ; + if (cdb_datalen(&c) != 4) return db->nshort + db->nlong ; + if (cdb_read(&c, pack, 4, cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + uint32_unpack_big(pack, &x) ; + cdb_free(&c) ; + close(fd) ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + if (str_diff(name, db->string + db->services[x].name)) + x = db->nshort + db->nlong ; + return x ; +} + +static void print_type (char const *name) +{ + unsigned int n = resolve_service(name) ; + char const *s = n >= db->nshort + db->nlong ? "bundle" : db->services[n].type ? "longrun" : "oneshot" ; + if (buffer_puts(buffer_1, s) < 0 + || buffer_putflush(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_timeout (char const *name, int h) +{ + unsigned int n = resolve_service(name) ; + unsigned int len ; + char fmt[UINT32_FMT] ; + if (n >= db->nshort + db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a bundle") ; + len = uint32_fmt(fmt, db->services[n].timeout[h]) ; + fmt[len++] = '\n' ; + if (buffer_putflush(buffer_1, fmt, len) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_servicedir (char const *name) +{ + unsigned int n = resolve_service(name) ; + if (n >= db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " does not represent a longrun service") ; + if (buffer_puts(buffer_1, db->string + db->services[n].x.longrun.servicedir) < 0 + || buffer_putflush(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_deps (char const *name, int h) +{ + uint32 const *p ; + unsigned int ndeps ; + unsigned int n = resolve_service(name) ; + if (n >= db->nshort + db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a bundle") ; + p = db->deps + h * db->ndeps + db->services[n].deps[h] ; + ndeps = db->services[n].ndeps[h] ; + while (ndeps--) + if (buffer_puts(buffer_1, db->string + db->services[*p++].name) < 0 + || buffer_put(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_script (char const *name, int h) +{ + unsigned int argc ; + char *const *argv ; + unsigned int n = resolve_service(name) ; + if (n >= db->nshort + db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a bundle") ; + if (n < db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a longrun service") ; + argv = db->argvs + db->services[n].x.oneshot.argv[h] ; + argc = db->services[n].x.oneshot.argc[h] ; + while (argc--) + if (buffer_puts(buffer_1, *argv++) < 0 + || buffer_put(buffer_1, "\0", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static inline void print_flags (char const *name) +{ + unsigned int n = resolve_service(name) ; + char fmt[9] = "00000000\n" ; + if (n >= db->nshort + db->nlong) + strerr_dief5x(1, "in database ", compiled, ": identifier ", name, " represents a bundle") ; + uint320_xfmt(fmt, db->services[n].flags, 8) ; + if (buffer_putflush(buffer_1, fmt, 9) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void print_atomics (char const *const *argv, int h, int doclosure) +{ + unsigned int n = db->nshort + db->nlong ; + cdb_t c = CDB_ZERO ; + int fd = open_readatb(fdcompiled, "resolve.cdb") ; + unsigned char state[n] ; + if (fd < 0) strerr_diefu3sys(111, "open ", compiled, "/resolve.cdb") ; + if (!cdb_init_map(&c, fd, 1)) + strerr_diefu3sys(111, "cdb_init ", compiled, "/resolve.cdb") ; + byte_zero(state, n) ; + for (; *argv ; argv++) + { + register int r = cdb_find(&c, *argv, str_len(*argv)) ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/resolve.cdb") ; + if (!r) strerr_dief3x(1, *argv, " is not a valid identifier in ", compiled) ; + { + unsigned int len = cdb_datalen(&c) >> 2 ; + char pack[cdb_datalen(&c) + 1] ; + register char const *p = pack ; + if (cdb_read(&c, pack, cdb_datalen(&c), cdb_datapos(&c)) < 0) + strerr_diefu3sys(111, "cdb_read ", compiled, "/resolve.cdb") ; + while (len--) + { + uint32 x ; + uint32_unpack_big(p, &x) ; p += 4 ; + if (x >= db->nshort + db->nlong) + strerr_dief2x(4, "invalid database in ", compiled) ; + state[x] |= 1 ; + } + } + } + cdb_free(&c) ; + close(fd) ; + if (doclosure) s6rc_graph_closure(db, state, 0, h) ; + while (n--) if (state[n]) + if (buffer_puts(buffer_1, db->string + db->services[n].name) < 0 + || buffer_put(buffer_1, "\n", 1) < 0) + strerr_diefu1sys(111, "write to stdout") ; + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static inline void print_help (void) +{ + static char const *help = +"s6-rc-db help\n" +"s6-rc-db check\n" +"s6-rc-db list all|services|oneshots|longruns|bundles\n" +"s6-rc-db type servicename\n" +"s6-rc-db [ -u | -d ] timeout atomicname\n" +"s6-rc-db contents bundlename\n" +"s6-rc-db [ -u | -d ] dependencies servicename\n" +"s6-rc-db servicedir longrunname\n" +"s6-rc-db [ -u | -d ] script oneshotname\n" +"s6-rc-db flags atomicname\n" +"s6-rc-db atomics servicename...\n" +"s6-rc-db [ -u | -d ] all-dependencies servicename...\n" ; + if (buffer_putsflush(buffer_1, help) < 0) + strerr_diefu1sys(111, "write to stdout") ; +} + +static unsigned int lookup (char const *const *table, char const *command) +{ + register unsigned int i = 0 ; + for (; table[i] ; i++) if (!str_diff(command, table[i])) break ; + return i ; +} + +static inline unsigned int parse_command (char const *command) +{ + static char const *const command_table[13] = + { + "help", + "check", + "list", + "type", + "timeout", + "contents", + "dependencies", + "servicedir", + "script", + "flags", + "atomics", + "all-dependencies", + 0 + } ; + register unsigned int i = lookup(command_table, command) ; + if (!command_table[i]) dieusage() ; + return i ; +} + +static inline unsigned int parse_list (char const *command) +{ + static char const *const list_table[6] = + { + "all", + "services", + "oneshots", + "longruns", + "bundles", + 0 + } ; + register unsigned int i = lookup(list_table, command) ; + if (!list_table[i]) dieusage() ; + return i ; +} + +int main (int argc, char const *const *argv) +{ + char const *live = S6RC_LIVE_BASE ; + unsigned int what, subwhat = 0 ; + int up = 1 ; + PROG = "s6-rc-db" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "udl:c:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'l' : live = l.arg ; break ; + case 'c' : compiled = l.arg ; break ; + case 'u' : up = 1 ; break ; + case 'd' : up = 0 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + if (!argc) dieusage() ; + what = parse_command(argv[0]) ; + if (!what) + { + print_help() ; + return 0 ; + } + if (what >= 2 && argc < 2) dieusage() ; + if (what == 2) subwhat = 1 + parse_list(argv[1]) ; + + { + unsigned int livelen = str_len(live) ; + s6rc_db_t dbblob ; + char compiledblob[compiled ? str_len(compiled) : livelen + 10] ; + db = &dbblob ; + + if (!compiled) + { + byte_copy(compiledblob, livelen, live) ; + byte_copy(compiledblob + livelen, 10, "/compiled") ; + compiled = compiledblob ; + } + + fdcompiled = open_readb(compiled) ; + if (fdcompiled < 0) + strerr_diefu2sys(111, "open ", compiled) ; + + + /* Read the sizes of the compiled db */ + + fdcompiled = open_readb(compiled) ; + if (!s6rc_db_read_sizes(fdcompiled, &dbblob)) + strerr_diefu3sys(111, "read ", compiled, "/n") ; + + + /* Allocate enough stack for the db */ + + { + unsigned int n = dbblob.nshort + dbblob.nlong ; + s6rc_service_t serviceblob[n] ; + char *argvblob[dbblob.nargvs] ; + uint32 depsblob[dbblob.ndeps << 1] ; + char stringblob[dbblob.stringlen] ; + register int r ; + + dbblob.services = serviceblob ; + dbblob.argvs = argvblob ; + dbblob.deps = depsblob ; + dbblob.string = stringblob ; + + + /* Read the db from the file */ + + r = s6rc_db_read(fdcompiled, &dbblob) ; + if (r < 0) strerr_diefu3sys(111, "read ", compiled, "/db") ; + if (!r) strerr_dief3x(4, "invalid service database in ", compiled, "/db") ; + + + /* Perform the action */ + + switch (what) + { + case 1 : /* check */ + { + unsigned int problem, w ; + if (!s6rc_db_check_revdeps(&dbblob)) + strerr_dief3x(4, "invalid service database in ", compiled, ": direct and reverse dependencies are mismatched") ; + w = s6rc_db_check_depcycles(&dbblob, 1, &problem) ; + if (w < n) strerr_dief6x(4, "invalid service database in ", compiled, ": service ", stringblob + serviceblob[w].name, " has a dependency cycle involving ", stringblob + serviceblob[problem].name) ; + break ; + } + case 2 : /* list */ + switch (subwhat) + { + case 1 : print_all(0) ; break ; + case 2 : print_services(0, n) ; break ; + case 3 : print_services(dbblob.nlong, n) ; break ; + case 4 : print_services(0, dbblob.nlong) ; break ; + case 5 : print_all(1) ; break ; + } + break ; + case 3 : /* type */ + print_type(argv[1]) ; + break ; + case 4 : /* timeout */ + print_timeout(argv[1], up) ; + break ; + case 5 : /* contents */ + print_bundle_contents(argv[1]) ; + break ; + case 6 : /* dependencies */ + print_deps(argv[1], up) ; + break ; + case 7 : /* servicedir */ + print_servicedir(argv[1]) ; + break ; + case 8 : /* script */ + print_script(argv[1], up) ; + break ; + case 9 : /* flags */ + print_flags(argv[1]) ; + break ; + case 10 : /* atomics */ + print_atomics(argv + 1, 1, 0) ; + break ; + case 11 : /* all-dependencies */ + print_atomics(argv + 1, up, 1) ; + break ; + } + } + } + return 0 ; +} diff --git a/src/s6-rc/s6-rc-dryrun.c b/src/s6-rc/s6-rc-dryrun.c new file mode 100644 index 0000000..f826ca8 --- /dev/null +++ b/src/s6-rc/s6-rc-dryrun.c @@ -0,0 +1,46 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-rc-dryrun [ -t timeout ] args..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv) +{ + tain_t deadline ; + PROG = "s6-rc-dryrun" ; + { + unsigned int t = 1000 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "t:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 't': if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + tain_from_millisecs(&deadline, t) ; + } + if (!argc) dieusage() ; + buffer_puts(buffer_1, PROG) ; + buffer_put(buffer_1, ":", 1) ; + for (; *argv ; argv++) + { + buffer_put(buffer_1, " ", 1) ; + buffer_puts(buffer_1, *argv) ; + } + buffer_putflush(buffer_1, "\n", 1) ; + tain_now_g() ; + tain_add_g(&deadline, &deadline) ; + deepsleepuntil_g(&deadline) ; + return 0 ; +} diff --git a/src/s6-rc/s6-rc-init.c b/src/s6-rc/s6-rc-init.c new file mode 100644 index 0000000..75f80ef --- /dev/null +++ b/src/s6-rc/s6-rc-init.c @@ -0,0 +1,282 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-rc-init [ -c compiled ] [ -l live ] [ -t timeout ] scandir" +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") ; + +static void cleanup (char const *live) +{ + int e = errno ; + rm_rf(live) ; + errno = e ; +} + +int main (int argc, char const *const *argv) +{ + tain_t deadline, tto ; + char const *live = S6RC_LIVE_BASE ; + char const *compiled = S6RC_COMPILED_BASE ; + PROG = "s6-rc-init" ; + { + unsigned int t = 0 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "c:l:t:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'c' : compiled = optarg ; break ; + case 'l' : live = optarg ; break ; + case 't' : if (!uint0_scan(optarg, &t)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&tto, t) ; + else tto = tain_infinite_relative ; + } + if (!argc) dieusage() ; + + if (compiled[0] != '/') + strerr_dief2x(100, "compiled", " must be an absolute path") ; + if (live[0] != '/') + strerr_dief2x(100, "live", " must be an absolute path") ; + if (argv[0][0] != '/') + strerr_dief2x(100, "scandir", " must be an absolute path") ; + + if (mkdir(live, 0755) < 0) + strerr_diefu2sys(111, "mkdir ", live) ; + + { + int fdlock ; + int fdcompiled ; + s6rc_db_t db ; + DIR *dir ; + unsigned int ndirs = 0 ; + unsigned int maxlen = 0 ; + unsigned int n ; + unsigned int llen = str_len(live) ; + char lfn[llen + 13] ; + char cfn[llen + 23] ; + + + /* lock */ + + byte_copy(lfn, llen, live) ; + byte_copy(lfn + llen, 6, "/lock") ; + fdlock = open_write(lfn) ; + if (fdlock < 0) + { + cleanup(live) ; + strerr_diefu2sys(111, "open ", lfn) ; + } + if (lock_ex(fdlock) < 0) + { + cleanup(live) ; + strerr_diefu2sys(111, "lock ", lfn) ; + } + + + /* compiled */ + + fdcompiled = open_readb(compiled) ; + if (fdcompiled < 0) strerr_diefu2sys(111, "open ", compiled) ; + byte_copy(lfn + llen + 1, 9, "compiled") ; + if (symlink(compiled, lfn) < 0) + { + cleanup(live) ; + strerr_diefu4sys(111, "symlink ", compiled, " to ", lfn) ; + } + + + /* scandir */ + + byte_copy(lfn + llen + 1, 8, "scandir") ; + if (symlink(argv[0], lfn) < 0) + { + cleanup(live) ; + strerr_diefu4sys(111, "symlink ", argv[0], " to ", lfn) ; + } + + + /* state */ + + + byte_copy(lfn + llen + 1, 6, "state") ; + { + register int r = s6rc_db_read_sizes(fdcompiled, &db) ; + if (r <= 0) + { + cleanup(live) ; + if (r < 0) strerr_diefu2sys(111, "read database size in ", compiled) ; + else strerr_dief2x(4, "invalid database size in ", compiled) ; + } + close(fdcompiled) ; + n = db.nshort + db.nlong ; + { + char zero[n] ; + byte_zero(zero, n) ; + if (!openwritenclose_unsafe(lfn, zero, n)) + { + cleanup(live) ; + strerr_diefu2sys(111, "write ", lfn) ; + } + } + } + + + /* servicedirs */ + + byte_copy(lfn + llen + 1, 12, "servicedirs") ; + byte_copy(cfn, llen + 1, lfn) ; + byte_copy(cfn + llen + 1, 21, "compiled/servicedirs") ; + if (!hiercopy(cfn, lfn)) + { + cleanup(live) ; + strerr_diefu4sys(111, "recursively copy ", cfn, " to ", lfn) ; + } + + tain_now_g() ; + tain_add_g(&deadline, &tto) ; + dir = opendir(lfn) ; + if (!dir) + { + cleanup(live) ; + strerr_diefu2sys(111, "opendir ", lfn) ; + } + for (;;) + { + unsigned int thislen ; + direntry *d ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + thislen = str_len(d->d_name) ; + if (thislen > maxlen) maxlen = thislen ; + ndirs++ ; + } + if (errno) + { + int e = errno ; + dir_close(dir) ; + errno = e ; + cleanup(live) ; + strerr_diefu2sys(111, "readdir ", lfn) ; + } + if (ndirs) + { + ftrigr_t a = FTRIGR_ZERO ; + gid_t gid = getgid() ; + unsigned int i = 0 ; + int r ; + uint16 ids[ndirs] ; + char srcfn[llen + 20 + maxlen] ; + char dstfn[llen + 9 + sizeof(S6_SVSCAN_CTLDIR "/control") + maxlen] ; + rewinddir(dir) ; + byte_copy(srcfn, llen + 12, lfn) ; + srcfn[llen + 12] = '/' ; + byte_copy(dstfn, llen, live) ; + byte_copy(dstfn + llen, 8, "/scandir") ; + dstfn[llen + 8] = '/' ; + if (!ftrigr_startf_g(&a, &deadline)) + { + int e = errno ; + dir_close(dir) ; + errno = e ; + cleanup(live) ; + strerr_diefu1sys(111, "start event listener process") ; + } + for (;;) + { + unsigned int thislen ; + direntry *d ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + thislen = str_len(d->d_name) ; + byte_copy(srcfn + llen + 13, thislen, d->d_name) ; + byte_copy(srcfn + llen + 13 + thislen, 6, "/down") ; + r = open_trunc(srcfn) ; + if (r < 0) + { + cleanup(live) ; + strerr_diefu2sys(111, "touch ", srcfn) ; + } + close(r) ; + byte_copy(srcfn + llen + 14 + thislen, 6, "event") ; + if (!ftrigw_fifodir_make(srcfn, gid, 0)) + { + cleanup(live) ; + strerr_diefu2sys(111, "make fifodir ", srcfn) ; + } + ids[i] = ftrigr_subscribe_g(&a, srcfn, "s", 0, &deadline) ; + if (!ids[i]) + { + int e = errno ; + dir_close(dir) ; + errno = e ; + cleanup(live) ; + strerr_diefu2sys(111, "subscribe to ", srcfn) ; + } + srcfn[llen + 13 + thislen] = 0 ; + byte_copy(dstfn + llen + 9, thislen + 1, d->d_name) ; + if (symlink(srcfn, dstfn) < 0) + { + int e = errno ; + dir_close(dir) ; + errno = e ; + cleanup(live) ; + strerr_diefu4sys(111, "symlink ", srcfn, " to ", dstfn) ; + } + i++ ; + } + if (errno) + { + int e = errno ; + dir_close(dir) ; + errno = e ; + cleanup(live) ; + strerr_diefu2sys(111, "readdir ", lfn) ; + } + dir_close(dir) ; + byte_copy(dstfn + llen + 9, sizeof(S6_SVSCAN_CTLDIR "/control"), S6_SVSCAN_CTLDIR "/control") ; + r = s6_svc_write(dstfn, "a", 1) ; + if (r < 0) + { + cleanup(live) ; + strerr_diefu2sys(111, "write to ", dstfn) ; + } + if (!r) strerr_warnw2x("s6-svscan not running on ", argv[0]) ; + else + { + if (ftrigr_wait_and_g(&a, ids, ndirs, &deadline) < 0) + { + cleanup(live) ; + strerr_diefu1sys(111, "wait for s6-supervise processes to come up") ; + } + } + ftrigr_end(&a) ; + } + else dir_close(dir) ; + } + return 0 ; +} diff --git a/src/s6-rc/s6-rc.c b/src/s6-rc/s6-rc.c new file mode 100644 index 0000000..0937379 --- /dev/null +++ b/src/s6-rc/s6-rc.c @@ -0,0 +1,545 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-rc [ -v verbosity ] [ -n dryruntimeout ] [ -t timeout ] [ -l live ] [ -u | -d ] [ -p ] [ -a ] [ -C ] [ -L ] [ -A ] [ -S ] servicenames..." +#define dieusage() strerr_dieusage(100, USAGE) + +typedef struct pidindex_s pidindex_t ; +struct pidindex_s +{ + pid_t pid ; + unsigned int i ; +} ; + +typedef struct what_s what_t ; +struct what_s +{ + unsigned char up : 1 ; + unsigned char prune : 1 ; + unsigned char selectlive : 1 ; + unsigned char check : 1 ; + unsigned char list : 1 ; + unsigned char listall : 1 ; + unsigned char change : 1 ; +} ; + +static unsigned int verbosity = 1 ; +static char const *live = S6RC_LIVE_BASE ; +static unsigned int livelen ; +static pidindex_t *pidindex ; +static unsigned int npids = 0 ; +static s6rc_db_t *db ; +static unsigned int n ; +static unsigned char *state ; +static unsigned int *pendingdeps ; +static tain_t deadline ; +static char dryrun[UINT_FMT] = "" ; + +static inline void announce (void) +{ + unsigned int i = n ; + char fn[livelen + 7] ; + char tmpstate[i] ; + if (dryrun[0]) return ; + byte_copy(fn, livelen, live) ; + byte_copy(fn + livelen, 7, "/state") ; + while (i--) tmpstate[i] = !!(state[i] & 1) ; + if (!openwritenclose_suffix(fn, tmpstate, n, ".new")) + strerr_diefu2sys(111, "write ", fn) ; +} + +static void print_services (void) +{ + register unsigned int i = 0 ; + for (; i < n ; i++) + if (state[i] & 2) + { + buffer_puts(buffer_1, db->string + db->services[i].name) ; + buffer_put(buffer_1, "\n", 1) ; + } + buffer_putflush(buffer_1, "\n", 1) ; +} + +static pid_t start_oneshot (unsigned int i, int h) +{ + unsigned int argc = db->services[i].x.oneshot.argc[h] ; + char const *const *argv = (char const *const *)db->argvs + h * db->ndeps + db->services[i].x.oneshot.argv[h] ; + unsigned int m = 0 ; + char const *newargv[9 + argc + (!!dryrun[0] << 2)] ; + char fmt[UINT32_FMT] ; + char socketfn[livelen + S6RC_ONESHOT_RUNNER_LEN + 3] ; + byte_copy(socketfn, livelen, live) ; + byte_copy(socketfn + livelen, 16 + S6RC_ONESHOT_RUNNER_LEN, "/servicedirs/" S6RC_ONESHOT_RUNNER "/s") ; + fmt[uint32_fmt(fmt, db->services[i].timeout[h])] = 0 ; + if (dryrun[0]) + { + newargv[m++] = S6_BINPREFIX "s6-rc-dryrun" ; + newargv[m++] = "-t" ; + newargv[m++] = dryrun ; + newargv[m++] = "--" ; + } + newargv[m++] = S6_BINPREFIX "s6-sudo" ; + newargv[m++] = verbosity >= 2 ? "-vel0" : "-el0" ; + newargv[m++] = "-t" ; + newargv[m++] = "2000" ; + newargv[m++] = "-T" ; + newargv[m++] = fmt ; + newargv[m++] = "--" ; + newargv[m++] = socketfn ; + while (argc--) newargv[m++] = *argv++ ; + newargv[m++] = 0 ; + return child_spawn0(newargv[0], newargv, (char const *const *)environ) ; +} + +static pid_t start_longrun (unsigned int i, int h) +{ + unsigned int namelen = str_len(db->string + db->services[i].name) ; + unsigned int m = 0 ; + char fmt[UINT32_FMT] ; + char servicefn[livelen + namelen + 19] ; + char const *newargv[11 + (!!dryrun[0] << 2)] ; + byte_copy(servicefn, livelen, live) ; + byte_copy(servicefn + livelen, 13, "/servicedirs/") ; + byte_copy(servicefn + livelen + 13, namelen, db->string + db->services[i].name) ; + byte_copy(servicefn + livelen + 13 + namelen, 6, "/down") ; + fmt[uint32_fmt(fmt, db->services[i].timeout[h])] = 0 ; + if (dryrun[0]) + { + newargv[m++] = S6_BINPREFIX "s6-rc-dryrun" ; + newargv[m++] = "-t" ; + newargv[m++] = dryrun ; + newargv[m++] = "--" ; + } + newargv[m++] = S6_BINPREFIX "s6-svlisten1" ; + newargv[m++] = h ? "-U" : "-d" ; + newargv[m++] = "-t" ; + newargv[m++] = fmt ; + newargv[m++] = "--" ; + newargv[m++] = servicefn ; + newargv[m++] = S6_BINPREFIX "s6-svc" ; + newargv[m++] = h ? "-u" : "-d" ; + newargv[m++] = "--" ; + newargv[m++] = servicefn ; + newargv[m++] = 0 ; + + if (!dryrun[0]) + { + if (h) + { + if (unlink(servicefn) < 0 && verbosity) + strerr_warnwu2sys("unlink ", servicefn) ; + } + else + { + int fd = open_trunc(servicefn) ; + if (fd < 0) + { + if (verbosity) + strerr_warnwu2sys("touch ", servicefn) ; + } + else fd_close(fd) ; + } + } + servicefn[livelen + 13 + namelen] = 0 ; + return child_spawn0(newargv[0], newargv, (char const *const *)environ) ; +} + +static void broadcast_success (unsigned int, int) ; + +static void examine (unsigned int i, int h) +{ + char const *name = db->string + db->services[i].name ; + if (verbosity >= 3) + strerr_warni2x("examining ", name) ; + + if (!pendingdeps[i] && !(state[i] & 4)) + { + state[i] |= 4 ; + if ((state[i] & 1) == h) + { + if (verbosity >= 2) + strerr_warni4x("processing service ", name, ": already ", h ? "up" : "down") ; + broadcast_success(i, h) ; + } + else + { + pidindex[npids].pid = db->services[i].type ? start_longrun(i, h) : start_oneshot(i, h) ; + if (pidindex[npids].pid) + { + pidindex[npids++].i = i ; + if (verbosity >= 2) + { + strerr_warni4x("processing service ", name, ": ", h ? "starting" : "stopping") ; + } + } + else + { + if (verbosity) + strerr_warnwu2sys("spawn subprocess for ", name) ; + if (verbosity >= 2) + strerr_warni5x("processing service ", name, ": ", h ? "start" : "stop", " failed") ; + } + } + } +} + +static void broadcast_success (unsigned int i, int h) +{ + unsigned int nd = db->services[i].ndeps[!h] ; + while (nd--) + { + unsigned int j = db->deps[!h * db->ndeps + db->services[i].deps[!h] + nd] ; + pendingdeps[j]-- ; + examine(j, h) ; + } +} + +static void on_success (unsigned int i, int h) +{ + if (h) state[i] |= 1 ; else state[i] &= 254 ; + announce() ; + if (verbosity >= 2) + strerr_warni4x("service ", db->string + db->services[i].name, h ? "started" : "stopped", " successfully") ; + broadcast_success(i, h) ; +} + +static void on_failure (unsigned int i, int h, int crashed, unsigned int code) +{ + if (verbosity) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, code)] = 0 ; + strerr_warnw7x("service ", db->string + db->services[i].name, h ? "started" : "stopped", " unsuccessfully", ": ", crashed ? " crashed with signal " : " exited ", fmt) ; + } +} + +static int handle_signals (int h) +{ + int ok = 1 ; + for (;;) + { + switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "selfpipe_read()") ; + case 0 : return ok ; + case SIGCHLD : + { + for (;;) + { + unsigned int j = 0 ; + int wstat ; + register pid_t r = wait_nohang(&wstat) ; + if (r < 0) + if (errno == ECHILD) break ; + else strerr_diefu1sys(111, "wait for children") ; + else if (!r) break ; + for (; j < npids ; j++) if (pidindex[j].pid == r) break ; + if (j < npids) + { + unsigned int i = pidindex[j].i ; + pidindex[j] = pidindex[--npids] ; + if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) + on_success(i, h) ; + else + { + ok = 0 ; + on_failure(i, h, WIFSIGNALED(wstat), WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat)) ; + } + } + } + break ; + } + default : strerr_dief1x(101, "inconsistent signal state") ; + } + } +} + +static int doit (int spfd, int h) +{ + iopause_fd x = { .fd = spfd, .events = IOPAUSE_READ } ; + int exitcode = 0 ; + unsigned int i = n ; + pidindex_t pidindexblob[n] ; + unsigned int pendingdepsblob[n] ; + pidindex = pidindexblob ; + pendingdeps = pendingdepsblob ; + + if (verbosity >= 3) + strerr_warni2x("bringing selected services ", h ? "up" : "down") ; + + while (i--) + { + state[i] &= 251 ; + pendingdeps[i] = db->services[i].ndeps[h] ; + } + i = n ; + while (i--) if (state[i] & 2) examine(i, h) ; + + for (;;) + { + register int r ; + buffer_flush(buffer_1) ; + if (!npids) break ; + r = iopause_g(&x, 1, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + if (!r) strerr_dief1x(2, "timed out") ; + if (!handle_signals(h)) exitcode = 1 ; + } + return exitcode ; +} + +static void invert_selection (void) +{ + register unsigned int i = n ; + + if (verbosity >= 3) + strerr_warni1x("inverting the selection") ; + + while (i--) state[i] ^= 2 ; +} + +int main (int argc, char const *const *argv) +{ + what_t what = { .up = 1 } ; + PROG = "s6-rc" ; + { + unsigned int t = 0 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "v:n:t:l:udpaCLAS", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'n' : + { + unsigned int d ; + if (!uint0_scan(l.arg, &d)) dieusage() ; + dryrun[uint_fmt(dryrun, d)] = 0 ; + break ; + } + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'l' : live = l.arg ; break ; + case 'u' : what.up = 1 ; break ; + case 'd' : what.up = 0 ; break ; + case 'p' : what.prune = 1 ; break ; + case 'a' : what.selectlive = 1 ; break ; + case 'C' : what.check = 1 ; break ; + case 'L' : what.list = 1 ; break ; + case 'A' : what.listall = 1 ; break ; + case 'S' : what.change = 1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&deadline, t) ; + else deadline = tain_infinite_relative ; + } + if (!argc) dieusage() ; + if (!what.list && !what.listall) what.change = 1 ; + if (!tain_now_g()) + strerr_warnwu1x("get correct TAI time. (Do you have a valid leap seconds file?)") ; + tain_add_g(&deadline, &deadline) ; + livelen = str_len(live) ; + + { + int livelock ; + int fdcompiled ; + s6rc_db_t dbblob ; + char dbfn[livelen + 10] ; + db = &dbblob ; + if (verbosity >= 3) + strerr_warni3x("reading services database in ", live, "/compiled") ; + + + /* Take the live lock */ + + byte_copy(dbfn, livelen, live) ; + byte_copy(dbfn + livelen, 6, "/lock") ; + livelock = open_write(dbfn) ; + if (livelock < 0) strerr_diefu2sys(111, "open ", dbfn) ; + if (coe(livelock) < 0) strerr_diefu2sys(111, "coe ", dbfn) ; + if (lock_ex(livelock) < 0) strerr_diefu2sys(111, "lock ", dbfn) ; + + + /* Read the sizes of the compiled db */ + + byte_copy(dbfn + livelen + 1, 9, "compiled") ; + fdcompiled = open_readb(dbfn) ; + if (!s6rc_db_read_sizes(fdcompiled, &dbblob)) + strerr_diefu3sys(111, "read ", dbfn, "/n") ; + + + /* Allocate enough stack for the db */ + + { + unsigned int n = dbblob.nshort + dbblob.nlong ; + s6rc_service_t serviceblob[n] ; + char *argvblob[dbblob.nargvs] ; + uint32 depsblob[dbblob.ndeps << 1] ; + char stringblob[dbblob.stringlen] ; + unsigned char stateblob[n] ; + register int r ; + + dbblob.services = serviceblob ; + dbblob.argvs = argvblob ; + dbblob.deps = depsblob ; + dbblob.string = stringblob ; + state = stateblob ; + byte_zero(state, n) ; + + + /* Read the db from the file */ + + r = s6rc_db_read(fdcompiled, &dbblob) ; + if (r < 0) strerr_diefu3sys(111, "read ", dbfn, "/db") ; + if (!r) strerr_dief3x(4, "invalid service database in ", dbfn, "/db") ; + + + /* Check the db consistency */ + + if (what.check) + { + unsigned int problem, w ; + if (verbosity >= 3) + strerr_warni1x("checking database consistency") ; + + if (!s6rc_db_check_revdeps(&dbblob)) + strerr_dief3x(4, "invalid service database in ", dbfn, ": direct and reverse dependencies are mismatched") ; + w = s6rc_db_check_depcycles(&dbblob, 1, &problem) ; + if (w < n) + strerr_dief6x(4, "invalid service database in ", dbfn, ": service ", stringblob + serviceblob[w].name, " has a dependency cycle involving ", stringblob + serviceblob[problem].name) ; + } + + + /* Resolve the args and add them to the selection */ + + if (verbosity >= 3) + strerr_warni1x("resolving command line arguments") ; + + { + cdb_t c = CDB_ZERO ; + int fd = open_readatb(fdcompiled, "resolve.cdb") ; + if (fd < 0) strerr_diefu3sys(111, "open ", dbfn, "/resolve.cdb") ; + if (!cdb_init_map(&c, fd, 1)) + strerr_diefu3sys(111, "cdb_init ", dbfn, "/resolve.cdb") ; + for (; *argv ; argv++) + { + unsigned int len ; + register int r = cdb_find(&c, *argv, str_len(*argv)) ; + char pack[cdb_datalen(&c) + 1] ; + char const *p = pack ; + if (r < 0) strerr_diefu3sys(111, "read ", dbfn, "/resolve.cdb") ; + if (!r) + strerr_dief4x(3, *argv, " is not a recognized identifier in ", dbfn, "/resolve.cdb") ; + if (cdb_datalen(&c) & 3) + strerr_dief3x(4, "invalid resolve database in ", dbfn, "/resolve.cdb") ; + len = cdb_datalen(&c) >> 2 ; + if (len > n) + strerr_dief3x(4, "invalid resolve database in ", dbfn, "/resolve.cdb") ; + if (cdb_read(&c, pack, cdb_datalen(&c), cdb_datapos(&c)) < 0) + if (r < 0) strerr_diefu3sys(111, "read ", dbfn, "/resolve.cdb") ; + while (len--) + { + uint32 x ; + uint32_unpack_big(p, &x) ; p += 4 ; + if (x >= n) + strerr_dief3x(4, "invalid resolve database in ", dbfn, "/resolve.cdb") ; + state[x] |= 2 ; + } + } + cdb_free(&c) ; + close(fd) ; + } + close(fdcompiled) ; + + + /* Read live state in bit 0 of state */ + + if (verbosity >= 3) + strerr_warni2x("reading current state in ", live) ; + + byte_copy(dbfn + livelen + 1, 6, "state") ; + { + char tmpstate[n] ; + r = openreadnclose(dbfn, tmpstate, n) ; + if (r != n) strerr_diefu2sys(111, "read ", dbfn) ; + { + register unsigned int i = n ; + while (i--) if (tmpstate[i]) state[i] |= 1 ; + } + } + + + /* Add live state to selection */ + + if (what.selectlive) + { + register unsigned int i = n ; + while (i--) if (state[i] & 1) state[i] |= 2 ; + } + + + /* Print the selection before closure */ + + if (what.list) print_services() ; + + + /* Compute the closure */ + + if (verbosity >= 3) + strerr_warni1x("computing the final set of services") ; + + s6rc_graph_closure(db, state, 1, what.up) ; + + + /* Print the selection after closure */ + + if (what.listall) print_services() ; + + + /* Perform a state change */ + + if (what.change) + { + int spfd = selfpipe_init() ; + if (spfd < 0) strerr_diefu1sys(111, "init selfpipe") ; + if (selfpipe_trap(SIGCHLD) < 0) + strerr_diefu1sys(111, "trap SIGCHLD") ; + + if (what.prune) + { + if (what.up) invert_selection() ; + r = doit(spfd, 0) ; + if (r) return r ; + invert_selection() ; + return doit(spfd, 1) ; + } + else return doit(spfd, what.up) ; + } + } + } + return 0 ; +} diff --git a/tools/gen-deps.sh b/tools/gen-deps.sh new file mode 100755 index 0000000..4615f28 --- /dev/null +++ b/tools/gen-deps.sh @@ -0,0 +1,83 @@ +#!/bin/sh -e + +. package/info + +echo '#' +echo '# This file has been generated by tools/gen-deps.sh' +echo '#' +echo + +for dir in src/include/${package} src/* ; do + for file in $(ls -1 $dir | grep -- \\.h$) ; do + { + grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ; + grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2 + } | sort -u | { + deps= + while read dep ; do + if echo $dep | grep -q "^${package}/" ; then + deps="$deps src/include/$dep" + elif test -f "${dir}/$dep" ; then + deps="$deps ${dir}/$dep" + else + deps="$deps src/include-local/$dep" + fi + done + if test -n "$deps" ; then + echo "${dir}/${file}:${deps}" + fi + } + done +done + +for dir in src/* ; do + for file in $(ls -1 $dir | grep -- \\.c$) ; do + { + grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ; + grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2 + } | sort -u | { + deps=" ${dir}/$file" + while read dep ; do + if echo $dep | grep -q "^${package}/" ; then + deps="$deps src/include/$dep" + elif test -f "${dir}/$dep" ; then + deps="$deps ${dir}/$dep" + else + deps="$deps src/include-local/$dep" + fi + done + o=$(echo $file | sed s/\\.c$/.o/) + lo=$(echo $file | sed s/\\.c$/.lo/) + echo "${dir}/${o} ${dir}/${lo}:${deps}" + } + done +done +echo + +for dir in $(ls -1 src | grep -v ^include) ; do + for file in $(ls -1 src/$dir/deps-lib) ; do + deps= + while read dep ; do + deps="$deps src/$dir/$dep" + done < src/$dir/deps-lib/$file + echo "lib$file.a:$deps" + echo "lib${file}.so:$(echo "$deps" | sed 's/\.o/.lo/g')" + done + + for file in $(ls -1 src/$dir/deps-exe) ; do + deps= + libs= + while read dep ; do + if echo $dep | grep -q -- \\.o$ ; then + dep="src/$dir/$dep" + fi + if echo $dep | grep -q '^\${.*_LIB}' ; then + libs="$libs $dep" + else + deps="$deps $dep" + fi + done < src/$dir/deps-exe/$file + echo "$file: private EXTRA_LIBS :=$libs" + echo "$file: src/$dir/$file.o$deps" + done +done diff --git a/tools/install.sh b/tools/install.sh new file mode 100755 index 0000000..89f9428 --- /dev/null +++ b/tools/install.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +usage() { + echo "usage: $0 [-D] [-l] [-m mode] src dst" 1>&2 + exit 1 +} + +mkdirp=false +symlink=false +mode=0755 + +while getopts Dlm: name ; do + case "$name" in + D) mkdirp=true ;; + l) symlink=true ;; + m) mode=$OPTARG ;; + ?) usage ;; + esac +done +shift $(($OPTIND - 1)) + +test "$#" -eq 2 || usage +src=$1 +dst=$2 +tmp="$dst.tmp.$$" + +case "$dst" in + */) echo "$0: $dst ends in /" 1>&2 ; exit 1 ;; +esac + +set -C +set -e + +if $mkdirp ; then + umask 022 + case "$2" in + */*) mkdir -p "${dst%/*}" ;; + esac +fi + +trap 'rm -f "$tmp"' EXIT INT QUIT TERM HUP + +umask 077 + +if $symlink ; then + ln -s "$src" "$tmp" +else + cat < "$1" > "$tmp" + chmod "$mode" "$tmp" +fi + +mv -f "$tmp" "$dst" +if test -d "$dst" ; then + rm -f "$dst/$(basename $tmp)" + if $symlink ; then + mkdir "$tmp" + ln -s "$src" "$tmp/$(basename $dst)" + mv -f "$tmp/$(basename $dst)" "${dst%/*}" + rmdir "$tmp" + else + echo "$0: $dst is a directory" 1>&2 + exit 1 + fi +fi -- cgit v1.2.3