pax_global_header00006660000000000000000000000064147015114120014506gustar00rootroot0000000000000052 comment=6724a0b86592fd293ead4bf66facec6e9afcbcb9 s6-2.13.1.0/000077500000000000000000000000001470151141200122605ustar00rootroot00000000000000s6-2.13.1.0/.gitignore000066400000000000000000000023271470151141200142540ustar00rootroot00000000000000*.o /*.a.xyzzy *.lo *.so.xyzzy /config.mak /src/include/s6/config.h /ucspilogd /s6-ftrigrd /s6-ftrig-listen1 /s6-ftrig-listen /s6-ftrig-notify /s6-ftrig-wait /s6lockd /s6lockd-helper /s6-cleanfifodir /s6-mkfifodir /s6-svscan /s6-supervise /s6-svc /s6-svscanctl /s6-svok /s6-svstat /s6-svwait /s6-svlisten1 /s6-svlisten /s6-svperms /s6-notifyoncheck /s6-svdt /s6-svdt-clear /s6-permafailon /s6-svlink /s6-svunlink /s6-envdir /s6-envuidgid /s6-fghack /s6-log /s6-setlock /s6-setsid /s6-socklog /s6-softlimit /s6-tai64n /s6-tai64nlocal /s6-accessrules-cdb-from-fs /s6-accessrules-fs-from-cdb /s6-connlimit /s6-ioconnect /s6-ipcclient /s6-ipcserver-access /s6-ipcserver-socketbinder /s6-ipcserver /s6-ipcserverd /s6-sudo /s6-sudoc /s6-sudod /s6-fdholder-daemon /s6-fdholderd /s6-fdholder-delete /s6-fdholder-deletec /s6-fdholder-store /s6-fdholder-storec /s6-fdholder-retrieve /s6-fdholder-retrievec /s6-fdholder-list /s6-fdholder-listc /s6-fdholder-getdump /s6-fdholder-getdumpc /s6-fdholder-setdump /s6-fdholder-setdumpc /s6-fdholder-transferdump /s6-fdholder-transferdumpc /s6-applyuidgid /s6-setuidgid /s6-usertree-maker /s6-instance-maker /s6-instance-create /s6-instance-delete /s6-instance-control /s6-instance-status /s6-instance-list s6-2.13.1.0/AUTHORS000066400000000000000000000030321470151141200133260ustar00rootroot00000000000000Main author: Laurent Bercot Contributors: Paul Jarc Gerrit Pape Clemens Fischer Stefan Karrman Jean Marot Olivier Brunel Colin Booth Eric Le Bihan Shengjing Zhu Alexis Mira Ressel Jan Kämpe Thanks to: Dan J. Bernstein Wayne Marshall David Madore Nicolas George Frans Haarman Vallo Kallaste Patrick Mahoney Roy Lanek Gorka Lertxundi Buck Evan John O'Meara Michael Zuo Casper Ti. Vector Lionel Van Bemten Martin Misuth Samuel Holland Hardware Earl Chew Jonathan de Boyne Pollard Eric Vidal Jens Rehsack Xavier Stonestreet Johannes Nixdorf Oliver Schad Alex Kiernan Earl Chew Saj Goonatilleke s6-2.13.1.0/CONTRIBUTING000066400000000000000000000004371470151141200141160ustar00rootroot00000000000000 Please add a Signed-Off-By: line at the end of your commit, which certifies that you have the right and authority to pass it on as an open-source patch, as explicited in the Developer's Certificate of Origin available in this project's DCO file, or at https://developercertificate.org/ s6-2.13.1.0/COPYING000066400000000000000000000013701470151141200133140ustar00rootroot00000000000000Copyright (c) 2011-2024 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. s6-2.13.1.0/DCO000066400000000000000000000026151470151141200126140ustar00rootroot00000000000000Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 1 Letterman Drive Suite D4700 San Francisco, CA, 94129 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. s6-2.13.1.0/INSTALL000066400000000000000000000151201470151141200133100ustar00rootroot00000000000000Build Instructions ------------------ * Requirements ------------ - A POSIX-compliant C development environment - GNU make version 3.81 or later - skalibs version 2.14.3.0 or later: https://skarnet.org/software/skalibs/ - Recommended: execline version 2.9.6.1 or later: https://skarnet.org/software/execline/ (You can disable this requirement at configure time, but will lose some functionality.) - Optional: nsss version 0.2.0.5 or later: https://skarnet.org/software/nsss/ This software will run on any operating system that implements POSIX.1-2008, available at: https://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. 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. The execline dependency can be removed with --disable-execline. * 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, a few standard environment variables are recognized. If the CC environment variable is set, its value will override compiler detection by configure. The --host=HOST option will still add a HOST- prefix to the value of CC. The values of CFLAGS, CPPFLAGS and LDFLAGS will be appended to flags auto-detected by configure. To entirely override the flags set by configure instead, use make variables. * Make variables -------------- You can invoke make with a few variables for more configuration. CC, CFLAGS, CPPFLAGS, LDFLAGS, LDLIBS, AR, RANLIB, STRIP, INSTALL and CROSS_COMPILE can all be overridden on the make command line. This is an even bigger hammer than running ./configure with environment variables, so it is advised to only do this when it is the only way of obtaining the behaviour you want. DESTDIR can be given on the "make install" command line in order to install to a staging directory. * 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: https://musl-libc.org/) s6 includes software running at a very low level, including a program that may run as process 1: to avoid run-time dependencies, it is recommended that you link the executables statically if you have a suitable libc. * 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 --host=HOST option to configure, HOST being the triplet for your target. * Make sure your cross-toolchain binaries (i.e. prefixed with HOST-) are accessible via your PATH environment variable. * 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. * Absolute pathnames ------------------ You may want to use fixed absolute pathnames even if you're not following the slashpackage convention: for instance, the Nix packaging system prefers calling binaries with immutable paths rather than rely on PATH resolution. If you are in that case, use the --enable-absolute-paths option to configure. This will ensure that programs calling binaries from this package will call them with their full installation path (in bindir) without relying on a PATH search. * 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. s6-2.13.1.0/Makefile000066400000000000000000000112171470151141200137220ustar00rootroot00000000000000# # This Makefile requires GNU make. # # Do not make changes here. # Use the included .mak files. # it: all make_need := 3.81 ifeq "" "$(strip $(filter $(make_need), $(firstword $(sort $(make_need) $(MAKE_VERSION)))))" fail := $(error Your make ($(MAKE_VERSION)) is too old. You need $(make_need) or newer) endif CC = $(error Please use ./configure first) STATIC_LIBS := SHARED_LIBS := INTERNAL_LIBS := EXTRA_TARGETS := LIB_DEFS := define library_definition LIB$(firstword $(subst =, ,$(1))) := lib$(lastword $(subst =, ,$(1))).$(if $(DO_ALLSTATIC),a,so).xyzzy ifdef DO_SHARED SHARED_LIBS += lib$(lastword $(subst =, ,$(1))).so.xyzzy endif ifdef DO_STATIC STATIC_LIBS += lib$(lastword $(subst =, ,$(1))).a.xyzzy endif endef -include config.mak include package/targets.mak $(foreach var,$(LIB_DEFS),$(eval $(call library_definition,$(var)))) include package/deps.mak version_m := $(basename $(version)) version_M := $(basename $(version_m)) version_l := $(basename $(version_M)) CPPFLAGS_ALL := $(CPPFLAGS_AUTO) $(CPPFLAGS) CFLAGS_ALL := $(CFLAGS_AUTO) $(CFLAGS) ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),) CFLAGS_SHARED := -fPIC else CFLAGS_SHARED := endif LDFLAGS_ALL := $(LDFLAGS_AUTO) $(LDFLAGS) AR := $(CROSS_COMPILE)ar RANLIB := $(CROSS_COMPILE)ranlib STRIP := $(CROSS_COMPILE)strip INSTALL := ./tools/install.sh ALL_BINS := $(LIBEXEC_TARGETS) $(BIN_TARGETS) ALL_LIBS := $(SHARED_LIBS) $(STATIC_LIBS) $(INTERNAL_LIBS) ALL_INCLUDES := $(wildcard src/include/$(package)/*.h) all: $(ALL_LIBS) $(ALL_BINS) $(ALL_INCLUDES) $(EXTRA_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 $(STATIC_LIBS)),) exec $(STRIP) -x -R .note -R .comment $(STATIC_LIBS) endif ifneq ($(strip $(ALL_BINS)$(SHARED_LIBS)),) exec $(STRIP) -R .note -R .comment $(ALL_BINS) $(SHARED_LIBS) endif install: install-dynlib install-libexec install-bin install-lib install-include install-dynlib: $(SHARED_LIBS:lib%.so.xyzzy=$(DESTDIR)$(dynlibdir)/lib%.so) install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%) install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%) install-lib: $(STATIC_LIBS:lib%.a.xyzzy=$(DESTDIR)$(libdir)/lib%.a) install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h) $(EXTRA_INCLUDES:src/include/%.h=$(DESTDIR)$(includedir)/%.h) ifneq ($(exthome),) $(DESTDIR)$(exthome): $(DESTDIR)$(home) exec $(INSTALL) -l $(notdir $(home)) $(DESTDIR)$(exthome) update: $(DESTDIR)$(exthome) global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so.xyzzy=$(DESTDIR)$(sproot)/library.so/lib%.so.$(version_M)) $(BIN_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 the inner workings of s6, and the mailing-list for questions about process supervision, init systems, and so on. s6-2.13.1.0/README.macos000066400000000000000000000002531470151141200142410ustar00rootroot00000000000000 This package will compile and run on Darwin (MacOS), but the building of shared libraries is not supported. Make sure you use the --disable-shared option to configure. s6-2.13.1.0/README.solaris000066400000000000000000000006521470151141200146160ustar00rootroot00000000000000 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. s6-2.13.1.0/configure000077500000000000000000000336621470151141200142010ustar00rootroot00000000000000#!/bin/sh cd `dirname "$0"` . 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="$*" } 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 $CPPFLAGS $CPPFLAGS_POST $CFLAGS_AUTO $CFLAGS $CFLAGS_POST "$2" -c -o "$tmpo" "$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 $CFLAGS $CFLAGS_POST $LDFLAGS_AUTO $LDFLAGS $LDFLAGS_POST -nostdlib "$2" -o "$tmpe" "$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= CPPFLAGS_AUTO="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -iquote src/include-local -Isrc/include" CPPFLAGS_POST="$CPPFLAGS" CPPFLAGS= CFLAGS_AUTO="-pipe -Wall" CFLAGS_POST="$CFLAGS" CFLAGS=-O2 LDFLAGS_AUTO= LDFLAGS_POST="$LDFLAGS" LDFLAGS= LDFLAGS_NOSHARED= LDFLAGS_SHARED=-shared prefix= exec_prefix='$prefix' dynlibdir='$prefix/lib' libexecdir='$exec_prefix/libexec' bindir='$exec_prefix/bin' libdir='$prefix/lib' includedir='$prefix/include' sysdeps='$prefix/lib/skalibs/sysdeps' manualsysdeps=false shared=false static=true allpic=true slashpackage=false abspath=false usensss=false useexecline=true sproot= home= exthome= allstatic=true evenmorestatic=false addincpath='' addlibspath='' addlibdpath='' vpaths='' vpathd='' build= 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#*=} ;; --libdir=*) libdir=${arg#*=} ;; --includedir=*) includedir=${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-all-pic|--enable-all-pic=yes) allpic=true ;; --disable-all-pic|--enable-all-pic=no) allpic=false ;; --enable-slashpackage=*) sproot=${arg#*=} ; slashpackage=true ; ;; --enable-slashpackage) sproot= ; slashpackage=true ;; --disable-slashpackage) sproot= ; slashpackage=false ;; --enable-absolute-paths|--enable-absolute-paths=yes) abspath=true ;; --disable-absolute-paths|--enable-absolute-paths=no) abspath=false ;; --enable-nsss|--enable-nsss=yes) usensss=true ;; --disable-nsss|--enable-nsss=no) usensss=false ;; --enable-execline|--enable-execline=yes) useexecline=true ;; --disable-execline|--enable-execline=no) useexecline=false ;; --enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;; --host=*|--target=*) target=${arg#*=} ;; --build=*) build=${arg#*=} ;; -* ) echo "$0: unknown option $arg" ;; *=*) eval "${arg%%=*}=\${arg#*=}" ;; *) target=$arg ;; esac done # Add /usr in the default default case if test -z "$prefix" ; then if test "$libdir" = '$prefix/lib' ; then libdir=/usr/lib 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 libdir includedir sysdeps sproot ; 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" tmpo="./tmp-configure-$$-$PPID-$i.o" tmpe="./tmp-configure-$$-$PPID-$i.tmp" 2>|/dev/null > "$tmpc" && break 2>|/dev/null > "$tmpo" && break 2>|/dev/null > "$tmpe" && break test "$i" -gt 50 && fail "$0: cannot create temporary files" done set +C trap 'rm -f "$tmpc" "$tmpo" "$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=${DESTDIR}${sproot}/package/prog/skalibs/sysdeps fi extbinprefix=${exthome}/command dynlibdir=${home}/library.so bindir=${home}/command libdir=${home}/library libexecdir=$bindir includedir=${home}/include while read dep condvar ; do if test -n "$condvar" ; then eval "cond=$condvar" else cond=true fi if $cond ; then addincpath="$addincpath -I${DESTDIR}${sproot}${dep}/include" vpaths="$vpaths ${DESTDIR}${sproot}${dep}/library" addlibspath="$addlibspath -L${DESTDIR}${sproot}${dep}/library" vpathd="$vpathd ${DESTDIR}${sproot}${dep}/library.so" addlibdpath="$addlibdpath -L${DESTDIR}${sproot}${dep}/library.so" fi done < package/deps-build fi # Find a C compiler to use if test -n "$target" && test x${build} != x${target} ; then cross=${target}- else cross= fi echo "Checking for C compiler..." trycc ${CC} if test -n "$CC_AUTO" ; then b=`basename "$CC"` adjust_cross=false if test "$b" != "$CC" ; then adjust_cross=true echo "$0: warning: compiler $CC is declared with its own path. If it's not accessible via PATH, you will need to pass AR, RANLIB and STRIP make variables to the make invocation." 1>&2 fi if test -n "$cross" ; then if test "$b" = "${b##$cross}" ; then echo "$0: warning: compiler $CC is declared as a cross-compiler for target $target but does not start with prefix ${cross}" 1>&2 elif $adjust_cross ; then cross=`dirname "$CC"`/"$cross" fi fi fi trycc ${cross}gcc trycc ${cross}clang 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 $CPPFLAGS $CPPFLAGS_POST $CFLAGS_AUTO $CFLAGS $CFLAGS_POST -c -o "$tmpo" "$tmpc" 2>"$tmpe" ; then echo " ... yes" else echo " ... no. Compiler output follows:" cat < "$tmpe" exit 1 fi echo "Checking target system type..." if test -z "$target" ; then if test -n "$build" ; then target=$build ; else target=$($CC_AUTO -dumpmachine 2>/dev/null) || target=unknown fi fi 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 spawn_lib=$(cat $sysdeps/spawn.lib) socket_lib=$(cat $sysdeps/socket.lib) sysclock_lib=$(cat $sysdeps/sysclock.lib) timer_lib=$(cat $sysdeps/timer.lib) util_lib=$(cat $sysdeps/util.lib) if $allpic ; then tryflag CPPFLAGS_AUTO -fPIC fi tryflag CFLAGS_AUTO -std=c99 tryflag CFLAGS -fomit-frame-pointer tryflag CFLAGS_AUTO -fno-exceptions tryflag CFLAGS_AUTO -fno-unwind-tables tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables 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 tryflag CFLAGS_AUTO -ffunction-sections tryflag CFLAGS_AUTO -fdata-sections tryldflag LDFLAGS_AUTO -Wl,--sort-section=alignment tryldflag LDFLAGS_AUTO -Wl,--sort-common CPPFLAGS_AUTO="${CPPFLAGS_AUTO}${addincpath}" if $evenmorestatic ; then LDFLAGS_NOSHARED=-static fi if $shared ; then tryldflag LDFLAGS -Wl,--hash-style=both fi LDFLAGS_SHARED="${LDFLAGS_SHARED}${addlibdpath}" if $allstatic ; then LDFLAGS_NOSHARED="${LDFLAGS_NOSHARED}${addlibspath}" tryldflag LDFLAGS_NOSHARED -Wl,--gc-sections else LDFLAGS_NOSHARED="${LDFLAGS_NOSHARED}${addlibdpath}" fi 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 libdir := $libdir includedir := $includedir sysdeps := $sysdeps slashpackage := $slashpackage sproot := $sproot version := $version home := $home exthome := $exthome SPAWN_LIB := ${spawn_lib} SOCKET_LIB := ${socket_lib} SYSCLOCK_LIB := ${sysclock_lib} TIMER_LIB := ${timer_lib} UTIL_LIB := ${util_lib} CC := $CC_AUTO CPPFLAGS_AUTO := $CPPFLAGS_AUTO CPPFLAGS := $CPPFLAGS $CPPFLAGS_POST CFLAGS_AUTO := $CFLAGS_AUTO CFLAGS := $CFLAGS $CFLAGS_POST LDFLAGS_AUTO := $LDFLAGS_AUTO LDFLAGS := $LDFLAGS $LDFLAGS_POST LDFLAGS_SHARED := $LDFLAGS_SHARED LDFLAGS_NOSHARED := $LDFLAGS_NOSHARED CROSS_COMPILE := $cross vpath lib%.a$vpaths vpath lib%.so$vpathd EOF if $allstatic ; then echo ".LIBPATTERNS := lib%.a" echo "DO_ALLSTATIC := 1" else echo ".LIBPATTERNS := lib%.so" fi if $static ; then echo "DO_STATIC := 1" else echo "DO_STATIC :=" fi if $shared ; then echo "DO_SHARED := 1" else echo "DO_SHARED :=" fi if $allpic ; then echo "STATIC_LIBS_ARE_PIC := 1" else echo "STATIC_LIBS_ARE_PIC :=" fi if $usensss ; then echo "LIBNSSS := -lnsss" echo "MAYBEPTHREAD_LIB := -lpthread" else echo "LIBNSSS :=" echo "MAYBEPTHREAD_LIB :=" fi if $useexecline ; then echo "EXECLINE_LIB := -lexecline" else echo "EXECLINE_LIB :=" 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." s6-2.13.1.0/doc/000077500000000000000000000000001470151141200130255ustar00rootroot00000000000000s6-2.13.1.0/doc/fifodir.html000066400000000000000000000125731470151141200153450ustar00rootroot00000000000000 s6: fifodirs

s6
Software
skarnet.org

Fifodirs

A fifodir is a rendez-vous point between the notifier of certain events and its listeners. It is implemented via a directory in the filesystem. No data is stored; it is appropriate to create fifodirs in a RAM filesystem.

Manipulating fifodirs

C API

For the notifier

  • You can create fifodirs via the ftrigw_fifodir_create() function in libftrig.
  • You can send an event to a fifodir via the ftrigw_notify() function in the notifier part of the libftrig.
  • You can clean up a fifodir via the ftrigw_clean() function in libftrig.
  • You can destroy fifodirs via the rm_rf() function in libstddjb.

For a listener

  • You can subscribe to a fifodir via the ftrigr_subscribe() function in the listener part of the libftrig.
  • Other functions in the libftrig allow you to receive and handle events synchronously or asynchronously.

Unix API

For the notifier

  • You can create fifodirs with the s6-mkfifodir command.
  • You can send an event to a fifodir with the s6-ftrig-notify command.
  • You can clean up a fifodir with the s6-cleanfifodir command.
  • You can destroy fifodirs with the rm -rf command.

For a listener

  • You can subscribe to a fifodir and wait for an event, or a series or events, with the s6-ftrig-wait command.
  • You can subscribe to a fifodir, then trigger a program, then wait for an event, with the s6-ftrig-listen1 and s6-ftrig-listen commands. This makes it possible to only send a notification after you're sure a notifier is actually listening, in order to prevent race conditions.

Internals and Unix permissions

  • Notifiers and listeners agree on a fifodir.
  • The fifodir directory is created by the notifier. It must be writable by listeners.
  • To subscribe, a listener atomically creates a named pipe (FIFO) in this directory and listens to the reading end. This named pipe must be writable by the notifier.
  • To send an event to listeners, the notifier writes the event byte to all the named pipes in the directory. Credit for this idea goes to Stefan Karrmann.
  • To unsubscribe, a listener unlinks his named pipe from the directory.

Note that in the s6 implementation of fifodirs, there are a few additional details: for instance, the named pipes created in a fifodir by a listener follow a strict naming convention, for efficiency and safety reasons. If you are using fifodirs, it is recommended that you use the provided C library functions or the s6-ftrig-* command line utilities instead of directly hacking into the fifodir internals.

Fifodirs are created by their notifier, so they always originally inherit its uid and gid. A notifier must be able to make his fifodir either publicly accessible (anyone can subscribe) or restricted (only a given group can subscribe).

A publicly accessible fifodir must have rights 1733:

  • Anyone can create a fifo in that fifodir
  • Only the notifier can see all the subscribers' fifos
  • A listener can only delete its own fifo
  • A notifier can delete any fifo for cleaning purposes

A restricted fifodir must have the gid g of the group of allowed listeners and have rights 3730. Unless the notifier is root, it must be in the group of allowed listeners to be able to create such a fifodir.

  • Only members of g can create a fifo in that fifodir
  • Only the notifier can see all the subscribers' fifos
  • Fifos are always created with gid g
  • A listener can only delete its own fifo
  • A notifier can delete any fifo for cleaning purposes

A named pipe in a fifodir must always belong to its listener and have rights 0622:

  • Only this listener can read on the fifo
  • Anyone who has reading rights on the fifodir (i.e. only the notifier) can write to the fifo

The libftrig interface takes care of all the subtleties.

s6-2.13.1.0/doc/ftrig.html000066400000000000000000000171431470151141200150340ustar00rootroot00000000000000 s6: libftrig

s6
Software
skarnet.org

libftrig

libftrig is a portable Unix C programming interface allowing a process (the subscriber or listener) to be instantly notified when another process (the notifier or writer) signals an event.

What is notification ?

Notification vs. polling

Process A is notified of an event E when it gets a instant notice that event E has happened; the notice may disrupt A's execution flow. Notification is the opposite of polling, where A has to periodically (every T milliseconds) check whether E happened and has no other way to be informed of it.

Polling is generally considered bad practice - and is inferior to notification in practically every case - for three reasons:

  • Reaction time. When event E happens, process A does not know it instantly. It will only learn of E, and be able to react to it, when it explicitly checks for E; and if E happened right after A performed the check, this can take as long as T milliseconds (the polling period). Polling processes have reaction delays due to the polling periods.
  • Resource consumption. Even if no event ever happens, process A will still wake up needlessly every T milliseconds. This might not seem like a problem, but it is a serious one in energy-critical environments. Polling processes use more CPU time than is necessary and are not energy-friendly.
  • Conflict between the two above reasons. The longer the polling period, the more energy-friendly the process, but the longer the reaction time. The shorter the polling period, the shorter the reaction time, but the more resource-consuming the process. A delicate balance has to be found, and acceptable behaviour is different in every case, so there's no general rule of optimization.

Notification, on the other hand, is generally optimal: reaction time is zero, and resource consumption is minimal - a process can sleep as soon as it's not handling an event, and only wake up when needed.

Of course, the problem of notification is that it's often more difficult to implement. Notification frameworks are generally more complex, involving lots of asynchronism; polling is widely used because it's easy.

Notifications and Unix

Unix provides several frameworks so that a process B (or the kernel) can notify process A.

  • Signals. The simplest Unix notification mechanism. Sending events amounts to a kill() call, and receiving events amounts to installing a signal handler (preferably using a self-pipe if mixing signals with an event loop). Unfortunately, Unix signals, even the more recent and powerful real-time POSIX signals, have important limitations when it's about generic notification:
    • non-root processes can only send signals to a very restricted and implementation-dependent set of processes (roughly, processes with the same UID). This is a problem when designing secure programs that make use of the Unix privilege separation.
    • you need to know the PID of a process to send it signals. This is generally impractical; process management systems that do not use supervisor processes have to do exactly that, and they resort to unreliable, ugly hacks (.pid files) to track down process PIDs.
  • BSD-style IPCs, i.e. file descriptors to perform select()/poll() system calls on, in an asynchronous event loop. This mechanism is very widely used, and rightly so, because it's extremely generic and works in every ordinary situation; you have to be doing very specific stuff to reach its limits. If process A is reading on fd f, it is notified everytime another process makes f readable - for instance by writing a byte to the other end if f is the reading end of a pipe. And indeed, this is how libftrig works internally; but libftrig is needed because direct use of BSD-style IPCs also has limitations.
    • Anonymous pipes are the simplest and most common BSD-style IPC. If there is a pipe from process B to process A, then B can notify A by writing to the pipe. The limitation is that A and B must have a common ancestor that created the pipe; two unrelated processes cannot communicate this way.
    • Sockets are a good many-to-one notification system: once a server is up, it can be notified by any client, and notify all its clients. The limitation of sockets is that the server must be up before the client, which prevents us from using them in a general notification scheme.
  • System V IPCs, i.e. message queues and semaphores. The interfaces to those IPCs are quite specific and can't mix with select/poll loops, that's why nobody in their right mind uses them.

What we want

We need a general framework to:

  • Allow an event-generating process to broadcast notifications to every process that asked for one, without having to know their PIDs
  • Allow a process to subscribe to a "notification channel" and be instantly, asynchronously notified when an event occurs on this channel.

This requires a many-to-many approach that Unix does not provide natively, and that is what libftrig does.

That's what a bus is for. D-Bus already does all this.

Yes, a bus is a good many-to-many notification mechanism indeed. However, a Unix bus can only be implemented via a daemon - you need a long-running process, i.e. a service, to implement a bus. And s6 is a supervision suite, i.e. a set of programs designed to manage services; we would like to be able to use notifications in the supervision suite, to be able to wait for a service to be up or down... without relying on a particular service to be up. libftrig provides a notification mechanism that does not need a bus service to be up, that's its main advantage over a bus.

If you are not concerned with supervision and can depend on a bus service, though, then yes, by all means, use a bus for your notification needs. There is a skabus project in the making, which aims to be simpler, smaller and more maintainable than D-Bus.

How to use libftrig

libftrig is really a part of libs6: all the functions are implemented in the libs6.a archive, or the libs6.so dynamic shared object. However, the interfaces are different for notifiers and listeners:

  • Notifiers use the ftrigw interface.
  • Listeners use the ftrigr interface.
s6-2.13.1.0/doc/index.html000066400000000000000000000423151470151141200150270ustar00rootroot00000000000000 s6 - skarnet's small supervision suite

Software
skarnet.org

s6

What is it ?

s6 is a small suite of programs for UNIX, designed to allow process supervision (a.k.a service supervision), in the line of daemontools and runit, as well as various operations on processes and daemons. It is meant to be a toolbox for low-level process and service administration, providing different sets of independent tools that can be used within or without the framework, and that can be assembled together to achieve powerful functionality with a very small amount of code.

Examples of things you can do by assembling together several programs provided by s6 - besides process supervision:

  • syslogd functionality, using much less resources than the traditional syslogd.
  • Reliable service readiness notification, which is the basis for service dependency management.
  • Controlled privileged gain as with sudo, without using any suid programs.
  • The useful parts of socket activation[1] without having to change application code or link servers against any specific library, and without having to switch to any specific init system.

The s6 documentation tries to be complete and self-contained; however, if you have never heard of process supervision before, you might be confused at first. See the related resources section below for pointers to more resources, and earlier approaches to process supervision that might help you understand the basics.



Installation

Requirements

  • A POSIX-compliant system with a standard C development environment
  • GNU make, version 3.81 or later
  • skalibs version 2.14.3.0 or later. It's a build-time requirement. It's also a run-time requirement if you link against the shared version of the skalibs library.
  • (Optional, but really recommended for full functionality): execline version 2.9.6.1 or later. When s6 is built with execline support (which is the default), execline is a build-time requirement, and also a run-time requirement for certain binaries that spawn scripts interpreted with execlineb.

The following optional dependencies are also supported:

  • If you're using musl and want nsswitch-like functionality: nsss version 0.2.0.5 or later (build-time and boot-time)

Licensing

s6 is free software. It is available under the ISC license.

Download

  • The current released version of s6 is 2.13.1.0.
  • Alternatively, you can checkout a copy of the s6 git repository:
     git clone git://git.skarnet.org/s6 
  • There's also a GitHub mirror of the s6 git repository.

Compilation

  • See the enclosed INSTALL file for installation details.

Upgrade notes

  • This page lists the differences to be aware of between the previous versions of s6 and the current one.

Reference

If you prefer to read this documentation as man pages, it is now possible! There is a project that ports the s6 documentation to a set of man pages.

Commands

All these commands exit 111 if they encounter a temporary error, and 100 if they encounter a permanent error - such as a misuse. They exit 127 if they're trying to execute into a program and cannot find it, and 126 if they fail to execute into a program for another reason. Short-lived commands exit 0 on success.

Supervision system

s6-svscan and s6-supervise are the long-lived processes maintaining the supervision tree. Other programs are a user interface to control those processes and monitor service states.

Daemontools-like utilities

These programs are a rewrite of the corresponding utilities from daemontools, with a few extras.

Fifodir management, notification and subscription

These programs are a clean rewrite of the obsolete "pipe-tools" package; they are now based on a properly designed notification library. They provide a command-line interface to inter-process notification and synchronization.

Local service management and access control

suidless privilege gain

Logging

Management of user supervision trees

Management of dynamic instances

fd-holding, a.k.a. the sensible part of socket activation

Libraries

Definitions


Related resources

s6 manual pages

Other components for s6-based init systems

  • s6-linux-init is a package to help you create a /sbin/init binary booting a Linux system with s6-svscan as process 1.
  • s6-overlay is a project that automates integration of s6 into Docker images.
  • s6-rc is a dependency-based service manager for s6.
  • anopa is another dependency-based service manager for s6.
  • 66 is another service manager working on top of s6.

s6 discussion

  • s6 is discussed on the supervision mailing-list.
  • There is a #s6 IRC channel on OFTC. Sometimes people are there and answer questions.

Similar work

  • daemontools, the pioneering process supervision software suite.
  • daemontools-encore, a derived work from daemontools with enhancements. (Note that although s6 follows the same naming scheme, the same general design, and many of the same architecture choices as daemontools, it is still original work, sharing no code at all with daemontools.)
  • runit, a slightly different approach to process supervision, with the same goals.
  • perp, yet another slightly different approach to process supervision, also with the same goals.
  • nosh is another suite of system-level utilities with similarities in the design and approach. It is written in C++, though, and is coded in quite a different way than the previous items on this list.

Other init systems

(This list hasn't been updated in a long while. I'm keeping it for reference.)

  • Felix von Leitner's minit is an init system for Linux, with process supervision capabilities.
  • suckless init is considered by many as the smallest possible init. I disagree: suckless init is incorrect, because it has no supervision capabilities, and thus, killing all processes but init can brick the machine. Nevertheless, suckless init, like many other suckless projects, is a neat exercise in minimalism.
  • sysvinit is the traditional init system for Linux.
  • Upstart is a well-known init system for Linux, with complete service management, that came with earlier versions of the Ubuntu distribution. It is now deprecated.
  • systemd is a problem in its own category.
  • The various BSD flavors have their own style of init.
  • MacOS X has its own init spaghetti monster called launchd.

All-in-one init systems generally feel complex and convoluted, and when most people find out about the process supervision approach to init systems, they usually find it much simpler. There is a good reason for this.

Miscellaneous

Why "s6" ?

skarnet.org's small and secure supervision software suite.

Also, s6 is a nice command name prefix to have: it identifies the origin of the software, and it's short. Expect more use of s6- in future skarnet.org software releases. And please avoid using that prefix for your own projects.

Footnotes

[1] Take everything you read on that link with two or three salt shakers. (This is true for anything written by the author of that document.)

s6-2.13.1.0/doc/instances.html000066400000000000000000000126441470151141200157110ustar00rootroot00000000000000 s6: dynamic instantiation

s6
Software
skarnet.org

Dynamic instantiation

An instanced service is a parameterized service that you want to run several copies of, with only the parameter changing. Each copy of the service is called an instance.

With s6, a service directory can only handle one process at a time. So, if we want instanced services, there will have to be one service directory per instance, always.

Static instantiation means that the set of possible instances is finite and known in advance. With s6, it means that all the service directories for all possible instances are created, typically by a preprocessor, and instances are treated like regular services.

Dynamic instantiation means that instances are created on demand instead of preallocated. Starting with version 2.11.2.0, s6 provides a few tools to help users set up and manage dynamically instanced services.

How to make a dynamically instanced service under s6

  • Write a template for a service directory that would run under s6-supervise. The run script should take the name of the instance as its first argument; the finish script, if present, should take the name of the instance as its third argument.
  • Call the s6-instance-maker program with this template as first argument, and a path dir as second argument. s6-instance-maker will create a service directory in dir. This is an offline tool: it does not interact with any currently active services or supervision trees.
  • Supervise dir by adding it to your regular scan directory. This will be your instanced service, but it's not running any instances yet. It is, instead, a nested supervision tree - the instanced service is an s6-svscan process that will supervise all the instances.
  • Create and delete instances at will with the s6-instance-create and s6-instance-delete programs; you can list all the available instances with s6-instance-list. These tools are online: they work with live service directories, i.e. that are being supervised by s6-supervise.
  • Instances are regular supervised processes. You can control individual instances with s6-instance-control, and check their status with s6-instance-status. These tools are online as well.

Internal workings

This section is not normative; users should not rely on it. It is only here for informational purposes.

Notes

  • This implementation of dynamic instances may seem expensive: it creates one s6-svscan process per instanced service, and one s6-supervise process per instance. However, remember that these processes use very little private memory, so having additional copies of them is far less expensive than it looks. It's really a convenient way to implement the feature by reusing existing code.
s6-2.13.1.0/doc/libs6/000077500000000000000000000000001470151141200140445ustar00rootroot00000000000000s6-2.13.1.0/doc/libs6/accessrules.html000066400000000000000000000302771470151141200172570ustar00rootroot00000000000000 s6: the accessrules library interface

libs6
s6
Software
skarnet.org

The accessrules library interface

The following functions and structures are declared in the s6/accessrules.h header, and implemented in the libs6.a or libs6.so library.

General information

accessrules is an access control library. It looks up a key in a user-specified database, then returns a code depending on whether the database allows access (in which case additional information can also be returned), denies access, or does not contain the key.

accessrules has been designed to be easily extensible to any database format and any key format.

Check the s6/accessrules.h header for the exact definitions.

Data structures

  • A s6_accessrules_result_t is a scalar that can have the following values: S6_ACCESSRULES_ERROR, S6_ACCESSRULES_DENY, S6_ACCESSRULES_ALLOW or S6_ACCESSRULES_NOTFOUND.
  • A s6_accessrules_params_t is a structure containing two strallocs, .env and .exec, used to return data contained in the database when a key has been allowed. The interpretation of this data is application-defined.

Function types

Backend lookups

A s6_accessrules_backend_func_t is the type of a function that takes a single key, looks it up in a database, and returns the result. Namely:

s6_accessrules_result_t f (char const *key, size_t keylen, void *handle, s6_accessrules_params_t *params)

f looks up key key of length keylen in the database represented by handle in an implementation-defined way. It returns a number that says the key has been allowed, denied or not found, or an error occurred. If the key has been allowed, f stores additional information from the database into *params.

Two s6_accessrules_backend_func_t functions are natively implemented:

  • s6_accessrules_backend_fs takes a char const * handle and interprets it as a base directory to look up key under, in the format understood by s6-accessrules-cdb-from-fs.
  • s6_accessrules_backend_cdb takes a struct cdb * handle and looks up key in the CDB it points to. handle must already be mapped to a CDB file. Such a file can be built with the s6-accessrules-cdb-from-fs utility.

Frontend key checking

A s6_accessrules_keycheck_func_t is the type of a function that takes a user-level key, makes a list of corresponding backend-level keys and calls a s6_accessrules_backend_func_t function until it finds a match. Namely:

s6_accessrules_result_t f (void const *key, void *handle, s6_accessrules_params_t *params, s6_accessrules_backend_func_t *backend)

f derives a list of low-level keys to check from key. Then, for each key k of length klen in this list, it calls (*backend)(k, klen, handle, params), returning *backend's result if it is not S6_ACCESSRULES_NOTFOUND. If no match can be found in the whole list, f finally returns S6_ACCESSRULES_NOTFOUND.

Five s6_accessrules_keycheck_func_t functions are natively implemented:

Ready-to-use functions

Those functions are mostly macros; they're built by associating a frontend function with a backend function.

s6_accessrules_result_t s6_accessrules_uidgid_cdb (uid_t u, gid_t g, struct cdb *c, s6_accessrules_params_t *params)
Checks the *c CDB database for an authorization for uid u and gid g. If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6_accessrules_result_t s6_accessrules_uidgid_fs (uid_t u, gid_t g, char const *dir, s6_accessrules_params_t *params)
Checks the dir base directory for an authorization for uid u and gid g. If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6_accessrules_result_t s6_accessrules_reversedns_cdb (char const *name, struct cdb *c, s6_accessrules_params_t *params)
Checks the *c CDB database for an authorization for the name FQDN. If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6_accessrules_result_t s6_accessrules_reversedns_fs (char const *name, char const *dir, s6_accessrules_params_t *params)
Checks the dir base directory for an authorization for the name FQDN. If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6_accessrules_result_t s6_accessrules_ip4_cdb (char const *ip4, struct cdb *c, s6_accessrules_params_t *params)
Checks the *c CDB database for an authorization for the ip4 IPv4 address (4 network byte order characters). If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6_accessrules_result_t s6_accessrules_ip4_fs (char const *ip4, char const *dir, s6_accessrules_params_t *params)
Checks the dir base directory for an authorization for the ip4 IPv4 address (4 network byte order characters). If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6_accessrules_result_t s6_accessrules_ip6_cdb (char const *ip6, struct cdb *c, s6_accessrules_params_t *params)
Checks the *c CDB database for an authorization for the ip6 IPv6 address (16 network byte order characters). If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6_accessrules_result_t s6_accessrules_ip6_fs (char const *ip6, char const *dir, s6_accessrules_params_t *params)
Checks the dir base directory for an authorization for the ip6 IPv6 address (16 network byte order characters). If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6_accessrules_result_t s6_accessrules_ip46_cdb (ip46_t *ip, struct cdb *c, s6_accessrules_params_t *params)
Checks the *c CDB database for an authorization for the ip IP address. If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6_accessrules_result_t s6_accessrules_ip46_fs (ip46_t const *ip, char const *dir, s6_accessrules_params_t *params)
Checks the dir base directory for an authorization for the ip IP address. If the result is S6_ACCESSRULES_ALLOW, additional information may be stored into params.

s6-2.13.1.0/doc/libs6/fdholder.html000066400000000000000000000160721470151141200165270ustar00rootroot00000000000000 s6: the s6-fdholder library interface

libs6
s6
Software
skarnet.org

The fdholder library interface

The fdholder library provides an API for clients wanting to communicate with a s6-fdholderd daemon.

Programming

Check the s6/fdholder.h header for the exact function prototypes.

A programming example

The src/fdholder/s6-fdholder-*.c files in the s6 package, for instance, illustrate how to use the fdholder library.

Synchronous functions with a specified maximum execution time

The explanation given there applies here too: the functions documented in this page are synchronous, but can return early if the deadline is reached, in which case the connection to the server should be closed immediately because no protocol consistency is guaranteed.

The s6-fdholderd server should be very quick to answer queries, so this mechanism is provided as a simple security against programming errors - for instance, connecting to the wrong daemon.

Starting and ending a session

s6_fdholder_t a = S6_FDHOLDER_ZERO ;
int fd = 6 ;

tain_now_g() ;

s6_fdholder_init(&a, fd) ;
(...)
s6_fdholder_free(&a) ;

s6_fdholder_init assumes that fd is a socket already connected to an s6-fdholderd daemon. The a structure must be initialized to S6_FDHOLDER_ZERO before use.

tain_now_g() initializes a global variable that keeps track of the current time, for use with later functions.

s6_fdholder_free frees the resources occupied by a. It does not, however, close fd. You should manually close it to end the connection to the server. Note that if your program has been started by s6-ipcclient, both fds 6 and 7 are open (and refer to the same socket), so you should close both.

Alternatively, if your connection to s6-fdholderd has not been created yet, you can use the following functions:

int s6_fdholder_start (s6_fdholder_t *a, char const *path, tain_t const *deadline, tain_t *stamp)

Starts a session with a s6-fdholderd instance listening on path. a must be initialized to S6_FDHOLDER_ZERO before calling this function. On success, returns nonzero and a can be used as a handle for the next s6_fdholder_* function calls. On failure, returns 0, and sets errno.

void s6_fdholder_end (s6_fdholder_t *a)

Ends the current session and frees all allocated resources. If needed, a is immediately reusable for another s6_fdholder_start call.

Storing a fd

int r ;
int fd ;
tain_t limit = TAIN_INFINITE ;
char const *id = "my_identifier" ;
r = s6_fdholder_store_g(&a, fd, id, &limit, &deadline) ;

s6_fdholder_store (and its variant s6_fdholder_store_g that uses the global timestamp variable) attempts to store a copy of descriptor fd into s6-fdholderd, using identifier id, with an expiration date of limit. In this example, limit is TAIN_INFINITE, which means no expiration date. The operation should return before deadline, else it will automatically return 0 ETIMEDOUT. The result is 1 on success and 0 on failure, with an appropriate errno code.

Deleting a fd

fd = s6_fdholder_delete_g(&a, id, &deadline) ;

s6_fdholder_delete attempts to delete the file descriptor identified by id. It returns 1 on success and 0 on failure, with an appropriate errno code.

Retrieving a fd

fd = s6_fdholder_retrieve_g(&a, id, &deadline) ;

s6_fdholder_retrieve attempts to retrieve the file descriptor identified by id. It returns a valid fd number on success, and -1 on failure, with an appropriate errno code.

s6_fdholder_retrieve_delete() performs a retrieval and a deletion at the same time, if the client is authorized to do so.

Listing the identifiers held by the server

stralloc list = STRALLOC_ZERO ;
int n ;
n = s6_fdholder_list_g(&a, &list, &deadline) ;

s6_fdholder_list gets the list of all identifiers currently held by the server. It stores it into the stralloc list, as a series of null-terminated strings, one after the other. There are n such strings. The function returns n on success, or -1 on failure, with an appropriate errno code.

Reading a dump

genalloc dump = GENALLOC_ZERO ;
r = s6_fdholder_getdump_g(&a, &dump, &deadline) ;

s6_fdholder_getdump attempts to retrieve the whole set of descriptors from the server. It returns 1 on success, and 0 on failure, with an appropriate errno code. The set is stored into the genalloc dump, which is to be interpreted as a stralloc containing an array of s6_fdholder_fd_t.

genalloc_s(s6_fdholder_fd_t, &dump) is a pointer to this array, and genalloc_len(s6_fdholder_fd_t, &dump) is the number of elements in the array. A s6_fdholder_fd_t contains at least a descriptor number, an identifier, and an expiration date, see the s6/fdholder.h header file.

Writing a dump

unsigned int dumplen ;
s6_fdholder_fd_t const *dumparray ;
r = s6_fdholder_setdump_g(&a, &dumparray, dumplen, &deadline) ;

s6_fdholder_setdump attempts to send a set of descriptors to the server. The descriptors are contained in the array dumparray of length dumplen. The function returns 1 on success, and 0 on failure, with an appropriate errno code.

s6-2.13.1.0/doc/libs6/ftrigr.html000066400000000000000000000261121470151141200162310ustar00rootroot00000000000000 s6: the ftrigr library interface

libs6
s6
Software
skarnet.org

The ftrigr library interface

The ftrigr library provides an API for listeners, i.e. programs that want to subscribe to fifodirs and be instantly notified when the proper sequence of events happens.

Programming

Check the s6/ftrigr.h header for the exact function prototypes.

Make sure your application is not disturbed by children it doesn't know it has. This means paying some attention to the SIGCHLD handler, if any, and to the way you perform waitpid()s. The best practice is to use a self-pipe to handle SIGCHLD (as well as other signals the application needs to trap), and to always use wait_nohang() to reap children, simply ignoring pids you don't know.

If your application has trouble handling unknown children, consider using an ftrigrd service. (And fix your application!)

A programming example

The src/pipe-tools/s6-ftrig-listen1.c and src/supervision/s6-svwait.c files in the s6 package, for instance, illustrate how to use the ftrigr library.

Synchronous functions with a specified maximum execution time

  • Synchronous functions take a tain_t const * (deadline) parameter and a tain_t * (stamp) parameter. Those are pointers to tain_t structures containing absolute times; the former represents a deadline (in most cases, this time will be in the future) and the latter must be an accurate enough timestamp. These structures can be filled using the tain_ primitives declared in skalibs/tai.h.
  • ("Accurate enough" means that no blocking system call must have been made since the last time stamp was updated (by tain_now(&stamp)). It's a good policy to always update stamp right after a (potentially) blocking system call like select() returns. And unless the application is extremely CPU-intensive (think calculus for physicists or astronomers) updating stamp more frequently is unnecessary.)
  • If such a synchronous function still hasn't returned when the deadline occurs, then it will immediately return a failure code and set errno to ETIMEDOUT. It is possible to pass null pointers to the function instead of pointers to tain_t structures, in which case the function will never timeout.
  • If a timeout occurs, the library does not guarantee proper interprocess communication later on; the application should either die, or at least close the communication channel and open a new one.
  • If any waiting occurred, the stamp structure is automatically updated by the called function, so it always represents an accurate enough estimation of the current time. This allows the programmer to call several such functions in a sequence without modifying the deadline and stamp parameters: then the whole sequence is bound in execution time.
  • This is a general safety mechanism implemented in libunixonacid: in interprocess communication, purely synchronous primitives are dangerous because they make the calling process rely on proper behaviour of the called process. Giving synchronous primitives the ability to timeout allows developers to write reliable programs even when interacting with software they have no control on.

Starting and ending a session

ftrigr_t a = FTRIGR_ZERO ;
tain_t deadline, stamp ;

tain_now(&stamp) ;
tain_addsec(&deadline, &stamp, 2)

// char const *path = FTRIGR_IPCPATH ;
// ftrigr_start(&a, path, &deadline, &stamp) ;
ftrigr_startf(&a, &deadline, &stamp) ;

ftrigr_start starts a session with an ftrigrd service listening on path.
ftrigr_startf starts a session with an ftrigrd process as a child (which is the simplest usage).
a is an ftrigr_t structure that must be declared in the stack and initialized to FTRIGR_ZERO. stamp must be an accurate enough timestamp.
If the session initialization fails, the function returns 0 and errno is set; else the function returns 1.

If the absolute time deadline is reached and the function has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT. Only local interprocess communications are involved; unless your system is heavily overloaded, the function should return near-instantly. One or two seconds of delay between stamp and deadline should be enough: if the function takes more than that to return, then there is a problem with the underlying processes.

You can have more than one session open in parallel, by declaring several distinct ftrigr_t structures and calling ftrigr_startf (or ftrigr_start) more than once. However, this is useless, since one single session can handle virtually as many concurrent fifodirs as your application needs.

ftrigr_end(&a) ;

ftrigr_end frees all the resources used by the session. The a structure is then reusable for another session.

Subscribing to a fifodir

char const *path = "/var/lib/myservice/fifodir" ;
char const *re = "a.*b|c*d" ;
uint32_t options = 0 ;

uint16_t id = ftrigr_subscribe (&a, path, re, options, &deadline, &stamp) ;

ftrigr_subscribe instructs the s6-ftrigrd daemon, related to the open session represented by the a structure, to subscribe to the path fifodir, and to notify the application when it receives a series of events that matches the re regexp. options can be 0 or FTRIGR_REPEAT. If it is 0, the daemon will automatically unsubscribe from path once re has been matched by a series of events. If it is FTRIGR_REPEAT, it will remain subscribed until told otherwise.

ftrigr_subscribe() returns 0 and sets errno in case of failure, or a nonzero 16-bit number identifying the subscription in case of success.

ftrigr_subscribe should return near-instantly, but if deadline is reached, it will return 0 ETIMEDOUT. If ftrigr_subscribe returns successfully, then the s6-ftrigrd daemon is guaranteed to be listening on path, and events can be sent without the risk of a race condition.

Synchronously waiting for events

uint16_t list[1] ;
unsigned int n = 1 ;
char trigger ;
list[0] = id ;

// r = ftrigr_wait_and(&a, list, n, &deadline, &stamp) ;
r = ftrigr_wait_or(&a, list, n, &deadline, &stamp, &trigger) ;

ftrigr_wait_and() waits for all the n fifodirs whose ids are listed in list to receive an event. It returns -1 in case of error or timeout, or a non-negative integer in case of success.
ftrigr_wait_or() waits for one of the n fifodirs whose ids are listed in list to receive an event. It returns -1 in case of error or timeout; if it succeeds, the return value is the position in list, starting at 0, of the identifier that received an event; and trigger is set to the character that triggered that event, i.e. the last character of a sequence that matched the regular expression re used in the subscription.

Asynchronously waiting for events

(from now on, the functions are listed with their prototypes instead of usage examples.)

int ftrigr_fd (ftrigr_t const *a)

Returns a file descriptor to select on for reading. Do not read() it though.

int ftrigr_updateb (ftrigr_t *a)

Call this function whenever the fd checks readability: it will update a's internal structures with information from the s6-ftrigrd daemon. It returns -1 if an error occurs; in case of success, it returns the number of identifiers for which something happened.

When ftrigr_updateb returns, genalloc_s(uint16_t, &a->list) points to an array of genalloc_len(uint16_t, &a->list) 16-bit unsigned integers. Those integers are ids waiting to be passed to ftrigr_check or ftrigr_checksa. The number of ids already acknowledged is stored in a->head, so the first unacknowledged id is genalloc_s(uint16_t, &a->list)[a->head].

int ftrigr_check (ftrigr_t *a, uint16_t id, char *what)

Checks whether an event happened to id. Use after a call to ftrigr_updateb().

  • If an error occurred, returns -1 and sets errno. The error number may have been transmitted from s6-ftrigrd.
  • If no notification happened yet, returns 0.
  • If something happened, writes the character that triggered the latest notification into what and returns the number of times that an event happened to this identifier since the last call to ftrigr_check().
int ftrigr_checksa (ftrigr_t *a, uint16_t id, stralloc *what)

Checks whether an event happened to id. Use after a call to ftrigr_update(), as an alternative to ftrigr_check().

  • If an error occurred, returns -1 and sets errno. The error number may have been transmitted from s6-ftrigrd.
  • If no notification happened yet, returns 0.
  • If something happened, appends one character to the end of the what stralloc for every time a notification was triggered since the last call to ftrigr_check(). Each character is the one that triggered a notification. The function then returns 1.
void ftrigr_ack (ftrigr_t *a, size_t n)

Acknowledges reading n ids from the id list updated by ftrigr_updateb.

int ftrigr_update (ftrigr_t *a)

Acknowledges all the pending ids (i.e. clears the stored id list) then calls ftrigr_updateb().

s6-2.13.1.0/doc/libs6/ftrigw.html000066400000000000000000000056621470151141200162450ustar00rootroot00000000000000 s6: the ftrigw library interface

libs6
s6
Software
skarnet.org

The ftrigw library interface

The ftrigw library provides an API for notifiers, i.e. programs that want to regularly announce what they're doing.

Notifiers should create a fifodir in a hardcoded place in the filesystem, and document its location. Listeners will then be able to subscribe to that fifodir, and receive the events.

Programming

Check the s6/ftrigw.h header for the exact function prototypes.

Creating a fifodir

char const *path = "/var/lib/myservice/fifodir" ;
gid_t gid = -1 ;
int forceperms = 0 ;
int r = ftrigw_fifodir_make(path, gid, forceperms) ;

ftrigw_fifodir_make creates a fifodir at the path location. It returns 0, and sets errno, if an error occurs. It returns 1 if it succeeds.
If a fifodir, owned by the user, already exists at path, and forceperms is zero, then ftrigw_fifodir_make immediately returns a success. If forceperms is nonzero, then it tries to adjust path's permissions before returning.

If gid is negative, then path is created "public". Any listener will be able to subscribe to path. If gid is nonnegative, then path is created "private". Only processes belonging to group gid will be able to subscribe to path.

Sending an event

char event = 'a' ;
r = ftrigw_notify(path, event) ;

ftrigw_notify sends event to all the processes that are currently subscribed to path. It returns -1 if an error occurs, or the number of successfully notified processes.

Cleaning up

When stray KILL signals hit s6-ftrigrd processes, 1. it's a sign of incorrect system administration, 2. they can leave unused named pipes in the fifodir. It's the fifodir's owner's job, i.e. the notifier's job, to periodically do some housecleaning and remove those unused pipes.

r = ftrigw_clean(path) ;

ftrigw_clean cleans path. It returns 0, and sets errno, if it encounters an error. It returns 1 if it succeeds.

s6-2.13.1.0/doc/libs6/index.html000066400000000000000000000046131470151141200160450ustar00rootroot00000000000000 s6: the s6 library interface

s6
Software
skarnet.org

The s6 library interface

General information

libs6 is a collection of utility C interfaces, used in the s6 executables.

Compiling

  • Make sure the s6 headers, as well as the skalibs headers, are visible in your header search path.
  • Use #include <s6/s6.h>

Linking

  • Make sure the s6 libraries, as well as the skalibs libraries, are visible in your library search path.
  • Link against -ls6 and -lskarnet. If you're using socket functions (which is the case with libftrigr, for instance, add `cat $sysdeps/socket.lib` to your command line. If you're using timed functions involving TAI timestamps (which is also the case with libftrigr for instance), add `cat $sysdeps/sysclock.lib`. $sysdeps stands for your skalibs sysdeps directory.

Programming

The s6/s6.h header is actually a concatenation of other headers: the libs6 is separated into several modules, each of them with its own header.

  • The s6/accessrules.h header provides functions to check credentials against configuration files.
  • The s6/ftrigr.h header provides functions to subscribe to fifodirs and be notified of events.
  • The s6/ftrigw.h header provides functions to manage fifodirs and send notifications to them.
  • The s6/fdholder.h header provides functions to communicate with a s6-fdholderd server and exchange file descriptors with it.
s6-2.13.1.0/doc/libs6/s6-ftrigrd.html000066400000000000000000000057401470151141200167270ustar00rootroot00000000000000 s6: the s6-ftrigrd program

libs6
s6
Software
skarnet.org

The s6-ftrigrd program

s6-ftrigrd is a helper program that manages a set of subscriptions to fifodirs as well as regular expressions on events. It takes orders from its client program that controls it via the ftrigr library, and notifies it when desired events happen.

Interface

s6-ftrigrd is not meant to be called directly.

  • If the client program uses ftrigr_startf(), it spawns an instance of s6-ftrigrd as a child. s6-ftrigrd's stdin is a pipe reading from the client; its stdout is a pipe writing to the client; its stderr is the same as the client's; and there's an additional pipe from s6-ftrigrd to the client, used for asynchronous notifications.
  • If the client program uses ftrigr_start(), then it tries to connect to a Unix domain socket. An ftrigrd local service should be listening to that socket, i.e. a Unix domain super-server such as s6-ipcserver spawning an s6-ftrigrd program on every connection. Then an s6-ftrigrd instance is created for the client.
  • When the client uses ftrigr_end(), or closes s6-ftrigrd's stdin in any way, s6-ftrigrd exits 0.

s6-ftrigrd handles the grunt work of creating fifos in a fifodir for a subscriber. It also wakes up on every event, and compares the chain of events it received on a given fifodir with the client-provided regexp. If the chain of events matches the regexp, it notifies the client.

Notes

The connection management between the client and s6-ftrigrd is entirely done by the textclient library from skalibs.

s6-ftrigrd is entirely asynchronous. It stores unread notifications into heap memory; it can grow in size if there are a lot of events and the client fails to read them. To avoid uncontrolled growth, make sure your client calls ftrigr_update() as soon as ftrigr_fd() becomes readable.

An s6-ftrigrd instance can only handle up to FTRIGRD_MAX (defined in s6/ftrigr.h) subscriptions at once. By default, this number is 1000, which is more than enough for any reasonable system.

s6-2.13.1.0/doc/localservice.html000066400000000000000000000134321470151141200163710ustar00rootroot00000000000000 s6: what is a local service

s6
Software
skarnet.org

Local services

A local service is a daemon that listens to incoming connections on a Unix domain socket. Clients of the service are programs connecting to this socket: the daemon performs operations on their behalf.

The service is called local because it is not accessible to clients from the network.

A widely known example of a local service is the syslogd daemon. On most implementations, it listens to the /dev/log socket. Its clients connect to it and send their logs via the socket. The openlog() function is just a wrapper arround the connect() system call, the syslog() function a wrapper around write(), and so on.

Benefits

Privileges

The most important benefit of a local service is that it permits controlled privilege gains without using setuid programs. The daemon is run as user S; a client running as user C and connecting to the daemon asks it to perform operations: those will be done as user S.

Standard Unix permissions on the listening socket can be used to implement some basic access control: to restrict access to clients belonging to group G, change the socket to user S and group G, and give it 0420 permissions. This is functionally equivalent to the basic access control for setuid programs: a program having user S, group G and permissions 4750 will be executable by group G and run with S rights.

But modern systems implement the getpeereid() system call or library function. This function allows the server to know the client's credentials: so fine-grained access control is possible. On those systems, local services can do as much authentication as setuid programs, in a much more controlled environment.

fd-passing

The most obvious difference between a local service and a network service is that a local service does not serve network clients. But local services have another nice perk: while network services usually only provide you with a single channel (a TCP or UDP socket) of communication between the client and the server, forcing you to multiplex your data into that channel, local services allow you to have as many communication channels as you want.

(The SCTP transport layer provides a way for network services to use several communication channels. Unfortunately, it is not widely deployed yet, and a lot of network services still depend on TCP.)

The fd-passing mechanism is Unix domain socket black magic that allows one peer of the socket to send open file descriptors to the other peer. So, if the server opens a pipe and sends one end of this pipe to a client via this mechanism, there is effectively a socket and a pipe between the client and the server.

UCSPI

The UCSPI protocol is an easy way of abstracting clients and servers from the network. A server written as a UCSPI server, just as it can be run under inetd or s6-tcpserver, can be run under s6-ipcserver: choose a socket location and you have a local service.

Fine-grained access control can be added by inserting s6-ipcserver-access in your server command line after s6-ipcserver.

A client written as an UCSPI client, i.e. assuming it has descriptor 6 (resp. 7) open and reading from (resp. writing to) the server socket, can be run under s6-ipcclient.

Use in skarnet.org software

skarnet.org libraries often use a separate process to handle asynchronicity and background work in a way that's invisible to the user. Among them are:

  • s6-ftrigrd, managing the reception of notifications and only waking up the client process when the notification pattern matches a regular expression.
  • s6lockd, handling time-constrained lock acquisition on client behalf.
  • skadnsd, performing asynchronous DNS queries and only waking up the client process when an answer arrives.

Those processes are usually spawned from a client, via the corresponding *_startf*() library call. But they can also be spawned from a s6-ipcserver program in a local service configuration. In both cases, they need an additional control channel to be passed from the server to the client: the main socket is used for synchronous commands from the client to the server and their answers, whereas the additional channel, which is now implemented as a socket as well (but created by the server on-demand and not bound to a local path), is used for asynchronous notifications from the server to the client. The fd-passing mechanism is used to transfer the additional channel from the server to the client.

s6-2.13.1.0/doc/notifywhenup.html000066400000000000000000000212511470151141200164530ustar00rootroot00000000000000 s6: service startup notifications

s6
Software
skarnet.org

Service startup notifications

It is easy for a process supervision suite to know when a service that was up is now down: the long-lived process implementing the service is dead. The supervisor, running as the daemon's parent, is instantly notified via a SIGCHLD. When it happens, s6-supervise sends a 'd' event to its ./event fifodir, so every subscriber knows that the service is down. All is well.

It is much trickier for a process supervision suite to know when a service that was down is now up. The supervisor forks and execs the daemon, and knows when the exec has succeeded; but after that point, it's all up to the daemon itself. Some daemons do a lot of initialization work before they're actually ready to serve, and it is impossible for the supervisor to know exactly when the service is really ready. s6-supervise sends a 'u' event to its ./event fifodir when it successfully spawns the daemon, but any subscriber reacting to 'u' is subject to a race condition - the service provided by the daemon may not be ready yet.

Reliable startup notifications need support from the daemons themselves. Daemons should do two things to signal the outside world that they are ready:

  1. Update a state file, so other processes can get a snapshot of the daemon's state
  2. Send an event to processes waiting for a state change.

This is complex to implement in every single daemon, so s6 provides tools to make it easier for daemon authors, without any need to link against the s6 library or use any s6-specific construct: daemons can simply write a line to a file descriptor of their choice, then close that file descriptor, when they're ready to serve. This is a generic mechanism that some daemons already implement.

s6 supports that mechanism natively: when the service directory for the daemon contains a valid notification-fd file, the daemon's supervisor, i.e. the s6-supervise program, will properly catch the daemon's message, update the status file (supervise/status), then notify all the subscribers with a 'U' event, meaning that the service is now up and ready.

This method should really be implemented in every long-running program providing a service. When it is not the case, it's impossible to provide reliable startup notifications, and subscribers should then be content with the unreliable 'u' events provided by s6-supervise.

Unfortunately, a lot of long-running programs do not offer that functionality; instead, they provide a way to poll them, an external program that runs and checks whether the service is ready. This is a bad mechanism, for several reasons. Nevertheless, until all daemons are patched to notify their own readiness, s6 provides a way to run such a check program to poll for readiness, and route its result into the s6 notification system: s6-notifyoncheck.

How to use a check program with s6 (i.e. readiness checking via polling)

  • Let's say you have a daemon foo, started under s6 via a /run/service/foo service directory, and that comes with a foo-check program that exhibits different behaviours when foo is ready and when it is not.
  • Create an executable script /run/service/foo/data/check that calls foo-check. Make sure this script exits 0 when foo is ready and nonzero when it's not.
  • In your /run/service/foo/run script that starts foo, instead of executing into foo, execute into s6-notifyoncheck foo. Read the s6-notifyoncheck page if you need to give it options to tune the polling.
  • echo 3 > /run/service/foo/notification-fd. If file descriptor 3 is already open when your run script executes foo, replace 3 with a file descriptor you know is not already open.
  • That's it.
    • Your check script will be automatically invoked by s6-notifyoncheck, until it succeeds.
    • s6-notifyoncheck will send the readiness notification to the file descriptor given in the notification-fd file.
    • s6-supervise will receive it and will mark foo as ready.

How to design a daemon so it uses the s6 mechanism without resorting to polling (i.e. readiness notification)

The s6-notifyoncheck mechanism was made to accommodate daemons that provide a check program but do not notify readiness themselves; it works, but is suboptimal. If you are writing the foo daemon, here is how you can make things better:

  • Readiness notification should be optional, so you should guard all the following with a run-time option to foo.
  • Assume a file descriptor other than 0, 1 or 2 is going to be open. You can hardcode 3 (or 4); or you can make it configurable via a command line option. See for instance the -D notif option to the mdevd program. It really doesn't matter what this number is; the important thing is that your daemon knows that this fd is already open, and is not using it for another purpose.
  • Do nothing with this file descriptor until your daemon is ready.
  • When your daemon is ready, write a newline to this file descriptor.
    • If you like, you may write other data before the newline, just in case it is printed to the terminal. It is not necessary, and it is best to keep that data short. If the line is read by s6-supervise, it will be entirely ignored; only the newline is important.
  • Then close that file descriptor.

The user who then makes foo run under s6 just has to do the following:

  • Write 3, or the file descriptor the foo daemon uses to notify readiness, to the /run/service/foo/notification-fd file.
  • In the /run/service/foo/run script, invoke foo with the option that activates the readiness notification. If foo makes the notification fd configurable, the user needs to make sure that the number that is given to this option is the same as the number that is written in the notification-fd file.
  • And that is all. Do not use s6-notifyoncheck in this case, because you do not need to poll to know whether foo is ready; instead, foo will directly communicate its readiness to s6-supervise, and that is a much more efficient mechanism.

What does s6-supervise do with this readiness information?

  • s6-supervise maintains a readiness state for other programs to read. You can check for it, for instance, via the s6-svstat program.
  • s6-supervise also broadcasts the readiness event to programs that are waiting for it - for instance the s6-svwait program. This can be used to make sure that other programs only start when the daemon is ready. For instance, the s6-rc service manager uses that mechanism to bring sets of services up or down: a service starts as soon as all its dependencies are ready, but never earlier.
s6-2.13.1.0/doc/overview.html000066400000000000000000000530511470151141200155650ustar00rootroot00000000000000 s6: an overview

s6
Software
skarnet.org

An overview of s6

s6 is a collection of utilities revolving around process supervision and management, logging, and system initialization. This page is a high-level description of the different parts of s6.

Process supervision

At its core, s6 is a process supervision suite, like its ancestor daemontools and its close cousin runit.

Concept

The concept of process supervision comes from several observations:

  • Unix systems, even minimalistic ones, need to run long-lived processes, aka daemons. That is one of the core design principles of Unix: one service → one daemon.
  • Daemons can die unexpectedly. Maybe they are missing a vital resource and cannot handle a certain failure; maybe they tripped on a bug; maybe a misconfigured administration program killed them; maybe the kernel killed them. Processes are fragile, but daemons are vital to a Unix system: a fundamental discrepancy that needs to be solved.
  • Automatically restarting daemons when they die is generally a good thing. In any case, sysadmin intervention is necessary, but at least the daemon is providing service, or trying to, until the sysadmin can log in and investigate the underlying problem.
  • Ad-hoc shell scripts that restart daemons suck, for several reasons that would each justify their own page. The difficulty of keeping track of the PID, explained below, is one of those reasons.
  • It is sometimes necessary to send signals to a daemon. To kill it, of course, but also to make it read its config file again, for instance; signalling a daemon is a natural and very common way of sending it simple commands.
  • Generally, to send a signal to a daemon, you need to know its PID. Without a supervision suite, knowing the proper PID is hard. Most non-supervision systems use a hack known as .pid files, i.e. the script that starts the daemon stores its PID into a file, and other scripts read that file. This is a bad mechanism for several reasons, and the case against .pid files would also justify its own page; the most important drawback of .pid files is that they create race conditions and management scripts may kill the wrong process.
  • Non-supervision systems provide scripts to start and stop daemons, but those scripts may fail at boot time even though they work when run manually, and vice versa. If a sysadmin logs in and runs the script to restart a daemon that has died, the result might not be the same as if the whole system had been rebooted, and the daemon may exhibit strange behaviours! This is because the boot-time environment and the restart-time environment are not the same when the script is run; and a non-supervision system just cannot ensure reproducibility of the environment. This is a core problem of non-supervision systems: countless bugs have been falsely reported because of simple environment differences or configuration errors, countless man-hours have been wasted to try and understand what was going on.

A process supervision system organizes the process hierarchy in a radically different way.

  • A process supervision system starts an independent hierarchy of processes at boot time, called a supervision tree. This supervision tree never dies: when one of its components dies, it is restarted automatically. To ensure availability of the supervision tree at all times, it should be rooted in process 1, which cannot die.
  • A daemon is never started, either manually or in a script, as a scion of the script that starts it. Instead, to start a daemon, you configure a specific directory which contains all the information about your daemon; then you send a command to the supervision tree. The supervision tree will start the daemon as a leaf. In a process supervision system, daemons are always spawned by the supervision tree, and never by an admin's shell.
  • The parent of your daemon is a supervisor. Since your daemon is its direct child, the supervisor always knows the correct PID of your daemon.
  • The supervisor watches your daemon and can restart it when it dies, automatically.
  • The supervision tree always has the same environment, so starting conditions are reproducible. Your daemon will always be started with the same environment, whether it is at boot time via init scripts or for the 100th automatic - or manual - restart.
  • To send signals to your daemon, you send a command to its supervisor, which will then send a signal to the daemon on your behalf. Your daemon is identified by the directory containing its information, which is stable, instead of by its PID, which is not stable; the supervisor maintains the correct association without a race condition or the other problems of .pid files.

Implementation

s6 is a straightforward implementation of those concepts.

  • The s6-svscan and s6-supervise programs are the components of the supervision tree. They are long-lived programs.
    • s6-supervise is a daemon's supervisor, its direct parent. For every long-lived process on a system, there is a corresponding s6-supervise process watching it. This is okay, because every instance of s6-supervise uses very few resources.
    • s6-svscan is, in a manner of speaking, a supervisor for the supervisors. It watches and maintains a collection of s6-supervise processes: it is the branch of the supervision tree that all supervisors are stemming from. It can be run and supervised by your regular init process, or it can run as process 1 itself. Running s6-svscan as process 1 requires some effort from the user, because of the inherent non-portability of init processes; the s6-linux-init package automates that effort and allows users to run s6 as an init replacement.
    • The configuration of a daemon to be supervised by s6-supervise is done via a service directory.
    • The place to gather all service directories to be watched by a s6-svscan instance is called a scan directory.
  • The command that controls a single supervisor, and allows you to send signals to a daemon, is s6-svc. It is a short-lived program.
  • The command that controls a set of supervisors, and allows you to start and stop supervision trees, is s6-svscanctl. It is a short-lived program.

These four programs, s6-svscan, s6-supervise, s6-svscanctl and s6-svc, are the very core of s6. Technically, once you have them, you have a functional s6 installation, and the other utilities are just a bonus.

Practical usage

To use s6's supervision features, you need to perform the following steps:

  • For every daemon you potentially want supervised, write a service directory. Make sure that your daemon does not background itself when started in the ./run script! Auto-backgrounding is a historical hack that was implemented when supervision suites did not exist; since you're using a supervision suite, auto-backgrounding is unnecessary and in this case detrimental.
  • Write a single scan directory for the set of daemons you want to actually run. This set can be modified at run time.
  • At some point in your initialization scripts, run s6-svscan on the scan directory. This will start the supervision tree, including your set of daemons. The exact way of running s6-svscan depends on your system: it is not quite the same when you want to run it as process 1 on a real machine, or under another init on a real machine, or as process 1 in a Docker container, or in another context entirely.
  • Alternatively, you can start s6-svscan on an empty scan directory, then populate it step by step and send an update command to s6-svscan via s6-svscanctl whenever the supervision tree should pick up the differences and start the services you added.
  • That's it, your services are running. To control them manually, you can use the s6-svc command.
  • At the end of the system's lifetime, you can use s6-svscanctl to bring down the supervision tree.

Service-specific logging

s6-svscan can monitor a supervision tree, but it can also do one more thing. It can ensure that a daemon's log, i.e. what the daemon outputs to its stdout (or stderr if you redirect it), gets processed by another, supervised, long-lived process, called a logger; and it can make sure that the logs are never lost between the daemon and the logger - even if the daemon dies, even if the logger dies.

If your daemon is outputting messages, you have a decision to make about where to send them.

  • You can do as non-supervision systems do, and send the messages to syslog. It's entirely possible with a supervision system too. However, like auto-backgrounding, syslog is a historical mechanism that predates supervision suites, and is technically inferior; it is recommended that you do not use it whenever you can avoid it.
  • You can send them to the daemon's stdout/stderr and do nothing special about it. The logs will then be sent to s6-svscan's stdout/stderr; what mechanism will read them depends on how you started s6-svscan.
  • You can use s6-svscan's service-specific logging mechanism and dedicate a logger process to your daemon's messages.

s6 provides you with a long-lived process to use as a logger: s6-log. It will store your logs in one (or more) specific directory of your choice, and rotate them automatically.

Helpers for run scripts

Creating a working service directory, and especially a good run script, is the most important part of the work when adapting a daemon to a supervision framework.

If you can find your daemon's invocation script on a non-supervision system, for instance a System V-style init script, you can see the exact options that the daemon is being run with: environment variables, uid and gid, open descriptors, etc. This is what you need to replicate in your run script.

(Do not replicate the auto-backgrounding, or things like start-stop-daemon invocation: start-stop-daemon and its friends are hideous and kludgy attempts to work around the lack of proper supervision mechanisms. Now that you have s6, you should remove them from your system, throw them into a bonfire, and dance and laugh while they burn. Generally speaking, as a system administrator you want daemons that have been designed following the principles described here, or at least you want to use the command-line options that make them behave in such a way.)

The vast majority of the tools provided by s6 are meant to be used in run scripts: they help you control the process state and environment in your script before it executes into your daemon. Or, sometimes, they are daemons themselves, designed to be supervised.

s6, like other skarnet.org software, makes heavy use of chain loading, also known as "Bernstein chaining": a lot of s6 tools will perform some action that changes the process state, then execute into the rest of their command line. This allows the user to change the process state in a very flexible way, by combining the right components in the right order. Very often, a run script can be reduced to a single command line - likely a long one, but still a single one. (That is the main reason why using the execline language to write run scripts is recommended: execline makes it natural to handle long command lines made of massive amounts of chain loading. This is by no means mandatory, though: a run script can be any executable file you want, provided that running it eventually results in a long-lived process with the same PID.)

Some examples of s6 programs meant to be used in run scripts:

  • The s6-log program is a long-lived process. It is meant to be executed into by a ./log/run script: it will be supervised, and will process what it reads on its stdin (i.e. the output of the ./run daemon).
  • The s6-envdir program is a short-lived process that will update its current environment according to what it reads in a given directory, then execute into the rest of its command line. It is meant to be used in a run script to adjust the environment with which the final daemon will be executed into.
  • Similarly, the s6-softlimit program adjusts its resource limits, then executes into the rest of its command line: it is meant to set the resources the final daemon will have access to.
  • The s6-applyuidgid program, part of the s6-*uidgid family, drops root privileges before executing into the rest of its command line: it is meant to be used in run scripts that need root privileges when starting but do not need it for the execution of the long-lived process.
  • s6-ipcserverd is a daemon that listens to a Unix socket and spawns a program for every connection. It is meant to be supervised, so it should be used in a run script, and it's also meant to be a flexible super-server that you can use for different applications: so it is a building block that may appear in several of your run scripts defining local services.

Readiness notification and dependency management

Now that you have a supervision tree, and long-lived processes running supervised, you may want to introduce dependencies between them: do not perform an action (e.g. start (with s6-svc -u) the Web server connecting to a database) before a given daemon is up and running (e.g. the database server). s6 provides tools to do that:

  • The s6-svwait, s6-svlisten1 and s6-svlisten programs will wait until a set of daemons is up, ready, down (as soon as the ./run process dies) or really down (when the ./finish process has also died).
  • Unfortunately, a daemon being up does not mean that it is ready: this page goes into the details. s6 supports a simple mechanism: when a daemon wants to signal that it is ready, it simply writes a newline to a file descriptor of its choice, and s6-supervise will pick that notification up and broadcast the information to processes waiting for it.
  • s6 also has a legacy mechanism for daemons that do not notify their own readiness but provide a way for an external program to check whether they're ready or not: s6-notifyoncheck. This is polling, which is bad, but unfortunately necessary for many daemons as of 2019.

s6 does not provide a complete dependency management framework, i.e. a program to automatically start (or stop) a set of services in a specific order - that order being automatically computed from a graph of dependencies between services. That functionality belongs to a service manager, and is implemented for instance in the s6-rc package.

Fine-grained control over services

s6 provides you with a few more tools to control and monitor your services. For instance:

  • s6-svstat gives you access to the detailed state of a service
  • s6-svperms allows you to configure what users can read that state, what users can send control commands to your service, and what users can be notified of service start/stop events
  • s6-svdt allows you to see what caused the latest deaths of a supervised process

These tools make s6 the most powerful and flexible of the existing process supervision suites.

Additional utilities

The other programs in the s6 package are various utilities that may be useful in designing servers, and more generally multi-process software. They can be used with or without a supervision environment, although it is of course recommended to have one; but they are not part of the core s6 functionality, and you may safely ignore them for now if you are just getting into the supervision world.

Generic inter-process notification

The s6-ftrig* family of programs allows notifications between unrelated processes: a set of processes can subscribe to a certain channel - identified by a directory in the filesystem - and ask to be notified of certain events on that channel; another set of processes can send events to the channel.

The underlying mechanism is the same as the one used by the supervision tree for readiness notification, but the s6-ftrig* tools provide a more generic access to that mechanism.

Helpers for designing local services

Local services, i.e. daemons listening to a Unix domain socket, are a powerful and flexible mechanism, especially with modern Unix systems that allow client authentication. s6 includes tools to take advantage of that mechanism.

  • The s6-ipc* family of programs is about designing clients or servers that communicate over Unix domain sockets.
  • The s6-*access* and s6-connlimit family of programs is about client access control.
  • The s6-sudo* family of programs is about using a local service in order to give selected clients the ability to run a command line with the privileges of the server, without using suid programs.

Keeping file descriptors open

Sometimes you want to keep a file descriptor open, even if the program normally using it dies - so the program can restart and use the same file descriptor without losing any data. To do that, you need to hold the descriptor in another process, i.e. that process should have it open but do nothing with it.

s6-svscan, for instance, holds the pipe existing between a supervised daemon and its logger, so even if the daemon or the logger dies while there are logs in the pipe, the pipe remains open and the logs are not lost.

s6 provides a mechanism to store and retrieve open file descriptors in a totally generic way: the s6-fdholder* family of programs.

  • The s6-fdholder-daemon program is a daemon (or, rather, executes into the s6-fdholderd daemon), meant to be supervised, that will hold file descriptors on its clients' behalf.
  • Other programs in the family, such as s6-fdholder-store, are client programs that interact with this daemon to store and retrieve file descriptors.

Note that "socket activation", one of the main advertised benefits of the systemd init system, sounds similar to fd-holding. The reality is that socket activation is a mixture of several different mechanisms, one of which is fd-holding; s6 allows you to implement the healthy parts of socket activation.

Other miscellaneous utilities

This page does not list or classify every s6 tool. Please explore the "Reference" section of the main s6 page for details on a specific program.

s6-2.13.1.0/doc/s6-accessrules-cdb-from-fs.html000066400000000000000000000130011470151141200206450ustar00rootroot00000000000000 s6: the s6-accessrules-cdb-from-fs program

s6
Software
skarnet.org

The s6-accessrules-cdb-from-fs program

s6-accessrules-cdb-from-fs compiles a directory containing a ruleset suitable for s6-ipcserver-access or s6-tcpserver-access into a CDB file.

Interface

     s6-accessrules-cdb-from-fs cdbfile dir
  • s6-accessrules-cdb-from-fs compiles the dir directory containing a ruleset into a CDB file cdbfile then exits 0.

Ruleset directory format

To be understood by s6-accessrules-cdb-from-fs, s6-ipcserver-access, or s6-tcpserver-access, dir must have a specific format.

dir contains a series of directories:

  • ip4 for rules on IPv4 addresses
  • ip6 for rules on IPv6 addresses
  • reversedns for rules on host names
  • uid for rules on user IDs
  • gid for rules on group IDs

Depending on the application, other directories can appear in dir and be compiled into cdbfile, but s6-tcpserver-access only uses the first three, and s6-ipcserver-access only uses the last two.

Each of those directories contains a set of rules. A rule is a subdirectory named after the set of keys it matches, and containing actions that will be executed if the rule is the first matching rule for the tested key.

The syntax for the rule name is dependent on the nature of keys, and fully documented on the accessrules library page. For instance, a subdirectory named 192.168.0.0_27 in the ip4 directory will match every IPv4 address in the 192.168.0.0/27 network that does not match a more precise rule.

The syntax for the actions, however, is the same for every type of key. A rule subdirectory can contain the following elements:

  • a file (that can be empty) named allow. If such a file exists, a key matching this rule will be immediately accepted.
  • a file (that can be empty) named deny. If such a file exists and no allow file exists, a key matching this rule will be immediately denied.
  • a subdirectory named env. If such a directory exists along with an allow file, then its contents represent environment modifications that will be applied after accepting the connection and before executing the next program in the chain, as if the s6-envdir program, without options, was applied to env. env has exactly the same format as a directory suitable for s6-envdir; however, if the modifications take up more than 4096 bytes when compiled into cdbfile, then s6-accessrules-cdb-from-fs will complain and exit 100.
  • a file named exec. If such a file exists along with an allow file, then its contents represent a command line that, interpreted by the execlineb launcher, will be executed after accepting the connection, totally bypassing the original command line. s6-accessrules-cdb-from-fs truncates the exec file to 4096 bytes max when embedding it into cdbfile, so make sure it is not larger than that.

Notes

  • cdbfile can exist prior to, and during, the compilation, which actually works in a temporary file in the same directory as cdbfile and performs an atomic replacement when it is done. So it is not necessary to interrupt a running service during the compilation.
  • If s6-accessrules-cdb-from-fs fails at some point, the temporary file is removed. However, this doesn't happen if s6-accessrules-cdb-from-fs is interrupted by a signal.
  • After the program successfully completes, if dir was a suitable candidate for the -i option of s6-ipcserver-access or s6-tcpserver-access, then cdbfile will be a suitable candidate for the -x option of the same program, implementing the same ruleset.
  • cdbfile can be decompiled by the s6-accessrules-fs-from-cdb program.
s6-2.13.1.0/doc/s6-accessrules-fs-from-cdb.html000066400000000000000000000044571470151141200206640ustar00rootroot00000000000000 s6: the s6-accessrules-fs-from-cdb program

s6
Software
skarnet.org

The s6-accessrules-fs-from-cdb program

s6-accessrules-fs-from-cdb decompiles a CDB database containing a ruleset suitable for s6-ipcserver-access or s6-tcpserver-access and that has been compiled with s6-accessrules-cdb-from-fs.

Interface

     s6-accessrules-fs-from-cdb dir cdbfile
  • s6-accessrules-fs-from-cdb decompiles the CDB file cdbfile into the directory dir, then exits 0.

Notes

  • dir must not exist prior to the decompilation.
  • dir must be considered a work in progress as long as s6-accessrules-fs-from-cdb is running. It is only safe to use dir as a ruleset once the program has exited.
  • If s6-accessrules-fs-from-cdb fails at some point, the partial arborescence at dir is removed. However, this doesn't happen if s6-accessrules-fs-from-cdb is interrupted by a signal.
  • After the program successfully completes, if cdbfile was a suitable candidate for the -x option of s6-ipcserver-access or s6-tcpserver-access, then dir will be a suitable candidate for the -i option of the same program, implementing the same ruleset.
s6-2.13.1.0/doc/s6-applyuidgid.html000066400000000000000000000045271470151141200165640ustar00rootroot00000000000000 s6: the s6-applyuidgid program

s6
Software
skarnet.org

The s6-applyuidgid program

s6-applyuidgid executes a program with reduced privileges.

Interface

     s6-applyuidgid [ -z ] [ -u uid ] [ -g gid ] [ -G gidlist ] [ -U ] prog...
  • s6-applyuidgid sets its uid, gid and supplementary group list to the values given, then executes into prog.

Options

  • -u uid : set the process' user ID to uid
  • -g gid : set the process' group ID to gid
  • -G gidlist : set the process' supplementary group list to gidlist, which must be given as a comma-separated list of numeric GIDs, without spaces.
  • -U : set the process' user ID, group ID and supplementary group list to the values of the UID, GID and GIDLIST environment variables. If a -u, -g or -G option is given after -U, the command line value overrides the environment variable.
  • -z : unexport. The UID, GID and GIDLIST variables will be removed from the process environment.

Notes

  • s6-applyuidgid can only be run as root. Its main use is to drop root privileges before starting a daemon.
  • s6-applyuidgid is a more generic version of s6-setuidgid. It is used as a command line building block by some programs that rewrite their command line, such as s6-tcpserver.
s6-2.13.1.0/doc/s6-cleanfifodir.html000066400000000000000000000024111470151141200166640ustar00rootroot00000000000000 s6: the s6-cleanfifodir program

s6
Software
skarnet.org

The s6-cleanfifodir program

s6-cleanfifodir cleans up a fifodir.

Interface

     s6-cleanfifodir fifodir

s6-cleanfifodir cleans up fifodir, that must belong to the current user. That means it removes all stale FIFOs in fifodir.

In normal use, it is not necessary to call s6-cleanfifodir. However, stale FIFOs can be left by s6-ftrigrd processes that were violently killed, so it's good practice to regularly clean up fifodirs.

s6-2.13.1.0/doc/s6-connlimit.html000066400000000000000000000072341470151141200162430ustar00rootroot00000000000000 s6: the s6-connlimit program

s6
Software
skarnet.org

The s6-connlimit program

s6-connlimit is a small utility to perform IP-based control on the number of client connections to a TCP socket, and uid-based control on the number of client connections to a Unix domain socket.

Interface

     s6-connlimit prog...
  • s6-connlimit reads its environment for the PROTO environment variable, and then for ${PROTO}CONNNUM and ${PROTO}CONNMAX, which must contain integers.
  • If the value of ${PROTO}CONNNUM is superior or equal to the value of ${PROTO}CONNMAX, s6-connlimit exits 1 with an error message.
  • Else it execs into prog....
  • If ${PROTO}CONNMAX is unset, s6-connlimit directly execs into prog... without performing any check: no maximum number of connections has been defined.

Usage

The s6-tcpserver program defines the PROTO environment variable to "TCP", and spawns every child server with the TCPCONNNUM environment variable set to the number of connections from the same IP address. The s6-tcpserver-access program can set environment variables depending on the client's IP address. If the s6-tcpserver-access database is configured to set the TCPCONNMAX environment variable for a given set of IP addresses, and s6-tcpserver-access execs into s6-connlimit, then s6-connlimit will drop connections if there already are ${TCPCONNMAX} connections from the same client IP address.

The s6-ipcserver and s6-ipcserver-access programs can be used the same way, with "IPC" instead of "TCP", to limit the number of client connections by UID.

Example

The following command line:

     s6-tcpserver -v2 -c1000 -C40 1.2.3.4 80 \
     s6-tcpserver-access -v2 -RHl0 -i dir \
     s6-connlimit \
     prog...

will run a server listening to IPv4 address 1.2.3.4, on port 80, serving up to 1000 concurrent connections, and up to 40 concurrent connections from the same IP address, no matter what the IP address. For every client connection, it will look up the database set up in dir; if the connection is accepted, it will run prog....

If the dir/ip4/5.6.7.8_32/env/TCPCONNMAX file exists and contains the string 30, then at most 30 concurrent connections from 5.6.7.8 will execute prog..., instead of the default of 40.

Notes

  • The s6-connlimit utility was once part of the s6-networking suite, and is mostly useful with TCP connections, which is why the examples here involve TCP. Nevertheless, it can be used with connections across Unix domain sockets, and that is why it has been moved to the s6 package.
s6-2.13.1.0/doc/s6-envdir.html000066400000000000000000000057021470151141200155340ustar00rootroot00000000000000 s6: the s6-envdir program

s6
Software
skarnet.org

The s6-envdir program

s6-envdir changes its environment, then executes into another program.

Interface

     s6-envdir [ -I | -i ] [ -n ] [ -f ] [ -L ] [ -c nullis ] dir prog...
  • s6-envdir reads files in dir. For every file f in dir, that does not begin with a dot and does not contain the '=' character:
  • If f is empty, remove a variable named f from the environment, if any.
  • Else add a variable named f to the environment (or replace f if it already exists) with the first line of the contents of file f as value. Spaces and tabs at the end of this line are removed, as well as any trailing newline; null characters in this line are changed to newlines in the environment variable.

Options

  • -i : strict. If dir does not exist, exit 111 with an error message. This is the default.
  • -I : loose. If dir does not exist, exec into prog without modifying the environment first.
  • -f : verbatim mode. All the file is given as the value of the environment variable, including newlines (except the last one if the -n option is not given). Null characters are still translated.
  • -n : do not chomp. If the -f option is given and the file ends with a newline, keep that last newline in the value. If the -f option is not given, keep the trailing blanks at the end of the first line (but not the ending newline).
  • -L : do not clamp. With this option, s6-envdir will process the whole first line of each file (if the -f option hasn't been given) or read each file entirely (if the -f option has been given), even if it means adding huge variables to the environment. Without this option, s6-envdir only reads the first 4096 bytes of each file.
  • -c nullis : replace null characters with the first character of nullis instead of a newline.

Notes

s6-envdir without options behaves exactly like envdir.

s6-2.13.1.0/doc/s6-envuidgid.html000066400000000000000000000074011470151141200162210ustar00rootroot00000000000000 s6: the s6-envuidgid program

s6
Software
skarnet.org

The s6-envuidgid program

s6-envuidgid potentially sets the UID, GID and GIDLIST environment variables according to the options and arguments it is given; then it executes into another program.

Interface

     s6-envuidgid [ -u | -g | -B ] [ -n ] [ -i | -D uid:gid:gidlist ] account prog...
  • s6-envuidgid looks account up by name in the account database.
  • It sets the UID environment variable to account's uid, and the GID environment variable to account's gid.
  • It also sets the GIDLIST environment variable to a comma-separated list of supplementary group ids account is a member of according to the group database. (If account doesn't belong to any other group than its primary group, GIDLIST is still set, but empty.)
  • Then it executes into prog....

Options

  • -u : user. account will be interpreted as a user name; the UID environment variable will be set to its numerical value, and the GID and GIDLIST variables will not be touched.
  • -g : group. account will be interpreted as a group name instead of a user name; the GID environment variable will be set to its numerical value, and the UID and GIDLIST variables will not be touched.
  • -B : both user and group. account will be interpreted as user:group. The GIDLIST variable will not be touched. If user does not exist, the UID variable will be set to 0 unless a better default is provided with the -D option. If group does not exist, the GID variable will be set to 0 unless a better default is provided with the -D option.
  • -n : numerical fallback. If account cannot be found in the user or group database, try to interpret the given values literally. For instance, s6-envuidgid -B root:42 will fail if there's no group named 42 in the group database, but s6-envuidgid -nB root:42 will set UID to 0 and GID to 42.
  • -i : insist. If account is unknown, exit 1 with an error message. This is the default.
  • -D uid:gid:gidlist : if account is unknown, use uid, gid and gidlist as the values for UID, GID and GIDLIST.

Notes

  • s6-envuidgid without options behaves like envuidgid, except that the exit code is 1 if account doesn't exist, and it also exports supplementary groups.
  • s6-envuidgid is useful when running a program that must start as root but can drop its privileges later. Such a program can read its new uid/gid/groups info from the UID, GID and GIDLIST environment variables. Super-servers such as s6-tcpserver make use of this.
s6-2.13.1.0/doc/s6-fdholder-daemon.html000066400000000000000000000163701470151141200173000ustar00rootroot00000000000000 s6: the s6-fdholder-daemon program

s6
Software
skarnet.org

The s6-fdholder-daemon program

s6-fdholder-daemon is a fd-holding daemon, i.e. a long-lived program. It listens on a Unix domain socket, then accepts client connections; it stores file descriptors on behalf of clients, along with an identifier for every file descriptor stored, and possibly an expiration date (after which the file descriptor will be forgotten). It also allows clients to retrieve a file descriptor by its identifier.

Interface

     s6-fdholder-daemon [ -1 ] [ -v verbosity ] [ -d | -D ] [ -c maxconn ] [ -n maxfds ] [ -b backlog ] [ -G gidlist ] [ -g gid ] [ -u uid ] [ -U ] [ -t clienttimeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ] path
  • s6-fdholder-daemon parses the options and arguments it is given, and builds a new command line with them. It then executes into that new command line.
  • The first program s6-fdholder-daemon executes into is s6-ipcserver-socketbinder. It will create and bind a Unix domain socket to path, then execute into the rest of the command line.
  • If a privilege-dropping operation has been requested, the program that s6-ipcserver-socketbinder executes into is s6-applyuidgid. It will drop the root privileges, then execute into the rest of the command line.
  • The last program in the chain is s6-fdholderd. It is executed into by s6-applyuidgid, or directly by s6-ipcserver-socketbinder if no privilege-dropping operation has been requested. s6-fdholderd is the long-lived process, the daemon itself, performing fd holding and accepting connections from clients.

Options

  • -1 : write a newline to stdout, before closing it, right after binding and listening to the Unix socket. If stdout is suitably redirected, this can be used by monitoring programs to check when the server is ready to accept connections.
  • -v verbosity : be quiet, normally verbose, or more verbose, depending on if verbosity is 0, 1, or more. The default is 1.
  • -d : allow instant rebinding to the same path even if it has been used not long ago - this is the SO_REUSEADDR flag to setsockopt() and is generally used with server programs. This is the default. Note that path will be deleted if it already exists at program start time.
  • -D : disallow instant rebinding to the same path.
  • -c maxconn : accept at most maxconn concurrent client connections. Default is 16. It is impossible to set it higher than the value of the S6_FDHOLDER_MAX macro, which is 256. Client connections to this server are short-lived, so this number needs not be too high. Every client connection eats up one available file descriptor, so it is best for maxconn to be as small as possible.
  • -n maxfds : store at most maxfds file descriptors. Default is 1000. It is impossible to set it higher than the number of files that can be opened by the s6-fdholder-daemon process minus a few descriptors needed for correct s6-fdholderd operation. Before running s6-fdholder-daemon, make sure to properly adjust the number of openable files of the current process.
  • -b backlog : set a maximum of backlog backlog connections on the socket. Extra connection attempts will rejected by the kernel.
  • -G gidlist : change s6-fdholder-daemon's supplementary group list to gidlist after binding the socket. This is only valid when run as root. gidlist must be a comma-separated list of numerical group IDs.
  • -g gid : change s6-fdholder-daemon's groupid to gid after binding the socket. This is only valid when run as root.
  • -u uid : change s6-fdholder-daemon's userid to uid after binding the socket. This is only valid when run as root.
  • -U : change s6-fdholder-daemon's user id, group id and supplementary group list according to the values of the UID, GID and GIDLIST environment variables after binding the socket. This is only valid when run as root. This can be used with the s6-envuidgid program to easily script a service that binds to a privileged socket then drops its privileges to those of a named non-root account.
  • -t clienttimeout : disconnect a client if it's in the middle of an operation and it has not written or read any data in clienttimeout milliseconds. By default, clienttimeout is 0, which means infinite.
  • -T lameducktimeout : give clients lameducktimeout milliseconds to finish their current operation before exiting after s6-fdholderd has received a SIGTERM. By default, lameducktimeout is 0, which means infinite.
  • -x rulesfile : read access rights configuration from CDB file rulesfile.
  • -i rulesdir : read access rights configuration from the filesystem in directory rulesdir.

Notes

  • s6-fdholder-daemon does not interpret its options itself. It just dispatches them to the appropriate program on the command line that it builds.
  • From the user's point of view, s6-fdholder-daemon behaves like a long-lived process, even if the long-lived process itself is called s6-fdholderd. Every operational detail of s6-fdholderd applies to s6-fdholder-daemon as well; in particular, make sure to properly configure the clients' access rights.
  • s6-fdholder-daemon is meant to be used in an s6 run script, as a supervised local service. It does not fork itself or write to syslog. However, it can be run under any infrastructure, including other supervision infrastructures, OpenRC, systemd, or SysV scripts.
s6-2.13.1.0/doc/s6-fdholder-delete.html000066400000000000000000000045011470151141200172700ustar00rootroot00000000000000 s6: the s6-fdholder-delete program

s6
Software
skarnet.org

The s6-fdholder-delete program

s6-fdholder-delete connects to a fd-holding daemon listening on a Unix domain socket, and deletes a file descriptor from the daemon storage.

Interface

     s6-fdholder-delete [ -t timeout ] path id
  • s6-fdholder-delete connects to a s6-fdholderd server process listening on path.
  • It tells the server to close the file descriptor that has been stored with identifier id.

Options

  • -t timeout : if the operation cannot be processed in timeout milliseconds, then fail with an error message. Communications with the server should be near-instant, so this option is only here to protect users against programming errors (connecting to the wrong socket, for instance).

Exit codes

  • 0: success.
  • 1: the server denied the operation. The meaning of the error messages is explained here.
  • 100: wrong usage.
  • 111: system call failed - this includes attempting to connect to a nonexistent socket, or one where no s6-fdholderd daemon is listening.

Usage example

   s6-fdholder-delete /service/fdholderd/s MYSOCKET

will tell an s6-fdholderd daemon listening on the /service/fdholderd/s socket to close the file descriptor dentified as MYSOCKET.

s6-2.13.1.0/doc/s6-fdholder-errorcodes.html000066400000000000000000000065441470151141200202060ustar00rootroot00000000000000 s6: the s6-fdholder error codes

s6
Software
skarnet.org

The s6-fdholder error codes

The following error messages (and corresponding errno codes) can be returned by the s6-fdholderd daemon to its various clients. This page explains why they occur.

  • Protocol error (EPROTO) or Protocol wrong type for socket (EPROTOTYPE): the client connected to the wrong kind of server and they cannot communicate. This is generally a programming error. It can also signal a bug in the s6-fdholder tools, but protocol bugs have usually been wiped out before an s6 release.
  • Broken pipe (EPIPE): the client was not authorized to connect to the server, which closed the connection. You need to configure the access rights to the server.
  • Operation not permitted (EPERM): even though the client was authorized to connect to the server, the specific operation it wanted to perform was denied. You need to configure the access rights to the server.
  • Too many open files in system (ENFILE): the client attempted to store more file descriptors than the server can hold. Or, the client attempted to retrieve more file descriptors than it can hold. You should check the -n option to s6-fdholderd, as well as the RLIMIT_NOFILE resource limits used by the client and the server, and adjust them accordingly.
  • Resource busy (EBUSY): the client attempted to store a descriptor under an identifier that is already used.
  • Filename too long (ENAMETOOLONG): the identifier provided by the client was too long.
  • No such file or directory (ENOENT): the identifier provided by the client was not found in the server database.
  • Bad file descriptor (EBADF): the client attempted to transmit a closed, or otherwise unsuitable for fd-passing, file descriptor.
  • Operation timed out (ETIMEDOUT): the client, or the server, took too long to perform the wanted operation. This is most probably a programming error, because both client and server should have a very fast reaction time. Check that the client is connecting to the right server, and check -t options to both client and server (the argument is interpreted as milliseconds!).
  • Other errors indicate a transient error such as lack of memory, hardware failure, etc.
s6-2.13.1.0/doc/s6-fdholder-getdump.html000066400000000000000000000101161470151141200174720ustar00rootroot00000000000000 s6: the s6-fdholder-getdump program

s6
Software
skarnet.org

The s6-fdholder-getdump program

s6-fdholder-getdump connects to a fd-holding daemon listening on a Unix domain socket, and retrieves its entire state: file descriptors with their identifiers and expiration dates. It then executes a program with those file descriptors still open, and the state stored in the environment.

Interface

     s6-fdholder-getdump [ -t timeout ] path prog...
  • s6-fdholder-getdump connects to a s6-fdholderd server process listening on path.
  • It retrieves a copy of the whole set of file descriptors stored in this daemon, including their identifiers and expiration dates.
  • It then executes into prog... with the additional open descriptors and the additional environment variables.

Options

  • -t timeout : if the operation cannot be processed in timeout milliseconds, then fail with an error message. Communications with the server should be near-instant, so this option is only here to protect users against programming errors (connecting to the wrong socket, for instance).

Exit codes

  • On success, the program doesn't exit; instead, it execs into prog....
  • 1: the server denied the operation. The meaning of the error messages is explained here.
  • 100: wrong usage.
  • 111: system call failed - this includes attempting to connect to a nonexistent socket, or one where no s6-fdholderd daemon is listening.

Environment format

prog... is executed with the following environment variables set:

  • S6_FD# : contains the number n of file descriptors retrieved from the server.
  • Then, for every i between 0 and n-1 inclusive:
    • S6_FD_i : contains the number of the ith open file descriptor.
    • S6_FDID_i : contains the identifier of the ith open file descriptor.
    • S6_FDLIMIT_i : contains the expiration date of the ith open file descriptor, if applicable. This date is stored in external TAI64N format.
      If the file descriptor is not supposed to expire, this environment variable is not defined.

Usage example

   s6-fdholder-getdump /service/fdholderd/s s6-fdholder-setdump /service/fdholderd-2/s

will get the state of the s6-fdholderd daemon listening on the /service/fdholderd/s socket, and transmit it to the other s6-fdholderd daemon listening on the /service/fdholderd-2/s socket. Note that in this precise case, the s6-fdholder-transferdump program does the same thing more efficiently.

Notes

s6-2.13.1.0/doc/s6-fdholder-list.html000066400000000000000000000040171470151141200170030ustar00rootroot00000000000000 s6: the s6-fdholder-list program

s6
Software
skarnet.org

The s6-fdholder-list program

s6-fdholder-list lists the descriptors currently held by a fd-holding daemon.

Interface

     s6-fdholder-list [ -t timeout ] path
  • s6-fdholder-list connects to a s6-fdholderd server process listening on path.
  • It gets the list of identifiers corresponding to the currently held file descriptors. It prints this list to stdout, one per line.

Options

  • -t timeout : if the operation cannot be processed in timeout milliseconds, then fail with an error message. Communications with the server should be near-instant, so this option is only here to protect users against programming errors (connecting to the wrong socket, for instance).

Exit codes

  • 0: success.
  • 1: the server denied the operation. The meaning of the error messages is explained here.
  • 100: wrong usage.
  • 111: system call failed - this includes attempting to connect to a nonexistent socket, or one where no s6-fdholderd daemon is listening.
s6-2.13.1.0/doc/s6-fdholder-retrieve.html000066400000000000000000000070271470151141200176610ustar00rootroot00000000000000 s6: the s6-fdholder-retrieve program

s6
Software
skarnet.org

The s6-fdholder-retrieve program

s6-fdholder-retrieve connects to a fd-holding daemon listening on a Unix domain socket, and retrieves a file descriptor from this daemon, then executes a program with this file descriptor as the program's standard input.

Interface

     s6-fdholder-retrieve [ -D ] [ -t timeout ] path id prog...
  • s6-fdholder-retrieve connects to a s6-fdholderd server process listening on path.
  • It attempts to retrieve a copy of the file descriptor that has been stored into that daemon under identifier id.
  • It then executes into prog..., with the retrieved file descriptor as standard input.

Options

  • -D : delete the file descriptor from the server's storage after retrieval. This option requires writing rights over the given identifier as well as reading rights: check the server's configuration.
  • -t timeout : if the operation cannot be processed in timeout milliseconds, then fail with an error message. Communications with the server should be near-instant, so this option is only here to protect users against programming errors (connecting to the wrong socket, for instance).

Exit codes

  • On success, the program doesn't exit; instead, it execs into prog....
  • 1: the server denied the operation. The meaning of the error messages is explained here.
  • 100: wrong usage.
  • 111: system call failed - this includes attempting to connect to a nonexistent socket, or one where no s6-fdholderd daemon is listening.

Usage example

   s6-fdholder-retrieve /service/fdholderd/s MYSOCKET s6-ipcserverd cat

will retrieve a file descriptor stored under the MYSOCKET identifier in the s6-fdholderd daemon listening on the /service/fdholderd/s socket, and execute into s6-ipcserverd cat with this file descriptor as stdin. In this case, if MYSOCKET referred to a Unix domain socket, s6-ipcserverd will then accept client connections on it and spawn a cat program for every connection.

Notes

  • To execute prog with the newly retrieved file descriptor as number n while preserving stdin, use the following construct: fdmove n 0 s6-fdholder-retrieve path id fdswap 0 n prog....
s6-2.13.1.0/doc/s6-fdholder-setdump.html000066400000000000000000000057731470151141200175230ustar00rootroot00000000000000 s6: the s6-fdholder-setdump program

s6
Software
skarnet.org

The s6-fdholder-setdump program

s6-fdholder-setdump connects to a fd-holding daemon listening on a Unix domain socket, and dumps a set of file descriptors into that daemon.

Interface

     s6-fdholder-setdump [ -t timeout ] path
  • s6-fdholder-setdump connects to a s6-fdholderd server process listening on path.
  • It attempts to pass a complete state - i.e. a set of file descriptors with identifiers and expiration dates - to the server.

Options

  • -t timeout : if the operation cannot be processed in timeout milliseconds, then fail with an error message. Communications with the server should be near-instant, so this option is only here to protect users against programming errors (connecting to the wrong socket, for instance).

Exit codes

  • 0: success.
  • 1: the server denied the operation. The meaning of the error messages is explained here.
  • 100: wrong usage.
  • 111: system call failed - this includes attempting to connect to a nonexistent socket, or one where no s6-fdholderd daemon is listening.

Notes

  • The file descriptors to transmit to the server should of course be already open in the s6-fdholder-setdump program; also, s6-fdholder-setdump should have certain environment variables that describe that set of file descriptors. The format of the environment is the same as the one set by s6-fdholder-getdump.
  • Setting the whole state of an s6-fdholderd daemon requires specific privileges. Make sure you properly configure the s6-fdholderd access rights so your client can perform that operation.
  • Previously held fds will still be kept by the server (so, "setting" the state is more like "adding to" the state), unless there is an identifier collision, in which case the fd in the transmitted set takes precedence and the previously held fd is overwritten.
s6-2.13.1.0/doc/s6-fdholder-store.html000066400000000000000000000073561470151141200171750ustar00rootroot00000000000000 s6: the s6-fdholder-store program

s6
Software
skarnet.org

The s6-fdholder-store program

s6-fdholder-store connects to a fd-holding daemon listening on a Unix domain socket, and gives it a copy of one of its open file descriptors for the daemon to hold.

Interface

     s6-fdholder-store [ -d fd ] [ -T fdtimeout ] [ -t timeout ] path id
  • s6-fdholder-store connects to a s6-fdholderd server process listening on path.
  • It attempts to pass a copy of its standard input, or of its descriptor fd, to the server, with identifier id.

Options

  • -d fd : store descriptor number fd. By default, fd is 0 (i.e. the program's stdin will be stored).
  • -T fdtimeout : the descriptor is stored with an expiration time of fdtimeout milliseconds, which means the s6-fdholderd daemon will close and get rid of the descriptor after this time. By default, fdtimeout is 0, which means infinite - no expiration time.
  • -t timeout : if the operation cannot be processed in timeout milliseconds, then fail with an error message. Communications with the server should be near-instant, so this option is only here to protect users against programming errors (connecting to the wrong socket, for instance).

Exit codes

  • 0: success.
  • 1: the server denied the operation. The meaning of the error messages is explained here.
  • 100: wrong usage.
  • 111: system call failed - this includes attempting to connect to a nonexistent socket, or one where no s6-fdholderd daemon is listening.

Usage example

   s6-ipcserver-socketbinder /tmp/mysocket s6-fdholder-store /service/fdholderd/s MYSOCKET

will open a Unix domain socket, bind it to /tmp/mysocket and listen to incoming connections, then give it to a s6-fdholderd instance listening on /service/fdholderd/s, with no expiration date, with the "MYSOCKET" identifier. Another program will be able to retrieve the socket later, using s6-fdholder-retrieve.

s6-2.13.1.0/doc/s6-fdholder-transferdump.html000066400000000000000000000064751470151141200205540ustar00rootroot00000000000000 s6: the s6-fdholder-transferdump program

s6
Software
skarnet.org

The s6-fdholder-transferdump program

s6-fdholder-transferdump connects to two separate fd-holding daemons and transfers the content of the first one to the second one.

Interface

     s6-fdholder-transferdump [ -t timeoutfrom:timeoutto ] pathfrom pathto
  • s6-fdholder-transferdump connects to a s6-fdholderd server process listening on pathfrom and gets the whole set of file descriptors from this server, with their identifiers and (when relevant) expiration dates.
  • It then connects to a different instance of s6-fdholderd, listening on pathto, and stores the set of file descriptors to it. The set is added to the second server, which keeps the descriptors it was already holding.

Options

  • -t timeoutfrom:timeoutto : if the operations cannot be processed in timeoutfrom (for the connection to pathfrom) or timeoutto (for the connection to pathto) milliseconds, then fail with an error message. Communications with the servers should be near-instant, so this option is only here to protect users against programming errors (connecting to the wrong socket, for instance).

Exit codes

  • 0: success.
  • 1: the source server denied the operation. The meaning of the error messages is explained here.
  • 2: the destination server denied the operation. The meaning of the error messages is explained here.
  • 100: wrong usage.
  • 111: system call failed - this includes attempting to connect to a nonexistent socket, or one where no s6-fdholderd daemon is listening.

Notes

  • Dumping the entire state of s6-fdholderd requires special authorizations. Make sure the s6-fdholderd instances are configured to accept dump-getting and dump-setting requests from your client.
  • A typical use case of s6-fdholder-transferdump is when the main fd-holding daemon needs to upgrade, or restart for some reason. Transferring the file descriptors into another, temporary fd-holding daemon instance allows it to restart without losing the descriptors.
s6-2.13.1.0/doc/s6-fdholderd.html000066400000000000000000000364721470151141200162100ustar00rootroot00000000000000 s6: the s6-fdholderd program

s6
Software
skarnet.org

The s6-fdholderd program

s6-fdholderd is the serving part of the s6-fdholder-daemon fd-holding server. It assumes that its stdin is a bound and listening Unix domain socket; it accepts connections from clients connecting to that socket, and stores and retrieves file descriptors on their behalf.

Interface

     s6-fdholderd [ -1 ] [ -v verbosity ] [ -c maxconn ] [ -n maxfds ] [ -t clienttimeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ]

Options

  • -1 : write a newline to stdout, and close stdout, right before entering the client-accepting loop. If stdout is suitably redirected, this can be used by monitoring programs to check when the server is accepting connections. See this page for more information on readiness notification.
  • -v verbosity : be more or less verbose. verbosity can be 0 (quiet), 1 (normal), or 2 or more (verbose).
  • -c maxconn : accept at most maxconn concurrent connections. Default is 16. It is impossible to set it higher than the value of the S6_FDHOLDER_MAX macro, i.e. 256. Client connections to this server are short-lived, so this number needs not be too high. Every client connection eats up one available file descriptor, so it is best for maxconn to be as small as possible.
  • -n maxfds : store at most maxfds file descriptors. Default is 1000. It is impossible to set it higher than the number of files that can be opened by the s6-fdholderd process, minus a few descriptors needed for correct operation. Before running s6-fdholderd, make sure to properly adjust the number of openable files of the current process.
  • -t clienttimeout : disconnect a client if it's in the middle of an operation and it has not written or read any data in clienttimeout milliseconds. By default, clienttimeout is 0, which means infinite.
  • -T lameducktimeout : give clients lameducktimeout milliseconds to finish their current operation before exiting after receiving a SIGTERM. By default, lameducktimeout is 0, which means infinite.
  • -x rulesfile : read access rights configuration from CDB file rulesfile.
  • -i rulesdir : read access rights configuration from the filesystem in directory rulesdir.

Signals

  • SIGTERM: enter lameduck mode, then exit when no more operation is pending.
  • SIGHUP: reopen rulesfile, if s6-fdholderd has been run with the -x option. It is not necessary to send s6-fdholderd a SIGHUP when the -i option is used instead: configuration changes in the filesystem are automatically picked up.

Identifiers

  • Every file descriptor is stored in the s6-fdholderd daemon via the s6-fdholder-store program, with an identifier. That identifier is a zero-terminated character string, containing 1 to 255 characters.
  • Any non-null character can be used in an identifier. Non-printable or special characters will be quoted when printed by s6-fdholder-list. However, it is recommended to only use reasonable characters in identifiers: clients should be able to know at a glance what file descriptor is represented by an identifier. Identifiers have no special meaning to the server.
  • A good convention is to use unix:path/to/socket for Unix domain sockets and protocol:ip:port for INET domain sockets.
  • An identifier is chosen by the storing client, within the limits of what the server authorizes it to use.
  • The retrieving client must know the exact identifier corresponding to a descriptor to be able to retrieve that descriptor. It must also be authorized by the server.
  • When an identifier is in use, it cannot be used again to store another descriptor. However, once the descriptor has been deleted or has expired, it is possible to reuse the same identifier.

Configuration

Before running s6-fdholderd (or its wrapper s6-fdholder-daemon), it is necessary to configure it. This is done by a series of rules, or ruleset, stored in either a rulesfile in the CDB format, or in a rulesdir, i.e. a directory in the filesystem following a certain format. s6-fdholderd will refuse to run if neither the -i nor the -x option has been provided.

Rulesets can be converted between the rulesdir and rulesfile formats with the s6-accessrules-cdb-from-fs and s6-accessrules-fs-from-cdb conversion tools.

Rules format

The rules file, or rules directory, follows the s6 accessrules format for uid and gid checking. For every connecting client, s6-fdholderd matches the uid and gid of the client against the provided ruleset, and determines what the client is authorized to do.

By default, no client is allowed to do anything - not even connect to the server. Even root, the super-user, will be denied access. That is why it is essential to create a sensible ruleset prior to running the server in order to do anything useful.

The various rights that a client can have are the following (using a rulesdir as an example, but a rulesfile works the same way):

  • Connect to the server. This is a prerequisite for doing anything. It will allow a client to perform "public" operations, ones that do not require specific access rights other than connecting. (There are no such operations for now, but it could change in the future; for now, when you allow a client to connect to the server, make sure to give him other rights too.) This right is given if an allow file is found in one of the subdirectories checked by s6_accessrules_keycheck_uidgid. For instance, to allow everyone to connect, touch rulesdir/uid/default/allow.

The other rights are defined in the "environment" part of the ruleset:

  • File descriptor storage rights. This will be checked for storage and deletion of individual file descriptors. This right is given if a non-empty file named S6_FDHOLDER_STORE_REGEX is found in the env/ subdirectory of one of the subdirectories checked by s6_accessrules_keycheck_uidgid. This file should contain a single line, which will be interpreted as an Extended Regular Expression by s6-fdholderd; the regular expression describes the set of identifiers that the client is allowed to use to store file descriptors. For instance, ^unix:/tmp/ indicates that a client that matches this rule will be allowed to store or delete file descriptors using any identifier starting with unix:/tmp/.
  • File descriptor retrieval rights. This will be checked for retrieval of individual file descriptors. This right is given if a non-empty file named S6_FDHOLDER_RETRIEVE_REGEX is found in the env/ subdirectory of one of the subdirectories checked by s6_accessrules_keycheck_uidgid. This file should contain a single line, which will be interpreted as an Extended Regular Expression by s6-fdholderd; the regular expression describes the set of identifiers that the client is allowed to use to retrieve file descriptors. For instance, ^unix:/tmp/ indicates that a client that matches this rule will be allowed to retrieve file descriptors that are identified by strings starting with unix:/tmp/.
  • Listing rights. This will be checked for clients wanting to list the identifiers of the descriptors currently stored in the server. This right is given if a non-empty file named S6_FDHOLDER_LIST is found in the env/ subdirectory of one of the subdirectories checked by s6_accessrules_keycheck_uidgid.
  • Dump reading rights. This will be checked for clients wanting to copy the whole state of the server. This right is given if a non-empty file named S6_FDHOLDER_GETDUMP is found is the env/ subdirectory of one of the subdirectories checked by s6_accessrules_keycheck_uidgid. This is very powerful: you should only give this right to root, or to a dedicated uid that is only used to perform dump transfers.
  • Dump writing rights. This will be checked for clients wanting to copy an entire set of file descriptors into the server. This right is given if a non-empty file named S6_FDHOLDER_SETDUMP is found is the env/ subdirectory of one of the subdirectories checked by s6_accessrules_keycheck_uidgid. This is very powerful: you should only give this right to root, or to a dedicated uid that is only used to perform dump transfers.

Configuration examples

Assuming you want to run an s6-fdholderd daemon in the /service/fdholder directory with the -i rules option, you should:

  • Prepare the rules directory: mkdir /service/fdholder/rules ; cd /service/fdholder/rules ; mkdir uid gid
  • Allow a few users, or everyone, to connect. To allow root to connect: mkdir uid/0 ; touch uid/0/allow. To allow everyone to connect: mkdir uid/default ; touch uid/default/allow.

Depending on your policy, you should now give certain rights to certain users or groups. For instance:

  • To allow user number 50 to perform dump transfers from and to this server: mkdir -p uid/50/env ; touch uid/50/allow ; echo > uid/50/env/S6_FDHOLDER_GETDUMP ; echo > uid/50/env/S6_FDHOLDER_SETDUMP
  • To allow user number 72 to store a descriptor under the name foobar and only this name: mkdir -p uid/72/env ; touch uid/72/allow ; echo '^foobar$' > uid/72/env/S6_FDHOLDER_STORE_REGEX
  • To allow users having 23 as their primary group number to retrieve file descriptors with an identifier containing foo, then one character, then bar: mkdir -p gid/23/env ; touch gid/23/allow ; echo foo.bar > gid/23/env/S6_FDHOLDER_RETRIEVE_REGEX
  • To allow the same users to list all identifiers: echo > gid/23/env/S6_FDHOLDER_LIST
  • To allow everyone to dump entire states into the server (never do this! it's only an example): mkdir -p uid/default/env ; touch uid/default/allow ; echo > uid/default/env/S6_FDHOLDER_SETDUMP.

Notes

  • s6-fdholderd is meant to be execve'd into by a program that gets the listening socket. That program is normally s6-ipcserver-socketbinder, which creates the socket itself; but it can be a different one if the socket is to be obtained by another means, for instance if it has been retrieved from another fd-holding daemon.
  • s6-fdholderd will store any open file descriptor, without discriminating on its type. However, it makes more sense to store certain file descriptor types than others: for instance, Unix domain or INET domain sockets, or named pipes, are good candidates for fd-holding. On the other hand, it makes little sense to fd-hold regular files, and if done anyway, the results can be surprising, because the read/write file offset is stored with the descriptor, and no automatic rewind is performed by the daemon.
  • Despite there being access control for listing, the security of the system should not depend on a client not knowing what identifier a certain descriptor is stored under. If you need to hold descriptors that only a few programs are supposed to access, you can always run a separate s6-fdholderd instance in a private directory with a configuration tailored to your needs - and you can even make the name of the listening socket private. s6-fdholderd is lightweight, you can start as many instances as you need, and you can run them as long as you need then kill them with SIGTERM.
  • s6-fdholderd pre-allocates its storage at start, in the stack. It uses a small amount of heap memory for communication with a client, but frees it as soon as the client disconnects. It should never run out of memory in normal usage, even if used intensively.
s6-2.13.1.0/doc/s6-fghack.html000066400000000000000000000033221470151141200154640ustar00rootroot00000000000000 s6: the s6-fghack program

s6
Software
skarnet.org

The s6-fghack program

s6-fghack is an anti-backgrounding tool.

Interface

     s6-fghack prog...
  • s6-fghack opens a lot of file descriptors (all writing to a single pipe).
  • Then it spawns prog... as a child.
  • If something gets written on one of those descriptors, it's a bug in prog. s6-fghack then complains and exits 102.
  • Unless prog... goes out of its way to close descriptors it does not know about, s6-fghack is able to detect when prog... exits. It exits with an approximation of the same exit code.

Notes

s6-fghack is what it says: a hack. Ideally, you should never have to use it. It is only useful when you want to supervise a daemon that does not provide a "stay in the foreground" option; and even then, the right thing is to report this as a bug to the daemon author and have it fixed.

s6-2.13.1.0/doc/s6-ftrig-listen.html000066400000000000000000000054721470151141200166600ustar00rootroot00000000000000 s6: the s6-ftrig-listen program

s6
Software
skarnet.org

The s6-ftrig-listen program

s6-ftrig-listen subscribes to several fifodirs, then spawns a program, then waits for pattern of events to occur on the fifodirs.

Interface

In an execlineb script:

     s6-ftrig-listen [ -a | -o ] [ -t timeout ] { fifodir1 regexp1 fifodir2 regexp2 ... } prog...
  • s6-ftrig-listen subscribes to fifodir1 with the regexp regexp1, to fifodir2 with the regexp regexp2, and so on.
  • It then forks and exec prog... with all its arguments
  • It waits for the series of events received on fifodir-i to match regexp-i, The regexp-i must be Extended Regular Expressions.
  • When the series of read events matches the regexps, s6-ftrig-listen exits 0.

Options

  • -t timeout : if the events on the fifodirs have not matched the regexps after timeout milliseconds, print an error message on stderr and exit 1. By default, s6-ftrig-listen waits indefinitely for a matching series of events.
  • -a : and (conjunction). s6-ftrig-listen will only exit when all the fifodir-i have been notified with events matching the corresponding regexp-i. This is the default.
  • -o : one (disjunction). s6-ftrig-listen will exit as soon as one of the fifodir-i has been notified with events matching its regexp-i.

Notes

s6-ftrig-listen can be used outside of an execlineb script by using the internal argv syntax, but this syntax is an implementation detail and is not documented as stable. In a shell script, use execlineb -Pc 's6-ftrig-listen ...' to get the benefits of the execlineb brace syntax.

s6-2.13.1.0/doc/s6-ftrig-listen1.html000066400000000000000000000056341470151141200167410ustar00rootroot00000000000000 s6: the s6-ftrig-listen1 program

s6
Software
skarnet.org

The s6-ftrig-listen1 program

s6-ftrig-listen1 subscribes to a fifodir, then spawns a program, then waits for a pattern of events to occur on the fifodir.

s6-ftrig-listen1 acts just as s6-ftrig-wait, except it can make sure that the process sending the notifications is actually started after there is a listener for those events.

Interface

     s6-ftrig-listen1 [ -t timeout ] fifodir regexp prog...
  • s6-ftrig-listen1 subscribes to fifodir
  • It then forks and exec prog... with all its arguments
  • It waits for the series of events received on fifodir to match regexp. regexp must be an Extended Regular Expression.
  • When the series of read events matches regexp, s6-ftrig-listen1 prints the last event it received to stdout (one byte followed by a newline), then exits 0.

Options

  • -t timeout : if the events on fifodir have not matched regexp after timeout milliseconds, print an error message on stderr and exit 1. By default, s6-ftrig-listen1 waits indefinitely for a matching series of events.

Usage example

The following sequence of shell commands has a race condition:

In terminal 1:

s6-mkfifodir /tmp/toto
s6-ftrig-wait /tmp/toto "message"

Then in terminal 2

s6-ftrig-notify /tmp/toto message

Depending on the operating system's scheduler, there is the possibility that the s6-ftrig-notify process starts sending "message" before the s6-ftrig-wait process has actually subscribed to /tmp/toto, in which case the notification will be missed. The following sequence of shell commands accomplishes the same goal in a reliable way, without the race condition:

s6-mkfifodir /tmp/toto
s6-ftrig-listen1 /tmp/toto "message" s6-ftrig-notify /tmp/toto message
s6-2.13.1.0/doc/s6-ftrig-notify.html000066400000000000000000000023641470151141200166670ustar00rootroot00000000000000 s6: the s6-ftrig-notify program

s6
Software
skarnet.org

The s6-ftrig-notify program

s6-ftrig-notify sends a series of events to a fifodir.

Interface

     s6-ftrig-notify fifodir message

s6-ftrig-notify notifies all the current listeners in fifodir with all the characters in message.

Notes

s6-ftrig-notify cannot be used to send the null character (event 0x00). If you need to send the null character, use the C API: ftrigw_notify().

s6-2.13.1.0/doc/s6-ftrig-wait.html000066400000000000000000000034241470151141200163210ustar00rootroot00000000000000 s6: the s6-ftrig-wait program

s6
Software
skarnet.org

The s6-ftrig-wait program

s6-ftrig-wait subscribes to a fifodir and waits for a pattern of events to occur on this fifodir.

Interface

     s6-ftrig-wait [ -t timeout ] fifodir regexp
  • s6-ftrig-wait subscribes to fifodir
  • It waits for the series of events received on fifodir to match regexp. regexp must be an Extended Regular Expression.
  • When the series of read events matches regexp, s6-ftrig-wait prints the last event it received to stdout (one byte followed by a newline), then exits 0.

Options

  • -t timeout : if the events on fifodir have not matched regexp after timeout milliseconds, print an error message on stderr and exit 1. By default, s6-ftrig-wait waits indefinitely for a matching series of events.
s6-2.13.1.0/doc/s6-instance-control.html000066400000000000000000000041521470151141200175250ustar00rootroot00000000000000 s6: the s6-instance-control program

s6
Software
skarnet.org

The s6-instance-control program

s6-instance-control sends commands to a running instance of an instanced service.

Interface

     s6-instance-control [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -abqhkti12pcyroduDUxO ] servicedir name
  • s6-instance-control expects a running, supervised instanced service in servicedir, as well as an existing instance of this service named name.
  • It sends the given series of commands to the supervisor monitoring the name instance.
  • It exits 0.

Exit codes

  • 0: success
  • 99: with one of the -w options, timed out while waiting for the command to complete
  • 100: wrong usage
  • 111: system call failed

Options

The options, and the commands they represent, are exactly the same as the ones understood by s6-svc.

In fact, s6-instance-control is nothing more than a call to s6-svc on the service directory representing the name instance. It is syntactic sugar so the user does not have to depend on the internal representation of instances and the location of instances' service directories.

s6-2.13.1.0/doc/s6-instance-create.html000066400000000000000000000106431470151141200173120ustar00rootroot00000000000000 s6: the s6-instance-create program

s6
Software
skarnet.org

The s6-instance-create program

s6-instance-create creates a new instance of a currently supervised instanced service.

Interface

     s6-instance-create [ -d | -D ] [ -P ] [ -f ] [ -t timeout ] servicedir name
  • s6-instance-create expects a running, supervised instanced service in servicedir. This service typically has been created by linking the result of an s6-instance-maker invocation into an existing scan directory.
  • s6-instance-create creates a new instance of that service, named name. Depending on the given options, it may start it immediately, or keep it down until a later s6-instance-control invocation.
  • It waits for the new instance to be ready to take commands from s6-instance-control.
  • It exits 0.

Exit codes

  • 0: success
  • 99: timeout while waiting for the instance supervisor to start
  • 100: wrong usage
  • 111: system call failed

Options

  • -d : down. The instance supervisor will be started, but the instance itself will remain down. Any down file for the instance will be deleted. By default, if neither the -d nor -D options have been given, the supervisor auto-starts the instance as soon as it runs.
  • -D : down, and stay down. The instance supervisor will be started, but the instance itself will remain down. A down file will be created for the instance. By default, if neither the -d nor -D options have been given, the supervisor auto-starts the instance as soon as it runs.
  • -P : public. Everyone will be able to subscribe to the instance supervisor's notification. By default, only processes running with the same gid as the instanced service can subscribe to it.
  • -f : force permissions. You should never need to use this option, it is only there for testing purposes.
  • -t timeout : if the instance supervisor has not started after timeout milliseconds, s6-instance-create will print a message to stderr and exit 99. By default, timeout is 0, which means no time limit.

Notes

  • s6-instance-create is similar to s6-svlink, because it uses the same underlying library functions. Under the hood, an instance is a regular service running on a supervision tree that is specific to the instanced service, and s6-instance-create adds a service directory to that tree and ensures it gets supervised.
  • If the template for the service is logged, then s6-instance-create will wait until supervisors have been spawned for both the instance and its logger.
  • s6-instance-create and s6-instance-delete are relatively expensive operations, because they have to recursively copy or delete directories and use the synchronization mechanism with the instance supervisor, compared to s6-instance-control which only has to send commands to already existing supervisors. If you are going to turn instances on and off on a regular basis, it is more efficient to keep the instance existing and control it with s6-instance-control than it is to repeatedly create and delete it.
s6-2.13.1.0/doc/s6-instance-delete.html000066400000000000000000000060261470151141200173110ustar00rootroot00000000000000 s6: the s6-instance-delete program

s6
Software
skarnet.org

The s6-instance-delete program

s6-instance-delete deletes an existing instance of a currently supervised instanced service.

Interface

     s6-instance-delete [ -X ] [ -t timeout ] servicedir name
  • s6-instance-delete expects a running, supervised instanced service in servicedir, as well as an existing instance of this service named name (it doesn't matter if the instance is up or down).
  • It deletes the name instance.
  • It exits 0.

Exit codes

  • 0: success
  • 100: wrong usage
  • 111: system call failed

Options

  • -X : don't wait. s6-instance-delete will exit right away, without waiting for the instance (and its supervisor) to properly disappear.
  • -t timeout : if the instance supervisor has not exited after timeout milliseconds, s6-instance-delete will still exit. By default, timeout is 0, which means no time limit.

Notes

  • s6-instance-delete is similar to s6-svunlink, because it uses the same underlying library functions. Under the hood, an instance is a regular service running on a supervision tree that is specific to the instanced service, and s6-instance-delete removes a service directory from that tree.
  • If the template for the service is logged, then s6-instance-delete will delete both the instance and its logger.
  • s6-instance-delete and s6-instance-create are relatively expensive operations, because they have to recursively copy or delete directories and use the synchronization mechanism with the instance supervisor, compared to s6-instance-control which only has to send commands to already existing supervisors. If you are going to turn instances on and off on a regular basis, it is more efficient to keep the instance existing and control it with s6-instance-control than it is to repeatedly create and delete it.
s6-2.13.1.0/doc/s6-instance-list.html000066400000000000000000000037771470151141200170340ustar00rootroot00000000000000 s6: the s6-instance-list program

s6
Software
skarnet.org

The s6-instance-list program

s6-instance-list gives a list of all currently created instances of an instanced service.

Interface

     s6-instance-list servicedir
  • s6-instance-list expects a running, supervised instanced service in servicedir, as well as an existing instance of this service named name.
  • It prints to stdout, one per line, the names of all existing instances of servicedir, i.e. the ones that have been created and not deleted. It does not matter if the instances are up or down; if they've been created and not deleted, they're printed.
  • It exits 0.

Exit codes

  • 0: success
  • 100: wrong usage
  • 111: system call failed

Notes

  • The list is unsorted, the instance names are printed in an unspecified order.
  • You can use s6-instance-list to script commands that handle sets of instances. For example, to get the status of all the instances of a given service, you could write: for i in `s6-instance-list service` ; do printf "%s: " $i ; s6-instance-status service $i ; done
s6-2.13.1.0/doc/s6-instance-maker.html000066400000000000000000000213541470151141200171470ustar00rootroot00000000000000 s6: the s6-instance-maker program

s6
Software
skarnet.org

The s6-instance-maker program

s6-instance-maker creates a service directory implementing an instanced service. Give it a templated service directory describing how to run an instance of a service, and it will create a different service directory that can launch and manage several instances; each of these instances will be running a copy of the service directory you gave.

Alternatively, s6-instance-maker can create source definition directories for the s6-rc service manager.

Interface

     s6-instance-maker \
       [ -c maxinstances ] \
       [ -r service[/logger[/pipeline]] ] \
       [ -u user ] \
       [ -l loguser ] \
       [ -L logdir ] \
       [ -t stamptype ] \
       [ -n nfiles ] \
       [ -s filesize ] \
       [ -S maxsize ] \
       [ -P prefix ] \
       template dir

s6-instance-maker creates a service directory in dir (which must not already exist). The created service will be a supervisor for a set of instances — initially empty — each running a copy of the service directory given in template.

s6-instance-maker is an offline tool: it is run before you need instances. Once the created service directory is live, i.e. there is a supervisor running on it, then you can create and delete individual instances via the s6-instance-create and s6-instance-delete online tools, that work with active services.

Exit codes

  • 0: success
  • 100: wrong usage
  • 111: system call failed

Options

  • -c max : Plan for a maximum of max instances. Default is 500. You can't set it lower than 2 or higher than 90000. If your template service directory is logged, it's unadvisable to set this above the default.

  • -r service[/logger[/pipeline]] : create s6-rc source definition directories. When this option is given, dir is not created as a service directory, but as a directory containing at least one service: dir/service. dir is suitable as a source argument to s6-rc-compile. If a logger part is given, then a second service, dir/logger, is also created, as a consumer for dir/service, and the -L option must also be used, to provide a directory to store the logs into. If the /pipeline part is also given, pipeline is used as a name for a bundle containing both service and logger. When the -r option is not given at all, dir is a regular service directory for direct inclusion (or linking) in a host scan directory, and if the -L option is given then the logger for the instance supervisor and all its instances is declared in dir/log.

  • -u user : run the instance supervisor, and all of the instances, as user user. This option should only be used when the supervision tree that will host the instanced service is run as root. The default is that the service runs as the same user as the host supervision tree.

  • -l loguser : run the logger of the instance supervisor, if any (see -L below) as user loguser. This option should only be used when the supervision tree that will host the instanced service is run as root. The default is that the logger runs as the same user as the host supervision tree.

  • -L logdir : make the service logged via s6-log, and ensure its log messages go into logdir. Error messages from the instance supervisor as well as from every instance will be logged to logdir. If this option is not given, these error messages will fall through to the host supervision tree's catch-all logger, if any, or standard error otherwise.
    The options listed below are only used to configured the logger and are meaningless if -L is not given.

  • -t stamptype : how logs are timestamped in logdir. 0 means no timestamp, 1 means external TAI64N format, 2 means ISO 8601 format, and 3 means both. Default is 1.

  • -n nfiles : maximum number of archive files in logdir. Default is 10.

  • -s filesize : maximum size of the current file (and archive files) in logdir. Default is 1000000.

  • -S maxsize : maximum total size of the archives in logdir. Default is 0, meaning no limits apart from those enforced by the -n and -s options.

  • -P prefix : when logging to logdir, prefix logged lines with the prefix string. Default is no prefix.

The templated service directory

template should be a directory that looks exactly like a service directory. It will not be live, i.e. at no point will template itself be supervised; instead, a copy of it is stored under dir (and a copy of that copy will be used for every instance of the service). You can safely move or delete template after running s6-instance-maker.

To differentiate between instances, the run and finish script in template should take one additional argument (the first argument for run and the third argument for finish). This argument will be the name of the instance, as provided by the s6-instance-create invocation.

Logging

The service is logged: its stderr and stdout are piped to an s6-log process running as loguser and writing to the logdir directory. This logger is the catch-all logger for all the instances and the supervision tree hosting them. If user and loguser are provided, it is recommended to make them distinct from each other.

Additionally, if template has a log subdirectory, then each instance will have its own dedicated logger. The run and finish scripts for the logger of an instance named name will be called with an additional argument of name/log. They should make use of this, to ensure loggers are properly differentiated between instances: for example, it is not possible to run several s6-log processes on the same log directory, so an instance logger script containing an invocation of s6-log on a fixed logdir will fail as soon as there are 2 instances.

Notes

  • s6-instance-maker makes use of the fact that execline scripts are much easier to generate programmatically and to harden than shell scripts, so it is only built if s6 is built with execline support - i.e. the --disable-execline switch has not been given to configure.
  • If s6-instance-maker encounters failure (and exits 111), it does not clean up the directories it created. Make sure to always test s6-instance-maker's return code and clean up after it if needed.
s6-2.13.1.0/doc/s6-instance-status.html000066400000000000000000000041431470151141200173700ustar00rootroot00000000000000 s6: the s6-instance-status program

s6
Software
skarnet.org

The s6-instance-status program

s6-instance-status gives the status of a running instance of an instanced service, as a short human-readable summary or programmatically parsable output.

Interface

     s6-instance-status [ -uwNrpest | -o up,wantedup,normallyup,ready,paused,pid,exitcode,signal,signum,updownsince,readysince,updownfor,readyfor ] [ -n ] servicedir name
  • s6-instance-status expects a running, supervised instanced service in servicedir, as well as an existing instance of this service named name.
  • It prints information to stdout about the monitored instance of servicedir named name.
  • It exits 0.

Exit codes

  • 0: success
  • 100: wrong usage
  • 111: system call failed

Options

The options are exactly the same as the ones understood by s6-svstat.

In fact, s6-instance-status is nothing more than a call to s6-svstat on the service directory representing the name instance. It is syntactic sugar so the user does not have to depend on the internal representation of instances and the location of instances' service directories.

s6-2.13.1.0/doc/s6-ioconnect.html000066400000000000000000000071301470151141200162230ustar00rootroot00000000000000 s6: the s6-ioconnect program

s6
Software
skarnet.org

The s6-ioconnect program

s6-ioconnect performs full-duplex data transmission between two sets of open file descriptors.

Interface

     s6-ioconnect [ -t millisecs ] [ -r fdr ] [ -w fdw ]
  • s6-ioconnect reads data from its stdin and writes it as is to file descriptor 7, which is assumed to be open.
  • It also reads data from its file descriptor 6, which is assumed to be open, and writes it as is to its stdout.
  • When both sides have transmitted EOF and s6-ioconnect has flushed its buffers, it exits 0.

Options

  • -t millisecs : if no activity on either side happens for millisecs milliseconds, s6-ioconnect closes the connection on both ends and exits 1. By default, millisecs is 0, which means no such timeout.
  • -r fdr : Use fd fdr for "remote" reading instead of fd 6.
  • -w fdw : Use fd fdw for "remote" writing instead of fd 7.
  • The -0, -1, -6 and -7 options are still recognized for compatibility, but do nothing; they are deprecated. They were previously used to tell s6-ioconnect that the local reading, local writing, remote reading and remote writing endpoints, respectively, were sockets — but this is now autodetected.

Notes

  • The point of s6-ioconnect is to be used together with s6-tcpclient or s6-ipcclient to establish a full- duplex connection between the client and the server, for instance for testing purposes. s6-ioconnect is to s6-tcpclient as cat is to s6-tcpserver: a program that will just echo what it gets.
  • The s6-ioconnect utility was once part of the s6-networking suite, which is why the examples here involve TCP. Nevertheless, it can be used with connections across Unix domain sockets as well, and has its place in the s6 package.
  • If one of the endpoints is a socket, s6-ioconnect will call shutdown() on it (for reading or writing, depending on the endpoint) when it needs to transmit EOF. This is a necessary workaround to a misdesign of the socket API, but it could have unintended consequences when the socket is shared among several processes that expect a persistent connection. Most of the time, however, it is a mistake to share a data socket between processes, so s6-ioconnect's behaviour is suited to an overwhelming majority of cases, and the exceptions are specialized enough that they won't need to use s6-ioconnect.
s6-2.13.1.0/doc/s6-ipcclient.html000066400000000000000000000043351470151141200162200ustar00rootroot00000000000000 s6: the s6-ipcclient program

s6
Software
skarnet.org

The s6-ipcclient program

s6-ipcclient is an UCSPI client tool for Unix domain sockets. It connects to a socket, then executes into a program.

Interface

     s6-ipcclient [ -q | -Q | -v ] [ -p localpath ] [ -l localname ] path prog...
  • s6-ipcclient connects to a Unix domain socket on path.
  • It executes into prog... with descriptor 6 reading from the socket and descriptor 7 writing to it.

Environment variables

prog... is run with the following variables set:

  • PROTO: always set to IPC
  • IPCLOCALPATH: set to the path associated with the local socket, if any. Be aware that it may contain arbitrary characters.

Options

  • -q : be quiet.
  • -Q : be normally verbose. This is the default.
  • -v : be verbose.
  • -p localpath : bind the local socket to localpath before connecting to path.
  • -l localname : use localname as the value of the IPCLOCALPATH environment variable.

Notes

  • s6-ipcclient is mostly used to connect a client to a local service without having to implement networking in the client. For instance, the s6-sudo program does this.
s6-2.13.1.0/doc/s6-ipcserver-access.html000066400000000000000000000156221470151141200175100ustar00rootroot00000000000000 s6: the s6-ipcserver-access program

s6
Software
skarnet.org

The s6-ipcserver-access program

s6-ipcserver-access is a command-line access control tool for Unix domain sockets on systems where the getpeereid() system call can be implemented. It is meant to be run after s6-ipcserverd and before the application program on the s6-ipcserver command line.

Interface

     s6-ipcserver-access [ -v verbosity ] [ -E | -e ] [ -l localname ] [ -i rulesdir | -x rulesfile ] prog...
  • s6-ipcserver-access checks it is run under a UCSPI server tool such as s6-ipcserver.
  • It checks that the remote end of the connection fits the accepted criteria defined by the database contained in rulesdir or rulesfile. If the database tells it to reject the connection, the program exits 1.
  • It sets up a few additional environment variables.
  • It executes into prog..., unless the first matching rule in the rule database includes instructions to override prog....

Environment variables

s6-ipcserver-access expects to inherit some environment variables from its parent:

  • PROTO: normally IPC, but could be anything else, like UNIX.
  • ${PROTO}REMOTEEUID: the effective UID of the client program connecting to the socket.
  • ${PROTO}REMOTEEGID: the effective GID of the client program connecting to the socket.

Additionally, it exports the following variables before executing into prog...:

  • ${PROTO}LOCALPATH: set to the local "address" of the socket, as reported by the getsockname() system call, truncated to 99 characters max.

Also, the access rules database can instruct s6-ipcserver-access to set up, or unset, more environment variables, depending on the client address.

Options

  • -v verbosity : be more or less verbose, i.e. print more or less information to stderr:
    • 0: only log error messages.
    • 1: only log error and warning messages, and accepted connections. This is the default.
    • 2: also log rejected connections and more warning messages.
  • -E : no environment. All environment variables potentially set by s6-ipcserver-access, as well as those set by s6-ipcserver, will be unset instead.
  • -e : set up environment variables normally. This is the default.
  • -l localname : use localname as the value for the ${PROTO}LOCALPATH environment variable, instead of looking it up via getsockname().
  • -i rulesdir : check client credentials against a filesystem-based database in the rulesdir directory.
  • -x rulesfile : check client credentials against a cdb database in the rulesfile file. -i and -x are mutually exclusive. If none of those options is given, no credential checking will be performed, and a warning will be emitted on every connection if verbosity is 2 or more.

Access rule checking

s6-ipcserver-access checks its client connection against a ruleset. This ruleset can be implemented:

  • either in the filesystem as an arborescence of directories and files, if the -i option has been given. This option is the most flexible one: the directory format is simple enough for scripts to understand and modify it, and the ruleset can be changed dynamically. This is practical, for instance, for roaming users.
  • or in a CDB file, if the -x option has been given. This option is the most efficient one if the ruleset is static enough: a lot less system calls are needed to perform searches in a CDB than in the filesystem.

The exact format of the ruleset is described on the s6-accessrules-cdb-from-fs page.

s6-ipcserver-access first reads the client UID uid and GID gid from the ${PROTO}REMOTEEUID and ${PROTO}REMOTEEGID environment variables, and checks them with the s6_accessrules_keycheck_uidgid() function. In other words, it tries to match:

  • (if the client's effective uid is the same as s6-ipcserver-access's effective uid) uid/self
  • uid/uid
  • (if the client's effective gid is the same as s6-ipcserver-access's effective gid) gid/self
  • gid/gid
  • uid/default

in that order. If no S6_ACCESSRULES_ALLOW result can be obtained, the connection is denied.

Environment and executable modifications

s6-ipcserver-access interprets non-empty env subdirectories and exec files it finds in the first matching rule of the ruleset, as explained in the s6-accessrules-cdb-from-fs page.

  • An env subdirectory is interpreted as if the s6-envdir command had been called before executing prog: the environment is modified according to the contents of env.
  • An exec file containing newprog completely bypasses the rest of s6-ipcserver-access' command line. After environment modifications, if any, s6-ipcserver-access execs into execlineb -c newprog. Please be aware that the exec file functionality is only supported when s6 has been built with execline support. Otherwise, a warning message is printed and executable diversion is not performed.
s6-2.13.1.0/doc/s6-ipcserver-socketbinder.html000066400000000000000000000073511470151141200207230ustar00rootroot00000000000000 s6: the s6-ipcserver-socketbinder program

s6
Software
skarnet.org

The s6-ipcserver-socketbinder program

s6-ipcserver-socketbinder binds a Unix domain socket, then executes a program.

Interface

     s6-ipcserver-socketbinder [ -d | -D ] [ -b backlog ] [ -M | -m ] [ -a perms ] [ -B ] path prog...
  • s6-ipcserver-socketbinder creates a Unix domain socket and binds it to path. It prepares the socket to accept connections by calling listen().
  • It then execs into prog... with the open socket as its standard input.

Options

  • -d : allow instant rebinding to the same path even if it has been used not long ago - this is the SO_REUSEADDR flag to setsockopt() and is generally used with server programs. This is the default. Note that path will be deleted if it already exists at program start time.
  • -D : disallow instant rebinding to the same path.
  • -b backlog : set a maximum of backlog backlog connections on the socket - extra connection attempts will rejected by the kernel. The default is SOMAXCONN, i.e. the maximum number allowed by the system. If backlog is 0, then the socket will be created, but it will not be listening.
  • -M : the type of the socket will be SOCK_STREAM. This is the default.
  • -m : the type of the socket will be SOCK_DGRAM. Note that by default SOCK_DGRAM sockets are not connection-mode, and listen() will fail - so you should always give the -b0 option to s6-ipcserver-socketbinder along with -m.
  • -a perms : create the socket with permissions perms, which is an octal number from 0000 to 0777. Default is 0777, meaning everyone can connect to it. 0700 means only processes having the same uid as the s6-ipcserver-socketbinder process can connect to it.
  • -B : the socket will be blocking. The default is nonblocking.

Notes

  • The socket is provided non-blocking by default.
  • s6-ipcserver-socketbinder is part of a set of basic blocks used to build a flexible Unix super-server. It normally should be given a command line crafted to make it execute into s6-ipcserverd to accept connections from clients, or into a program such as s6-applyuidgid to drop privileges before doing so.
  • The s6-ipcserver program does exactly this. It implements a full Unix super-server by building a command line starting with s6-ipcserver-socketbinder and ending with s6-ipcserverd followed by the application program, and executing into it.
s6-2.13.1.0/doc/s6-ipcserver.html000066400000000000000000000157511470151141200162540ustar00rootroot00000000000000 s6: the s6-ipcserver program

s6
Software
skarnet.org

The s6-ipcserver program

s6-ipcserver is an UCSPI server tool for Unix domain sockets, i.e. a super-server. It accepts connections from clients, and forks a program to handle each connection.

Interface

     s6-ipcserver [ -1 ] [ -q | -Q | -v ] [ -d | -D ] [ -P | -p ] [ -a perms ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gidlist ] [ -g gid ] [ -u uid ] [ -U ] path prog...
  • s6-ipcserver binds a Unix domain socket to path.
  • It can drop its root privileges.
  • It closes its stdin and stdout.
  • For every client connection to this socket, it forks. The child sets some environment variables, then executes prog... with stdin reading from the socket and stdout writing to it.
  • Depending on the verbosity level, it logs what it does to stderr.
  • It runs until killed by a signal. Depending on the received signal, it may kill its children before exiting.
  • s6-ipcserver actually doesn't do any of this itself. It is a wrapper, rewriting the command line and executing into a chain of programs that perform those duties.

Implementation

  • s6-ipcserver parses the options and arguments it is given, and builds a new command line with them. It then executes into that new command line.
  • The first program s6-ipcserver executes into is s6-ipcserver-socketbinder. It will create and bind a Unix domain socket to path, then execute into the rest of the command line.
  • If a privilege-dropping operation has been requested, the program that s6-ipcserver-socketbinder executes into is s6-applyuidgid. It will drop the root privileges, then execute into the rest of the command line.
  • The next program in the chain is s6-ipcserverd. It is executed into by s6-applyuidgid, or directly by s6-ipcserver-socketbinder if no privilege-dropping operation has been requested. s6-ipcserverd is the long-lived process, the "daemon" itself, accepting connections from clients.
  • For every client, s6-ipcserverd will spawn an instance of prog..., the remainder of the command line.

Options

  • -1 : write a newline to stdout, before closing it, right after binding and listening to the Unix socket. If stdout is suitably redirected, this can be used by monitoring programs to check when the server is ready to accept connections.
  • -q : be quiet.
  • -Q : be normally verbose. This is the default.
  • -v : be verbose.
  • -d : allow instant rebinding to the same path even if it has been used not long ago - this is the SO_REUSEADDR flag to setsockopt() and is generally used with server programs. This is the default. Note that path will be deleted if it already exists at program start time.
  • -D : disallow instant rebinding to the same path.
  • -P : disable client credentials lookups. The IPCREMOTEEUID and IPCREMOTEEGID environment variables will be unset in every instance of prog.... This is the portable option, because not every system supports credential lookup across Unix domain sockets; but it is not as secure.
  • -p : enable client credentials lookups. This is the default; it works at least on Linux, Solaris, and *BSD systems. On systems that do not support it, every connection attempt will fail with a warning message.
  • -c maxconn : accept at most maxconn concurrent connections. Default is 40. It is impossible to set it higher than 1000.
  • -C localmaxconn : accept at most localmaxconn connections from the same user ID. Default is 40. It is impossible to set it higher than maxconn.
  • -b backlog : set a maximum of backlog backlog connections on the socket. Extra connection attempts will rejected by the kernel.
  • -a perms : create the socket with permissions perms, which is an octal number from 0000 to 0777. Default is 0777, meaning everyone can connect to it. 0700 means only processes having the same uid as the s6-ipcserver process can connect to it.
  • -G gidlist : change s6-ipcserver's supplementary group list to gidlist after binding the socket. This is only valid when run as root. gidlist must be a comma-separated list of numerical group IDs.
  • -g gid : change s6-ipcserver's groupid to gid after binding the socket. This is only valid when run as root.
  • -u uid : change s6-ipcserver's userid to uid after binding the socket. This is only valid when run as root.
  • -U : change s6-ipcserver's user id, group id and supplementary group list according to the values of the UID, GID and GIDLIST environment variables after binding the socket. This is only valid when run as root. This can be used with the s6-envuidgid program to easily script a service that binds to a privileged socket then drops its privileges to those of a named non-root account.

Notes

  • s6-ipcserver does not interpret its options itself. It just dispatches them to the appropriate program on the command line that it builds.
  • Previous versions of s6-ipcserver were monolithic: it did the work of s6-ipcserver-socketbinder, s6-applyuidgid and s6-ipcserverd itself. The functionality has now been split into several different programs because some service startup schemes require the daemon to get its socket from an external program instead of creating and binding it itself. The most obvious application of this is upgrading a long-lived process without losing existing connections.
s6-2.13.1.0/doc/s6-ipcserverd.html000066400000000000000000000117011470151141200164070ustar00rootroot00000000000000 s6: the s6-ipcserverd program

s6
Software
skarnet.org

The s6-ipcserverd program

s6-ipcserverd is the serving part of the s6-ipcserver super-server. It assumes that its stdin is a bound and listening Unix domain socket, and it accepts connections from clients connecting to it, forking a program to handle each connection.

Interface

     s6-ipcserverd [ -1 ] [ -v verbosity ] [ -P | -p ] [ -c maxconn ] [ -C localmaxconn ] prog...
  • s6-ipcserverd accepts connections from clients to an already bound and listening SOCK_STREAM Unix domain socket which is its standard input.
  • For every client connection to this socket, it forks. The child sets some environment variables, then executes prog... with stdin reading from the socket and stdout writing to it.
  • Depending on the verbosity level, it logs what it does to stderr.
  • It runs until killed by a signal. Depending on the received signal, it may kill its children before exiting.

Environment variables

For each connection, an instance of prog... is spawned with the following variables set:

  • PROTO: always set to IPC
  • IPCREMOTEEUID: set to the effective UID of the client, unless credentials lookups have been disabled
  • IPCREMOTEEGID: set to the effective GID of the client, unless credentials lookups have been disabled
  • IPCREMOTEPATH: set to the path associated with the remote socket, if any. Be aware that it may contain arbitrary characters.
  • IPCCONNNUM: set to the number of connections originating from the same user (i.e. same uid)

If client credentials lookup has been disabled, IPCREMOTEEUID and IPCREMOTEEGID will be set, but empty.

Options

  • -1 : write a newline to stdout, and close stdout, right before entering the client-accepting loop. If stdout is suitably redirected, this can be used by monitoring programs to check when the server is accepting connections. See this page for more information on readiness notification.
  • -v verbosity : be more or less verbose. verbosity can be 0 (quiet), 1 (normal), or 2 (verbose).
  • -P : disable client credentials lookups. The IPCREMOTEEUID and IPCREMOTEEGID environment variables will be unset in every instance of prog.... This is the portable option, because not every system supports credential lookup across Unix domain sockets; but it is not as secure.
  • -p : enable client credentials lookups. This is the default; it works at least on Linux, Solaris, and *BSD systems. On systems that do not support it, every connection attempt will fail with a warning message.
  • -c maxconn : accept at most maxconn concurrent connections. Default is 40. It is impossible to set it higher than 1000.
  • -C localmaxconn : accept at most localmaxconn connections from the same user ID. Default is 40. It is impossible to set it higher than maxconn.

Signals

  • SIGTERM: exit.
  • SIGHUP: send a SIGTERM and a SIGCONT to all children.
  • SIGQUIT: send a SIGTERM and a SIGCONT to all children, then exit.
  • SIGABRT: send a SIGKILL to all children, then exit.

Notes

  • Unlike his close cousin ipcserver, s6-ipcserverd does not perform operations such as access control. Those are delegated to the s6-ipcserver-access program.
  • s6-ipcserverd can be used to set up local services.
  • s6-ipcserverd is meant to be execve'd into by a program that gets the listening socket. That program is normally s6-ipcserver-socketbinder, which creates the socket itself; but it can be a different one if the socket is to be retrieved by another means, for instance by fd-passing from a fd-holding daemon (some people call this "socket activation").
s6-2.13.1.0/doc/s6-log.html000066400000000000000000000634231470151141200150320ustar00rootroot00000000000000 s6: the s6-log program

s6
Software
skarnet.org

The s6-log program

s6-log is a reliable logging program with automated log rotation, similar to daemontools' multilog, with full POSIX regular expression support.

Interface

     s6-log [ -d notif ] [ -q | -v ] [ -b ] [ -p ] [ -l linelimit ] [ -t lastlinetimeout ] [ -- ] logging script

s6-log reads and compiles logging script to an internal form. Then it reads its standard input, line by line, and performs actions on it, following the script it is given. It does its best to ensure there is never any log loss. It exits cleanly when stdin closes or when it receives a SIGTERM or a SIGHUP.

Options

  • -d notif : readiness notification. With this option, s6-log writes a newline character to file descriptor notif when it is ready, i.e. when it has successfully parsed its logging script and initialized all its necessary resources such as the logdirs defined in the script, and is now listening to stdin in order to process log lines.
  • -b : blocking mode. With this option, s6-log stops reading its standard input while it has unflushed buffers. This ensures that every log line has been fully processed before reading the next one; this is also multilog's behaviour. By default, s6-log keeps reading from stdin even if its buffers still contain data. -b is safer, but may slow down your service; the default is faster, but may lead to unbound memory use if you have a lot of output to write to a slow file system.
  • -p : protect against SIGTERM. Do not exit on receipt of a SIGTERM; only exit on a SIGHUP or when reading EOF on stdin. This is useful for a logger that you really do not want to lose even if automated administration (e.g. the downing of a supervision tree) would kill it.
  • -q | -v : quiet | verbose. Decreases | increases s6-log's verbosity, i.e. which messages are sent to stderr. The default verbosity is 1. Currently supported verbosity levels:
    • 0: only write alerts and fatal errors
    • 1: write alerts, warnings and fatal errors
  • -l linelimit : if a log line is longer than linelimit bytes, split it by inserting a newline after the linelimitth byte. After the split, the remainder of the line will also be processed, so it will go through the selection process, timestamping, etc. linelimit cannot be less than 48, unless it is 0 (which means infinite). The default is 8192 bytes. Setting linelimit to 0 ensures that lines will never be split; this may cause important memory consumption by s6-log if it is fed extremely long lines, so use with caution.
  • -t lastlinetimeout : if s6-log receives a termination signal but has a read a partial line in its buffer, it will wait for at most lastlinetimeout milliseconds for its service to send it the remainder of the line; if it still hasn't read a newline character by then, it will add a newline character itself and process the line, then exit. By default, lastlinetimeout is 2000, which means s6-log will wait for at most 2 seconds for completion of its last partial line. If lastlinetimeout is given as 0, then s6-log will wait forever; it won't exit until it actually reads a newline or EOF.

Logdirs

A logdir (logging directory) is a place where logs are stored. s6-log can be scripted to write into one or more logdirs.

A logdir may contain the following files:

  • lock: this file is locked by s6-log at start, to make sure only one instance is running at the same time.
  • current: the file where selected log lines are appended. If current has the executable-by-user flag, it means that no s6-log process is currently writing to it and the previous s6-log process managed to cleanly finalize it. If it does not, either an s6-log process is writing to it or the previous one has been interrupted without finalizing it.
  • state: last processor's output, see below.
  • previous: a rotation is happening in that logdir.
  • processed, newstate: a rotation is happening in that logdir and the processor script is running.
  • timestamped files: those files are @timestamp.s or @timestamp.u and are old log files that have been processed and rotated (if they're ending in .s) or that were the current file when s6-log got interrupted (if they're ending in .u), in which case they have not been processed.

Rotation

In a logdir, selected lines are appended to the current file. When current becomes too big, a rotation happens. The current file will be possibly processed, then it will become an archived log file named @timestamp.s, where timestamp, a TAI64N timestamp, is the absolute time of the rotation. If there are too many archived log files in the logdir, the older ones are then deleted. Logging then resumes, to a brand new current file.

You can use this mechanism to ensure that your logs never fill up the available disk space, for instance: something that neither syslogd, nor syslog-ng, nor rsyslog offers.

Processors

A processor script can be set for every logdir. When a rotation occurs, current (which has then been renamed previous) is fed to processor's stdin, and processor's stdout is saved and archived. processor can also read the state file on its fd 4; what it writes to its fd 5 will be saved as the next state file, for the next rotation. A processor script runs with the logdir as its working directory.

Processors should not background themselves: s6-log considers the processing done when its processor direct child dies. Processors should exit 0 on success and nonzero on failure; if a processor fails, s6-log will try it again after some cooldown time.

Processors make s6-log Turing-complete by allowing you to use any external program to handle log files that are going to be archived.

Logging script syntax

When starting up, s6-log reads its arguments one by one; this argument sequence, or directive sequence, forms a logging script which tells s6-log what to log, where, and how.

Every directive can be a selection directive, a control directive or an action directive. A valid logging script always contains at least one action directive; every action directive can be preceded by zero or more selection or control directives. s6-log will exit 100 if the script is invalid. If it can process the script but the last directive is not an action directive, s6-log will emit a warning.

Selection directives

These directives tell s6-log whether to select or deselect lines it reads from stdin; actions will only happen on selected lines. By default, every line is selected.

  • +regexp: select yet-unselected lines that match regexp, which must be a POSIX Extended Regular Expression.
  • -regexp: deselect yet-selected lines that match regexp, which must be a POSIX Extended Regular Expression.
  • f: select exactly lines that have not yet been acted upon (i.e. that were always deselected when the script encountered an action directive).

Control directives

These directives tune s6-log's behaviour for the next actions.

  • nnumber: next logdirs will contain up to number archived log files. If there are more, the oldest archived log files will be deleted, only the latest number will be kept. By default, number is 10.
  • sfilesize: next rotations will occur when current log files approach filesize bytes. By default, filesize is 99999; it cannot be set lower than 4096 or higher than 268435455.
  • Stotalsize: next logdirs will contain up to totalsize bytes of archived (and maybe processed) log files. If archived log files take more space than that, the older ones are deleted until the total size fits. A totalsize of zero means no such limit; use n0 instead if you don't want any archived log files. By default, totalsize is 0 (unlimited).
  • ltolerance: next rotations will be triggered when the size of current goes higher than filesize minus tolerance. tolerance cannot be more than half of filesize. By default, tolerance is 2000.
  • rcooldown: if an error occurs during operations on the next logdirs, retry every cooldown milliseconds. By default, cooldown is 2000; it's strongly discouraged to set it to a value under 50.
  • Ealertsize: only the first alertsize bytes of the selected lines will be used in the next alerts. An alertsize of 0 means no limit. By default, alertsize is 200.
  • ^statussize: only the first statussize bytes of the selected lines will be used in the next status file updates. If a line is shorter than statussize bytes, the status file will be padded with newlines so it is always statussize bytes long. 0 means an unpadded, unlimited status file. By default, statussize is 1001.
  • pprefix: sets prefix as a prefix to be printed on every output line. For instance, a pfoobar: directive means that the next action directives should prepend every line with foobar: (plus a space) before outputting it. Note that a prefix is always printed after the timestamps, if any. To remove a prefix for the next action directives, use a standalone p.
  • !processor: registers execlineb -Pc processor as a processor for the next logdirs; execlineb must be found in s6-log's PATH. This directive is only supported if s6 has been built with execline support; otherwise, it yields a syntax error at starting time. If processor is empty, no processor will be set for the next logdirs. By default, no processor is set.
  • ?processor: registers /bin/sh -c processor as a processor for the next logdirs. It is just like the ! directive, except that the processor string is interpreted by /bin/sh, not execlineb. It is useful for people who want to build s6 without execline support.
  • t: the logged line will be prepended with a TAI64N timestamp (and a space) before being processed by the next action directive. Giving the t directive several times before an action directive has no effect.
  • T: the selected line will be prepended with a ISO 8601 timestamp for combined date and time representing local time according to the system's timezone, with a space (not a 'T') between the date and the time and two spaces after the time, before being processed by the next action directive. Giving the T directive several times before an action directive has no effect.

Note that unlike the other control directives, the t and T directives are not sticky: their effect will disappear after the next action directive, and they need to be reapplied if necessary. If both a t and a T directives are given before an action directive, the TAI64N timestamp will always appear before the ISO 8601 timestamp.

Action directives

These directives determine what s6-log actually does with the selected lines.

  • 2: alert. s6-log will print "s6-log: alert: ", possibly prepended with a timestamp, followed by the first alertsize bytes of the line, to its standard error.
  • 1: forward to stdout. s6-log will print the selected line to its stdout. If any error occurs, e.g. if stdout was a pipe and the reading end closed, this directive will be ignored for the rest of s6-log's lifetime.
  • =statusfile: status. s6-log will atomically update the statusfile file with the first statussize bytes of the line, and pad it with newlines. s6-log must have the right to write to statusfile and to statusfile's directory.
  • dir (must start with '/' or '.'): logdir. s6-log will log the line into the logdir dir. s6-log must have the right to write to dir.

Signals

  • SIGTERM: If s6-log has been run with the -p option, does nothing. Without this option, SIGTERM instructs s6-log to stop reading stdin after the next newline and exit after logging the last line.
  • SIGALRM: triggers a rotation on every logdir s6-log is monitoring, as if the current file in those logdirs had reached the size limit.

Examples

     s6-log -b n20 s1000000 t /var/log/services/stuff

Logs all of stdin, prepending every line with a TAI64N timestamp, into the /var/log/services/stuff logdir, with a maximum archive of 20 log files of 1 MB each; makes sure every line has been written before reading the next one.

     s6-log n30 E500 - +fatal: 2 - +^STAT =/var/log/foobard/status f s10000000 S15000000 T !"gzip -nq9" /var/log/foobard
  • Sends alerts to stderr with the 500 first bytes of lines containing "fatal:".
  • Maintains the /var/log/foobard/status file at 1001 bytes, updating it when it finds a log line starting with "STAT".
  • Logs all other lines to logdir /var/log/foobard, prepending them with an ISO 8601 timestamp. When current reaches at least 9998 kB (i.e. 10 MB filesize minus 2kB tolerance), pipe it through gzip -nq9 and save the result into a timestamped archive file, with a maximum of 30 such files or a total of 15 MB of compressed archive files.

Why use execlineb to interpret the "processor" string?

Because it is exactly what execlineb is for.

  • Directly executing processor is not flexible enough. We want to be able to run a complete command line, with an executable name and its arguments.
  • We could interpret the processor string via /bin/sh. This is what multilog does. However, /bin/sh, despite being the traditional Unix interpreter, is overpowered for this. We don't need a complete shell script interpreter: most processor commands will be very simple, with only two or three words, and we only need a way to turn a string into an argv, i.e. a command line.
  • execlineb was designed just for this: to turn simple strings into command lines. It is a very fast and lightweight script launcher, that does not do any heavy startup initialization like /bin/sh does. It happens to be the perfect tool for the job.
  • To be perfectly honest: I also did this on purpose so people have a reason to use the execline language. But seriously, it really is the perfect tool for the job.

Why have another logging mechanism?

Because the syslog mechanism and all its implementations (save one) suck. I'm not being judgmental; I'm just stating the obvious.

The syslog design is flawed from the start

When asked why he started rsyslog, Rainer Gerhards came up with a lot of hand-waving and not a single word about technical points. There is a reason for that: rsyslog is forked from sysklogd! So, no matter how many bells and whistles are added to it, it still suffers from the same basic flaws.

The problem with syslogd does not come from such or such implementation. The problem comes from syslog's design in the first place.

  • syslog makes you send all your logs to the same place. The logs from a zillion processes are read by a single syslogd server. The server checks log lines against system-wide regular expressions to decide where to write them. This raises the following issues:
    • Unless the client explicitly mentions its name in every log line, there is no way for log readers to know what process generated a given line. Some syslogd implementations can log the pid of the client; big deal.
    • Log lines from every client have to run through the same regular expression matching. This requires huge regular expression sets, and an obvious performance impact, to do anything meaningful. And as a matter of fact, standard syslogd configurations don't do anything meaningful: they separate the logs into a few streams such as /var/log/messages, /var/log/auth.log, /var/log/daemon.log or /var/log/syslog with very vague semantics. All of syslogd's line processing power remains unused, because making real use of it would be too complex.
  • syslogd logs to files. This is wrong, because files grow and disks fill up. Sure, there are utilities such as logrotate to perform cleaning up, but as long as logging and log rotation are kept separate, there is a race condition: a log file can grow and fill up a disk before a rotation occurs. I am all for separating tasks that can be separated, but there is no choice here: logging and log rotation management must be done by the same tool. Only a few non-mainstream implementations of syslogd do this, including the Busybox one - and that is a feature added by the Busybox developers who are aware of the problem but want to maintain compatibility with the historical syslogd. Neither syslogd (-ng or not) nor rsyslogd manages its log files: that's a flaw that no amount of external tools is going to fix.
  • syslogd is a complex process that runs as root. We all know what complex processes running as root mean: bugs turning into security holes.
  • syslog requires a syslogd service, and fails otherwise. A syslogd service may not be present, it may fail... or it may want to log stuff. Who's going to take care of syslogd's error messages?

syslog is slow, it's unsafe, and it's incomplete. The only reason people use it is because it's historical, it exists, and there hasn't been any serious alternative yet, except maybe multilog, which s6-log improves upon.

A not-so-modest proposal: the logging chain

Unix distributions already do this to some extent, but it's at best unclear where the logs go for any given program.

  • Every program, without exception, should send its logs (be it error messages, warning messages, or anything) to its standard error descriptor, i.e. fd 2. This is why it's open for.
  • When process 1 starts, the logging chain is rooted to the machine console: anything process 1 sends to its stderr appears, without modification, on the machine console, which should at any time remain the last resort place where logs are sent.
  • Process 1 should spawn and supervise a catch-all logging mechanism that handles logs from every service that does not take care of its own logging. Error messages from this logging mechanism naturally go to the machine console.
  • Process 1's own error messages can go to the machine console, or dirty tricks can be used so they go to the catch-all logging mechanism.
  • Services that are spawned by process 1 should come with their own logger service; the supervision mechanism offered by s6-svscan makes it easy. Error messages from the loggers themselves naturally go to the catch-all mechanism.
  • User login mechanisms such as getty, xdm or sshd are services: they should be started with their own loggers. Of course, when a user gets a terminal and a shell, the shell's stderr should be redirected to the terminal: interactive programs break the automatic logging chain and delegate responsibility to the user.
  • A syslogd service may exist, to catch logs sent via syslog() by legacy programs. But it is a normal service, and logs caught by this syslogd service are not part of the logging chain. It is probably overkill to provide the syslogd service with its own logger; error messages from syslogd can default to the catch-all logger. The s6 package, including the ucspilogd program, provides enough tools to easily implement a complete syslogd system, for a small fraction of the resource needs and the complexity of native syslogd implementations.

So, given a program, where are its logs sent ?

  • Logs sent via syslog() will be handled by the syslogd service as usual. Smart administrators will make sure that those ones are as few as possible. The rest of this analysis is about logs sent to stderr.
  • If the program is descended from a user's interactive program, its logs are sent to the user's terminal or the user's choice redirection target.
  • If the program is descended from a logged service, its logs are naturally sent to the service's logger.
  • Else the logs are sent to the catch-all logger.
  • Only the catch-all logger's error messages, the kernel's fatal error messages, and maybe process 1's error messages, are sent to the system console.

What does s6-log have to do with all this?

In a logging chain situation, every service must have its own logger. To avoid syslogd's design mistakes, one logger process per service must be run. s6-log fits that role. Using s6-log as your one-stop logger offers the following benefits:

  • Every instance of s6-log can run as a different user, so it's easy to give different access rights to different logs. It is also more secure not to have any logger process running as root.
  • s6-log consumes very little memory per instance (unless it accumulates unflushed log lines, which you can avoid with the -b option). So, launching a lot of s6-log processes does not waste resources.
  • s6-log is vastly configurable via logging scripts; every instance is as powerful as a traditional syslogd.
  • s6-log can log to a RAM filesystem and thus is suitable as a catch-all logger. Clever tricks like Upstart's logd or daemontools' readproctitle are just that: tricks. s6-log gives a unified interface to all of your system's loggers.

You're wrong about being as powerful as syslogd: s6-log does not do remote logging.

You mean you want to send, live, every log line over the network via UDP ? You can't be serious.

Do yourself a favor and use s6-log to write log lines to a logdir, with a processor script that sends files-being-archived to the network, possibly after compressing them. More reliability, less log lines lost, less network traffic, better engineering. If you have no disk to even write the current files to, write to a small RAM filesystem.

If you have to log stuff live via the network, you do not need any local logging software. You don't even need syslogd. Just filter your stderr via some grep that selects lines for you, then sends them to a network socket. A trivial shell script, or execline script, can do that for you.

Do not insist on using syslogd. It does nothing magical, and nothing that can't be done in a simpler way using simpler tools.

s6-2.13.1.0/doc/s6-mkfifodir.html000066400000000000000000000026051470151141200162160ustar00rootroot00000000000000 s6: the s6-mkfifodir program

s6
Software
skarnet.org

The s6-mkfifodir program

s6-mkfifodir creates a fifodir.

Interface

     s6-mkfifodir [ -f ] [ -g gid ] fifodir

s6-mkfifodir creates fifodir, belonging to the current user.

Options

  • -f : force permissions. If fifodir already exists, change its permissions according to the -g options. By default, if fifodir exists, s6-mkfifodir does nothing.
  • -g gid : make fifodir only listenable by members of group gid. If this option is not given, the fifodir is made publically listenable.
s6-2.13.1.0/doc/s6-notifyoncheck.html000066400000000000000000000175701470151141200171160ustar00rootroot00000000000000 s6: the s6-notifyoncheck program

s6
Software
skarnet.org

The s6-notifyoncheck program

s6-notifyoncheck is a chain-loading program meant to be used in run scripts, in a service that has been declared to honor readiness notification. It implements a policy of running a user-provided executable in the background that polls the service currently being launched, in order to check when it becomes ready. It feeds the result of this check into the s6 notification mechanism.

s6-notifyoncheck should only be used with daemons that can be polled from the outside to check readiness, and that do not implement readiness notification themselves.

Interface

     s6-notifyoncheck [ -d ] [ -3 notiffd ] [ -s initialsleep ] [ -T globaltimeout ] [ -t localtimeout ] [ -w waitingtime ] [ -n n ] [ -c checkprog ] prog...

s6-notifyoncheck forks and runs as the child; the parent immediately execs into prog..., the daemon that must be checked for readiness.

s6-notifyoncheck first waits for a little time, then it spawns the ./data/check executable and waits for it to exit. If ./data/check exits 0, then s6-notifyoncheck reports that the service is ready, then exits. If ./data/check exits anything else, s6-notifyoncheck sleeps for a little time, then spawns ./data/check again. It loops until ./data/check succeeds, or 7 attempts fail, or a certain amount of time elapses.

Exit codes

s6-notifyoncheck can exit before executing into prog:

  • 100: wrong usage
  • 111: system call failed

After forking, s6-notifyoncheck (running as the child) can exit with the following exit codes, but those are meaningless because no process will, or should, check them. They are only differentiated for clarity in the code:

  • 0: service readiness achieved and notification sent
  • 1: maximum number of attempts reached, all unsuccessful
  • 2: prog died, so s6-notifyoncheck exited early
  • 3: timed out before readiness was achieved
  • 111: system call failed

Options

  • -d : doublefork. s6-notifyoncheck will run as the grandchild of prog... instead of its direct child. This is useful if prog... never reaps zombies it does not know it has.
  • -3 notiffd : use notiffd as the file descriptor to send a readiness notification to. By default, this number is automatically read from the ./notification-fd file.
  • -s initialsleep : sleep for initialsleep milliseconds before starting to poll the service for readiness. Default is 10 milliseconds.
  • -T globaltimeout : give up (and leave the service up but not ready) if service readiness still has not been detected after globaltimeout milliseconds. Default is 0, meaning infinite: s6-notifyoncheck will keep polling until it succeeds.
  • -t localtimeout : on every attempt, if ./data/check still has not exited after localtimeout milliseconds, kill it and declare that attempt failed. Default is 0, meaning infinite: s6-notifyoncheck will wait forever for ./data/check to exit.
  • -w waitingtime : sleep for waitingtime milliseconds between two invocations of ./data/check. This is basically the polling period. Default is 1000: the service will be polled every second.
  • -n n : give up after n unsuccessful invocations of ./data/check. 0 means infinite, i.e. keep polling until it succeeds, or times out, or the service dies first. Default is 7.
  • -c checkprog... : invoke checkprog... instead of ./data/check. The checkprog string will be parsed by execlineb, so it can contain a full command line. This option is mainly useful is the program used to poll the service is very simple and can be inlined as a simple command line, to avoid needing to manage a whole script and a ./data/check file. This option is only supported if the execline package is installed; if it is not, the -c option cannot be used and the checking script must always be placed in ./data/check.

Usage

s6-notifyoncheck is designed to make it possible for services to use the s6 notification mechanism even with daemons that do not natively implement the mechanism of writing a newline to a file descriptor of their choice when they're ready.

Polling is evil. Please make sure you really have no other choice before writing a ./data/check program and using s6-notifyoncheck in your run script. If you have access to the source code of the daemon you want to check for readiness, consider patching it to add readiness notification support, which is extremely simple and does not require linking against any s6 library.

If using a ./data/check program is your only option:

  • Make sure the ./data subdirectory is readable and that ./data/check is executable, exits 0 if the daemon it checks is ready, and exits nonzero if the daemon is not ready.
  • Add a ./notification-fd file to your service directory, which can contain any number that is not 0, 1 or 2, or anything else explicitly used in your run script. The daemon does not need to care about that file descriptor; from the daemon's point of view, nothing changes.
  • In your run script, insert s6-notifyoncheck in the command line that will execute into your daemon.
  • ./data/check will run as the same user as s6-notifyoncheck. If s6-notifyoncheck runs after the run script's process has lost its root privileges, make sure that ./data/check is accessible and runnable as that user.
  • Note that s6-notifyoncheck should be used to check if the current service itself is running. You cannot use it to poll for the readiness of another service.
  • s6-notifyoncheck must be run with the service directory as its current working directory. In other words: you cannot use cd in your run script before you execute the command line containing s6-notifyoncheck (else you will get a weird error message saying the supervisor is not running). If your service needs to run in a different working directory, you need to change working directories after the s6-notifyoncheck invocation. The cd utility from the execline package can be used to change working directories in the middle of a command line (and exec the rest of the command line) without invoking a shell.
s6-2.13.1.0/doc/s6-permafailon.html000066400000000000000000000100311470151141200165310ustar00rootroot00000000000000 s6: the s6-permafailon program

s6
Software
skarnet.org

The s6-permafailon program

s6-permafailon is a program that is meant to be used in the ./finish script of a service directory supervised by s6-supervise. When used, it reads and analyses the death tally of a service (i.e. the recent process death events that happened), and if the death tally matches a given pattern, it causes permanent failure of the service, i.e. it tells the supervisor not to try and restart it.

Interface

     s6-permafailon secs deathcount events prog...
  • s6-permafailon must have the service directory of the tested service as its current directory. This is the default if it is called from the finish script of the service.
  • It reads the death tally of the service, which is maintained by s6-supervise.
  • If the supervised process has died at least deathcount times in the last secs seconds with a cause listed in events, then s6-permafailon exits 125.
  • Else s6-permafailon execs into prog....

events is a comma-separated list of events. An event can be one of the following:

  • An exit code, which is an integer between 0 and 255. Example: 1
  • An exit code interval, which is two exit codes separated by a dash. Example: 1-50
  • A signal name, or a signal number preceded by "SIG". Examples: SIGTERM, sigabrt, sig11

Usage

  • s6-supervise detects when the ./finish script of its service exits 125, and stops respawning the service. So, if the ./finish script is a chain-loading command line starting with a s6-permafailon invocation (or containing such an invocation), when s6-permafailon exits 125, then the ./finish script also exits 125 (because it is the same process), and the service is then marked as failing permanently.
  • The ./finish script is naturally a chain-loading command line if it is written in the execline language. It can also be made into a chain-loading command line from a shell script by using exec s6-permafailon secs deathcount events rest-of-chainloading-cmdline...
  • Multiple invocations of s6-permafailon can be chained, in order to test several death patterns.
  • If a permanent failure is triggered and secs is high, it is possible that when the administrator manually launches the service again, the next death triggers a permanent failure again. If this is not wanted, the administrator should clear the death tally with the s6-svdt-clear command.
  • The current death tally can be viewed via the s6-svdt command.

Example

s6-permafailon 60 5 1,101-103,SIGSEGV,SIGBUS prog... will exit 125 if the service has died 5 times in the last 60 seconds with an exit code of 1, 101, 102 or 103, a SIGSEGV or a SIGBUS. Else it will chainload into the prog... command line.

s6-2.13.1.0/doc/s6-setlock.html000066400000000000000000000045331470151141200157120ustar00rootroot00000000000000 s6: the s6-setlock program

s6
Software
skarnet.org

The s6-setlock program

s6-setlock takes a lock on a file, then executes into another program.

Interface

     s6-setlock [ -n | -N ] [ -t timeout ] [ -d fd ] [ -r | -w ] file prog...
  • s6-setlock creates file if it does not exist and opens it for writing.
  • It locks file. If it cannot take the lock for any reason, it exits 1.
  • It executes into prog....

Options

  • -n : nonblocking lock. If s6-setlock cannot acquire the lock, it will exit 1 immediately.
  • -N : blocking lock. s6-setlock will wait until it can acquire the lock. This is the default.
  • -t timeout : timed lock. If s6-setlock cannot acquire the lock after timeout milliseconds, it will exit 1.
  • -r : shared lock. Other shared locks on the same file will not prevent the lock from being acquired (but an exclusive lock will).
  • -w : exclusive lock. This is the default.
  • -d fd : make the lock visible in prog on file descriptor fd.

Notes

  • s6-setlock leaks an open file descriptor into the prog execution. This is intended: the fd holds the lock, which is released when prog exits. prog must not touch fds it does not know about; by default it has no need to know the descriptor that holds the lock. But if you need to officialize the presence of the lock in prog, that's where the -d option comes in.
s6-2.13.1.0/doc/s6-setsid.html000066400000000000000000000062061470151141200155400ustar00rootroot00000000000000 s6: the s6-setsid program

s6
Software
skarnet.org

The s6-setsid program

s6-setsid runs a program as a new session leader, or in a new foreground or background process group.

Interface

     s6-setsid [ -s | -b | -f | -g ] [ -i | -I | -q ] [ -d ctty ] prog...
  • s6-setsid creates a new session, or a new process group, and may make that process group the foreground process group, depending on the options it is run with.
  • As session leader or process group leader, s6-setsid then executes into prog....

Options

  • -s : session. s6-setsid will try and execute prog as a session leader. This is the default.
  • -b : background process group. s6-setsid will not create a new session, but will create a new process group, and try and execute prog as the new process group leader.
  • -f : foreground process group. s6-setsid will not create a new session, but will create a new process group and attach its session's controlling terminal to the new process group before executing prog. However, the new process group will likely be stopped, waiting for the former foreground process group to relinquish the controlling terminal, and will need to be sent a SIGCONT to resume. To avoid that, use the next option.
  • -g : grab terminal. s6-setsid will not create a new session, but will create a new process group and attach its session's controlling terminal to the new process group before executing prog. It will forcefully grab the controlling terminal from the former foreground process group: a process belonging to that former foreground process group will be stopped if it attempts to read from or write to that terminal.
  • -i : strict. If s6-setsid cannot perform the operations it needs, it will exit 111 with an error message.
  • -I : loose. If s6-setsid cannot perform the operations, it will print a warning message, but exec into prog nonetheless. This is the default.
  • -q : silent. s6-setsid will not print any warning message; it will exec into prog even if it cannot perform the operations.
  • -d ctty : assume file descriptor number ctty is the controlling terminal for the current session. Default is 0. This is only useful when used with the -f or -g options.
s6-2.13.1.0/doc/s6-setuidgid.html000066400000000000000000000033101470151141200162170ustar00rootroot00000000000000 s6: the s6-setuidgid program

s6
Software
skarnet.org

The s6-setuidgid program

s6-setuidgid executes a program as another user.

Interface

     s6-setuidgid account prog...
  • If account is the empty string, then s6-setuidgid directly execs into prog... without any state changes.
  • If account contains a colon, it is interpreted as uid:gid, else it is interpreted as a username and looked up by name in the account database.
  • If account is unknown, s6-setuidgid exits 1.
  • s6-setuidgid sets its (real and effective) uid and gid to those of account. It also sets the list of supplementary groups to the correct one for account according to the group database.
  • Then it executes into prog....

Notes

  • Unless account is empty, s6-setuidgid can only be run as root. Its main use is to drop root privileges before starting a daemon.
s6-2.13.1.0/doc/s6-socklog.html000066400000000000000000000146031470151141200157060ustar00rootroot00000000000000 s6: the s6-socklog program

s6
Software
skarnet.org

The s6-socklog program

s6-socklog is a minimal syslog daemon. It reads datagrams from the /dev/log Unix domain socket, or from a Unix domain or Internet domain socket of the user's choice, converts the encoded syslog facility and priority names to their human-readable equivalents, and prints the logs to its stdout.

s6-socklog is a reimplementation of the socklog program with a few more features.

Interface

     s6-socklog [ -d notif ] [ -r ] [ -U | -u uid -g gid -G gidlist ] [ -l linelen ] [ -t lameducktimeout ] [ -x unixsocket | -i ipport ]
  • s6-socklog binds to /dev/log.
  • It drops its root privileges.
  • For every datagram it reads, it turns its content into a log line:
    • Messages are truncated to 1024 characters
    • Trailing nulls or newlines are removed
    • Embedded nulls or newlines are converted to ~ characters (tildes)
    • A <syslogcode> at the beginning of the line is converted to facility.priority:
  • It prints the log line to its stdout, terminated by a newline.
  • It exits 0 on a SIGTERM.

Exit codes

  • 0: SIGTERM received, clean exit
  • 99: SIGTERM received but the buffer could not be flushed in time, some logs may be lost
  • 100: wrong usage
  • 111: system call failed

Signals

s6-socklog reacts to the following signals:

  • SIGTERM: exit as soon as possible

Options

  • -r : raw logging. <syslogcode> codes will not be converted to facility/priority names.
  • -d notif : when ready (actually bound to its socket), write a newline to file descriptor notif then close it. This allows s6-socklog to use the s6 mechanism to notify readiness. notif cannot be less than 3. If this option is not given, no readiness notification is sent.
  • -u uid : drop user id to uid
  • -g gid : drop group id to gid
  • -G gidlist : set supplementary group list to gidlist, which must be given as a comma-separated list of numeric gids, without spaces.
  • -U : set user id, group id and supplementary group list to the values of the UID, GID and GIDLIST environment variables. If a -u, -g or -G option is given after -U, the command line value overrides the environment variable.
  • -l linelen : Set the maximum datagram size to linelen. Default is 1024. It cannot be set to less than 76 or more than 1048576. If a datagram is bigger than linelen, it will be truncated to linelen characters, and the logged line will end with a ... ellipsis to show the truncation.
  • -t lameducktimeout : on receipt of a SIGTERM, give s6-socklog a grace period of lameducktimeout milliseconds to flush any log lines that are still in its buffer. Default is 0, which means infinite: the program will only exit when its buffer is empty, and may wait forever. If lameducktimeout is nonzero and the timeout expires, the program will exit 99.
  • -x unixsocket : bind to a Unix domain socket at unixsocket. Default is /dev/log.
  • -i ipport : bind to a UDP socket. ipport is a combination of ip, which must be an IPv4 or IPv6 address, and port, which must be an integer. port may be omitted, in which case it defaults to 514. If port is given, ip and port must be separated by a _ character (an underscore). If ip is IPv4, a : (colon) can be used instead of an underscore. When this option is used, s6-socklog will prepend every log line with clientip_clientport: , clientip and clientport being the IP address and port of the client that sent the log datagram.

Typical use

s6-socklog can be paired with s6-log to implement syslogd functionality. s6-socklog acts as the frontend: it reads the log lines and processes them, then pipes them to an s6-log instance that acts as the backend, i.e. sorts the log lines depending on regular expressions that typically involve the facility and priority names, then stores them into the filesystem.

The pipe between s6-socklog and s6-log is typically a logging pipe automatically provided by s6-svscan when the s6-log instance is declared as a logger service for the s6-socklog instance.

The examples/ subdirectory of the s6 package contains a turnkey syslogd service that implements this pattern.

Notes

  • s6-socklog cannot be used under s6-ipcserver or another super-server. It does not implement the socklog ucspi functionality, which is provided by the ucspilogd program instead.
s6-2.13.1.0/doc/s6-softlimit.html000066400000000000000000000051631470151141200162600ustar00rootroot00000000000000 s6: the s6-softlimit program

s6
Software
skarnet.org

The s6-softlimit program

s6-softlimit changes its process limits, then executes into another program.

Interface

     s6-softlimit [ -a allmem ] [ -c core ] [ -d data ] [ -f fsize ] [ -l lock ] [ -m mem ] [ -o ofiles ] [ -p proc ] [ -r res ] [ -s stack ] [ -t cpusecs ] prog...
  • s6-softlimit parses its options and sets process (soft) resource limits accordingly.
  • A value of '=' for any option means "set that limit to the hard limit".
  • Depending on your operating system, an option may do nothing.
  • When s6-softlimit has modified all the limits successfully, it executes into prog....

Options

  • -a allmem : limit the total available memory to allmem bytes.
  • -c core : limit the core file size to core bytes.
  • -d data : limit the available heap memory to data bytes.
  • -f fsize : limit the file size to fsize bytes.
  • -l lock : limit the available locked memory to lock bytes.
  • -m mem : limit all types of memory to mem bytes.
  • -o ofiles : limit the number of open fds to ofiles.
  • -p proc : limit the number of processes to proc (per user).
  • -r res : limit the available physical memory to res bytes.
  • -s stack : limit the available stack memory to stack bytes.
  • -t cpusecs : limit the available CPU time to cpusecs seconds.
s6-2.13.1.0/doc/s6-sudo.html000066400000000000000000000043321470151141200152150ustar00rootroot00000000000000 s6: the s6-sudo program

s6
Software
skarnet.org

The s6-sudo program

s6-sudo connects to a Unix domain socket and passes its standard file descriptors, command-line arguments and environment to a program running on the server side, potentially with different privileges.

Interface

     s6-sudo [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] [ -e ] [ -t timeoutconn ] [ -T timeoutrun ] path [ args... ]
  • s6-sudo executes into s6-ipcclient path s6-sudoc args... It does nothing else: it is just a convenience program. The s6-ipcclient program connects to a Unix socket at path, and the s6-sudoc program transmits the desired elements over the socket.
  • It should be used to connect to a local service running the s6-sudod program, which will run a server program on the client's behalf.

Options

  • The -q, -Q, -v, -p and -l options are passed to s6-ipcclient.
  • The -e, -t and -T options are passed to s6-sudoc.
  • Command-line arguments, if any, are also passed to s6-sudoc, which will transmit them to s6-sudod over the socket.
s6-2.13.1.0/doc/s6-sudoc.html000066400000000000000000000066751470151141200153740ustar00rootroot00000000000000 s6: the s6-sudoc program

s6
Software
skarnet.org

The s6-sudoc program

s6-sudoc talks to a peer s6-sudod program over a Unix socket, passing it command-line arguments, environment variables and standard descriptors.

Interface

     s6-sudoc [ -e ] [ -t timeoutconn ] [ -T timeoutrun ] [ args... ]
  • s6-sudoc transmits its standard input, standard output and standard error via fd-passing over a Unix socket that must be open on its descriptors 6 and 7. It expects an s6-sudod process to be receiving them on the other side.
  • It also transmits its command-line arguments args, and also its environment by default. Note that s6-sudod will not necessarily accept all the environment variables that s6-sudoc tries to transmit.
  • s6-sudoc waits for the server program run by s6-sudod to finish. It exits the same exit code as the server program. If the server program is killed by a signal, s6-sudoc kills itself with the same signal.

Options

  • -e : do not attempt to transmit any environment variables to s6-sudod.
  • -t timeoutconn : if s6-sudod has not managed to process the given information and start the server program after timeoutconn milliseconds, give up. By default, timeoutconn is 0, meaning infinite. Note that there is no reason to set up a nonzero timeoutconn with a large value: s6-sudod is not supposed to block. The option is only there to protect against ill-written services.
  • -T timeoutrun : if the server program has not exited after timeoutrun milliseconds, give up. By default, timeoutrun is 0, meaning infinite.

Notes

  • If s6-sudoc is killed, or exits after timeoutrun milliseconds, while the server program is still running, s6-sudod will send a SIGTERM and a SIGCONT to the server program - but this does not guarantee that it will die. If the server program keeps running, it might still read from the file that was s6-sudoc's stdin, or write to the files that were s6-sudoc's stdout or stderr. This is a potential security risk. Administrators should audit their server programs to make sure this does not happen.
  • More generally, anything using signals or terminals will not be handled transparently by the s6-sudoc + s6-sudod mechanism. The mechanism was designed to allow programs to gain privileges in specific situations: short-lived, simple, noninteractive processes. It was not designed to emulate the full suid functionality and will not go out of its way to do so.
s6-2.13.1.0/doc/s6-sudod.html000066400000000000000000000216521470151141200153650ustar00rootroot00000000000000 s6: the s6-sudod program

s6
Software
skarnet.org

The s6-sudod program

s6-sudod receives command-line arguments, environment variables and standard descriptors from a peer s6-sudoc program over a Unix socket, then forks another program.

Interface

     s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -d ] [ -t timeout ] [ sargv... ]
  • s6-sudod gets 3 file descriptors via fd-passing over a Unix socket that must be open on its descriptors 0 and 1. (The received descriptors will be the stdin, stdout and stderr of the server program.) It expects a s6-sudoc process to be sending them on the client side.
  • It also receives a list of command-line arguments cargv..., and an environment clientenv.
  • s6-sudod forks and executes sargv... cargv... The client command line is appended to the server command line.
  • s6-sudod waits for its child to exit and transmits its exit code to the peer s6-sudoc process. It then exits 0.

Environment

s6-sudod transmits its own environment to its child, plus the environment sent by s6-sudoc, filtered in the following manner: for every variable sent by s6-sudoc, if the variable is present but empty in s6-sudod's environment, then its value is overriden by the value given by s6-sudoc. A variable that is already nonempty, or that doesn't exist, in s6-sudod's environment, will not be transmitted to the child. In other words:

  • If there's no variable X in s6-sudod's environment, the child will have no variable X defined
  • If there's a non-empty variable X in s6-sudod's environment, the child will inherit that variable, with its value, from s6-sudod
  • If there's an empty variable X in s6-sudod's environment, and s6-sudoc transmits variable X, then the child will inherit that variable with the value from s6-sudoc. (If s6-sudoc does not transmit X, the variable will be present, but empty, in the child's environment.)

Options

  • -0 : do not inherit stdin from s6-sudoc. The child will be run with its stdin pointing to /dev/null instead.
  • -1 : do not inherit stdout from s6-sudoc. The child will be run with its stdout pointing to /dev/null instead.
  • -2 : do not inherit stderr from s6-sudoc. The child will be run with its stderr being a copy of s6-sudod's stderr instead. (This is useful to still log the child's error messages without sending them to the client.)
  • -d : detach. The child will keep running until it naturally exits, even if the client disconnects. Setting this option also enforces -0, -1 and -2. Bear in mind that this option relinquishes a lot of control over the child, and administrators should make sure it is appropriately short-lived.
  • -t timeout : if s6-sudod has not received all the needed data from the client after timeout milliseconds, it will exit without spawning a child. By default, timeout is 0, meaning infinite. This mechanism exists to protect the server from malicious or buggy clients that would uselessly consume resources.

Usage example

The typical use of s6-sudod is in a local service with a s6-ipcserver process listening on a Unix socket, an s6-ipcserver-access process performing client authentication and access control, and possibly a s6-envdir process setting up the environment variables that will be accepted by s6-sudod. The following script, meant to be a run script in a service directory, will set up a privileged program:

#!/command/execlineb -P
fdmove -c 2 1
fdmove 1 3
s6-envuidgid serveruser
s6-ipcserver -U -1 -- serversocket
s6-ipcserver-access -v2 -l0 -i rules --
exec -c
s6-envdir env
s6-sudod
sargv
  • execlineb executes the script.
  • fdmove makes sure the script's error messages are sent to the service's logger.
  • fdmove redirects the script's stdout to file descriptor 3. This is useful if the service directory contains a notification-fd file containing 3, so the daemon can perform readiness notification by writing a newline to its stdout. (The -1 option to s6-ipcserver tells it to do this.)
  • s6-envuidgid sets the UID, GID and GIDLIST environment variables for s6-ipcserver to interpret.
  • s6-ipcserver binds to serversocket, drops its privileges to those of serveruser, and announces its readiness. Then, for every client connecting to serversocket:
    • s6-ipcserver-access checks the client's credentials according to the rules in directory rules.
    • exec -c clears the environment.
    • s6-envdir sets environment variables according to the directory env. You can make sure that a variable VAR will be present but empty by performing echo > env/VAR. (A single newline is interpreted by s6-envdir as an empty variable; whereas if env/VAR is totally empty, then the VAR variable will be removed from the environment.)
    • s6-sudod reads a command line cargv, a client environment and file descriptors over the socket.
    • s6-sudod spawns sargv cargv.
    (Actually, s6-ipcserver does not do this itself: it executes into other programs that each do one of the tasks. But for our example, it does not matter.)

This means that user clientuser running s6-sudo serversocket cargv will be able, if authorized by the configuration in rules, to run sargv cargv as user serveruser, with stdin, stdout, stderr and the environment variables properly listed in env transmitted to sargv.

Notes

  • If the -d option to s6-sudod has not been given, and s6-sudoc is killed (or exits after timeoutrun milliseconds) while the server program is still running, s6-sudod will send a SIGTERM and a SIGCONT to its child, then exit 1. However, sending a SIGTERM to the child does not guarantee that it will die; and if it keeps running, it might still read from the file that was s6-sudoc's stdin, or write to the files that were s6-sudoc's stdout or stderr. This is a potential security risk. Administrators should audit their server programs to make sure this does not happen.
  • More generally, anything using signals or terminals will not be handled transparently by the s6-sudoc + s6-sudod mechanism. The mechanism was designed to allow programs to gain privileges in specific situations: short-lived, simple, noninteractive processes. It was not designed to emulate the full suid functionality and will not go out of its way to do so.
  • Administrators should also make sure that it's not a problem if s6-sudod's child keeps running after the s6-sudoc client exits, if they have given the -d option to s6-sudod. In particular, they should study what happens if another connection to the same service occurs while an instance is still running.
  • sargv may be empty. In that case, the client is in complete control of the command line executed as serveruser. This setup is permitted by s6-sudod, but it is very dangerous, and extreme attention should be paid to the construction of the s6-ipcserver-access rules.
s6-2.13.1.0/doc/s6-supervise.html000066400000000000000000000302501470151141200162660ustar00rootroot00000000000000 s6: the s6-supervise program

s6
Software
skarnet.org

The s6-supervise program

s6-supervise monitors a long-lived process (or service), making sure it stays alive, sending notifications to registered processes when it dies, and providing an interface to control its state. s6-supervise is designed to be the last non-leaf branch of a supervision tree, the supervised process being a leaf.

Interface

     s6-supervise servicedir

s6-supervise's behaviour is approximately the following:

  • s6-supervise changes its current directory to servicedir.
  • It exits 100 if another s6-supervise process is already monitoring this service.
  • It forks and executes the ./run file in the service directory.
  • ./run should be a long-lived process: it can chain load (i.e. exec into other binaries), but should not die. It's the daemon that s6-supervise monitors and manages.
  • When ./run dies, s6-supervise spawns ./finish, if it exists. This script should be short-lived: it's meant to clean up application state, if necessary, that has not been cleaned up by ./run itself before dying.
  • When ./finish dies, s6-supervise spawns ./run again.
  • s6-supervise operation can be controlled by the s6-svc program. It can be sent commands like "restart the service", "bring the service down", etc.
  • s6-supervise normally runs forever. If told to exit by s6-svc, it waits for the service to go down one last time, then exits 0.

For a precise description of s6-supervise's behaviour, check the Detailed operation section below, as well as the service directory page: s6-supervise operation can be extensively configured by the presence of certain files in the service directory.

Options

s6-supervise does not support options, because it is normally not run manually via a command line; it is usually launched by its own supervisor, s6-svscan. The way to tune s6-supervise's behaviour is via files in the service directory.

Readiness notification support

If the service directory contains a valid notification-fd file when the service is started, or restarted, s6-supervise creates and listens to an additional pipe from the service for readiness notification. When the notification occurs, s6-supervise updates the ./supervise/status file accordingly, then sends a 'U' event to ./event.

If the service is logged, i.e. if the service directory has a log subdirectory that is also a service directory, and the s6-supervise process has been launched by that is also s6-svscan, then by default the service's stdout goes into the logging pipe. If you set notification-fd to 1, the logging pipe will be overwritten by the notification pipe, which is probably not what you want. Instead, if your daemon writes a notification message to its stdout, you should set notification-fd to (for instance) 3, and redirect outputs in your run script. For instance, to redirect stderr to the logger and stdout to a notification-fd set to 3, you would start your daemon as fdmove -c 2 1 fdmove 1 3 prog... (in execline), or exec 2>&1 1>&3 3<&- prog... (in shell).

Signals

s6-supervise reacts to the following signals:

  • SIGTERM: bring down the service and exit, as if a s6-svc -xd command had been received
  • SIGHUP: close its own stdin and stdout, and exit as soon as the service stops, as if an s6-svc -x command had been received
  • SIGQUIT: exit immediately without touching the service in any way.
  • SIGINT: send a SIGINT to the process group of the service, then exit immediately. (The point here is to correctly forward SIGINT in the case where s6-supervise is running in a terminal and the user sent ^C to interrupt it.)

Detailed operation

  • s6-supervise switches to the servicedir service directory.
  • It creates a supervise/ subdirectory (if it doesn't exist yet) to store its internal data.
  • It exits 100 if another s6-supervise process is already monitoring this service.
  • If the ./event fifodir does not exist, s6-supervise creates it and allows subscriptions to it from processes having the same effective group id as the s6-supervise process. If it already exists, it uses it as is, without modifying the subscription rights.
  • It sends a 's' event to ./event.
  • If the default service state is up (i.e. there is no ./down file), s6-supervise spawns ./run. One argument is given to the ./run program: servicedir, the name of the directory s6-supervise is being run on. It is given exactly as given to s6-supervise, without recanonicalization. In particular, if s6-supervise is being managed by s6-svscan, servicedir is always of the form foo or foo/log, and foo contains no slashes.
  • s6-supervise sends a 'u' event to ./event whenever it successfully spawns ./run.
  • If there is a ./notification-fd file in the service directory and, at some point after the service has been spawned, s6-supervise is told that the service is ready, it sends a 'U' event to ./event. There are several ways to tell s6-supervise that the service is ready:
  • When ./run dies, s6-supervise sends a 'd' event to ./event. It then spawns ./finish if it exists. ./finish will have ./run's exit code as first argument, or 256 if ./run was signaled; it will have the number of the signal that killed ./run as second argument, or an undefined number if ./run was not signaled; and it will have servicedir as third argument.
  • By default, ./finish must exit in less than 5 seconds. If it takes more than that, s6-supervise kills it with a SIGKILL. This can be configured via the ./timeout-finish file, see the description in the service directory page.
  • When ./finish dies (or is killed), s6-supervise sends a 'D' event to ./event. Then it restarts ./run unless it has been told not to.
  • If ./finish exits 125, then s6-supervise sends a 'O' event to ./event before the 'D', and it does not restart the service, as if s6-svc -O had been called. This can be used to signify permanent failure to start the service.
  • There is a minimum 1-second delay between two ./run spawns, to avoid busylooping if ./run exits too quickly. If the service has been ready for more than one second, it will restart immediately, but if it is not ready when it dies, s6-supervise will always pause for 1 second before spawning it again.
  • When killed or asked to exit, it waits for the service to go down one last time, then sends a 'x' event to ./event before exiting 0.

Make sure to also check the service directory documentation page, for the full list of files that can be present in a service directory and impact s6-supervise's behaviour in any way.

Usage notes

  • s6-supervise is a long-lived process. It normally runs forever, from the system's boot scripts, until shutdown time; it should not be killed or told to exit. If you have no use for a service, just turn it off; the s6-supervise process does not hurt.
  • Even in boot scripts, s6-supervise should normally not be run directly. It's better to have a collection of service directories in a single scan directory, and just run s6-svscan on that scan directory. s6-svscan will spawn the necessary s6-supervise processes, and will also take care of logged services.
  • s6-supervise always spawns its child in a new session, as a session leader. The goal is to protect the supervision tree from misbehaved services that would send signals to their whole process group. Nevertheless, s6-supervise's handling of SIGINT ensures that its service is killed if you happen to run it in a terminal and send it a ^C.
  • You can use s6-svc to send commands to the s6-supervise process; mostly to change the service state and send signals to the monitored process.
  • You can use s6-svok to check whether s6-supervise is successfully running.
  • You can use s6-svstat to check the status of a service.
  • s6-supervise maintains internal information inside the ./supervise subdirectory of servicedir. servicedir itself can be read-only, but both servicedir/supervise and servicedir/event need to be read-write.
  • If servicedir isn't writable by s6-supervise, for any reason, then the s6-svc -D and -U commands will not work properly since s6-supervise will be unable to create or delete a servicedir/down file; in this case s6-supervise will print a warning on stderr, and perform the equivalent of -d or -u instead — it will just be unable to change the permanent service configuration.

Implementation notes

  • s6-supervise tries its best to stay alive and running despite possible system call failures. It will write to its standard error everytime it encounters a problem. However, unlike s6-svscan, it will not go out of its way to stay alive; if it encounters an unsolvable situation, it will just die.
  • Unlike other "supervise" implementations, s6-supervise is a fully asynchronous state machine. That means that it can read and process commands at any time, even when the machine is in trouble (full process table, for instance).
  • s6-supervise does not use malloc(). That means it will never leak memory. However, s6-supervise uses opendir(), and most opendir() implementations internally use heap memory - so unfortunately, it's impossible to guarantee that s6-supervise does not use heap memory at all.
  • s6-supervise has been carefully designed so every instance maintains as little data as possible, so it uses a very small amount of non-sharable memory. It is not a problem to have several dozens of s6-supervise processes, even on constrained systems: resource consumption will be negligible.
s6-2.13.1.0/doc/s6-svc.html000066400000000000000000000232521470151141200150400ustar00rootroot00000000000000 s6: the s6-svc program

s6
Software
skarnet.org

The s6-svc program

s6-svc sends commands to a running s6-supervise process. In other words, it's used to control a supervised process; among other benefits, it allows an administrator to send signals to daemons without knowing their PIDs, and without using horrible hacks such as .pid files.

Interface

     s6-svc [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -s signal | -abqhkti12pcyrPCK ] [ -oduDUxO ] servicedir

s6-svc sends the given series of commands to the s6-supervise process monitoring the servicedir directory, then exits 0. It exits 111 if it cannot send a command, or 100 if no s6-supervise process is running on servicedir.

Options

  • -a : send a SIGALRM to the supervised process
  • -b : send a SIGABRT to the supervised process
  • -q : send a SIGQUIT to the supervised process
  • -h : send a SIGHUP to the supervised process
  • -k : send a SIGKILL to the supervised process
  • -t : send a SIGTERM to the supervised process
  • -i : send a SIGINT to the supervised process
  • -1 : send a SIGUSR1 to the supervised process
  • -2 : send a SIGUSR2 to the supervised process
  • -p : send a SIGSTOP to the supervised process
  • -c : send a SIGCONT to the supervised process
  • -y : send a SIGWINCH to the supervised process
  • -s signal : send signal to the supervised process. signal can be given as its name (case- insensitive) or its number, but only the signals listed above are accepted - you cannot, for instance, manually send a SIGSEGV to the supervised process.

  • -o : once. Equivalent to "-uO".
  • -d : down. If the supervised process is up, send it a SIGTERM (by default) then a SIGCONT (to make sure even stopped processes receive the signal aimed to kill them) and do not restart it. The SIGTERM default can be changed by editing the ./down-signal file in the service directory.
  • -D : down, and create a ./down file so the service does not restart automatically if the supervisor dies. This option is mostly used by automated systems working on top of s6; as a human user, you probably don't need it.
  • -u : up. If the supervised process is down, start it. Automatically restart it when it dies.
  • -U : up, and remove any ./down file that may exist, in order to make sure the service is automatically restarted even if the supervisor dies. This option is mostly used by automated systems working on top of s6; as a human user, you probably don't need it.
  • -x : exit. When the service is asked to be down and the supervised process dies, s6-supervise will exit too. This command should normally never be used on a working system. Note that if this command is sent and a ./finish script exists for the service, the last ./finish invocation before s6-supervise exits will run with its stdin and stdout redirected to /dev/null.
  • -O : mark the service to run once at most. iow: do not restart the supervised process when it dies. If it is down when the command is received, do not even start it.
  • -Q : once at most, and create a ./down file. Like -D, but do not terminate the service if it is currently running.
  • -r : If the service is up, restart it, by sending it a signal to kill it and letting s6-supervise start it again. By default, the signal is a SIGTERM; this can be configured via the ./down-signal file in the service directory.
  • -T timeout : if the -wstate option has been given, -T specifies a timeout (in milliseconds) after which s6-svc will exit 1 with an error message if the service still hasn't reached the desired state. By default, the timeout is 0, which means that s6-svc will block indefinitely.
  • -wd : s6-svc will not exit until the service is down, i.e. until the run process has died.
  • -wD : s6-svc will not exit until the service is down and ready to be brought up, i.e. a possible finish script has exited.
  • -wu : s6-svc will not exit until the service is up, i.e. there is a process running the run executable.
  • -wU : s6-svc will not exit until the service is up and ready as notified by the daemon itself. If the service directory does not contain a notification-fd file to tell s6-supervise to accept readiness notification, s6-svc will print a warning and act as if the -wu option had been given instead.
  • -wr : s6-svc will not exit until the service has been started or restarted.
  • -wR : s6-svc will not exit until the service has been started or restarted and has notified readiness.

  • -P : send a SIGSTOP to the process group of the supervised process
  • -C : send a SIGCONT to the process group of the supervised process
  • -K : send a SIGKILL to the process group of the supervised process

Usage examples

 s6-svc -h /service/httpd 

Send a SIGHUP to the process represented by the /service/httpd service directory. Traditionally, this makes web servers reload their configuration file.

 s6-svc -r /service/sshd 

Kill (and automatically restart, if the wanted state of the service is up) the process represented by the /service/sshd service directory - typically the sshd server.

 s6-svc -wD -d /service/ftpd 

Take down the ftpd server and block until the process is down and the finish script has completed.

 s6-svc -wU -T 5000 -u /service/ftpd 

Bring up the ftpd server and block until it has sent notification that it is ready. Exit 1 if it is still not ready after 5 seconds.

 s6-svc -wR -t /service/ftpd 

Send a SIGTERM to the ftpd server; wait for s6-supervise to restart it, and block until it has notified that it is ready to serve again. See the NOTES section below for a caveat.

 s6-svc -a /service/httpd/log 

Send a SIGALRM to the logger process for the httpd server. If this logger process is s6-log, this triggers a log rotation.

Internals

  • s6-svc writes control commands into the servicedir/supervise/control FIFO. An s6-supervise process running on servicedir will be listening to this FIFO, and will read and interpret those commands.
  • When invoked with one of the -w options, s6-svc executes into s6-svlisten1, which will listen to service state changes and spawn another s6-svc instance (without the -w option) that will send the commands to the service. Any error message written during the waiting period will mention it is being written by s6-svlisten1; this is normal.

Notes

  • The -t and -r options make s6-supervise send a signal to the service if it is up; but if the service is currently down, they do nothing, and in particular they do not instruct s6-supervise to bring the service up. Consequently, s6-svc -rwr servicedir may wait forever for the service to be up, if it is currently wanted down. To avoid that, make sure your service is wanted up by using s6-svc -ruwr servicedir instead.
  • The U and D letters, which convey the same idea as u and d (up and down) but with added emphasis, do not have the same meaning in the -U/-D and -wU/-wD options. In the -U/-D case, they mean "change the external service configuration to match what the supervisor has been instructed that the starting state of the service should be". In the -wU/-wD case, they mean "wait until the service has reached the wanted state and also is ready" (or "ready to be started again" for -wD). The thing to remember is "it's up/down, with something more", but the "something" isn't the same.
s6-2.13.1.0/doc/s6-svdt-clear.html000066400000000000000000000033311470151141200163050ustar00rootroot00000000000000 s6: the s6-svdt-clear program

s6
Software
skarnet.org

The s6-svdt-clear program

s6-svdt-clear clears the recorded death tally of a service.

Interface

     s6-svdt-clear servicedir

s6-svdt-clear clears the recorded death tally of the service being currently supervised at the servicedir service directory.

  • 0: success
  • 100: wrong usage
  • 111: system call failed

Notes

  • Use of s6-svdt-clear impacts the listings obtained by the s6-svdt command.
  • It also impacts the behaviour of the s6-permafailon command. This is the main reason to use s6-svdt-clear: once a service has failed permanently due to an excessive number of deaths in a given time, it can be useful to erase that record of deaths before starting the service again, in order to avoid permanently failing again too fast.
s6-2.13.1.0/doc/s6-svdt.html000066400000000000000000000047231470151141200152270ustar00rootroot00000000000000 s6: the s6-svdt program

s6
Software
skarnet.org

The s6-svdt program

s6-svdt prints the recorded death tally of a service, i.e. a list of the times the process died, with the cause of death.

Interface

     s6-svdt [ -S | -s ] [ -n maxlines ] servicedir

s6-svdt prints the contents of the recorded death tally of the service being currently supervised at the servicedir service directory, then exits 0.

For each recorded death, s6-svdt prints one line. This line contains the following fields, separated with spaces:

  • A TAI64N timestamp .
  • The word "signal" if the death was caused by a signal, or the word "exitcode" if the death was a normal exit.
  • The name of the signal that caused the death, or the exit code of the process.

Options

  • -S : print signal names. This is the default.
  • -s : print signal numbers. The numerical value of the signal will be printed instead of the signal name.
  • -n maxlines : limit the output to at most the latest maxlines deaths.

Exit codes

  • 0: success
  • 100: wrong usage
  • 111: system call failed

Notes

  • To obtain human-readable local time or GMT time instead of TAI64N timestamps, simply pipe s6-svdt's output into s6-tai64nlocal.
  • Process deaths are recorded up to a default maximum of 100. This default can be modified via the max-death-tally file in the service directory.
s6-2.13.1.0/doc/s6-svlink.html000066400000000000000000000125521470151141200155540ustar00rootroot00000000000000 s6: the s6-svlink program

s6
Software
skarnet.org

The s6-svlink program

s6-svlink creates, in a scan directory, a symlink to a service directory, and notifies s6-svscan that a new service has been registered. It waits until a s6-supervise supervisor process has been spawned to manage the new service, then exits.

The point of s6-svlink is to help integrate service directories into an existing service manager sequence and eliminate race conditions.

Interface

     s6-svlink [ -d | -D ] [ -P ] [ -f ] [ -t timeout ] scandir servicedir [ name ]
  • s6-svlink expects a running s6-svscan process on scandir and a fully functional, but unsupervised, service directory in servicedir.
  • It symlinks servicedir into scandir. The symbolic link is named name; if no name argument has been given, the name given to the symbolic link is the basename of servicedir.
  • It sends a command to s6-svscan to signal it that a new service is available.
  • It waits for an s6-supervise process to be spawned on servicedir.
  • It exits 0.

Exit codes

  • 0: success
  • 99: timeout while waiting for the supervisor to start
  • 100: wrong usage
  • 111: system call failed

Options

  • -d : down. The supervisor will be started, but the service itself will remain down. Any servicedir/down file will be deleted. By default, if neither the -d nor -D options have been given, the supervisor auto-starts the service as soon as it runs.
  • -D : down, and stay down. The supervisor will be started, but the service itself will remain down. A servicedir/down file will be created. By default, if neither the -d nor -D options have been given, the supervisor auto-starts the service as soon as it runs.
  • -P : public. If servicedir/event does not exist, it will be created as public, i.e. anyone will be able to subscribe to this fifodir. By default, it will be created as private, i.e. only processes running with the same gid as the s6-svscan process will be able to susbscribe to it.
  • -f : force permissions. The presence or absence of the -P option (i.e. the public or private state of servicedir/event) will be enforced even if servicedir/event already exists. By default, s6-svlink exits with an error message if servicedir/event exists and its public/private state mismatches what is requested.
  • -t timeout : if the supervisor has not started after timeout milliseconds, s6-svlink will print a message to stderr and exit 99. By default, timeout is 0, which means no time limit.

Notes

  • Using s6-svlink to start services is a suboptimal pattern: it requires precise manipulations involving use of s6-ftrigrd in order to avoid race conditions, so it is relatively expensive. The simpler, more efficient pattern is to have all the supervisors already started at boot time, so the existence of the supervisor can be relied on, and starting the service becomes a trival and instant operation - this is, for instance, how the s6-rc service manager behaves. However, it can be difficult to implement this pattern with other services managers such as OpenRC; in those cases, s6-svlink, which starts the supervisors one at a time, can be used instead.
  • If servicedir is logged, i.e. servicedir/log is also a valid service directory, then s6-svlink will wait until supervisors have been spawned for both the service and its logger.
  • s6-svlink sends an s6-svscanctl -a command to scandir, which means that the system's view of services will be refreshed. Depending on what links exist in scandir, other services than servicedir may also appear.
  • The symmetrical program to s6-svlink is named s6-svunlink.
s6-2.13.1.0/doc/s6-svlisten.html000066400000000000000000000121261470151141200161120ustar00rootroot00000000000000 s6: the s6-svlisten program

s6
Software
skarnet.org

The s6-svlisten program

s6-svlisten runs a program while listening on notifications from a collection of supervised services, and blocks until they all go up, or down.

s6-svlisten only waits for notifications; it never polls.

Interface

In an execline script:

     s6-svlisten [ -U | -u | -D | -d | -r | -R ] [ -a | -o ] [ -t timeout ] { servicedir servicedir... } prog...

Outside of an execline script:

     s6-svlisten [ -U | -u | -D | -d | -r | -R ] [ -a | -o ] [ -t timeout ] servicedir servicedir... "" prog...
  • s6-svlisten checks the state of one or more service directories given as arguments in the first block and monitor their state changes.
  • It spawns prog... as a child right after getting the initial state of all the monitored services.
  • It then blocks until the wanted state happens.
  • If no service directories are listed (the block is empty), then instead of doing all that, it immediately execs into prog....

Exit codes

  • 0: success, the wanted state has been reached
  • 99: timed out
  • 100: wrong usage
  • 102: the s6-supervise process monitoring the service died
  • 111: system call failed
  • n: services were expected to come up, but n of them reported permanent failure

Options

  • -u : up. s6-svlisten will wait until the services are up, as reported by s6-supervise. This is the default; it is not reliable, but it does not depend on specific support in the service programs. See this page for details.
  • -U : really up. s6-svlisten will wait until the services are up and ready as reported by the services themselves. This requires specific support in the service programs, and the use of the notification-fd file in the service directory. See the explanation on this page.
  • -d : down. s6-svlisten will wait until the services are down.
  • -D : really down. s6-svlisten will wait until the services are down and the cleanup scripts in servicedir/finish for every servicedir have finished executing (or have timed out and been killed).
  • -r : restart. s6-svlisten will wait until all the services have been started or restarted, i.e. they have been in the down state, then the up state.
  • -R : restart and ready. s6-svlisten will wait until all the services have been started or restarted and have notified readiness.
  • -o : or. s6-svlisten will wait until one of the given services comes up or down.
  • -a : and. s6-svlisten will wait until all of the given services come up or down. This is the default.
  • -t timeout : if the requested events have not happened after timeout milliseconds, s6-svlisten will print a message to stderr and exit 99. By default, timeout is 0, which means no time limit.

Notes

  • s6-svlisten is the service-specific version of s6-ftrig-listen. The point of s6-svlisten is to use it to spawn a program such as s6-svc, in order to send signals to services while making sure to catch their state changes - thus avoiding the race condition that occurs when running s6-svc then s6-svwait sequentially.
  • s6-svlisten needs to handle a variable length list of service directories. For that, it uses an encoding provided by execline, so it's best to only use it in execline scripts (only the execline syntax is guaranteed not to change). There is a variant of s6-svlisten that does not use execline syntax, but only handles one service directory: s6-svlisten1.
  • The -R or -r options imply the -a option. It is not possible to wait for one of the listed services to restart.
s6-2.13.1.0/doc/s6-svlisten1.html000066400000000000000000000077551470151141200162070ustar00rootroot00000000000000 s6: the s6-svlisten1 program

s6
Software
skarnet.org

The s6-svlisten1 program

s6-svlisten1 runs a program while listening on notifications from a supervised service, and blocks until said service goes up, or down.

s6-svlisten1 only waits for notifications; it never polls.

Interface

     s6-svlisten1 [ -U | -u | -D | -d | -r | -R ] [ -t timeout ] servicedir prog...
  • s6-svlisten1 checks the state of the servicedir service directory and monitor its state changes.
  • It spawns prog... as a child right after getting the initial state of the service.
  • It then blocks until the wanted state happens.

Exit codes

  • 0: success, the wanted state has been reached
  • 1: the service was supposed to go up, but reported permanent failure
  • 99: timed out
  • 100: wrong usage
  • 102: the s6-supervise process monitoring the service died
  • 111: system call failed

Options

  • -u : up. s6-svlisten1 will wait until the service is up, as reported by s6-supervise. This is the default; it is not reliable, but it does not depend on specific support in the service programs. See this page for details.
  • -U : really up. s6-svlisten1 will wait until the service is up and ready as reported by the daemon itself. This requires specific support in the service programs, and the use of the notification-fd file in the service directory. See the explanation on this page.
  • -d : down. s6-svlisten1 will wait until the service is down.
  • -D : really down. s6-svlisten1 will wait until the service is down and the cleanup script in servicedir/finish has finished executing (or has timed out and been killed).
  • -r : restart. s6-svlisten1 will wait until the service has been started or restarted, i.e. they have been in the down state, then the up state.
  • -R : restart and ready. s6-svlisten1 will wait until the service has been started or restarted and has notified readiness.
  • -t timeout : if the requested event has not happened after timeout milliseconds, s6-svlisten1 will print a message to stderr and exit 99. By default, timeout is 0, which means no time limit.

Notes

  • s6-svlisten1 is the service-specific version of s6-ftrig-listen1. The point of s6-svlisten1 is to use it to spawn a program such as s6-svc, in order to send signals to a service while making sure to catch its state changes - thus avoiding the race condition that occurs when running s6-svc then s6-svwait sequentially.
  • The s6-svlisten program is an extension of s6-svlisten1. It can watch the state of several services at once; however, its syntax makes it best used in execline scripts only.
s6-2.13.1.0/doc/s6-svok.html000066400000000000000000000021311470151141200152200ustar00rootroot00000000000000 s6: the s6-svok program

s6
Software
skarnet.org

The s6-svok program

s6-svok checks whether a service directory is currently supervised.

Interface

     s6-svok servicedir
  • s6-svok checks whether an s6-supervise process is currently monitoring servicedir.
  • It exits 0 if there is one, or 1 if there is none.
s6-2.13.1.0/doc/s6-svperms.html000066400000000000000000000130011470151141200157330ustar00rootroot00000000000000 s6: the s6-svperms program

s6
Software
skarnet.org

The s6-svperms program

s6-svperms allows the user to see, or modify, for a given list of services: who can read their states, who can send them control commands, and who can subscribe to up/down events for those services.

Interface

     s6-svperms [ -v ] [ -u | -g group | -G group | -o | -O group ] [ -e | -E group ] servicedirs...

Without options, or with only the -v option, s6-svperms prints 3 lines to stdout for every service directory listed in servicedirs. Every line contains the name of the service directory, then the following information:

  • status: - indicates who is allowed to read status information on the service, with commands such as s6-svstat or s6-svdt. The values can be owner, for only the owner of the service; group: name, for the owner and members of group name; or public, for all users.
  • control: - indicates who is allowed to send control commands to the service, with commands such as s6-svc. The values can be owner, for only the owner of the service; or group: name, for the owner and members of group name.
  • events: - indicates who is allowed to subscribed to events sent by s6-supervise for this service, with commands such as s6-svwait or s6-svlisten1. The values can be group: name, for the owner and members of group name, or public, for all users.

If something goes wrong while reading a part of the configuration of a service directory, s6-svperms does not print the corresponding line to stdout; instead, it prints a warning message to stderr.

When invoked with other options, s6-svperms modifies the permissions of the service directories listed in servicedirs... as specified by the options. The same permissions will be applied to all the services listed in servicedirs....

Options

  • -v : re-read the permissions after writing them, and print them to stdout.
  • -u : restrict the status: and control: permissions to owner: only the owner of a service directory will be able to read its state or control the service. This is the default when s6-supervise starts a service for the first time.
  • -g group : allow members of group group to read the status of the service, but not to control it - control will be restricted to the owner.
  • -G group : allow members of group group to read and control the service.
  • -o : allow everyone to read the status of the service, but restrict control: to the owner.
  • -O group : allow everyone to read the status, and allow members of group group to control the service.
  • -e : allow everyone to subscribe to events.
  • -E group : only allow members of group group to subscribe to events. This is the default when s6-supervise starts a service for the first time, with group being the primary group of the s6-supervise process (most likely root).

group is normally a group name that will be searched in the group database. But if it starts with a colon (:), the rest of group will be interpreted as a numerical gid, and the group database will not be read.

Exit codes

  • 0: success
  • 1: something went wrong when reading permissions in one of the service directories
  • 100: wrong usage
  • 111: system call failed

Notes

  • The default (restrictive) permissions are safe.
  • Unless operation of a service is restricted information, it is also safe to make status: more permissive.
  • Opening control: to a group can be useful for instance in a shared administration situation when individual administrators are not given full root powers.
  • Making events: public bears a small risk of a local DoS attack preventing more subscriptions to events, so it is not recommended for supervision trees where such subscriptions are critical to operations - such as a set of root services managed by s6-rc.
s6-2.13.1.0/doc/s6-svscan-1.html000066400000000000000000000460471470151141200157070ustar00rootroot00000000000000 s6: How to run s6-svscan as process 1

s6
Software
skarnet.org

How to run s6-svscan as process 1

Since 2015-06-17, if you're a Linux user, you can use the s6-linux-init package to help you do so! Please read this documentation page first, though, it will help you understand what s6-linux-init does.

It is possible to run s6-svscan as process 1, i.e. the init process. However, that does not mean you can directly boot on s6-svscan; that little program cannot do everything your stock init does. Replacing the init process requires a bit of understanding of what is going on.

The three stages of init

Okay, it's actually four, but the fourth stage is an implementation detail that users don't care about, so we'll stick with three.

The life of a Unix machine has three stages. Yes, three.

  1. The early initialization phase. It starts when the kernel launches the first userland process, traditionally called init. During this phase, init is the only lasting process; its duty is to prepare the machine for the start of other long-lived processes, i.e. services. Work such as mounting filesystems, setting the system clock, etc. can be done at this point. This phase ends when process 1 launches its first services.
  2. The cruising phase. This is the "normal", stable state of an up and running Unix machine. Early work is done, and init launches and maintains services, i.e. long-lived processes such as gettys, the ssh server, and so on. During this phase, init's duties are to reap orphaned zombies and to supervise services - also allowing the administrator to add or remove services. This phase ends when the administrator requires a shutdown.
  3. The shutdown phase. Everything is cleaned up, services are stopped, filesystems are unmounted, the machine is getting ready to be halted. At the end of this phase, all processes are killed, first with a SIGTERM, then with a SIGKILL (to catch processes that resist SIGTERM). The only processes that survive it are process 1; if this process is s6-svscan and its scandir is not empty, then the supervision tree is restarted.
  4. The hardware shutdown phase. The system clock is stored, filesystems are unmounted, and the system call that reboots the machine or powers it off is called.

Unless you're implementing a shutdown procedure over a supervision tree, you can absolutely consider that the hardware shutdown is part of stage 3.

As you can see, process 1's duties are radically different from one stage to the next, and init has the most work when the machine is booting or shutting down, which means a normally negligible fraction of the time it is up. The only common thing is that at no point is process 1 allowed to exit.

Still, all common init systems insist that the same init executable must handle these three stages. From System V init to launchd, via busybox init, you name it - one init program from bootup to shutdown. No wonder those programs, even basic ones, seem complex to write and complex to understand!

Even the runit program, designed with supervision in mind, remains as process 1 all the time; at least runit makes things simple by clearly separating the three stages and delegating every stage's work to a different script that is not run as process 1. (Since runit does not distinguish between stage 3 and stage 4, it needs very careful handling of the kill -9 -1 part of stage 3: getting /etc/runit/3 killed before it unmounts the filesystems would be bad.)

One init to rule them all? It ain't necessarily so!

The role of s6-svscan

init does not have the right to die, but fortunately, it has the right to execve()! During stage 2, why use precious RAM, or at best, swap space, to store data that are only relevant to stages 1 or 3-4? It only makes sense to have an init process that handles stage 1, then executes into an init process that handles stage 2, and when told to shutdown, this "stage 2" init executes into a "stage 3" init which just performs shutdown. Just as runit does with the /etc/runit/[123] scripts, but exec'ing the scripts as process 1 instead of forking them.

It becomes clear now that s6-svscan is perfectly suited to exactly fulfill process 1's role during stage 2.

  • It does not die
  • The reaper takes care of every zombie on the system
  • The scanner maintains services alive
  • It can be sent commands via the s6-svscanctl interface
  • It execs into a given script when told to

However, an init process for stage 1 and another one for stage 3 are still needed. Fortunately, those processes are very easy to design! The only difficulty here is that they're heavily system-dependent, so it's not possible to provide a stage 1 init and a stage 3 init that will work everywhere. s6 was designed to be as portable as possible, and it should run on virtually every Unix platform; but outside of stage 2 is where portability stops.

The s6-linux-init package provides a tool, s6-linux-init-maker, to automatically create a suitable stage 1 init (so, the /sbin/init binary) for Linux. It is also possible to write similar tools for other operating systems, but the details are heavily system-dependent.

For the adventurous and people who need to do this by hand, though, here are are some general design tips.

How to design a stage 1 init

What stage 1 init must do

  • Prepare an initial scan directory, say in /run/service, with a few vital services, such as s6-svscan's own logger, and an early getty (in case debugging is needed). That implies mounting a read-write filesystem, creating it in RAM if needed, if the root filesystem is read-only.
  • Either perform all the one-time initialization, as stage 1 runit does;
  • or fork a process that will perform most of the one-time initialization once s6-svscan is in charge.
  • Be extremely simple and not fail, because recovery is almost impossible here.

Unlike the /etc/runit/1 script, an init-stage1 script running as process 1 has nothing to back it up, and if it fails and dies, the machine crashes. Does that mean the runit approach is better? It's certainly safer, but not necessarily better, because init-stage1 can be made extremely small, to the point it is practically failproof, and if it fails, it means something is so wrong that you would have had to reboot the machine with init=/bin/sh anyway.

To make init-stage1 as small as possible, only this realization is needed: you do not need to perform all of the one-time initialization tasks before launching s6-svscan. Actually, once init-stage1 has made it possible for s6-svscan to run, it can fork a background "init-stage2" process and exec into s6-svscan immediately! The "init-stage2" process can then pursue the one-time initialization, with a big advantage over the "init-stage1" process: s6-svscan is running, as well as a few vital services, and if something bad happens, there's a getty for the administrator to log on. No need to play fancy tricks with /dev/console anymore! Yes, the theoretical separation in 3 stages is a bit more flexible in practice: the "stage 2" process 1 can be already running when a part of the "stage 1" one-time tasks are still being run.

Of course, that means that the scan directory is still incomplete when s6-svscan first starts, because most services can't yet be run, for lack of mounted filesystems, network etc. The "init-stage2" one-time initialization script must populate the scan directory when it has made it possible for all wanted services to run, and trigger the scanner. Once all the one-time tasks are done, the scan directory is fully populated and the scanner has been triggered, the machine is fully operational and in stage 2, and the "init-stage2" script can die.

Is it possible to write stage 1 init in a scripting language?

It is very possible, and if you are attempting to write your own stage 1, I definitely recommend it. If you are using s6-svscan as stage 2 init, stage 1 init should be simple enough that it can be written in any scripting language you want, just as /etc/runit/1 is if you're using runit. And since it should be so small, the performance impact will be negligible, while maintainability is enhanced. Definitely make your stage 1 init a script.

Of course, most people will use the shell as scripting language; however, I advocate the use of execline for this, and not only for the obvious reasons. Piping s6-svscan's stderr to a logging service before said service is even up requires some tricky fifo handling that execline can do and the shell cannot.

How to design a stage 3-4 init

If you're using s6-svscan as stage 2 init on /run/service, then stage 3 init is naturally the /run/service/.s6-svscan/finish program. Of course, /run/service/.s6-svscan/finish can be a symbolic link to anything else; just make sure it points to something in the root filesystem (unless your program is an execline script, in which case it is not even necessary).

What stage 3-4 init must do

  • Destroy the supervision tree and stop all services
  • Kill all processes save itself, first gently, then harshly, and reap all the zombies.
  • Up until that point we were in stage 3; now we're in stage 4.
  • Unmount all the filesystems
  • Halt or reboot the machine, depending on what root asked for

This is seemingly very simple, even simpler than stage 1, but experience shows that it's trickier than it looks.

One tricky part is the kill -9 -1 operation at the end of stage 3: you must make sure that process 1 regains control and keeps running after it, because it will be the only process left alive. If you are running a stage 3 script as process 1, it is almost automatic: your script survives the kill and continues running, up into stage 4. If you are using another model, the behaviour becomes system-dependent: your script may or may not survive the kill, so on systems where it does not, you will have to design a way to regain control in order to accomplish stage 4 tasks.

Another tricky part, that is only apparent with practice, is solidity. It is even more vital that nothing fails during stages 3 and 4 than it is in stage 1, because in stage 1, the worst that can happen is that the machine does not boot, whereas in stages 3 and 4, the worst that can happen is that the machine does not shut down, and that is a much bigger issue.

For these reasons, I now recommend not tearing down the supervision tree for stages 3-4. It is easier to work in a stable environment, as a regular process, than it is to manage a whole shutdown sequence as pid 1: the presence of s6-svscan as pid 1, and of a working supervision tree, is a pillar you can rely on, and with experience I find it a good idea to keep the supervision infrastructure running until the end. Of course, that requires the scandir, and the active supervision directories, to be on a RAM filesystem such as tmpfs; that is good policy anyway.

Is it possible to write stage 3 init in a scripting language?

Yes, definitely, just like stage 1.

However, you really should leave /run/service/.s6-svscan/finish (and the other scripts in /run/service/.s6-svscan) alone, and write your shutdown sequence without dismantling the supervision tree. You will still have to stop most of the services, but s6-svscan should stay. For a more in-depth study of what to do in stages 3-4 and how to do it, you can look at the source of s6-linux-init-shutdownd in the s6-linux-init package.

How to log the supervision tree's messages

When the Unix kernel launches your (stage 1) init process, it does it with descriptors 0, 1 and 2 open and reading from or writing to /dev/console. This is okay for the early boot: you actually want early error messages to be displayed to the system console. But this is not okay for stage 2: the system console should only be used to display extremely serious error messages such as kernel errors, or errors from the logging system itself; everything else should be handled by the logging system, following the logging chain mechanism. The supervision tree's messages should go to the catch-all logger instead of the system console. (And the console should never be read, so no program should run with /dev/console as stdin, but this is easy enough to fix: s6-svscan will be started with stdin redirected from /dev/null.)

The catch-all logger is a service, and we want every service to run under the supervision tree. Chicken and egg problem: before starting s6-svscan, we must redirect s6-svscan's output to the input of a program that will only be started once s6-svscan is running and can start services.

There are several solutions to this problem, but the simplest one is to use a FIFO, a.k.a. named pipe. s6-svscan's stdout and stderr can be redirected to a named pipe before s6-svscan is run, and the catch-all logger service can be made to read from this named pipe. Only two minor problems remain:

  • If s6-svscan or s6-supervise writes to the FIFO before there is a reader, i.e. before the catch-all logging service is started, the write will fail (and a SIGPIPE will be emitted). This is not a real issue for an s6 installation because s6-svscan and s6-supervise ignore SIGPIPE, and they only write to their stderr if an error occurs; and if an error occurs before they are able to start the catch-all logger, this means that the system is seriously damaged (as if an error occurs during stage 1) and the only solution is to reboot with init=/bin/sh anyway.
  • Normal Unix semantics do not allow a writer to open a FIFO before there is a reader: if there is no reader when the FIFO is opened for writing, the open() system call blocks until a reader appears. This is obviously not what we want: we want to be able to actually start s6-svscan with its stdout and stderr pointing to the logging FIFO, even without a reader process, and we want it to run normally so it can start the logging service that will provide such a reader process.

This second point cannot be solved in a shell script, and that is why you are discouraged to write your stage 1 init script in the shell language: you cannot properly set up a FIFO output for s6-svscan without resorting to horrible and unreliable hacks involving a temporary background FIFO reader process.

Instead, you are encouraged to use the execline language - or, at least, the redirfd command, which is part of the execline distribution. The redirfd command does just the right amount of trickery with FIFOs for you to be able to properly redirect process 1's stdout and stderr to the logging FIFO without blocking: redirfd -w 1 /run/service/s6-svscan-log/fifo blocks if there's no process reading on /run/service/s6-svscan-log/fifo, but redirfd -wnb 1 /run/service/s6-svscan-log/fifo does not.

This trick with FIFOs can even be used to avoid potential race conditions in the one-time initialization script that runs in stage 2. If forked from init-stage1 right before executing s6-svscan, depending on the scheduler mood, this script may actually run a long way before s6-svscan is actually executed and running the initial services - and may do dangerous things, such as writing messages to the logging FIFO before there's a reader, and eating a SIGPIPE and dying without completing the initialization. To avoid that and be sure that s6-svscan really runs and initial services are really started before the stage 2 init script is allowed to continue, it is possible to redirect the child script's output (stdout and/or stderr) once again to the logging FIFO, but in the normal way without redirfd trickery, before it execs into the init-stage2 script. So, the child process blocks on the FIFO until a reader appears, while process 1 - which does not block - execs into s6-svscan and starts the logging service, which then opens the logging FIFO for reading and unblocks the child process, which then runs the initialization tasks with the guarantee that s6-svscan is running.

It really is simpler than it sounds. :-)

A working example

This whole page may sound very theoretical, dry, wordy, and hard to grasp without a live example to try things on; unfortunately, s6 cannot provide live examples without becoming system-specific.

However, the s6-linux-init package provides you with the s6-linux-init-maker command, which produces a set of working scripts, including a script that is suitable as /sbin/init, for you to study and edit. You can run the s6-linux-init-maker command even on non-Linux systems: it will produce scripts that do not work as is for another OS, but can still be used for study and as a basis for a working stage 1 script.

s6-2.13.1.0/doc/s6-svscan-not-1.html000066400000000000000000000114171470151141200164760ustar00rootroot00000000000000 s6: How to run s6-svscan under another init process

s6
Software
skarnet.org

How to run s6-svscan under another init process

You can have a reliable supervision tree even if s6-svscan is not your process 1. The supervision tree just has to be rooted in process 1: that means that your process 1 will have to supervise your s6-svscan process somehow. That way, if s6-svscan dies, it will be restarted, and your set of services will always be maintained.

Be aware, though, that pipes between services and loggers are maintained by the s6-svscan process; if this process dies, the pipes will be closed and some logs may be lost.

Logging the supervision tree's output

s6-svscan and the various s6-supervise processes might produce error or warning messages; those messages are written to s6-svscan's stderr (which is inherited by the s6-supervise processes). To log these messages:

  • You can use your init system's logging tools, and make your init system launch s6-svscan as is; its stderr should already be taken care of by the logging tools.
  • You can use a trick similar to the process 1 output logging trick so the supervision tree's messages are logged via a service that's maintained by the supervision tree itself. Then your init system should not launch s6-svscan directly, but a wrapper script that performs the proper redirections. The examples/s6-svscanboot file in the s6 distribution gives an example of such a script. Make sure that your initial scan directory contains a service directory for your initial logging service, that must read on the logging FIFO.

In some of the following examples, we'll assume that /command/s6-svscanboot is the name of the script you are using to start s6-svscan. We will also assume that all of the s6 executables are available through the /command path. Adjust this accordingly.

System V init

Put an appropriate line in your /etc/inittab file, then reload this config file with telinit q.

Example

 SV:123456:respawn:/command/s6-svscanboot 

Upstart

Put an appropriate configuration file in the /etc/init folder, for instance /etc/init/s6-svscan.conf, then start the service with start s6-svscan.

Example

# s6-svscan
start on runlevel [2345]
stop on runlevel [!2345]

oom never
respawn
exec /command/s6-svscanboot

systemd

Put an appropriate unit file in the /etc/systemd/system folder, for instance /etc/systemd/system/s6.service. It will be picked up by systemd at boot time.

Example

[Unit]
Description=s6 supervision tree
Documentation=https://skarnet.org/software/s6/

[Install]
WantedBy=multi-user.target

[Service]
Type=simple
ExecStart=/command/s6-svscan /run/service
ExecReload=/command/s6-svscanctl -an /run/service
Restart=always
RestartSec=1
KillMode=mixed

Please note that, among other things, systemd performs process supervision, so depending on the level of integration with your distribution that you wish to achieve, you may be better off using systemd to directly manage your daemons. Please also note that systemd is a terrible piece of software engineering, and if at all possible, you should try and switch to a distribution that does not use it.

BSD init

Put an appropriate line in your /etc/ttys file, then reload this file with kill -s HUP 1.

Example

 sv /command/s6-svscanboot "" on 

MacOS launchd

Like systemd, launchd comes with its own way of supervising services; if you are a launchd user, you probably do not need s6.

s6-2.13.1.0/doc/s6-svscan.html000066400000000000000000000305721470151141200155450ustar00rootroot00000000000000 s6: the s6-svscan program

s6
Software
www.skarnet.org

The s6-svscan program

s6-svscan starts and monitors a collection of s6-supervise processes, each of these processes monitoring a single service. It is designed to be either the root or a branch of a supervision tree.

Interface

     s6-svscan [ -d notif ] [ -X consoleholder ] [ -c max | -C services_max ] [ -L name_max ] [ -t rescan ] [ scandir ]
  • If given a scandir argument, s6-svscan switches to it. Else it uses its current directory as the scan directory.
  • It exits 100 if another s6-svscan process is already monitoring this scan directory.
  • If the ./.s6-svscan control directory does not exist, s6-svscan creates it. However, it is recommended to already have a .s6-svscan subdirectory in your scan directory, because it is used to configure s6-svscan operation, see below.
  • From this point on, s6-svscan never dies. It tries its best to keep control of what's happening. In case of a major system call failure, which means that the kernel or hardware is broken in some fashion, it executes into the .s6-svscan/crash program. (But if that execution fails, s6-svscan exits 111.)
  • s6-svscan performs an initial scan of its scan directory.
  • s6-svscan then occasionally runs scans or reaps, see below.
  • s6-svscan runs until it is told to stop via s6-svscanctl, or an appropriate signal (see below). Then it executes into the .s6-svscan/finish program, if present; if not, it exits 0.

Options

  • -d notif : notify readiness on file descriptor notif. When s6-svscan is ready to accept commands from s6-svscanctl, it will write a newline to notif. notif cannot be lesser than 3. By default, no notification is sent. Please note that using this option signals shallow readiness: s6-svscan being "ready" only means that it is ready to accept commands. It does not mean that all the services it launches at start are themselves ready, or even started, or even that the relevant s6-supervise processes have been started. If you need to test for deep readiness, meaning that all the services in the supervision tree have been started and are ready, you cannot rely on this option.
  • -X consoleholder : assume the output console is available on descriptor consoleholder. If this option is given, and a s6-svscan-log service exists, the s6-supervise process for that service will be run with consoleholder as its standard error. This is mainly useful for a setup done via s6-linux-init, where all error messages go to the s6-svscan-log catch-all logger service by default, except messages from this service itself, which fall back to consoleholder. If you're not sure what to use this option for, or how, you don't need it.
  • -C services_max : maintain services for up to services_max service directories, including loggers. Default is 1000. Lower limit is 4. Upper limit is 160000. If you're increasing this value from the default, please note that:
    • The higher max is, the more stack memory s6-svscan will use, up to 200 bytes per service, also depending on the value of name_max.
    • s6-svscan uses 2 file descriptors per logged service.
    It is the admin's responsibility to make sure that s6-svscan has enough available descriptors to function properly and does not exceed its stack limit. The default of 1000 is safe and provides enough room for every reasonable system.
  • -c max : a deprecated way of setting services_max. If the -c option is given, the value of max is doubled, and the result is used as services_max. The reason for the change is that previous versions of s6-svscan handled services+loggers as a single entity; but this version of s6-svscan handles services and loggers in the same way, so with the default values it's now possible to handle e.g. 600 unlogged services, whereas previously you were limited to 500 because s6-svscan was reserving room for the loggers.
  • -L name_max : the maximum length of a name in the scan directory. Names longer than name_max won't be taken into account. Default is 251. It cannot be set lower than 11 or higher than 1019.
  • -t rescan : perform a scan every rescan milliseconds. If rescan is 0 (the default), automatic scans are never performed after the first one and s6-svscan will only detect new services when told to via a s6-svscanctl -a command. Use of this option is discouraged; it should only be given to emulate the behaviour of other supervision suites. (-t5000 for daemontools' svscan, -t14000 for runit's runsvdir.)

Signals

s6-svscan has special handling for the following signals:

  • SIGCHLD
  • SIGALRM
  • SIGABRT
  • SIGHUP
  • SIGINT
  • SIGTERM
  • SIGQUIT
  • SIGUSR1
  • SIGUSR2
  • SIGPWR (on systems that support it)
  • SIGWINCH (on systems that support it)

Signals that are not in the above list are not caught by s6-svscan and will have the system's default effect.

The behaviour for the first three signals in the list is always fixed:

  • SIGCHLD : trigger the reaper.
  • SIGALRM : trigger the scanner.
  • SIGABRT : immediately exec into .s6-svscan/finish (or exit 0 if that script does not exist), without waiting for any processes to die

The behaviour for the rest of the list is configurable: on receipt of a SIGFOO, s6-svscan will try to run an executable .s6-svscan/SIGFOO file. For instance, a .s6-svscan/SIGTERM executable script will be run on receipt of a SIGTERM. If the file cannot be found, or cannot be executed for any reason, the default behaviour for the signal will be applied. Default behaviours are:

  • SIGHUP : rescan and prune the supervision tree, i.e. make sure that new service directories visible from the scan directory have a s6-supervise process running on them, and instruct s6-supervise processes running on service directories that have become invisible from the scan directory to stop their service and exit. This behaviour can also be achieved via the s6-svscanctl -an scandir command. This is the closest that s6-svscan can get to the traditional "reload your configuration" behaviour.
  • SIGINT : same as SIGTERM below.
  • SIGTERM : Instruct all the s6-supervise processes to stop their service and exit; wait for the whole supervision tree to die, without losing any logs; then exec into .s6-svscan/finish or exit 0. This behaviour can also be achieved via the s6-svscanctl -t scandir command.
  • SIGQUIT : same as SIGTERM above, except that if a service is logged (i.e. there is a foo service and a foo/log service) then the logging service will also be killed, instead of being allowed to exit naturally after its producer has flushed its output and died. This can solve problems with badly written logging programs, but it can also cause loss of logs since the logger may die before the producer has finished flushing everything. The behaviour can also be achieved via the s6-svscanctl -q scandir command; you should only use this if SIGTERM/-t fails to properly tear down the supervision tree.
  • Others: no effect if an appropriate executable file in .s6-svscan/ cannot be run.

The reaper

Upon receipt of a SIGCHLD, or an s6-svscanctl -z command, s6-svscan runs a reaper routine.

The reaper acknowledges (via some wait() function), without blocking, every terminated child of s6-svscan, even ones it does not know it has. This is especially important when s6-svscan is run as process 1.

If the dead child is an s6-supervise process watched by s6-svscan, and the last scan flagged that process as active, then it is restarted one second later.

The scanner

Upon receipt of a SIGALRM or a s6-svscanctl -a command, s6-svscan runs a scanner routine. (It also runs it every rescan milliseconds if the -t option has been given.)

The scanner scans the current directory for subdirectories (or symbolic links to directories), which must be service directories. It skips names starting with dots. It will not create services for more than max subdirectories.

For every new subdirectory dir it finds, the scanner spawns a s6-supervise process on it. If dir/log exists, it spawns an s6-supervise process on both dir and dir/log, and maintains a never-closing pipe from the service's stdout to the logger's stdin. This is starting the service, with or without a corresponding logger. Every service the scanner finds is flagged as "active".

The scanner remembers the services it found. If a service has been started in an earlier scan, but the current scan can't find the corresponding directory, the service is then flagged as inactive. No command is sent to stop inactive s6-supervise processes (unless the administrator uses s6-svscanctl -n or a SIGHUP), but inactive s6-supervise processes will not be restarted if they die.

Notes

  • s6-svscan is designed to run until the machine is shut down. It is also designed as a suitable candidate for process 1. So, it goes out of its way to stay alive, even in dire situations. When it encounters a fatal situation, something it really cannot handle, it executes into .s6-svscan/crash instead of dying; when it is told to exit, it executes into .s6-svscan/finish. Administrators should make sure to design appropriate crash and finish routines.
  • s6-svscan is a fully asynchronous state machine. It will read and process commands at any time, even when the computer is in trouble.
  • s6-svscan does not use malloc(). That means it will never leak memory. However, s6-svscan uses opendir(), and most opendir() implementations internally use heap memory - so unfortunately, it's impossible to guarantee that s6-svscan does not use heap memory at all.
  • Unless run with a nonzero -t option, which is only a legacy feature used to emulate other supervision suites such as daemontools or runit, s6-svscan never polls; it only wakes up on notifications. The s6 supervision tree can be used in energy-critical environments.
s6-2.13.1.0/doc/s6-svscanctl.html000066400000000000000000000071361470151141200162500ustar00rootroot00000000000000 s6: the s6-svscanctl program

s6
Software
skarnet.org

The s6-svscanctl program

s6-svscanctl sends commands to a running s6-svscan process.

Interface

     s6-svscanctl [ -zabhitqnN ] scandir

s6-svscanctl sends the given series of commands to the s6-svscan process monitoring the scandir directory, then exits 0. It exits 111 if it cannot send a command, or 100 if no s6-svscan process is running on scandir.

Options

  • -z : destroy zombies. Immediately triggers s6-svscan's reaper mechanism.
  • -a : Alarm. s6-svscan will immediately perform a scan of scandir to check for services.
  • -b : abort. s6-svscan will exec into its finishing procedure. It will not kill any of the maintained s6-supervise processes, unless a t or q option is also present before the b option in the s6-svscanctl invocation.
  • -h : Reload configuration. s6-svscan will perform a scan, and destroy inactive services. Equivalent to -an.
  • -i : equivalent to -t below.
  • -t : Terminate. s6-svscan will send a SIGTERM to all the s6-supervise processes supervising a service and a SIGHUP to all the s6-supervise processes supervising a logger, then exec into its finish procedure. This means that services will be brought down but loggers will exit naturally on EOF, and s6-svscan will wait for them to exit before exec'ing into .s6-svscan/finish or exiting itself: it's a clean shutdown with no loss of logs.
  • -q : Quit. s6-svscan will send all its s6-supervise processes a SIGTERM, then exec into its finish procedure. This is different from -t in that services and loggers will be forcibly killed, so the quit procedure may be faster but in-flight logs may be lost.
  • -n : nuke. s6-svscan will kill all the s6-supervise processes it has launched but that did not match a service directory last time scandir was scanned, i.e. it prunes the supervision tree so that it matches exactly what was in scandir at the time of the last scan. A SIGTERM is sent to the s6-supervise processes supervising services and a SIGHUP is sent to the s6-supervise processes supervising loggers.
  • -N : Really nuke. Does the same thing as -n, except that SIGTERM is sent to all the relevant s6-supervise processes, even if they are supervising loggers. This is not recommended in a situation where you do not need to tear down the supervision tree.

Internals

s6-svscanctl writes control commands into the scandir/.s6-svscan/control FIFO. An s6-svscan process running on scandir will be listening to this FIFO, and will read and interpret those commands.

s6-2.13.1.0/doc/s6-svstat.html000066400000000000000000000176071470151141200156000ustar00rootroot00000000000000 s6: the s6-svstat program

s6
Software
skarnet.org

The s6-svstat program

s6-svstat prints a short, human-readable or programmatically parsable summary of the state of a process monitored by s6-supervise.

Interface

     s6-svstat [ -uwNrpest | -o up,wantedup,normallyup,ready,paused,pid,pgid,exitcode,signal,signum,updownsince,readysince,updownfor,readyfor ] [ -n ] servicedir

s6-svstat gives information about the process being monitored at the servicedir service directory, then exits 0.

When s6-svstat is invoked without options, or with only the -n option, it prints a human-readable summary of all the available information on the service. In this case, the -n option instructs it to print a signal number (instead of a signal name) if the supervised process was killed by a signal. The summary includes the following data:

  • whether the process is up or down, and if it's up, the number of seconds that it has been up.
  • the process' pid, if it is up, or its last exit code or terminating signal, if it is down
  • what its default state is, if it is different from its current state
  • the number of seconds since it last changed states
  • whether the service is ready, as notified by the daemon itself, and if it is, the number of seconds that it has been. A service reported as down and ready simply means that it is ready to be brought up. A service is down and not ready when it is in the cleanup phase, i.e. the ./finish script is still being executed.

When s6-svstat is invoked with one or several options other than -n, it outputs programmatically parsable information instead. The output is a series of space-separated values, one value per requested field. The valid options are as follows.

Options

  • -o fields : list fields to print. fields is a list of comma-separated field names. The valid field names are the following:
    • up: print true if the service is up and false if it is down.
    • wantedup: print true if s6-supervise is currently instructed to (re)start the service when it is down, and false if s6-supervise is currently instructed to leave the service alone.
    • normallyup: print true if the service is supposed to start when s6-supervise starts (i.e. no ./down file), and false if it is not (i.e. there is a ./down file).
    • ready: print true if the service is ready, and false if it is not. Note that a service can be both down and ready: it simply means that it is ready to be started (i.e. no ./finish script is currently running). To check for service readiness, you should give this option along with up: the service is ready iff s6-svstat -o up,ready prints true true. (The true true case will never happen if the service does not support readiness notification.)
    • paused: print true if the service is paused (i.e. the process is currently stopped) and false if it is not. It is a rare flag, you shouldn't normally need to use this option.
    • pid: print the pid of the supervised process. If the service is currently down, -1 is printed instead.
    • pgid: print the process group of the supervised process. If the service is currently down, print the process group of the last living instance of the service.
    • exitcode: print the exit code of the last execution of ./run. If the service is currently up, or the last ./run process was killed by a signal, -1 is printed instead.
    • signal: print the name of the signal the last ./run process was killed with. If the service is currently up, or the last ./run process was not killed by a signal, NA is printed instead.
    • signum: print the number of the signal the last ./run process was killed with. If the service is currently up, or the last ./run process was not killed by a signal, -1 is printed instead.
    • updownsince: print a TAI64N label representing the absolute date when the service last changed states.
    • readysince: print a TAI64N label representing the absolute date when the service last became ready. Note that this can either mean "service readiness" (if the service is currently up and ready), or "down readiness", i.e. the last time when the service was down and ready to be started (if the service is not currently up and ready).
    • updownfor: print the number of seconds that have elapsed since the service last changed states.
    • readyfor: print the number of seconds that have elapsed since the service last became ready (or ready to be started if it's currently not up and ready).
  • -u: equivalent to -o up.
  • -w: equivalent to -o wantedup.
  • -N: equivalent to -o normallyup.
  • -r: equivalent to -o ready.
  • -p: equivalent to -o pid.
  • -g: equivalent to -o pgid.
  • -e: equivalent to -o exitcode.
  • -s: equivalent to -o signal.
  • -t: equivalent to -o updownfor.

Exit codes

  • 0: success
  • 1: s6-supervise not running on servicedir
  • 100: wrong usage
  • 111: system call failed

Examples

  • s6-svstat -o up,ready (or its equivalent s6-svstat -ur) will print true true if the service is up and ready, true false if the service has been started but has not notified readiness yet, false true if it is down and can be started, and false false if it is down and there's a ./finish script running that needs to exit before the service can be restarted.
  • s6-svstat -o pid,exitcode,signal (or its equivalent s6-svstat -pes) will print 42 -1 NA if the service has been started and ./run's pid is 42; it will print -1 0 NA if the service is down and ./run last exited 0; it will print -1 -1 SIGTERM if the service is down and ./run was last killed by a SIGTERM - as can happen, for instance, when you down the service via a call to s6-svc -d.
s6-2.13.1.0/doc/s6-svunlink.html000066400000000000000000000073501470151141200161170ustar00rootroot00000000000000 s6: the s6-svunlink program

s6
Software
skarnet.org

The s6-svunlink program

s6-svunlink unlinks a service directory from a scan directory, then notifies s6-svscan that a service has been unregistered. It waits until the s6-supervise supervisor process managing the service has disappeared, then exits.

The point of s6-svunlink is to help integrate service directories into an existing service manager sequence and eliminate race conditions.

Interface

     s6-svunlink [ -X ] [ -t timeout ] scandir name
  • s6-svunlink expects a running s6-svscan process on scandir and a fully functional supervised service on service directory in scandir/name, which must be a symbolic link to a real directory located somewhere else.
  • It deletes the scandir/name symlink.
  • It sends a command to s6-svscan to signal it that a service has disappeared.
  • It waits for the s6-supervise process managing the service directory to exit.
  • It exits 0.

Exit codes

  • 0: success
  • 100: wrong usage
  • 111: system call failed

Options

  • -X : don't wait. s6-svunlink will exit right away, without waiting for the supervisor to exit first.
  • -t timeout : if the supervisor has not exited after timeout milliseconds, s6-svunlink will still exit. The default is 0, meaning no time limit.

Notes

  • Using s6-svunlink to stop services is a suboptimal pattern: starting and stopping supervisors is a heavier operation than just stopping services. The simpler, more efficient pattern is to simply perform s6-svc -dwD scandir/name, which only commands, and waits for, the death of the service, without impacting the supervisor. Nevertheless, for symmetry with s6-svlink, this program is provided.
  • s6-svunlink is a destructor; as is, it returns 0 even in situations that are nominal failures. For instance, it returns 0 even if its timeout expires; the rationale is that there is no sensible action for the user to do if this error is reported. s6-svunlink only reports errors when they uncover a deeper problem in the system.
  • s6-svunlink sends an s6-svscanctl -an command to scandir, which means that the system's view of services will be refreshed and inactive services will be killed and unsupervised. Depending on what links exist in scandir, new services may appear, and other services than name may disappear.
s6-2.13.1.0/doc/s6-svwait.html000066400000000000000000000107571470151141200155700ustar00rootroot00000000000000 s6: the s6-svwait program

s6
Software
skarnet.org

The s6-svwait program

s6-svwait blocks until a collection of supervised services goes up, or down.

s6-svwait only waits for notifications; it never polls.

Interface

     s6-svwait [ -U | -u | -D | -d | -r | -R ] [ -a | -o ] [ -t timeout ] servicedir...

s6-svwait monitors one or more service directories given as its arguments, waiting for a state (ready, up or down) to happen. If no service directories are listed, it immediately exits 0.

Exit codes

  • 0: success, the wanted state has been reached
  • 99: timed out
  • 100: wrong usage
  • 102: the s6-supervise process monitoring the service died
  • 111: system call failed
  • n: services were expected to come up, but n of them reported permanent failure

Options

  • -u : up. s6-svwait will wait until the services are up, as reported by s6-supervise. This is the default; it is not reliable, but it does not depend on specific support in the service programs. See this page for details.
  • -U : really up. s6-svwait will wait until the services are up and ready as reported by the services themselves. This requires specific support in the service programs, and the use of the notification-fd file in the service directory. See the explanation on this page.
  • -d : down. s6-svwait will wait until the services are down.
  • -D : really down. s6-svwait will wait until the services are down and the cleanup scripts in servicedir/finish for every servicedir have finished executing (or have timed out and been killed).
  • -r : restart. s6-svwait will wait until the services are down, then until they are up. If the -o option is given, it waits until all services are down and one service is up; otherwise it waits until all the services have restarted.
  • -R : restart and ready.. s6-svwait will wait until the services are down, then until they are up and ready. If the -o option is given, it waits until all services are down and one service is up and ready; otherwise it waits until all the services have restarted and are ready.
  • -o : or. s6-svwait will wait until one of the given services comes up or down.
  • -a : and. s6-svwait will wait until all of the given services come up or down. This is the default.
  • -t timeout : if the requested events have not happened after timeout milliseconds, s6-svwait will print a message to stderr and exit 99. By default, timeout is 0, which means no time limit.

Notes

  • s6-svwait should be given one or more service directories as arguments, not a scan directory. If you need to wait for a whole scan directory, give all its contents as arguments to s6-svwait.
  • s6-svwait will only work on service directories that are already active, i.e. have an s6-supervise process running on them. It will not work on a service directory where s6-supervise has not been started yet.

Internals

s6-svwait spawns a s6-ftrigrd child to listen to notifications sent by s6-supervise. It also checks supervise/status files to get the current service states, so it is immune to race conditions.

s6-2.13.1.0/doc/s6-tai64n.html000066400000000000000000000027231470151141200153520ustar00rootroot00000000000000 s6: the s6-tai64n program

s6
Software
skarnet.org

The s6-tai64n program

s6-tai64n acts as a filter, reading from stdin and writing to stdout. It prepends lines with a TAI64N timestamp and a space.

Interface

     s6-tai64n
  • s6-tai64n exits 0 when it sees the end of stdin. If there's an unfinished line, s6-tai64n processes it, adds a newline character to it, and writes it before exiting.

Notes

s6-tai64n does neither "line buffering" nor "block buffering". It does optimal buffering, i.e. it flushes its output buffer every time it risks blocking on input. Every filter should behave this way, whether its output is a tty or not: it's simpler and more efficient in every case.

s6-2.13.1.0/doc/s6-tai64nlocal.html000066400000000000000000000050701470151141200163630ustar00rootroot00000000000000 s6: the s6-tai64nlocal program

s6
Software
skarnet.org

The s6-tai64nlocal program

s6-tai64nlocal acts as a filter, reading from stdin and writing to stdout. For every line that begins with a TAI64N timestamp, it replaces this timestamp with a human-readable local date and time.

Interface

     s6-tai64nlocal [ -g ]
  • s6-tai64nlocal exits 0 when it sees the end of stdin. If there's an unfinished line, s6-tai64nlocal processes it and writes it before exiting.

Options

  • -g : print GMT time instead of local time.

Notes

  • The typical use case of s6-tai64nlocal is to read files that have been filtered through s6-tai64n, or log files that have been produced by s6-log with the -t option. For instance, to read the latest httpd logs with human-readable timestamps, s6-tai64nlocal < /var/log/httpd/current | less is a possible command.
  • s6-tai64nlocal does neither "line buffering" nor "block buffering". It does optimal buffering, i.e. it flushes its output buffer every time it risks blocking on input.

Troubleshooting

If s6-tai64nlocal does not appear to give the correct local time:

  • Check the compilation options that were used for the skalibs libraries your s6-tai64nlocal program was linked against. In particular, check whether the --enable-tai-clock or --enable-right-tz configure options have been given.
  • Compare these flags and their meanings with your current timezone. In particular, check /etc/localtime, /etc/timezone, /etc/TZ, and the TZ environment variable.
s6-2.13.1.0/doc/s6-usertree-maker.html000066400000000000000000000303531470151141200172000ustar00rootroot00000000000000 s6: the s6-usertree-maker program

s6
Software
skarnet.org

The s6-usertree-maker program

s6-usertree-maker creates a service directory implementing a service that runs an s6-svscan process owned by a given user, on a scan directory belonging to that user. It is meant to help admins deploy systems where each user has their own supervision subtree, rooted in the main supervision tree owned by root.

Alternatively, s6-usertree-maker can create source definition directories for the s6-rc service manager.

Interface

     s6-usertree-maker \
       [ -d userscandir ] \
       [ -p path ] \
       [ -E envdir [ -e var -e var ... ] ] \
       [ -r service/logger[/pipeline] ] \
       [ -l loguser ] \
       [ -t stamptype ] \
       [ -n nfiles ] \
       [ -s filesize ] \
       [ -S maxsize ] \
       [ -P prefix ] \
       user logdir dir

s6-usertree-maker creates a service directory in dir, that launches a supervision tree as user user on scan directory userscandir, with a catch-all logger logging the tree's output via s6-log to the logdir directory.

Exit codes

  • 0: success
  • 100: wrong usage
  • 111: system call failed

Options

  • -d userscandir : the supervision tree will be run on the userscandir directory. userscandir is subject to variable substitution (see below). Default is ${HOME}/service.

  • -p path : the supervision tree will be run with a PATH environment variable set to path. path is subject to variable substitution. Default is /usr/bin:/bin, or whatever has been given to the --with-default-path option to skalibs' configure script.

  • -E envdir : the supervision tree will be run with the environment variables defined in the directory envdir, which will be read via s6-envdir without options. By default, no envdir is defined and the supervision tree will only be run with the basic environment variables listed below.

  • -e var : Perform variable substitution on var. This option is repeatable, and only makes sense when the -E option is also given. For every var listed via a -e option, the contents of var will be subjected to variable substitution before the supervision tree is run. This is only useful if var is defined in envdir, as a template, that is then instanced for user when the service is run. By default, only the PATH environment variable, customizable via -p, is subjected to variable substitution.

  • -r service/logger/pipeline : create s6-rc source definition directories. When this option is given, dir is not created as a service directory, but as a directory containing two services: dir/service and dir/logger, and dir is suitable as a source argument to s6-rc-compile. The /pipeline part can be omitted, but if it is present, pipeline is used as a name for a bundle containing both service and logger. When this option is not given, dir is a regular service directory for direct inclusion (or linking) in the parent scan directory (and the catch-all logger for the user subtree is declared in dir/log).

  • -l loguser : run the catch-all logger of the user subdirectory as user loguser. Default is root.

  • -t stamptype : how logs are timestamped by the catch-all logger. 0 means no timestamp, 1 means external TAI64N format, 2 means ISO 8601 format, and 3 means both. Default is 1.

  • -n nfiles : maximum number of archive files in logdir. Default is 10.

  • -s filesize : maximum size of the current file (and archive files) in logdir. Default is 1000000.

  • -S maxsize : maximum total size of the archives in the logdir. Default is 0, meaning no limits apart from those enforced by the -n and -s options.

  • -P prefix : when logging to logdir, prefix logged lines with the prefix string.

Operation of the service

When the service is started, its run script will execute the following operations:

  • Clear all its environment variables, except PATH. This prevents any data leak from the parent supervision tree into the user subtree.
  • Fill its environment with data related to user:
    • USER is set to user
    • HOME is set to user's home directory
    • UID is set to user's uid
    • GID is set to user's primary gid
    • GIDLIST is set to user's supplementary groups list
  • If the service has been created with the -E option to s6-usertree-maker:
    • Add all the variables defined in envdir to its environment
    • For every variable var given via a -e option, subject var to substitution with the USER, HOME, UID, GID and GIDLIST variables (see below).
  • Set the PATH environment variable to path, subjected to variable substitution.
  • Execute into s6-svscan, running in userscandir (which is first subjected to variable substitution).

The service is logged: its stderr and stdout are piped to an s6-log process running as loguser and writing to the logdir directory. This logger is the catch-all logger for the supervision tree owned by user; it is recommended to make loguser distinct from user, and to have logdir in a place that is not under the control of user. If user wants to keep control of their logs, they can declare a logger for each of their services.

Variable substitution

When the service starts, the USER, HOME, UID, GID and GIDLIST environment variables are deduced from user's identity. The value of those variables may be used in a few configuration knobs:

  • The value of userscandir: it is likely that the scan directory belonging to user resides under user's home directory. Or under /run/user/${UID}, or some similar scheme.
  • The PATH environment variable, declared in path: it is often useful to prepend the default system PATH with a user-specific directory that hosts that user's binaries. For instance, you may want the PATH to be set as something like ${HOME}/bin:/usr/bin:/bin.
  • Any variable declared in envdir and given as an argument to a -e option to s6-usertree-maker. If envdir is a template valid for all users, it may contain variables that depends on user-specific data: for instance, the XDG_CONFIG_HOME variable may be set to ${HOME}/.config.

When the strings ${USER}, ${HOME}, ${UID}, ${GID}, or ${GIDLIST} appear in the value for userscandir, path, or any of the var variables, they are substituted with the corresponding value of the USER, HOME, UID, GID, or GIDLIST environment variable instead.

For instance, if no -d option is provided, the default value for userscandir is ${HOME}/service. If the provided user is ska and ska's home directory is /home/ska, then s6-svscan will be run on /home/ska/service.

Examples

     s6-usertree-maker -d '/run/user/${UID}/service' -p '${HOME}/bin:/usr/bin:/bin' -E /etc/user-env -e XDG_CONFIG_HOME -l catchlog ska /var/log/usertree/ska usertree-ska

creates a service directory in usertree-ska declaring a service that starts a supervision tree on /run/user/1000/service if ska has uid 1000, with /home/ska/bin:/usr/bin/bin as its PATH if ska's home directory is /home/ska, and with all the environment variables declared in /etc/user-env, among which the XDG_CONFIG_HOME variable is processed for variable substitution. The supervision tree has a catch-all logger running as user catchlog, and storing its data in the /var/log/usertree/ska directory.

Note that simple quotes are used here to prevent the shell from interpreting ${UID} and ${HOME}.

     s6-usertree-maker -d '/run/user/${UID}/service' -p '${HOME}/bin:/usr/bin:/bin' -E /etc/user-env -e XDG_CONFIG_HOME -l catchlog -r usertree-ska/usertree-ska-log/usertree-ska-pipeline ska /var/log/usertree/ska usertree

Same as above, except it does not create a service directory — instead, it creates a usertree directory containing two subdirectories: usertree-ska, the s6-rc source definition directory for the service, and usertree-ska-log, the source definition directory for its logger. It also creates an implicit usertree-ska-pipeline bundle containing both the service and the logger.

Notes

  • s6-usertree-maker makes use of the fact that execline scripts are much easier to generate programmatically and to harden than shell scripts, so it is only built if s6 is built with execline support - i.e. the --disable-execline switch has not been given to configure.
  • For the admin who wants to automate user tree management, s6-usertree-maker is a building block meant to be used in scripts, not a complete turnkey solution. For instance, s6-usertree-maker does not create userscandir for a user: it assumes that that scandir is already in place. It does not create logdir either: logdir, or at least its parent directory, must already exist before the logger is run, else s6-log will fail repeatedly. Make sure that all the data and metadata referenced by the service's and the logger's run scripts are actually present and valid before starting the service.
  • If s6-usertree-maker encounters failure (and exits 111), it does not clean up the directories it created. Make sure to always test s6-usertree-maker's return code and clean up after it if needed.
s6-2.13.1.0/doc/scandir.html000066400000000000000000000140031470151141200153340ustar00rootroot00000000000000 s6: scan directories

s6
Software
skarnet.org

Scan directories

A scan directory is a directory containing a list of service directories, or symbolic links pointing to service directories.

A scan directory represents a list of services that are supposed to be supervised. Running s6-svscan on this scan directory launches a supervision tree: every service listed in the scan directory will be supervised.

There is normally only one scan directory per system, although nothing prevents a system administrator from having more. daemontools traditionally uses /service, and runit traditionally uses /etc/service. s6 does not care where your scan directory is, but I would advise /service for compatibility with daemontools. Depending on your installation, /service could be a symbolic link and point to a place either in a RAM filesystem or in /var.

Where and how to build a scan directory

Opinions and practices differ.

It is generally accepted that the place where you store all your service directories (your "service repository") should not be used as a scan directory - for a simple reason: you might want to have service directories for more services than what you want to start at any given time. In other words, your scan directory will be a subset of your service repository, so you cannot just run s6-svscan on every service you have a service directory for. So, the first thing is to separate your service repository, which is just a storage place for all the services you might want to manage someday, and your scan directory, which is a directory representing all the services that you are currently managing.

Service repository

Where to store your service repository is purely a matter of personal preference. You just have to be aware that s6-supervise needs writable supervise and event subdirectories in a service directory it monitors.

Scan directory

Where and how to build your scan directory depends heavily on your boot system - and on your personal preference too.

Standard daemontools and runit installations like to have a fixed scan directory containing symlinks to service directories located in the service repository. In other words, the service repository contains the real working copies of the service directories. This works, as long as:

  • It is possible to create writable supervise and event subdirectories in every managed service directory. This can be achieved for instance via symlinks, or by having the service repository stored on a writable filesystem.
  • The scan program (s6-svscan, svscan, runsvdir...) is started late enough for all the necessary filesystems to be mounted.

My own recommendation would be to have working copies of the service directories entirely separate from the service repository. The service repository can be safely stored on the root filesystem, and the needed directories copied to a RAM filesystem at boot time. The scan directory can be either the place where the working copies are written, or another directory containing symlinks to those working copies. (The latter is useful if you are using the -t option to s6-svscan with a nonzero argument: copying a directory is not atomic, but making a symlink is, so there is no risk of your scanner finding a directory while it is being copied - which could result in s6-supervise getting the wrong information and not managing the service properly.)

An example:

  • Have your service repository in /img/services, i.e. have service directories in /img/services/ftpd, /img/services/httpd, /img/services/sshd, etc.
  • When booting, make /tmp a RAM filesystem, and create the directories /tmp/services and /tmp/service.
  • Have s6-svscan run on /tmp/service, as early as possible in your boot sequence. This is possible whether you want to run s6-svscan as process 1 or not.
  • During the boot sequence, populate /tmp/services with copies of the service directories you need: for instance,
    • cp -a /img/services/sshd /tmp/services/sshd
    • cp -a /img/services/ftpd /tmp/services/ftpd
    • etc.
  • When you are ready to start a service, make a symlink in the /tmp/service scan directory pointing to the working copy of the service directory you need in /tmp/services, then notify s6-svscan. For instance, to start ftpd and httpd together:
     ln -s ../services/ftpd /tmp/service
     ln -s ../services/httpd /tmp/service
     s6-svscanctl -a /tmp/service
s6-2.13.1.0/doc/servicedir.html000066400000000000000000000460251470151141200160610ustar00rootroot00000000000000 s6: service directories

s6
Software
skarnet.org

Service directories

A service directory is a directory containing all the information related to a service, i.e. a long-running process maintained and supervised by s6-supervise.

(Strictly speaking, a service is not always equivalent to a long-running process. Things like Ethernet interfaces fit the definition of services one may want to supervise; however, s6 does not provide service supervision; it provides process supervision, and it is impractical to use the s6 architecture as is to supervise services that are not equivalent to one long-running process. However, we still use the terms service and service directory for historical and compatibility reasons.)

Contents

A service directory foo may contain the following elements:
  • An executable file named run. It can be any executable file (such as a binary file or a link to any other executable file), but most of the time it will be a script, called run script. This file is the most important one in your service directory: it contains the commands that will setup and run your foo service.
    • It is spawned by s6-supervise every time the service must be started, i.e. normally when s6-supervise starts, and whenever the service goes down when it is supposed to be up.
    • It is given one argument, which is the same argument that the s6-supervise process is running with, i.e. the name of the service directory — or, if s6-supervise is run under s6-svscan, the name of the service directory as seen by s6-svscan in its scan directory. That is, foo or foo/log, if foo is the name of the symbolic link in the scan directory.

    A run script should normally:

    • adjust redirections for stdin, stdout and stderr. When a run script starts, it inherits its standard file descriptors from s6-supervise, which itself inherits them from s6-svscan. stdin is normally /dev/null. If s6-svscan was launched by another init system, stdout and stderr likely point to that init system's default log (or /dev/null in the case of sysvinit). If s6-svscan is running as pid 1 via the help of software like s6-linux-init, then its stdout and stderr point to a catch-all logger, which catches and logs any output of the supervision tree that has not been caught by a dedicated logger. If the defaults provided by your installation are not suitable for your run script, then your run script should perform the proper redirections before executing into the final daemon. For instance, dedicated logging mechanisms, such as the log subdirectory (see below) or the s6-rc pipeline feature, pipe your run script's stdout to the logging service, but chances are you want to log stderr as well, so the run script should make sure that its stderr goes into the log pipe. This is achieved by fdmove -c 2 1 in execline, and exec 2>&1 in shell.
    • adjust the environment for your foo daemon. Normally the run script inherits its environment from s6-supervise, which normally inherits its environment from s6-svscan, which normally inherits a minimal environment from the boot scripts. Service-specific environment variables should be set in the run script.
    • adjust other parameters for the foo daemon, such as its uid and gid. Normally the supervision tree, i.e. s6-svscan and the various s6-supervise processes, is run as root, so run scripts are also run as root; however, for security purposes, services should not run as root if they don't need to. You can use the s6-setuidgid utility in foo/run to lose privileges before executing into foo's long-lived process; or the s6-envuidgid utility if your long-lived process needs root privileges at start time but can drop them afterwards.
    • execute into the long-lived process that is to be supervised by s6-supervise, i.e. the real foo daemon. That process must not "background itself": being run by a supervision tree already makes it a "background" task.
  • An optional executable file named finish. Like run, it can be any executable file. This finish script, if present, is executed everytime the run script dies. Generally, its main purpose is to clean up non-volatile data such as the filesystem after the supervised process has been killed. If the foo service is supposed to be up, foo/run is restarted after foo/finish dies.
    • By default, a finish script must do its work and exit in less than 5 seconds; if it takes more than that, it is killed. (The point is that the run script, not the finish script, should be running; the finish script should really be short-lived.) The maximum duration of a finish execution can be configured via the timeout-finish file, see below.
    • The finish script is executed with four arguments:
      1. the exit code from the run script (resp. 256 if the run script was killed by a signal)
      2. an undefined number (resp. the number of the signal that killed the run script)
      3. the name of the service directory, the same that has been given to ./run
      4. the process group id of the defunct run script. This is useful to clean up services that leave children behind: for instance, if test "$1" -gt 255 ; then kill -9 -- -"$4" ; fi in the finish script will SIGKILL all children processes if the service crashed. This is not an entirely reliable mechanism, because an annoying service could spawn children processes in a different process group, but it should catch most offenders.
    • If the finish script exits 125, then s6-supervise interprets this as a permanent failure for the service, and does not restart it, as if an s6-svc -O command had been sent.
    • If s6-supervise has been instructed to exit after the service dies, via a s6-svc -x command or a SIGHUP, then the next invocation of finish will (obviously) be the last, and it will run with stdin and stdout pointing to /dev/null.
  • A directory named supervise. It is automatically created by s6-supervise if it does not exist. This is where s6-supervise stores its internal information. The directory must be writable.
  • An optional, empty, regular file named down. If such a file exists, the default state of the service is considered down, not up: s6-supervise will not automatically start it until it receives a s6-svc -u command. If no down file exists, the default state of the service is up.
  • An optional regular file named notification-fd. If such a file exists, it means that the service supports readiness notification. The file must only contain an unsigned integer, which is the number of the file descriptor that the service writes its readiness notification to. (For instance, it should be 1 if the daemon is s6-ipcserverd run with the -1 option.) When a service is started, or restarted, by s6-supervise, if this file exists and contains a valid descriptor number, s6-supervise will wait for the notification from the service and broadcast readiness, i.e. any s6-svwait -U, s6-svlisten1 -U or s6-svlisten -U processes will be triggered.
  • An optional regular file named lock-fd. If such a file exists, it must contain an unsigned integer, representing a file descriptor that will be open in the service. The service should not write to that descriptor and should not close it. In other words, it should totally ignore it. That file descriptor holds a lock, that will naturally be released when the service dies. The point of this feature is to prevent s6-supervise from accidentally spawning several copies of the service in case something goes wrong: for instance, the service backgrounds itself (which it shouldn't do when running under a supervision suite), or s6-supervise is killed, restarted by s6-svscan, and attempts to start another copy of the service while the first copy is still alive. If s6-supervise detects that the lock is held when it tries to start the service, it will print a warning message; the new service instance will block until the lock is released, then proceed as usual.
  • An optional regular file named timeout-kill. If such a file exists, it must only contain an unsigned integer t. If t is nonzero, then on receipt of an s6-svc -d command, which sends a SIGTERM (by default, see down-signal below) and a SIGCONT to the service, a timeout of t milliseconds is set; and if the service is still not dead after t milliseconds, then it is sent a SIGKILL. If timeout-kill does not exist, or contains 0 or an invalid value, then the service is never forcibly killed (unless, of course, an s6-svc -k command is sent).
  • An optional regular file named timeout-finish. If such a file exists, it must only contain an unsigned integer, which is the number of milliseconds after which the ./finish script, if it exists, will be killed with a SIGKILL. The default is 5000: finish scripts are killed if they're still alive after 5 seconds. A value of 0 allows finish scripts to run forever.
  • An optional regular file named max-death-tally. If such a file exists, it must only contain an unsigned integer, which is the maximum number of service death events that s6-supervise will keep track of. If the service dies more than this number of times, the oldest events will be forgotten. Tracking death events is useful, for instance, when throttling service restarts. The value cannot be greater than 4096. If the file does not exist, a default of 100 is used.
  • An optional regular file named down-signal. If such a file exists, it must only contain the name or number of a signal, followed by a newline. This signal will be used to kill the supervised process when a s6-svc -d or s6-svc -r command is used. If the file does not exist, SIGTERM will be used by default.
  • An optional regular file named flag-newpidns. If such a file exists:
    • On Linux (and potentially in the future, other systems that implement such functionality): at service starting time, the ./run script will be spawned in a new PID namespace. It will be pid 1 in that namespace.
    • On systems that do not support the functionality: the service will fail to start, so do not create this file if you're unsure. (Yes, it is a better behaviour than ignoring the flag. Having the flag be silently ignored on some systems would be very bad.)
  • A fifodir named event. It is automatically created by s6-supervise if it does not exist. foo/event is the rendez-vous point for listeners, where s6-supervise will send notifications when the service goes up or down.
  • Optional directories named instance and instances. Those are internal subdirectories created by s6-instance maker in a templated service directory. Outside of instanced services, these directories should never appear, and you should never create them manually.
  • An optional service directory named log. If it exists and foo is in a scandir, and s6-svscan runs on that scandir, then two services are monitored: foo and foo/log. A pipe is open and maintained between foo and foo/log, i.e. everything that foo/run writes to its stdout will appear on foo/log/run's stdin. The foo service is said to be logged; the foo/log service is called foo's logger. A logger service cannot be logged: if foo/log/log exists, nothing special happens.

Stability

With the evolution of s6, it is possible that s6-supervise configuration uses more and more files in the service directory. The notification-fd and timeout-finish files, for instance, have appeared in 2015; users who previously had files with the same name had to change them. There is no guarantee that s6-supervise will not use additional names in the service directory in the same fashion in the future.

There is, however, a guarantee that s6-supervise will never touch subdirectories named data or env. So if you need to store user information in the service directory with the guarantee that it will never be mistaken for a configuration file, no matter the version of s6, you should store that information in the data or env subdirectories of the service directory.

Where should I store my service directories?

Service directories describe the way services are launched. Once they are designed, they have little reason to change on a given machine. They can theoretically reside on a read-only filesystem - for instance, the root filesystem, to avoid problems with mounting failures.

However, two subdirectories - namely supervise and event - of every service directory need to be writable. So it has to be a bit more complex. Here are a few possibilities.

  • The laziest option: you're not using s6-svscan as process 1, you're only using it to start a collection of services, and your booting process is already handled by another init system. Then you can just store your service directories and your scan directory on some read-write filesystem such as /var; and you tell your init system to launch (and, if possible, maintain) s6-svscan on the scan directory after that filesystem is mounted.
  • The almost-as-lazy option: just have the service directories on the root filesystem. Then your service directory collection is for instance in /etc/services and you have a /service scan directory containing symlinks to that collection. This is the easy setup, not requiring an external init system to mount your filesystems - however, it requires your root filesystem to be read-write, which is unacceptable if you are concerned with reliability - if you are, for instance, designing an embedded platform.
  • Some people like to have their service directories in a read-only filesystem, with supervise symlinks pointing to various places in writable filesystems. This setup looks a bit complex to me: it requires careful handling of the writable filesystems, with not much room for error if the directory structure does not match the symlinks (which are then dangling). But it works.
  • Service directories are usually small; most daemons store their information elsewhere. Even a complete set of service directories often amounts to less than a megabyte of data - sometimes much less. Knowing this, it makes sense to have an image of your service directories in the (possibly read-only) root filesystem, and copy it all to a scan directory located on a RAM filesystem that is mounted at boot time. This is the setup I recommend, and the one used by the s6-rc service manager. It has several advantages:
    • Your service directories reside on the root filesystem and are not modified during the lifetime of the system. If your root filesystem is read-only and you have a working set of service directories, you have the guarantee that a reboot will set your system in a working state.
    • Every boot system requires an early writeable filesystem, and many create it in RAM. You can take advantage of this to copy your service directories early and run s6-svscan early.
    • No dangling symlinks or potential problems with unmounted filesystems: this setup is robust. A simple /bin/cp -a or tar -x is all it takes to get a working service infrastructure.
    • You can make temporary modifications to your service directories without affecting the main ones, safely stored on the disk. Conversely, every boot ensures clean service directories - including freshly created supervise and event subdirectories. No stale files can make your system unstable.
s6-2.13.1.0/doc/socket-activation.html000066400000000000000000000116721470151141200173510ustar00rootroot00000000000000 s6: socket activation

s6
Software
skarnet.org

How do I perform socket activation with s6 ?

First, it's important to realize that you don't need socket activation. It's a marketing word used by systemd advocates that mixes a couple useful architecture concepts and several horrible ideas, for a very minor speed benefit. Read this mail and this post for details.

  • s6 will not help you implement super-servers in process 1, because doing so is bad engineering. However, it will help you set up super-servers. The s6-ipcserver program, for Unix domain sockets, as well as the s6-tcpserver program, for TCP INET domain sockets (available in the s6-networking package) are super-servers you can use to your heart's content. They are even wrappers around simpler programs, and you can use their components in the way you choose: bind sockets, drop privileges, accept connections from clients, it's all about what you write in your command line. Super-servers are a good thing; using process 1 to act as a super-server is not. s6 provides you with the tools to get the good without the bad.
  • s6 will not help you run all your services before their dependencies are met, because doing so is very bad engineering. However, it will provide you with:
  • s6 will not help you centralize all your socket information in process 1, because doing so is contrary to modularity and independence of services. However, s6 will provide you with a way to store your open sockets and retrieve them when you want, which it calls "fd holding": s6-fdholder-daemon.

So, how do I open all my sockets first, store them, and dispatch them to daemons later ?

Again, it's not necessary to do that: you'll be fine, and quite speedy, just starting your daemons in their good time. You will not reap any noticeable benefit from performing "socket activation". But if you really want to:

  1. Make sure you have an early supervision infrastructure running. Ideally, you would make s6-svscan your process 1.
  2. Start an early fd-holding service. Let's say the fd-holding daemon is listening on socket /service/fdholder/s.
  3. For every Unix domain socket /my/socket you need to open, run s6-ipcserver-socketbinder /my/socket s6-fdholder-store /service/fdholder/s unix:/my/socket. You can do the same with INET domain sockets.
  4. Proceed to your initialization.
  5. When you want to run a daemon myserverd that accepts clients connecting to /my/socket, run s6-fdholder-retrieve /service/fdholder/s unix:/my/socket myserverd. myserverd will be executed with /my/socket as its standard input.
  6. The descriptors remain safely stored in the fd-holding daemon and you can retrieve them again whenever you want, for instance when your service crashes and is restarted.

That is all there is to it. You don't have to use specific libraries or write complex unit files, you just need to understand how a command line works. This is Unix.

s6-2.13.1.0/doc/ucspilogd.html000066400000000000000000000066231470151141200157130ustar00rootroot00000000000000 s6: the ucspilogd program

s6
Software
skarnet.org

The ucspilogd program

ucspilogd acts as a filter, converting syslog facility numbers and alert levels into names.

Interface

     ucspilogd [ -d undef ] [ var ... ]
  • ucspilogd reads a stream of syslog-like messages on stdin. Those messages can be newline-terminated or null-terminated.
  • For every line it reads: if it has been given var arguments, it writes the value of every var environment variable, followed by a colon and a space.
  • If the line begins with a syslog facility number and/or alert level in the syslog format, it converts them into a human-readable name in the syslogd fashion.
  • It then writes the processed line to stdout.

Options

  • -d undef : when a variable var given on the command line is actually undefined at ucspilogd execution time, print undef in place of what would be the variable's value on every line. Default is the string <undefined>.

Common use

You can emulate the whole syslogd behaviour by combining the following components:

  • A Unix stream super-server such as s6-ipcserver listening to the Unix domain socket /dev/log, to connect to the kernel log-reading interface.
  • ucspilogd running under that super-server, to read the logs and perform adequate transformations.
  • A logger such as s6-log to store the logs into the filesystem.
  • A supervision mechanism such as s6, to ensure ease of use and reliability of the whole chain.

The resulting suite of programs is still smaller, and way more reliable, than a standard syslogd.

In the examples/syslogd-linux subdirectory of the s6 package, you will find a suitable ucspilogd service directory. The run scripts are written in the execline language.

Using ucspilogd as a klogd replacement

Certain Unix kernels offer a nice interface to the kernel logs. For instance, the Linux kernel provides the /proc/kmsg fake file, that can be opened and read like a normal file, excepts that it gives the kernel logs when they are available and blocks otherwise. You can use ucspilogd to process data from those interfaces.

The examples/klogd-linux subdirectory of the s6 package is a service directory providing such a klogd service for Linux, using the /proc/kmsg interface.

s6-2.13.1.0/doc/unit-conversion.html000066400000000000000000001532751470151141200170720ustar00rootroot00000000000000 s6: how to convert systemd unit files to an s6 installation

s6
Software
skarnet.org

How to convert systemd unit files to an s6 installation

Converting a set of services managed by systemd to a set of services managed by s6 is a recurring question on our support channels, and having an automated conversion tool parsing a set of systemd unit files and outputting a set of s6 service directories or s6-rc service definition directories would be useful to people who want to migrate to s6 but have an existing base of services running under systemd.

Unfortunately, automating such a conversion is extremely difficult. The main reason for this is that systemd and s6 are architectured in very different ways: how they view a set of services, how they modelize a machine, what kind of solution they bring to a given problem, and the extent of what they're supposed to manage — there are few similarities between how systemd and s6 operate. As a consequence, the way systemd maps a set of services into unit files is not isomorphic to the way s6 does, so translating a setup between systemd and s6 requires intelligence. It is not possible to write an automated tool that converts a set of services accurately and idiomatically without doing a full, deep system analysis - and writing such a tool would be a huge undertaking.

Fortunately, in practice, most unit files only use a small subset of all the theoretically supported directives: most services that run under systemd can be converted to run under s6 without too much trouble. So, depending on the exact nature of the set of services, it may be possible to write a reasonable converter, that is limited in what it supports but does not require a full understanding of systemd (or even s6).

This document targets people who think about writing a tool to automatically convert a set of unit files to a set of s6 services and are trying to assess its feasability and difficulty. We analyze all the directives that may appear in a unit file for their services, and rate the difficulty of translating the directive, i.e. the amount of complexity that would need to go into the converter tool if that directive were to be supported.

We rate difficulty on a scale from 0 to 10. A 0 means that the directive is irrelevant to s6 and can be ignored. A 1 means that there is a straightforward, one-for-one way of translating the systemd directive into s6 parlance. A 10 means that the directive is so systemd-specific that it is impossible to express it on an s6 system and the unit file cannot be converted as is. A 9 means that it is theoretically possible to convert, given infinite time and manpower to write a tool that analyzes the systemd-managed system holistically and outputs an equivalent system managed by s6, but in practice nobody's ever going to write such a tool.

We only address directives that can appear in service units, which are the ones that can reasonably be expected to translate to s6 or s6-rc services. We do not address directives that can only appear in other kinds of unit files such as slices, timers, sockets, etc.



Difficulty by directive

Use this section to answer the question: "I have this directive in my unit file, how hard would it be to write a converter tool that processes this file?"

Directives documented in systemd.unit(5)

[Unit] section

  • Description= : 0.
  • Documentation= : 0.
  • Wants= : 3. Dependencies between services are implemented via s6-rc; in order to implement Wants=. the converter needs to target s6-rc, which is a reasonable requirement for a complete set of services. However, Wants= expresses weak dependencies, which are not supported by the current version of s6-rc, so the exact nature of the dependency needs to be checked by hand.
  • Requires= : 2. The converter needs to target s6-rc, but Requires= dependencies map well to the s6-rc dependency model.
  • Requisite= : 3. systemd supports a lot of weird types of dependencies that the current version of s6-rc does not (by design).
  • BindsTo= : 3. Same.
  • PartOf= : 3. Same.
  • Upholds= : 0. In s6, services are upheld by the supervisor, not by other services.
  • Conflicts= : 5. There are no negative dependencies in the s6 world, and a converter tool would have to implement it on top of the existing system.
  • Before= : 2. systemd loves to have a zillion of keywords to express slightly different kinds of dependencies, and only a small subset of all the possible combinations are useful.
  • After= : 2.
  • OnFailure= : 5. Permanent failure is a very exceptional state in s6, there are no hooks to do something when permanent failure occurs; so a converter would need to add scripting around that.
  • OnSuccess= : 2. These are oneshot dependencies.
  • PropagatesReloadTo= : 0. Under s6-rc you can either reload a single service or the whole dependency chain that starts at the service. Other configurations just make no sense.
  • ReloadPropagatedFrom= : 0. Same.
  • PropagatesStopTo= : 0. Same with stopping a single service or a dependency chain of services.
  • StopPropagatedFrom= : 0. Same.
  • JoinsNamespaceOf= : 8. There are no native s6 tools to manage namespaces.
  • RequiresMountsFor= : 4. Mounts are not handled in a special way in s6, the converter would have to know what service mounts what filesystem.
  • OnFailureJobMode= : 6. This is what happens when you mix layers.
  • IgnoreOnIsolate= : 0.
  • StopWhenUnneeded= : 0.
  • RefuseManualStart= : 4. Would need some scripting around. There's no reason to ever use that directive though.
  • RefuseManualStop= : 1.
  • AllowIsolate= : 0.
  • DefaultDependencies= : 2.
  • CollectMode= : 0.
  • FailureAction= : 3. Anything involving permanent failure need to be scripted around, because s6 considers that it is an extreme state that requires administrator attention and will stop making automatic decisions.
  • SuccessAction= : 3. That's a oneshot dependency, but systemd doesn't realize that.
  • FailureActionExitStatus= : 1.
  • SuccessActionExitStatus= : 1.
  • JobTimeoutSec= : 9. s6 has no concept of jobs.
  • JobRunningTimeoutSec= : 9. Same.
  • JobTimeoutAction= : 9. Same.
  • JobTimeoutRebootArgument= : 9. Same.
  • StartLimitIntervalSec= : 3. s6 does not limit start rate. It can stop a service that has a high death rate: that's the configuration knob that makes sense. That directive can be converted to check death interval instead.
  • StartLimitBurst= : 3. Same.
  • StartLimitAction= : 3. Same.
  • RebootArgument= : 5. Any "system mode" action such as a reboot has no place in an s6 set of services anyway; systemd obviously likes to mix unrelated layers. In the s6 world, the only place where a reboot should occur is s6-linux-init, and the related scripting in rc.init files.
  • SourcePath= : 0.
  • Conditions and Asserts: 9. Most of these are tied to the global machine state and absolutely not local to a given set of services. And even for those that are not, what they do is change the whole service manager's behaviour depending on some external dynamic state such as the existence of a file in the filesystem — and that is entirely contrary to the s6 philosophy of making services predictable and reproducible.

[Install] section

Any directive under [Install] can be ignored, since it has no meaning on the run-time behaviour of the service. So the difficulty is 0. However, an automatic converter would need to analyze the whole installed service configuration, e.g. the links in /etc/systemd/system/multi-user.target.wants/, in order to understand the dependencies between services. systemd targets can typically be converted into s6-rc bundles.

Directives documented in systemd.service(5)

[Service] section

  • Type= : depending on its type, a systemd "service" can translate to wildly different things under s6.
    • simple : 1.
    • exec : 1.
    • forking : 2. This type is strongly discouraged.
    • oneshot : 1.
    • dbus : 5. This type requires implementing dbus management programs for s6.
    • notify : 7. This type requires an implementation of a compatibility server for sd_notify(), which is heavily tied to the monolithic systemd architecture and is difficult to support in a modular system such as s6. It is much easier to modify the services themselves so they use the s6 readiness notification mechanism instead of sd_notify.
    • idle : 0. This type is meaningless under s6 and should be treated like simple.
  • ExitType= : again, it depends on the value.
    • main : 1.
    • cgroup : 3. This requires implementing, or having access to, command-line cgroup tools.
  • RemainAfterExit= : 0.
  • GuessMainPid= : 0. But don't use forking if you can avoid it.
  • PIDFile= : 2. Same.
  • BusName= : 5. Avoid type dbus if possible.
  • ExecStart= : 2, with caveats. This is the bread and butter of service definitions; all your services will likely have such a directive, and the contents of ExecStart= will typically go into a run script (for longruns) or an up file (for oneshots). However, since systemd hates simplicity, there are a number of transformations that have to happen to the command line before it can be used in a script, and in particular if the command has a special executable prefix. Implementing these has its own difficulty ratings:
    • @ : 1.
    • - : 1.
    • : : 2.
    • + : 5.
    • ! : 3.
    • !! : 3.
  • ExecStartPre= : 2. In order to have a strict semantic equivalence with their systemd version, ExecStartPre= lines must be implemented as s6-rc oneshots, and the whole unit file must be implemented as a bundle.
  • ExecStartPost= : 2. Same.
  • ExecCondition= : 2.
  • ExecReload= : 0. A reloading command that does not involve restarting the service does not need the service manager as a third-party. systemd cannot help inserting itself where it does not belong.
  • ExecStop= : 5. s6 only supports terminating services via signals, so if a service needs a specific command to be stopped, the converter needs to target an interface layer on top of s6 with a repository of stop commands; such a layer would likely need to be on top of s6-rc as well and a lot of complexity would ensue. Fortunately, a huge majority of services support termination via signals and it is often easy to avoid relying on ExecStop=.
  • ExecStopPost= : 2. These are the down scripts of the ExecStartPre= oneshots.
  • RestartSec= : 2. s6 has no setting for that, because it aims for maximum uptime; but there are still ways to implement that bad idea.
  • TimeoutStartSec= : 1.
  • TimeoutStopSec= : 1.
  • TimeoutAbortSec= : 7. This would need a watchdog implementation, in the server implementation of sd_notify().
  • TimeoutSec= : 1.
  • TimeoutStartFailureMode= : 0 for terminate and kill, 7 for abort.
  • TimeoutStopFailureMode= : 0 for terminate and kill, 7 for abort.
  • RuntimeMaxSec= : 4. This is the exact opposite of what you need a process supervisor for, so s6 does not implement it. It is best to run such a process (not a service) outside of any kind of supervision framework.
  • RuntimeRandomizedExtraSec= : 4. Same.
  • WatchdogSec= : 7. Requires a watchdog implementation in the very specific systemd way.
  • Restart= : depends on the value.
    • no : 2, but why use a process supervisor in the first place?
    • on-success : 2.
    • on-failure : 2.
    • on-abnormal : 2.
    • on-watchdog : 7. Again, this requires a watchdog implementation.
    • on-abort : 2.
    • always : 1.
  • SuccessExitStatus= : 3. This can be easily scripted, but supporting all the systemd formats is annoying.
  • RestartPreventExitStatus= : 1. This is exactly what s6-permafailon is for.
  • RestartForceExitStatus= : 2.
  • RootDirectoryStartOnly= : 2.
  • NonBlocking= : 5. This requires an emulation of systemd's socket activation. s6 provides the useful parts of it, like a process to hold file descriptors, but the systemd-specific API around socket activation, sd_listen_fds(3), still needs to be implemented.
  • NotifyAccess= : 1 if none, 7 otherwise, because it needs an implementation of sd_notify().
  • Sockets= : 5. Requires an implementation of systemd's socket activation.
  • FileDescriptorStoreMax= : 7. Requires an implementation of sd_notify(); the fd store itself is native to s6.
  • USBFunctionDescriptors= : 9. This is low-level machine management, not service management.
  • USBFunctionStrings= : 9. Same.
  • OOMPolicy= : 9. Same.

Directives documented in systemd.exec(5)

Paths

  • ExecSearchPath= : 1.
  • WorkingDirectory= : 2.
  • RootDirectory= : 1.
  • RootImage= : 6. I am discovering these options in real time and shaking my head - systemd still manages to baffle me with the amount of gratuitous ad-hoc that went into it. Still, this is regular low-level programming for Linux, this is less difficult to implement than systemd-specific stuff, that's why it only gets a 6.
  • RootImageOptions= : 6. Same.
  • RootHash= : 9. Please.
  • RootHashSignature= : 9.
  • RootVerity= : 9.
  • MountAPIVFS= : 6.
  • ProtectProc= : 6.
  • ProcSubset= : 6.
  • BindPaths= : 6.
  • BindReadOnlyPaths= : 6.
  • MountImages= : 6.
  • ExtensionImages= : 6.
  • ExtensionDirectories= : 6. It's a full low-level userspace implementation at this point. It's not very complex, the 6 feels accurate, but it's a whole lot of work. This is how systemd maintains its hegemony: not because it does incredible, awe-inspiring magic, but because it substitutes its monolithic self to a whole ecosystem, imposes its own API, and locks users in — people who want to smoothly transition away from systemd need to implement the whole shtick, which is obviously a huge undertaking, as the author of uselessd can confirm.

User/group identity

  • User= : 3. This looks very simple, but is treacherous, because User= is a mash of two very different features: setting users statically (via numeric ID, which don't change meanings between two invocations) and setting users dynamically (via user name, which needs to be interpreted by getpwnam(3) on every invocation of the service. s6 supports both, but since these are two different things, the way to do them in s6 is different (statically setting the UID and GID variables and calling s6-applyuidgid -U, or calling s6-setuidgid). This is the exact kind of small detail that makes writing an automatic converter more difficult than it should be: the systemd unit file syntax is rife with semantic pitfalls, and managing your way across it requires very close attention.
  • Group= : 3. Same.
  • DynamicUser= : 5. Of course this flag has an entirely different meaning from setting users statically or dynamically, or else it would be too easy to understand. No, this flag is about allocating temporary new users, which is a prime example of overengineering.
  • SupplementaryGroups= : 1. Same.
  • PAMName= : 4. Requires PAM command-line utilities.

Capabilities

  • CapabilityBoundingSet= : 4. This requires command-line tools implementing capabilities.
  • AmbientCapabilities= : 4. Same.

Security

  • NoNewPrivileges= : 4. Similar to capabilities, this requires a command-line tool controlling the Linux-specific prctl(2) system call.
  • SecureBits= : 4. Same.

Mandatory access control

  • SELinuxContext= : 5. This requires command-line tools managing SELinux.
  • AppArmorProfile= : 5. Same, with AppArmor.
  • SmackProcessLabel= : 5. Same, with SMACK. systemd needs to know and understand all the Linux security modules in order to interact with them. s6 does not - it expects a service's run script to perform all the needed process state changes and controls before executing into the daemon.

Process properties

  • LimitCPU= : 1.
  • LimitFSIZE= : 1.
  • LimitDATA= : 1.
  • LimitSTACK= : 1.
  • LimitCORE= : 1.
  • LimitRSS= : 1.
  • LimitNOFILE= : 1.
  • LimitAS= : 1.
  • LimitNPROC= : 1.
  • LimitMEMLOCK= : 1.
  • LimitLOCKS= : 2. This resource limit isn't natively supported by s6-softlimit, but it could appear in a future s6 version - and it's easy to add in any case.
  • LimitSIGPENDING= : 2. Same.
  • LimitMSGQUEUE= : 2. Same.
  • LimitNICE= : 2. Same.
  • LimitRTPRIO= : 2. Same.
  • LimitRTTIME= : 2. Same.
  • UMask= : 1.
  • CoredumpFilter= : 4. Requires a command-line tool around prctl(2).
  • KeyringMode= : 5. Requires interaction with PAM and an understanding of keyrings. Fortunately, practically nothing will need that.
  • OOMScoreAdjust= : 2.
  • TimerSlackNSec= : 4. Requires a command-line tool around prctl(2).
  • Personality= : 3. Requires a command-line tool around personality(2).
  • IgnoreSIGPIPE= : 2.

Scheduling

  • Nice= : 1.
  • CPUSchedulingPolicy= : 3. Requires a command-line tool around sched_setscheduler(2).
  • CPUSchedulingPriority= : 3. Same.
  • CPUSchedulingResetOnFork= : 3. Same.
  • CPUAffinity= : 3. Requires a command-line tool around sched_setaffinity(2).
  • NUMAPolicy= : 3. Requires a command-line tool around set_mempolicy(2).
  • NUMAMask= : 3. Same.
  • IOSchedulingClass= : 3. Requires a command-line tool around ioprio_set(2).
  • IOSchedulingPriority= : 3. Same.

Sandboxing

systemd gracefully turns off the sandboxing options on systems that do not support the necessary functionality, so all these options can be ignored (difficulty 0) and the services will still work. However, in order to actually implementing the sandboxing, it is necessary to pair s6 with command-line tools that perform the required process state changes by interfacing with namespaces, cgroups, seccomp, or any other relevant feature of the Linux kernel, so for all these directives the difficulty is somewhere between 2, if the tool already exists, and 8, if it requires writing a full container implementation from scratch. The following numbers are only a rough guess.

  • ProtectSystem= : 6.
  • ProtectHome= : 6.
  • RuntimeDirectory= : 1.
  • StateDirectory= : 1.
  • CacheDirectory= : 1.
  • LogsDirectory= : 1.
  • ConfigurationDirectory= : 1.
  • RuntimeDirectoryMode= : 1.
  • StateDirectoryMode= : 1.
  • CacheDirectoryMode= : 1.
  • LogsDirectoryMode= : 1.
  • ConfigurationDirectoryMode= : 1.
  • RuntimeDirectoryPreserve= : 3.
  • TimeoutCleanSec= : 3.
  • ReadWritePaths= : 5.
  • ReadOnlyPaths= : 5.
  • InaccessiblePaths= : 5.
  • ExecPaths= : 5.
  • NoExecPaths= : 5.
  • TemporaryFileSystem= : 5.
  • PrivateTmp= : 5.
  • PrivateDevices= : 5.
  • PrivateNetwork= : 8.
  • NetworkNamespacePath= : 8.
  • PrivateIPC= : 5.
  • IPCNamespacePath= : 5.
  • PrivateUsers= : 5.
  • ProtectHostname= : 5.
  • ProtectClock= : 7.
  • ProtectKernelTunables= : 7.
  • ProtectKernelModules= : 7.
  • ProtectKernelLogs= : 7.
  • ProtectControlGroups= : 7.
  • RestrictAddressFamilies= : 7.
  • RestrictFilesystems= : 7.
  • RestrictNamespaces= : 7.
  • LockPersonality= : 7.
  • MemoryDenyWriteExecute= : 7.
  • RestrictRealtime= : 7.
  • RestrictSUIDSGID= : 7.
  • RemoveIPC= : 5.
  • PrivateMounts= : 5.

System call filtering

  • SystemCallFilter= : 8.
  • SystemCallErrorNumber= : 8.
  • SystemCallArchitectures= : 8.
  • SystemCallLog= : 8.

Environment

  • Environment= : 1.
  • EnvironmentFile= : 1.
  • PassEnvironment= : 1.
  • UnsetEnvironment= : 1.

Logging and standard input/output

  • StandardInput= : 1.
  • StandardOutput= : 1, except for journal which cannot be supported (10).
  • StandardError= : same.
  • StandardInputText= : 2.
  • StandardInputData= : 2.
  • LogLevelMax= : 3.
  • LogExtraFields= : 10.
  • LogRateLimitIntervalSec= : 10.
  • LogRateLimitBurst= : 10.
  • LogFilterPatterns= : 4.
  • LogNamespace= : 10.
  • SyslogIdentifier= : 2.
  • SyslogFacility= : 2.
  • SyslogLevel= : 2.
  • SyslogLevelPrefix= : 4.
  • TTYPath= : 1.
  • TTYReset= : 3.
  • TTYVHangup= : 3.
  • TTYRows= : 3.
  • TTYColumns= : 3.
  • TTYDisallocate= : 3.

Credentials

systemd implements its own credentials store mechanism, for no obvious benefit. The whole credentials system needs to be reimplemented outside of systemd in order for credentials-related directives to be supported by s6. Consequently, all these directives are rated 6.

System V compatibility

The utmp directives can be made significantly easier to implement if the target system is using utmps, because the directives then become a single call to utmps-write with the relevant options. If utmps cannot be used, then the utmp calls need to be reimplemented.

  • UtmpIdentifier= : 4.
  • UtmpMode= : 4.

Directives documented in systemd.kill(5)

  • KillMode= : depends on the kill mode. control-group and mixed are 3, because they require cgroup control commands (that can be implemented in shell). process is 1. none is 2.
  • KillSignal= : 1.
  • RestartKillSignal= : 5. s6 doesn't use a different signal for restarting; implementing this requires an outer layer. (This is overengineering.)
  • SendSIGHUP= : 5. Same.
  • SendSIGKILL= : 1.
  • FinalKillSignal= : 5. (This is absolute overengineering.)
  • WatchdogSignal= : 7. Only meaningful with an implementation of systemd's watchdog system.

Directives documented in systemd.resource-control(5)

All these directives relate to cgroups, so implementing them means having access to at least some cgroups commands (difficulty at least 3. Others are even more involved, requiring tight service integration with the system. Generally, seeing these directives in your unit files is a bad sign; despite some of them only having 3 or 4 listed, we do not recommend implementing systemd.resource-control(5) without having a full holistic view of the system.

  • CPUAccounting= : 3.
  • CPUWeight= : 4.
  • StartupCPUWeight= : 9. Involves startup and shutdown.
  • CPUQuota= : 4.
  • CPUQuotaPeriodSec= : 4.
  • AllowedCPUs= : 3.
  • StartupAllowedCPUs= : 9. Involves startup and shutdown.
  • AllowedMemoryNodes= : 3.
  • StartupAllowedMemoryNodes= : 9. Involves startup and shutdown.
  • MemoryAccounting= : 3.
  • MemoryMin= : 3.
  • MemoryLow= : 3.
  • MemoryHigh= : 3.
  • MemoryMax= : 3.
  • MemorySwapMax= : 3.
  • MemoryZSwapMax= : 3.
  • TasksAccounting= : 3.
  • TasksMax= : 3.
  • IOAccounting= : 3.
  • IOWeight= : 3.
  • StartupIOWeight= : 9. Involves startup and shutdown.
  • IODeviceWeight= : 5.
  • IOReadBandwidthMax= : 5.
  • IOWriteBandwidthMax= : 5.
  • IOReadIOPSMax= : 5.
  • IOWriteIOPSMax= : 5.
  • IODeviceLatencyTargetSec= : 4.
  • IPAccounting= : 7.
  • IPAddressAllow= : 7.
  • IPAddressDeny= : 7.
  • IPIngressFilterPath= : 8.
  • IPEgressFilterPath= : 8.
  • BPFProgram= : 8.
  • SocketBindAllow= : 6.
  • SocketBindDeny= : 6.
  • RestrictNetworkInterfaces= : 4.
  • DeviceAllow= : 6.
  • DevicePolicy= : 6.
  • Slice= : 9. Slices are a systemd concept, and fully implementing them requires a holistic system analysis.
  • Delegate= : 4.
  • DisableControllers= : 3.
  • ManagedOOMSwap= : 8. Out-of-memory management is an entire systemd subsystem; instead of implementing that, we recommend provisioning your machines correctly, and freeing up more resources for your applications by using s6.

  • ManagedOOMMemoryPressure= : 8.
  • ManagedOOMMemoryPressureLimit= : 8.
  • ManagedOOMPreference= : 8.

Directives rated by difficulty

Use this section to answer the question: "If I were to write a converter tool from systemd to s6, what subset of the unit file syntax should I focus on first?"

Take this classification with a grain of salt: for instance, it does not make sense to implement an easy directive if it's only used in the context of a larger subsystem that's much harder to implement - typically, most cgroups-related resource control directives.

Difficulty: 0 — directives that can be ignored

  • Description=
  • Documentation=
  • Upholds=
  • PropagatesReloadTo=
  • PropagatesStopTo=
  • StopPropagatedFrom=
  • IgnoreOnIsolate=
  • StopWhenUnneeded=
  • AllowIsolate=
  • CollectMode=
  • SourcePath=
  • Type=idle
  • RemainAfterExit=
  • GuessMainPid=
  • ExecReload=
  • TimeoutStartFailureMode=
  • TimeoutStopFailureMode=

Difficulty: 1 — direct functionality mapping

  • RefuseManualStop=
  • FailureActionExitStatus=
  • SuccessActionExitStatus=
  • Type=simple, exec or oneshot
  • ExitType=main
  • TimeoutStartSec=
  • TimeoutStopSec=
  • TimeoutSec=
  • Restart=always
  • RestartPreventExitStatus=
  • NotifyAccess=none
  • ExecSearchPath=
  • RootDirectory=
  • SupplementaryGroups=
  • LimitCPU=
  • LimitFSIZE=
  • LimitDATA=
  • LimitSTACK=
  • LimitCORE=
  • LimitRSS=
  • LimitNOFILE=
  • LimitAS=
  • LimitNPROC=
  • LimitMEMLOCK=
  • UMask=
  • Nice=
  • RuntimeDirectory=
  • StateDirectory=
  • CacheDirectory=
  • LogsDirectory=
  • ConfigurationDirectory=
  • RuntimeDirectoryMode=
  • StateDirectoryMode=
  • CacheDirectoryMode=
  • LogsDirectoryMode=
  • ConfigurationDirectoryMode=
  • Environment=
  • EnvironmentFile=
  • PassEnvironment=
  • UnsetEnvironment=
  • StandardInput=
  • StandardOutput= (except journal)
  • StandardError= (except journal)
  • TTYPath=
  • KillMode=process
  • KillSignal=
  • SendSIGKILL=

Difficulty: 2 — straightforward implementation

  • Requires=
  • Before=
  • After=
  • OnSuccess=
  • DefaultDependencies=
  • Type=forking
  • PIDFile=
  • ExecStart= (omitting some prefixes)
  • ExecStartPre=
  • ExecStartPost=
  • ExecCondition=
  • ExecStopPost=
  • RestartSec=
  • Restart= except on-watchdog
  • RestartForceExitStatus=
  • RootDirectoryStartOnly=
  • WorkingDirectory=
  • LimitLOCKS=
  • LimitSIGPENDING=
  • LimitMSGQUEUE=
  • LimitNICE=
  • LimitRTPRIO=
  • LimitRTTIME=
  • OOMScoreAdjust=
  • IgnoreSIGPIPE=
  • StandardInputText=
  • StandardInputData=
  • SyslogIdentifier=
  • SyslogFacility=
  • SyslogLevel=
  • KillMode=none

Difficulty: 3 — requires easy additional programming

  • Wants=
  • Requisite=
  • BindsTo=
  • PartOf=
  • FailureAction=
  • SuccessAction=
  • StartLimitIntervalSec=
  • StartLimitBurst=
  • StartLimitAction=
  • ExitType=cgroup
  • SuccessExitStatus=
  • User=
  • Group=
  • Personality=
  • CPUSchedulingPolicy=
  • CPUSchedulingPriority=
  • CPUSchedulingResetOnFork=
  • CPUAffinity=
  • NUMAPolicy=
  • NUMAMask=
  • IOSchedulingClass=
  • IOSchedulingPriority=
  • RuntimeDirectoryPreserve=
  • TimeoutCleanSec=
  • LoglevelMax=
  • TTYReset=
  • TTYVHangup=
  • TTYRows=
  • TTYColumns=
  • TTYDisallocate=
  • KillMode=control-group and mixed
  • CPUAccounting=
  • AllowedCPUs=
  • AllowedMemoryNodes=
  • MemoryAccounting=
  • MemoryMin=
  • MemoryLow=
  • MemoryHigh=
  • MemoryMax=
  • MemorySwapMax=
  • MemoryZSwapMax=
  • TasksAccounting=
  • TasksMax=
  • IOAccounting=
  • IOWeight=
  • DisableControllers=

Difficulty: 4 — requires medium additional programming

  • RuntimeMaxSec=
  • RuntimeRandomizedExtraSec=
  • RequiresMountsFor=
  • RefuseManualStart=
  • PAMName=
  • CapabilityBoundingSet=
  • AmbientCapabilities=
  • NoNewPrivileges=
  • SecureBits=
  • CoredumpFilter=
  • TimerSlackNSec=
  • LogFilterPatterns=
  • SyslogLevelPrefix=
  • UtmpIdentifier=
  • UtmpMode=
  • CPUWeight=
  • CPUQuota=
  • CPUQuotaPeriodSec=
  • IODeviceLatencyTargetSec=
  • RestrictNetworkInterfaces=
  • Delegate=

Difficulty: 5 — requires complex additional programming

  • Conflicts=
  • OnFailure=
  • RebootArgument=
  • Type=dbus
  • BusName=
  • ExecStop=
  • NonBlocking=
  • Sockets=
  • DynamicUser=
  • SELinuxContext=
  • AppArmorProfile=
  • SmackProcessLabel=
  • KeyringMode=
  • ReadWritePaths=
  • ReadOnlyPaths=
  • InaccessiblePaths=
  • ExecPaths=
  • NoExecPaths=
  • TemporaryFilesystem=
  • PrivateTmp=
  • PrivateDevices=
  • PrivateIPC=
  • IPCNamespacePath=
  • PrivateUsers=
  • ProtectHostname=
  • RemoveIPC=
  • PrivateMounts=
  • RestartKillSignal=
  • SendSIGHUP=
  • FinalKillSignal=
  • IODeviceWeight=
  • IOReadBandwidthMax=
  • IOWriteBandwidthMax=
  • IOReadIOPSMax=
  • IOWriteIOPSMax=

Difficulty: 6 — requires a full independent implementation of a Linux subsystem and/or a part of the systemd architecture

  • OnFailureJobMode=
  • RootImage=
  • RootImageOptions=
  • MountAPIVFS=
  • ProtectProc=
  • ProcSubset=
  • BindPaths=
  • BindReadOnlyPaths=
  • MountImages=
  • ExtensionImages=
  • ExtensionDirectories=
  • ProtectSystem=
  • ProtectHome=
  • All credentials-related directives
  • SocketBindAllow=
  • SocketBindDeny=
  • DeviceAllow=
  • DevicePolicy=

Difficulty: 7 — requires a full independent implementation of a complex Linux subsystem and/or a significant part of the systemd architecture

  • Type=notify
  • TimeoutAbortSec=
  • TimeoutStartFailureMode=abort
  • TimeoutStopFailureMode=abort
  • WatchdogSec=
  • Restart=on-watchdog
  • NotifyAccess=
  • FileDescriptorStoreMax=
  • ProtectClock=
  • ProtectKernelTunables=
  • ProtectKernelModules=
  • ProtectKernelLogs=
  • ProtectControlGroups=
  • RestrictAddressFamilies=
  • RestrictFilesystems=
  • RestrictNamespaces=
  • LockPersonality=
  • MemoryDenyWriteExecute=
  • RestrictRealtime=
  • RestrictSUIDSGID=
  • WatchdogSignal=
  • IPAccounting=
  • IPAddressAllow=
  • IPAddressDeny=

Difficulty: 8 — requires a complete implementation of complex Linux-specific tooling

  • JoinsNamespaceOf=
  • PrivateNetwork=
  • NetworkNamespacePath=
  • SystemCallFilter=
  • SystemCallErrorNumber=
  • SystemCallArchitectures=
  • SystemCallLog=
  • IPIngressFilterPath=
  • IPEgressFilterPath=
  • BPFProgram=
  • ManagedOOMSwap=
  • ManagedOOMMemoryPressure=
  • ManagedOOMMemoryPressureLimit=
  • ManagedOOMPreference=

Difficulty: 9 — requires a full system analysis and remodelization

  • JobTimeoutSec=
  • JobRunningTimeoutSec=
  • JobTimeoutAction=
  • JobTimeoutRebootArgument=
  • Conditions and Asserts
  • USBFunctionDescriptors=
  • USBFunctionStrings=
  • OOMPolicy=
  • RootHash=
  • RootHashSignature=
  • RootVerity=
  • StartupCPUWeight=
  • StartupAllowedCPUs=
  • StartupAllowedMemoryNodes=
  • StartupIOWeight=
  • Slice=

Difficulty: 10 — implementing this basically means reimplementing systemd

  • StandardOutput=journal
  • StandardError=journal
  • LogExtraFields=
  • LogRateLimitIntervalSec=
  • LogRateLimitBurst=
  • LogNamespace=
s6-2.13.1.0/doc/upgrade.html000066400000000000000000000542401470151141200153470ustar00rootroot00000000000000 How to upgrade s6

s6
Software
skarnet.org

What has changed in s6

in 2.13.1.0

  • skalibs dependency bumped to 2.14.3.0.
  • nsss optional dependency bumped to 0.2.0.5.
  • execline recommended dependency bumped to 2.9.6.1.
  • Static libraries are installed in /usr/lib by default.

in 2.13.0.0

  • skalibs dependency bumped to 2.14.2.0.
  • execline recommended dependency bumped to 2.9.6.0.
  • s6-svstat has new options, -g and -o pgid, to print the pgid of the service.
  • s6-supervise now passes the pgid of the defunct service as 4th argument to the finish script.
  • s6-svc can now send SIGSTOP, SIGCONT and SIGKILL to the service's process group, via the -P, -C and -K options.

in 2.12.0.4

  • skalibs dependency bumped to 2.14.1.1.
  • execline recommended dependency bumped to 2.9.5.0.

in 2.12.0.3

  • skalibs dependency bumped to 2.14.1.0.

in 2.12.0.2

  • No functional changes.

in 2.12.0.1

  • skalibs dependency bumped to 2.14.0.1.

in 2.12.0.0

  • skalibs dependency bumped to 2.14.0.0.
  • nsss optional dependency bumped to 0.2.0.4.
  • execline recommended dependency bumped to 2.9.4.0.
  • New -s option to s6-svc.
  • New -t option to s6-log.
  • s6-svscan rewrite; new options -C and -L.
  • Obsolete s6lockd subsystem removed.
  • s6-setlock now has a -d option to pin the lock to a fixed descriptor. Timeout management also simplified.

in 2.11.3.2

  • skalibs dependency bumped to 2.13.1.1.
  • execline recommended dependency bumped to 2.9.3.0.

in 2.11.3.1

  • execline recommended dependency bumped to 2.9.2.1.

in 2.11.3.0

  • skalibs dependency bumped to 2.13.1.0.
  • nsss optional dependency bumped to 0.2.0.3.
  • execline recommended dependency bumped to 2.9.2.0.
  • New s6-svc command: -Q.

in 2.11.2.0

  • skalibs dependency bumped to 2.13.0.0.
  • nsss optional dependency bumped to 0.2.0.2.
  • execline recommended dependency bumped to 2.9.1.0.
  • New feature: implementation of instances.
  • New s6-svc commands: -D and -U.

in 2.11.1.2

  • execline optional dependency bumped to 2.9.0.1.

in 2.11.1.1

  • skalibs dependency bumped to 2.12.0.0.
  • execline optional dependency bumped to 2.9.0.0.

in 2.11.1.0

  • skalibs dependency bumped to 2.11.2.0.
  • execline optional dependency bumped to 2.8.3.0.

in 2.11.0.1

  • skalibs dependency bumped to 2.11.1.0.
  • nsss optional dependency bumped to 0.2.0.1.
  • execline optional dependency bumped to 2.8.2.0.

in 2.11.0.0

  • skalibs dependency bumped to 2.11.0.0.
  • nsss optional dependency bumped to 0.2.0.0.
  • execline optional dependency bumped to 2.8.1.0.
  • s6-svwait now supports the -r and -R options, to wait for service restarts.
  • The s6/lock.h, s6/supervise.h and s6/fdholder.h headers replace their previous versions that had an extra s6 prefix.
  • New binaries: s6-svlink, s6-svunlink, and s6-socklog.

in 2.10.0.3

  • skalibs dependency bumped to 2.10.0.3.
  • execline dependency bumped to 2.8.0.1.

in 2.10.0.2

  • skalibs dependency bumped to 2.10.0.2.
  • execline dependency bumped to 2.7.0.1.

in 2.10.0.1

  • skalibs dependency bumped to 2.10.0.1.

in 2.10.0.0

  • skalibs dependency bumped to 2.10.0.0.
  • nsss optional dependency bumped to 0.1.0.0.
  • execline dependency bumped to 2.7.0.0.
  • Commands received by s6-svscan and sent by s6-svscanctl have changed significantly. Different semantics of SIGHUP for s6-svscan.
  • Commands received by s6-supervise and sent by s6-svc have changed slightly (no s6-svc -X anymore). Different semantics of SIGHUP and SIGQUIT for s6-supervise.
  • s6-supervise now handles SIGINT appropriately, by forwarding it to its child's process group.
  • The nosetsid file is not recognized anymore in service directories. s6-supervise now always starts it child as a session leader.
  • Split permissions on service control are now officially supported. New binary: s6-svperms.
  • New program that creates service directories to run a supervision tree managed by a user: s6-usertree-maker.

in 2.9.2.0

in 2.9.1.0

  • skalibs dependency bumped to 2.9.2.0.
  • execline dependency bumped to 2.6.0.0. (And made optional, see below.)
  • The execline dependency is now optional, and can be disabled via the --disable-execline option to configure. Functionality that depends on execline in s6 binaries is disabled when execline support is disabled.
  • A new '?' directive has been added to s6-log, it declares a processor command line that is spawned with the /bin/sh interpreter.
  • A new -X option has been added to s6-svscan to better support s6-linux-init installations.
  • s6-svscan now handles SIGPWR and SIGWINCH, when diverted, on systems that define those signals.

in 2.9.0.1

  • skalibs dependency bumped to 2.9.1.0.
  • execline dependency bumped to 2.5.3.0.

in 2.9.0.0

  • skalibs dependency bumped to 2.9.0.0.
  • execline dependency bumped to 2.5.2.0.
  • All the s6-fdholder-*c binaries have been removed.

in 2.8.0.1

  • skalibs dependency bumped to 2.8.1.0.

in 2.8.0.0

  • skalibs dependency bumped to 2.8.0.0.
  • execline dependency bumped to 2.5.1.0.
  • New -d notif option to s6-log.
  • New default for the -l linelimit option to s6-log: 8192 bytes.
  • In the accessrules library, checking against uid/gid now checks the uid/self key if the client and the server have the same uid, and the gid/self key if the client and the server have the same gid.
  • Everything now builds as PIC by default no matter the toolchain's settings. Use the --disable-all-pic configure option to build executables and static libraries as non-PIC.
  • This is a major version release of s6. It is recommended to first stop all your s6 services, then upgrade, then immediately restart your supervision tree, then start your services again. Failure to do so *will* cause issues if you're relying on s6-ftrigrd notifications (which is the case, for instance, with services managed by s6-rc).

in 2.7.2.2

  • No functional changes.

in 2.7.2.1

  • No functional changes.

in 2.7.2.0

in 2.7.1.1

  • No functional changes.

in 2.7.1.0

in 2.7.0.0

  • skalibs dependency bumped to 2.6.3.0.
  • s6lockd and s6-ftrigrd now use the new textclient API and ABI from skalibs, instead of the more generic skaclient. This is cleaner, faster code. (s6-fdholderd still uses skaclient, since its point is to transmit file descriptors, which textclient does not support.)

in 2.6.2.0

  • skalibs dependency bumped to 2.6.2.0.
  • execline dependency bumped to 2.3.0.4.
  • New s6_fdholder functions: s6_fdholder_start(), s6_fdholder_end().

in 2.6.1.1

  • skalibs dependency bumped to 2.6.0.1.
  • execline dependency bumped to 2.3.0.3.

in 2.6.1.0

  • Additions of a few interfaces to libs6 in order to fix a race condition in s6-rc.
  • New program: s6-notifyoncheck.

in 2.6.0.0

  • skalibs dependency bumped to 2.5.1.1.

in 2.5.1.0

  • skalibs dependency bumped to 2.5.1.0.
  • execline dependency bumped to 2.3.0.1.
  • s6-ftrig-listen1 prints the last event it received to stdout.
  • The timeout-kill file is now recognized in a service directory and allows s6-svc -d to send a SIGKILL after a timeout if the process is still not dead after the initial SIGTERM and SIGCONT.

in 2.5.0.0

  • skalibs dependency bumped to 2.5.0.0.
  • execline dependency bumped to 2.3.0.0.
  • Services can now report permanent failure by having ./finish exit 125.
  • s6-svwait, s6-svlisten and s6-svlisten1 now exit 99 when they time out. They exit n when there are n services reporting permanent failure.

in 2.4.0.0

  • skalibs dependency bumped to 2.4.0.0.
  • execline dependency bumped to 2.2.0.0.
  • s6-ipcserver-socketbinder can now create datagram sockets.

in 2.3.0.0

  • skalibs dependency bumped to 2.3.10.0.
  • execline dependency bumped to 2.2.5.0.
  • s6-svscan now defaults to -t0 instead of -t5000.

in 2.2.4.3

  • skalibs dependency bumped to 2.3.9.0.

in 2.2.4.2

  • No functional changes

in 2.2.4.1

  • s6-envuidgid behaviour fixed to be compatible with 2.2.3.1 and previous releases.

in 2.2.4.0

  • Additional options to s6-envuidgid for more flexibility
  • skalibs dependency bumped to 2.3.8.3.
  • execline dependency bumped to 2.1.4.5.

in 2.2.3.1

  • No functional changes

in 2.2.3.0

  • Processor scripts for s6-log now switch their working directory to the logdir they process.
  • skalibs dependency bumped to 2.3.8.2.
  • execline dependency bumped to 2.1.4.4.

in 2.2.2.2

  • No functional changes

in 2.2.2.1

  • No functional changes

in 2.2.2.0

  • s6-svscan now supports the -s option for configurable signal management.
  • GNU make dependency pushed back to 3.81.
  • skalibs dependency bumped to 2.3.8.0.
  • execline dependency bumped to 2.1.4.2.

in 2.2.1.1

  • skalibs dependency bumped to 2.3.7.1.
  • execline dependency bumped to 2.1.4.1.

in 2.2.1.0

  • skalibs dependency bumped to 2.3.7.0.
  • execline dependency bumped to 2.1.4.0.
  • s6-svc, s6-svlisten1 and s6-svlisten support new -wr and -wR options.

in 2.2.0.1

  • skalibs dependency bumped to 2.3.6.1.
  • execline dependency bumped to 2.1.3.1.

in 2.2.0.0

  • skalibs dependency bumped to 2.3.6.0.
  • execline dependency bumped to 2.1.3.0.
  • The internals of s6-supervise have changed; the supervise/status file ABI has changed and is not compatible with the daemontools/runit supervise/status files anymore. (This should not impact anything.)
  • s6-supervise features a configurable timeout for ./finish scripts, via the ./timeout-finish file.
  • s6-svwait, s6-svlisten1 and s6-svlisten can now wait for a 'D' event, which means the ./finish script has terminated.
  • The deprecated s6-notifywhenup program has been removed.
  • The syntax for synchronous s6-svc waiting has changed.

in 2.1.6.0

  • s6-svc now checks for the existence of notification-fd before accepting a U command.
  • New -X option to s6-svc.
  • Different meaning of SIGQUIT for s6-supervise: now the same as s6-svc -X.

in 2.1.5.0

  • s6-log now exits cleanly on SIGHUP.

in 2.1.4.0

in 2.1.3.0

  • skalibs dependency bumped to 2.3.2.0.
  • execline dependency bumped to 2.1.1.0.
  • New options to s6-envuidgid.

in 2.1.2.0

in 2.1.1.2

  • skalibs dependency bumped to 2.3.1.0.
  • execline dependency bumped to 2.1.0.0.
  • Apart from that, this is a bugfix release. (The bump in dependencies is part of the bugfix.)

in 2.1.1.1

Bugfix release, no important changes.

in 2.1.1.0

  • skalibs dependency bumped to 2.3.0.0.
  • execline dependency bumped to 2.0.2.1.
  • New functionality added to s6-log. Its syntax has been extended; some options are now marked as obsolescent.
  • Access control instated in s6-fdholderd for listing

in 2.1.0.1

Bugfix release, no important changes.

in 2.1.0.0

  • skalibs dependency bumped to 2.2.1.0.
  • execline dependency bumped to 2.0.2.0.
  • Unix domain socket utilities moved from the s6-networking package to s6.
  • ./finish argument for "service crashed" is now 256 instead of 255. Check your finish scripts!
  • supervise/status now includes wstat information when the service died, and s6-svstat reports that information.
  • s6-svstat now reports symbolic signal information by default, use -n to get the old behaviour.
  • s6-log now properly calls execlineb with its installation prefix, in the slashpackage case.
  • New commands: s6-svlisten and s6-svlisten1, to signal a service and wait on it without race condition.
  • A brand new set of programs, s6-fdholder*, to perform fd-holding.
  • Catchy blurb on intro page.

in 2.0.1.0

  • skalibs dependency bumped to 2.2.0.0.
  • execline dependency bumped to 2.0.1.1.
  • Better readiness notification management via s6-svwait -U.

in 2.0.0.1

  • skalibs dependency bumped to 2.1.0.0.

in 2.0.0.0

  • The build system has completely changed. It is now a standard ./configure && make && sudo make install build system. See the enclosed INSTALL file for details.
  • slashpackage is not activated by default.
  • shared libraries are not used by default.
  • skalibs dependency bumped to 2.0.0.0.
  • execline dependency bumped to 2.0.0.0.
  • s6-supervise does not create the ./event fifodir as public. Subscriptions are restricted to users having the same gid as the s6-supervise process.
  • The s6-notifywhenup program is a new addition.
s6-2.13.1.0/doc/why.html000066400000000000000000000264241470151141200145320ustar00rootroot00000000000000 s6: why another supervision suite

s6
Software
skarnet.org

Why another supervision suite ?

Supervision suites are becoming quite common. Today, we already have:

  • Good (?) old System V init, which can be made to supervise services if you perform /etc/inittab voodoo. BSD init can also be used the same way with the /etc/ttys file, but for some reason, nobody among BSD developers is using /etc/ttys to this purpose, so I won't consider BSD init here.
  • daemontools, the pioneer
  • daemontools-encore, Bruce Guenter's upgrade to daemontools
  • runit, Gerrit Pape's suite, well-integrated with Debian
  • perp, Wayne Marshall's take on supervision
  • Integrated init systems providing a lot of features, process supervision being one of them. For instance, Upstart, MacOS X's launchd, and Fedora's systemd.

Why is s6 needed ? What does it do differently ? Here are the criteria I used.

Supervision suites should not wake up unless notified.

  • System V init fails the test: it wakes up every 5 seconds, for the reason that /dev/initctl might have changed. m(
  • daemontools fails the test: it wakes up every 5 seconds to check for new services.
  • daemontools-encore does the same.
  • the current version of runit fails the test: it wakes up every 14 seconds. But this is a workaround for a bug in some Linux kernels; there is no design flaw in runit that prevents it from passing the test.
  • perp works.
  • Upstart works. I have no idea what other integrated init systems do: it's much too difficult to strace them to see exactly where they're spending their time, and when it is possible, the trace output is so big that it's hard to extract any valuable information from it.
  • s6 works. The -t option to s6-svscan makes it check its services with a configurable timeout; by default, this timeout is infinite, i.e. it never wakes up unless it receives a command via s6-svscanctl.

Supervision suites should provide a program that can run as process 1.

  • System V init is process 1, so no problem here.
  • Integrated init systems, by definition, provide a process 1.
  • daemontools was not designed to take over init, although it can be made to work with enough hacking skills. Same thing with daemontools-encore.
  • runit provides an init functionality, but the mechanism is separate from the supervision itself; the runit process, not the runsvdir process, runs as process 1. This lengthens the supervision chain.
  • perp was not designed to run as process 1. It probably could be made to work too without too much trouble.
  • s6-svscan was designed from the start to be run as process 1, although it does not have to.

Supervision suites should be bug-free, lightweight and easy to understand.

  • daemontools, daemontools-encore, runit and perp all qualify. All of this is excellent quality code, unsurprisingly.
  • System V init is understandable, and reasonably lightweight; but it is still too big for what it does - poorly. The /etc/inittab file needs to be parsed; that parser has to be in process 1. There is support in process 1 for the whole "runlevel" concept, which is a primitive form of service management. The same executable handles all 3 stages of the machine's lifetime and does not separate them properly. All in all, System V init does its job, but is showing its age and nowadays we know much better designs.
  • This is where integrated init systems fail, hard. By wanting to organize the way a the machine is operated - so, machine state management - in the same package as the init and process supervision system, they add incredible complexity where it does not belong.
    • Upstart uses ptrace to watch its children fork(), and links process 1 against libdbus. This is insane. Process 1 should be absolutely stable, it should be guaranteed to never crash, so the whole of its source code should be under control. At Upstart's level of complexity, those goals are outright impossible to achieve, so this approach is flawed by design. It is a shame, because the concepts and ideas behind Upstart are good and sound; it's the implementation choices that are its downfall.
    • launchd suffers from the same kind of problems. Example: Services running under launchd must be configured using XML; the launchctl process interprets the XML, converts it into a key-value store (which is strictly less powerful than XML, so why do they even use XML in the first place?) and sends it to launchd via a Mach-specific IPC. Process 1 needs to be linked against the library that handles the Mach IPC, it needs to decode the key-value store, and use it to run and supervise a daemon. And it needs to keep everything in memory. This is a lot more complex and resource-consuming than it needs to be.
    • systemd is much, much worse than the other ones, and a real danger for the future of GNU/Linux. I have a special page dedicated to it.
    What those systems fail to recognize is that process supervision, rooted in process 1, is a good thing, and machine management is also a good thing, but those are two different functions, and a good init system needs, and should, only provide process supervision, in order to keep such a crucial piece of code as easy to maintain as possible. Machine management can be added on top of a process supervision suite, in a different package, and it has nothing to do with process 1.
  • s6, which has been designed with embedded environments in mind, tries harder than anyone to pass this. It tries so hard that s6-svscan and s6-supervise, the two long-running programs that make the supervision chain, do not even allocate heap memory, and their main program source files are less than 500 lines long.

Supervision suites should provide a basis for high-level service management.

  • Neither System V init, daemontools, runit or perp provides any hooks to wait for a service to go up or down. runit provides a waiting mechanism, but it's based on polling, and the ./check script has to be manually written for every service.
  • daemontools-encore qualifies: the notify script can be used for inter-service communication. But it's just a hook: all the real notification work has to be done by the notify script itself, no notification framework is provided.
  • Integrated init systems provide high-level service management themselves. Again, this is not good design: service management has nothing to do with init or process supervision, and should be implemented on top of it, not as a part of it.
  • s6 comes with an event notification library, and command-line tools based on this library, thus providing a simple API for future service management tools to build upon.

Artistic considerations

  • s6-svscan and s6-supervise are entirely asynchronous. Even during trouble (full process table, for instance), they'll remain reactive and instantly respond to commands they may receive. s6-supervise has even been implemented as a full deterministic finite automaton, to ensure it always does the right thing under any circumstance. Other supervision suites do not achieve that for now.
  • daemontools' svscan maintains an open pipe between a daemon and its logger, so even if the daemon, the logger, and both supervise processes die, the pipe is still the same so no logs are lost, ever, unless svscan itself dies.
  • runit has only one supervisor, runsv, for both a daemon and its logger. The pipe is maintained by runsv. If the runsv process dies, the pipe disappears and logs are lost. So, runit does not offer as strong a guarantee as daemontools.
  • perp has only one process, perpd, acting both as a "daemon and logger supervisor" (like runsv) and as a "service directory scanner" (like runsvdir). It maintains the pipes between the daemons and their respective loggers. If perpd dies, everything is lost. Since perpd cannot be run as process 1, this is a possible SPOF for a perp installation; however, perpd is well-written and has virtually no risk of dying, especially compared to process 1 behemoths provided by integrated init systems.
  • Besides, the runsv model, which has to handle both a daemon and its logger, is more complex than the supervise model (which only has to handle a daemon). Consequently, the runsvdir model is simpler than the svscan model, but there is only one svscan instance when there are several runsvs and supervises. The perpd model is obviously the most complex; while very understandable, perpd is unarguably harder to maintain than the other two.
  • So, to achieve maximum simplicity and code reuse, and minimal memory footprint, s6's design is close to daemontools' one. And when s6-svscan is run as process 1, pipes between daemons and loggers are never lost.

Conclusion

All in all, I believe that s6 offers the best overall implementation of a supervision suite as it should be designed. At worst, it's just another take on daemontools with a reliable base library and a few nifty features.

s6-2.13.1.0/examples/000077500000000000000000000000001470151141200140765ustar00rootroot00000000000000s6-2.13.1.0/examples/klogd-linux/000077500000000000000000000000001470151141200163335ustar00rootroot00000000000000s6-2.13.1.0/examples/klogd-linux/README000066400000000000000000000003311470151141200172100ustar00rootroot00000000000000This is an example of a service directory for process supervision by s6. This klogd emulation is only valid under Linux. The service only processes logs from /proc/kmsg and sends them to stdout, i.e. its own logger. s6-2.13.1.0/examples/klogd-linux/log/000077500000000000000000000000001470151141200171145ustar00rootroot00000000000000s6-2.13.1.0/examples/klogd-linux/log/README000066400000000000000000000001031470151141200177660ustar00rootroot00000000000000Processed kernel logs will be logged to the /var/log/klogd logdir. s6-2.13.1.0/examples/klogd-linux/log/notification-fd000066400000000000000000000000021470151141200221040ustar00rootroot000000000000003 s6-2.13.1.0/examples/klogd-linux/log/run000077500000000000000000000001271470151141200176460ustar00rootroot00000000000000#!/bin/execlineb -P s6-setuidgid klog exec -c s6-log -d3 t s1000000 n20 /var/log/klogd s6-2.13.1.0/examples/klogd-linux/run000077500000000000000000000001151470151141200170620ustar00rootroot00000000000000#!/bin/execlineb -S0 fdmove -c 2 1 redirfd -r 0 /proc/kmsg exec -c ucspilogd s6-2.13.1.0/examples/s6-svscanboot000077500000000000000000000002771470151141200165410ustar00rootroot00000000000000#!/bin/execlineb -P /bin/s6-setsid -qb /bin/redirfd -r 0 /dev/null /bin/redirfd -wnb 1 /run/service/s6-svscan-log/fifo /bin/fdmove -c 2 1 /bin/exec -ca s6-svscan /bin/s6-svscan /run/service s6-2.13.1.0/examples/syslogd/000077500000000000000000000000001470151141200155625ustar00rootroot00000000000000s6-2.13.1.0/examples/syslogd/README000066400000000000000000000003061470151141200164410ustar00rootroot00000000000000This is an example of a service directory for process supervision by s6. This syslogd emulation works on any Unix where syslog() is implemented via a connection on the /dev/log Unix-domain socket. s6-2.13.1.0/examples/syslogd/log/000077500000000000000000000000001470151141200163435ustar00rootroot00000000000000s6-2.13.1.0/examples/syslogd/log/README000066400000000000000000000003041470151141200172200ustar00rootroot00000000000000 This logger service logs everything the syslogd service receives into subdirectories of /var/log/syslogd logdir. It emulates the default syslogd behaviour on a majority of Linux distributions. s6-2.13.1.0/examples/syslogd/log/notification-fd000066400000000000000000000000021470151141200213330ustar00rootroot000000000000003 s6-2.13.1.0/examples/syslogd/log/run000077500000000000000000000012731470151141200171000ustar00rootroot00000000000000#!/bin/execlineb -P s6-setuidgid syslog multisubstitute { define T t define dir /var/log/syslogd } exec -c s6-log -d3 -- - +^auth\\. +^authpriv\\. $T ${dir}/secure - +^cron\\. $T ${dir}/cron - +^daemon\\. $T ${dir}/daemon - +^[[:alnum:]]*\\.debug: $T ${dir}/debug - +^[[:alnum:]]*\\.err: +^[[:alnum:]]*\\.error: +^[[:alnum:]]*\\.emerg: +^[[:alnum:]]*\\.alert: +^[[:alnum:]]*\\.crit: $T ${dir}/errors - +^kern\\. $T ${dir}/kernel - +mail\\. $T ${dir}/mail - +user\\. $T ${dir}/user - +^[[:alnum:]]*\\.info: +^[[:alnum:]]*\\.notice: +^[[:alnum:]]*\\.warn: -^auth\\. -^authpriv\\. -^cron\\. -daemon\\. -mail\\. $T ${dir}/messages + -^auth\\. -^authpriv\\. $T ${dir}/everything s6-2.13.1.0/examples/syslogd/notification-fd000066400000000000000000000000021470151141200205520ustar00rootroot000000000000003 s6-2.13.1.0/examples/syslogd/run000077500000000000000000000001201470151141200163050ustar00rootroot00000000000000#!/bin/execlineb -S0 fdmove -c 2 1 s6-envuidgid nobody s6-socklog -d3 -U -t3000 s6-2.13.1.0/package/000077500000000000000000000000001470151141200136535ustar00rootroot00000000000000s6-2.13.1.0/package/deps-build000066400000000000000000000001301470151141200156200ustar00rootroot00000000000000/package/prog/skalibs /package/admin/nsss $usensss /package/admin/execline $useexecline s6-2.13.1.0/package/deps.mak000066400000000000000000000767051470151141200153170ustar00rootroot00000000000000# # This file has been generated by tools/gen-deps.sh # src/include/s6/compat.h: src/include/s6/config.h src/include/s6/ftrigr.h: src/include/s6/config.h src/include/s6/lock.h: src/include/s6/config.h src/include/s6/s6.h: src/include/s6/accessrules.h src/include/s6/auto.h src/include/s6/compat.h src/include/s6/ftrigr.h src/include/s6/ftrigw.h src/include/s6/lock.h src/include/s6/supervise.h src/supervision/s6-svlisten.h: src/include/s6/ftrigr.h src/conn-tools/s6-accessrules-cdb-from-fs.o src/conn-tools/s6-accessrules-cdb-from-fs.lo: src/conn-tools/s6-accessrules-cdb-from-fs.c src/conn-tools/s6-accessrules-fs-from-cdb.o src/conn-tools/s6-accessrules-fs-from-cdb.lo: src/conn-tools/s6-accessrules-fs-from-cdb.c src/conn-tools/s6-connlimit.o src/conn-tools/s6-connlimit.lo: src/conn-tools/s6-connlimit.c src/conn-tools/s6-ioconnect.o src/conn-tools/s6-ioconnect.lo: src/conn-tools/s6-ioconnect.c src/conn-tools/s6-ipcclient.o src/conn-tools/s6-ipcclient.lo: src/conn-tools/s6-ipcclient.c src/conn-tools/s6-ipcserver-access.o src/conn-tools/s6-ipcserver-access.lo: src/conn-tools/s6-ipcserver-access.c src/include/s6/accessrules.h src/include/s6/config.h src/conn-tools/s6-ipcserver-socketbinder.o src/conn-tools/s6-ipcserver-socketbinder.lo: src/conn-tools/s6-ipcserver-socketbinder.c src/conn-tools/s6-ipcserver.o src/conn-tools/s6-ipcserver.lo: src/conn-tools/s6-ipcserver.c src/include/s6/config.h src/conn-tools/s6-ipcserverd.o src/conn-tools/s6-ipcserverd.lo: src/conn-tools/s6-ipcserverd.c src/conn-tools/s6-sudo.o src/conn-tools/s6-sudo.lo: src/conn-tools/s6-sudo.c src/include/s6/config.h src/conn-tools/s6-sudoc.o src/conn-tools/s6-sudoc.lo: src/conn-tools/s6-sudoc.c src/conn-tools/s6-sudo.h src/conn-tools/s6-sudod.o src/conn-tools/s6-sudod.lo: src/conn-tools/s6-sudod.c src/conn-tools/s6-sudo.h src/daemontools-extras/lolsyslog.o src/daemontools-extras/lolsyslog.lo: src/daemontools-extras/lolsyslog.c src/daemontools-extras/lolsyslog.h src/daemontools-extras/s6-applyuidgid.o src/daemontools-extras/s6-applyuidgid.lo: src/daemontools-extras/s6-applyuidgid.c src/daemontools-extras/s6-envdir.o src/daemontools-extras/s6-envdir.lo: src/daemontools-extras/s6-envdir.c src/daemontools-extras/s6-envuidgid.o src/daemontools-extras/s6-envuidgid.lo: src/daemontools-extras/s6-envuidgid.c src/daemontools-extras/s6-fghack.o src/daemontools-extras/s6-fghack.lo: src/daemontools-extras/s6-fghack.c src/daemontools-extras/s6-log.o src/daemontools-extras/s6-log.lo: src/daemontools-extras/s6-log.c src/include/s6/config.h src/daemontools-extras/s6-setlock.o src/daemontools-extras/s6-setlock.lo: src/daemontools-extras/s6-setlock.c src/include/s6/config.h src/daemontools-extras/s6-setsid.o src/daemontools-extras/s6-setsid.lo: src/daemontools-extras/s6-setsid.c src/daemontools-extras/s6-setuidgid.o src/daemontools-extras/s6-setuidgid.lo: src/daemontools-extras/s6-setuidgid.c src/include/s6/config.h src/daemontools-extras/s6-socklog.o src/daemontools-extras/s6-socklog.lo: src/daemontools-extras/s6-socklog.c src/daemontools-extras/lolsyslog.h src/daemontools-extras/s6-softlimit.o src/daemontools-extras/s6-softlimit.lo: src/daemontools-extras/s6-softlimit.c src/daemontools-extras/s6-tai64n.o src/daemontools-extras/s6-tai64n.lo: src/daemontools-extras/s6-tai64n.c src/daemontools-extras/s6-tai64nlocal.o src/daemontools-extras/s6-tai64nlocal.lo: src/daemontools-extras/s6-tai64nlocal.c src/daemontools-extras/ucspilogd.o src/daemontools-extras/ucspilogd.lo: src/daemontools-extras/ucspilogd.c src/daemontools-extras/lolsyslog.h src/fdholder/s6-fdholder-daemon.o src/fdholder/s6-fdholder-daemon.lo: src/fdholder/s6-fdholder-daemon.c src/include/s6/config.h src/fdholder/s6-fdholder-delete.o src/fdholder/s6-fdholder-delete.lo: src/fdholder/s6-fdholder-delete.c src/include/s6/fdholder.h src/fdholder/s6-fdholder-getdump.o src/fdholder/s6-fdholder-getdump.lo: src/fdholder/s6-fdholder-getdump.c src/include/s6/fdholder.h src/fdholder/s6-fdholder-list.o src/fdholder/s6-fdholder-list.lo: src/fdholder/s6-fdholder-list.c src/include/s6/fdholder.h src/fdholder/s6-fdholder-retrieve.o src/fdholder/s6-fdholder-retrieve.lo: src/fdholder/s6-fdholder-retrieve.c src/include/s6/fdholder.h src/fdholder/s6-fdholder-setdump.o src/fdholder/s6-fdholder-setdump.lo: src/fdholder/s6-fdholder-setdump.c src/include/s6/fdholder.h src/fdholder/s6-fdholder-store.o src/fdholder/s6-fdholder-store.lo: src/fdholder/s6-fdholder-store.c src/include/s6/fdholder.h src/fdholder/s6-fdholder-transferdump.o src/fdholder/s6-fdholder-transferdump.lo: src/fdholder/s6-fdholder-transferdump.c src/include/s6/fdholder.h src/fdholder/s6-fdholderd.o src/fdholder/s6-fdholderd.lo: src/fdholder/s6-fdholderd.c src/include/s6/accessrules.h src/include/s6/fdholder.h src/instance/s6-instance-control.o src/instance/s6-instance-control.lo: src/instance/s6-instance-control.c src/include/s6/config.h src/instance/s6-instance-create.o src/instance/s6-instance-create.lo: src/instance/s6-instance-create.c src/include/s6/supervise.h src/instance/s6-instance-delete.o src/instance/s6-instance-delete.lo: src/instance/s6-instance-delete.c src/include/s6/supervise.h src/instance/s6-instance-list.o src/instance/s6-instance-list.lo: src/instance/s6-instance-list.c src/include/s6/supervise.h src/instance/s6-instance-maker.o src/instance/s6-instance-maker.lo: src/instance/s6-instance-maker.c src/include/s6/auto.h src/include/s6/config.h src/instance/s6-instance-status.o src/instance/s6-instance-status.lo: src/instance/s6-instance-status.c src/include/s6/config.h src/libs6/ftrig1_free.o src/libs6/ftrig1_free.lo: src/libs6/ftrig1_free.c src/libs6/ftrig1.h src/libs6/ftrig1_make.o src/libs6/ftrig1_make.lo: src/libs6/ftrig1_make.c src/libs6/ftrig1.h src/libs6/ftrigr1_zero.o src/libs6/ftrigr1_zero.lo: src/libs6/ftrigr1_zero.c src/include/s6/ftrigr.h src/libs6/ftrigr_ack.o src/libs6/ftrigr_ack.lo: src/libs6/ftrigr_ack.c src/include/s6/ftrigr.h src/libs6/ftrigr_check.o src/libs6/ftrigr_check.lo: src/libs6/ftrigr_check.c src/include/s6/ftrigr.h src/libs6/ftrigr_checksa.o src/libs6/ftrigr_checksa.lo: src/libs6/ftrigr_checksa.c src/include/s6/ftrigr.h src/libs6/ftrigr_end.o src/libs6/ftrigr_end.lo: src/libs6/ftrigr_end.c src/include/s6/ftrigr.h src/libs6/ftrigr_start.o src/libs6/ftrigr_start.lo: src/libs6/ftrigr_start.c src/include/s6/ftrigr.h src/libs6/ftrigr_startf.o src/libs6/ftrigr_startf.lo: src/libs6/ftrigr_startf.c src/include/s6/ftrigr.h src/libs6/ftrigr_subscribe.o src/libs6/ftrigr_subscribe.lo: src/libs6/ftrigr_subscribe.c src/include/s6/ftrigr.h src/libs6/ftrigr_unsubscribe.o src/libs6/ftrigr_unsubscribe.lo: src/libs6/ftrigr_unsubscribe.c src/include/s6/ftrigr.h src/libs6/ftrigr_update.o src/libs6/ftrigr_update.lo: src/libs6/ftrigr_update.c src/include/s6/ftrigr.h src/libs6/ftrigr_updateb.o src/libs6/ftrigr_updateb.lo: src/libs6/ftrigr_updateb.c src/include/s6/ftrigr.h src/libs6/ftrigr_wait_and.o src/libs6/ftrigr_wait_and.lo: src/libs6/ftrigr_wait_and.c src/include/s6/ftrigr.h src/libs6/ftrigr_wait_or.o src/libs6/ftrigr_wait_or.lo: src/libs6/ftrigr_wait_or.c src/include/s6/ftrigr.h src/libs6/ftrigr_zero.o src/libs6/ftrigr_zero.lo: src/libs6/ftrigr_zero.c src/include/s6/ftrigr.h src/libs6/ftrigw_clean.o src/libs6/ftrigw_clean.lo: src/libs6/ftrigw_clean.c src/libs6/ftrig1.h src/include/s6/ftrigw.h src/libs6/ftrigw_fifodir_make.o src/libs6/ftrigw_fifodir_make.lo: src/libs6/ftrigw_fifodir_make.c src/include/s6/ftrigw.h src/libs6/ftrigw_notify.o src/libs6/ftrigw_notify.lo: src/libs6/ftrigw_notify.c src/include/s6/ftrigw.h src/libs6/ftrigw_notifyb.o src/libs6/ftrigw_notifyb.lo: src/libs6/ftrigw_notifyb.c src/include/s6/ftrigw.h src/libs6/ftrigw_notifyb_nosig.o src/libs6/ftrigw_notifyb_nosig.lo: src/libs6/ftrigw_notifyb_nosig.c src/libs6/ftrig1.h src/include/s6/ftrigw.h src/libs6/s6-ftrigrd.o src/libs6/s6-ftrigrd.lo: src/libs6/s6-ftrigrd.c src/libs6/ftrig1.h src/include/s6/ftrigr.h src/libs6/s6_accessrules_backend_cdb.o src/libs6/s6_accessrules_backend_cdb.lo: src/libs6/s6_accessrules_backend_cdb.c src/include/s6/accessrules.h src/libs6/s6_accessrules_backend_fs.o src/libs6/s6_accessrules_backend_fs.lo: src/libs6/s6_accessrules_backend_fs.c src/include/s6/accessrules.h src/libs6/s6_accessrules_keycheck_ip4.o src/libs6/s6_accessrules_keycheck_ip4.lo: src/libs6/s6_accessrules_keycheck_ip4.c src/include/s6/accessrules.h src/libs6/s6_accessrules_keycheck_ip6.o src/libs6/s6_accessrules_keycheck_ip6.lo: src/libs6/s6_accessrules_keycheck_ip6.c src/include/s6/accessrules.h src/libs6/s6_accessrules_keycheck_reversedns.o src/libs6/s6_accessrules_keycheck_reversedns.lo: src/libs6/s6_accessrules_keycheck_reversedns.c src/include/s6/accessrules.h src/libs6/s6_accessrules_keycheck_uidgid.o src/libs6/s6_accessrules_keycheck_uidgid.lo: src/libs6/s6_accessrules_keycheck_uidgid.c src/include/s6/accessrules.h src/libs6/s6_accessrules_params_free.o src/libs6/s6_accessrules_params_free.lo: src/libs6/s6_accessrules_params_free.c src/include/s6/accessrules.h src/libs6/s6_accessrules_uidgid_cdb.o src/libs6/s6_accessrules_uidgid_cdb.lo: src/libs6/s6_accessrules_uidgid_cdb.c src/include/s6/accessrules.h src/libs6/s6_accessrules_uidgid_fs.o src/libs6/s6_accessrules_uidgid_fs.lo: src/libs6/s6_accessrules_uidgid_fs.c src/include/s6/accessrules.h src/libs6/s6_auto_write_logger.o src/libs6/s6_auto_write_logger.lo: src/libs6/s6_auto_write_logger.c src/include/s6/auto.h src/libs6/s6_auto_write_logger_tmp.o src/libs6/s6_auto_write_logger_tmp.lo: src/libs6/s6_auto_write_logger_tmp.c src/include/s6/auto.h src/libs6/s6_auto_write_logrun.o src/libs6/s6_auto_write_logrun.lo: src/libs6/s6_auto_write_logrun.c src/include/s6/auto.h src/libs6/s6_auto_write_logrun_tmp.o src/libs6/s6_auto_write_logrun_tmp.lo: src/libs6/s6_auto_write_logrun_tmp.c src/include/s6/auto.h src/include/s6/config.h src/libs6/s6_auto_write_service.o src/libs6/s6_auto_write_service.lo: src/libs6/s6_auto_write_service.c src/include/s6/auto.h src/libs6/s6_compat_el_semicolon.o src/libs6/s6_compat_el_semicolon.lo: src/libs6/s6_compat_el_semicolon.c src/include/s6/config.h src/libs6/s6_dtally_pack.o src/libs6/s6_dtally_pack.lo: src/libs6/s6_dtally_pack.c src/include/s6/supervise.h src/libs6/s6_dtally_read.o src/libs6/s6_dtally_read.lo: src/libs6/s6_dtally_read.c src/include/s6/supervise.h src/libs6/s6_dtally_unpack.o src/libs6/s6_dtally_unpack.lo: src/libs6/s6_dtally_unpack.c src/include/s6/supervise.h src/libs6/s6_dtally_write.o src/libs6/s6_dtally_write.lo: src/libs6/s6_dtally_write.c src/include/s6/supervise.h src/libs6/s6_fdholder_delete.o src/libs6/s6_fdholder_delete.lo: src/libs6/s6_fdholder_delete.c src/include/s6/fdholder.h src/libs6/s6_fdholder_delete_async.o src/libs6/s6_fdholder_delete_async.lo: src/libs6/s6_fdholder_delete_async.c src/include/s6/fdholder.h src/libs6/s6_fdholder_end.o src/libs6/s6_fdholder_end.lo: src/libs6/s6_fdholder_end.c src/include/s6/fdholder.h src/libs6/s6_fdholder_getdump.o src/libs6/s6_fdholder_getdump.lo: src/libs6/s6_fdholder_getdump.c src/include/s6/fdholder.h src/libs6/s6_fdholder_list.o src/libs6/s6_fdholder_list.lo: src/libs6/s6_fdholder_list.c src/include/s6/fdholder.h src/libs6/s6_fdholder_list_async.o src/libs6/s6_fdholder_list_async.lo: src/libs6/s6_fdholder_list_async.c src/include/s6/fdholder.h src/libs6/s6_fdholder_list_cb.o src/libs6/s6_fdholder_list_cb.lo: src/libs6/s6_fdholder_list_cb.c src/include/s6/fdholder.h src/libs6/s6_fdholder_retrieve.o src/libs6/s6_fdholder_retrieve.lo: src/libs6/s6_fdholder_retrieve.c src/include/s6/fdholder.h src/libs6/s6_fdholder_retrieve_async.o src/libs6/s6_fdholder_retrieve_async.lo: src/libs6/s6_fdholder_retrieve_async.c src/include/s6/fdholder.h src/libs6/s6_fdholder_retrieve_cb.o src/libs6/s6_fdholder_retrieve_cb.lo: src/libs6/s6_fdholder_retrieve_cb.c src/include/s6/fdholder.h src/libs6/s6_fdholder_setdump.o src/libs6/s6_fdholder_setdump.lo: src/libs6/s6_fdholder_setdump.c src/include/s6/fdholder.h src/libs6/s6_fdholder_start.o src/libs6/s6_fdholder_start.lo: src/libs6/s6_fdholder_start.c src/include/s6/fdholder.h src/libs6/s6_fdholder_store.o src/libs6/s6_fdholder_store.lo: src/libs6/s6_fdholder_store.c src/include/s6/fdholder.h src/libs6/s6_fdholder_store_async.o src/libs6/s6_fdholder_store_async.lo: src/libs6/s6_fdholder_store_async.c src/include/s6/fdholder.h src/libs6/s6_instance_chdirservice.o src/libs6/s6_instance_chdirservice.lo: src/libs6/s6_instance_chdirservice.c src/include/s6/supervise.h src/libs6/s6_servicedir_file_list.o src/libs6/s6_servicedir_file_list.lo: src/libs6/s6_servicedir_file_list.c src/include/s6/servicedir.h src/libs6/s6_servicedir_instances_recreate_offline.o src/libs6/s6_servicedir_instances_recreate_offline.lo: src/libs6/s6_servicedir_instances_recreate_offline.c src/include/s6/servicedir.h src/libs6/s6_servicedir_instances_recreate_offline_tmp.o src/libs6/s6_servicedir_instances_recreate_offline_tmp.lo: src/libs6/s6_servicedir_instances_recreate_offline_tmp.c src/include/s6/servicedir.h src/libs6/s6_supervise_link.o src/libs6/s6_supervise_link.lo: src/libs6/s6_supervise_link.c src/include/s6/supervise.h src/libs6/s6_supervise_link_names.o src/libs6/s6_supervise_link_names.lo: src/libs6/s6_supervise_link_names.c src/include/s6/ftrigr.h src/include/s6/ftrigw.h src/include/s6/supervise.h src/libs6/s6_supervise_unlink.o src/libs6/s6_supervise_unlink.lo: src/libs6/s6_supervise_unlink.c src/include/s6/supervise.h src/libs6/s6_supervise_unlink_names.o src/libs6/s6_supervise_unlink_names.lo: src/libs6/s6_supervise_unlink_names.c src/include/s6/ftrigr.h src/include/s6/supervise.h src/libs6/s6_svc_ok.o src/libs6/s6_svc_ok.lo: src/libs6/s6_svc_ok.c src/include/s6/supervise.h src/libs6/s6_svc_write.o src/libs6/s6_svc_write.lo: src/libs6/s6_svc_write.c src/include/s6/supervise.h src/libs6/s6_svc_writectl.o src/libs6/s6_svc_writectl.lo: src/libs6/s6_svc_writectl.c src/include/s6/supervise.h src/libs6/s6_svstatus_pack.o src/libs6/s6_svstatus_pack.lo: src/libs6/s6_svstatus_pack.c src/include/s6/supervise.h src/libs6/s6_svstatus_read.o src/libs6/s6_svstatus_read.lo: src/libs6/s6_svstatus_read.c src/include/s6/supervise.h src/libs6/s6_svstatus_unpack.o src/libs6/s6_svstatus_unpack.lo: src/libs6/s6_svstatus_unpack.c src/include/s6/supervise.h src/libs6/s6_svstatus_write.o src/libs6/s6_svstatus_write.lo: src/libs6/s6_svstatus_write.c src/include/s6/supervise.h src/pipe-tools/s6-cleanfifodir.o src/pipe-tools/s6-cleanfifodir.lo: src/pipe-tools/s6-cleanfifodir.c src/include/s6/ftrigw.h src/pipe-tools/s6-ftrig-listen.o src/pipe-tools/s6-ftrig-listen.lo: src/pipe-tools/s6-ftrig-listen.c src/include/s6/compat.h src/include/s6/ftrigr.h src/pipe-tools/s6-ftrig-listen1.o src/pipe-tools/s6-ftrig-listen1.lo: src/pipe-tools/s6-ftrig-listen1.c src/include/s6/ftrigr.h src/pipe-tools/s6-ftrig-notify.o src/pipe-tools/s6-ftrig-notify.lo: src/pipe-tools/s6-ftrig-notify.c src/include/s6/ftrigw.h src/pipe-tools/s6-ftrig-wait.o src/pipe-tools/s6-ftrig-wait.lo: src/pipe-tools/s6-ftrig-wait.c src/include/s6/ftrigr.h src/pipe-tools/s6-mkfifodir.o src/pipe-tools/s6-mkfifodir.lo: src/pipe-tools/s6-mkfifodir.c src/include/s6/ftrigw.h src/supervision/s6-notifyoncheck.o src/supervision/s6-notifyoncheck.lo: src/supervision/s6-notifyoncheck.c src/include/s6/s6.h src/supervision/s6-permafailon.o src/supervision/s6-permafailon.lo: src/supervision/s6-permafailon.c src/include/s6/supervise.h src/supervision/s6-supervise.o src/supervision/s6-supervise.lo: src/supervision/s6-supervise.c src/include/s6/config.h src/include/s6/ftrigw.h src/include/s6/supervise.h src/supervision/s6-svc.o src/supervision/s6-svc.lo: src/supervision/s6-svc.c src/include/s6/config.h src/include/s6/supervise.h src/supervision/s6-svdt-clear.o src/supervision/s6-svdt-clear.lo: src/supervision/s6-svdt-clear.c src/include/s6/supervise.h src/supervision/s6-svdt.o src/supervision/s6-svdt.lo: src/supervision/s6-svdt.c src/include/s6/supervise.h src/supervision/s6-svlink.o src/supervision/s6-svlink.lo: src/supervision/s6-svlink.c src/include/s6/supervise.h src/supervision/s6-svlisten.o src/supervision/s6-svlisten.lo: src/supervision/s6-svlisten.c src/supervision/s6-svlisten.h src/include/s6/compat.h src/supervision/s6-svlisten1.o src/supervision/s6-svlisten1.lo: src/supervision/s6-svlisten1.c src/supervision/s6-svlisten.h src/supervision/s6-svok.o src/supervision/s6-svok.lo: src/supervision/s6-svok.c src/include/s6/supervise.h src/supervision/s6-svperms.o src/supervision/s6-svperms.lo: src/supervision/s6-svperms.c src/include/s6/supervise.h src/supervision/s6-svscan.o src/supervision/s6-svscan.lo: src/supervision/s6-svscan.c src/include/s6/config.h src/include/s6/supervise.h src/supervision/s6-svscanctl.o src/supervision/s6-svscanctl.lo: src/supervision/s6-svscanctl.c src/include/s6/supervise.h src/supervision/s6-svstat.o src/supervision/s6-svstat.lo: src/supervision/s6-svstat.c src/include/s6/supervise.h src/supervision/s6-svunlink.o src/supervision/s6-svunlink.lo: src/supervision/s6-svunlink.c src/include/s6/supervise.h src/supervision/s6-svwait.o src/supervision/s6-svwait.lo: src/supervision/s6-svwait.c src/supervision/s6-svlisten.h src/supervision/s6_svlisten_loop.o src/supervision/s6_svlisten_loop.lo: src/supervision/s6_svlisten_loop.c src/supervision/s6-svlisten.h src/include/s6/ftrigr.h src/include/s6/ftrigw.h src/include/s6/supervise.h src/supervision/s6_svlisten_signal_handler.o src/supervision/s6_svlisten_signal_handler.lo: src/supervision/s6_svlisten_signal_handler.c src/supervision/s6-svlisten.h src/usertree/s6-usertree-maker.o src/usertree/s6-usertree-maker.lo: src/usertree/s6-usertree-maker.c src/include/s6/auto.h src/include/s6/config.h s6-accessrules-cdb-from-fs: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-accessrules-cdb-from-fs: src/conn-tools/s6-accessrules-cdb-from-fs.o s6-accessrules-fs-from-cdb: EXTRA_LIBS := -lskarnet s6-accessrules-fs-from-cdb: src/conn-tools/s6-accessrules-fs-from-cdb.o s6-connlimit: EXTRA_LIBS := -lskarnet s6-connlimit: src/conn-tools/s6-connlimit.o s6-ioconnect: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-ioconnect: src/conn-tools/s6-ioconnect.o s6-ipcclient: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} s6-ipcclient: src/conn-tools/s6-ipcclient.o s6-ipcserver: EXTRA_LIBS := -lskarnet s6-ipcserver: src/conn-tools/s6-ipcserver.o s6-ipcserver-access: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} s6-ipcserver-access: src/conn-tools/s6-ipcserver-access.o ${LIBS6} s6-ipcserver-socketbinder: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} s6-ipcserver-socketbinder: src/conn-tools/s6-ipcserver-socketbinder.o s6-ipcserverd: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-ipcserverd: src/conn-tools/s6-ipcserverd.o s6-sudo: EXTRA_LIBS := -lskarnet s6-sudo: src/conn-tools/s6-sudo.o s6-sudoc: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-sudoc: src/conn-tools/s6-sudoc.o s6-sudod: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SPAWN_LIB} ${SYSCLOCK_LIB} s6-sudod: src/conn-tools/s6-sudod.o s6-applyuidgid: EXTRA_LIBS := -lskarnet s6-applyuidgid: src/daemontools-extras/s6-applyuidgid.o s6-envdir: EXTRA_LIBS := -lskarnet s6-envdir: src/daemontools-extras/s6-envdir.o s6-envuidgid: EXTRA_LIBS := -lskarnet ${MAYBEPTHREAD_LIB} s6-envuidgid: src/daemontools-extras/s6-envuidgid.o ${LIBNSSS} s6-fghack: EXTRA_LIBS := -lskarnet ${SPAWN_LIB} s6-fghack: src/daemontools-extras/s6-fghack.o s6-log: EXTRA_LIBS := -lskarnet ${SPAWN_LIB} ${SYSCLOCK_LIB} s6-log: src/daemontools-extras/s6-log.o s6-setlock: EXTRA_LIBS := -lskarnet ${TIMER_LIB} s6-setlock: src/daemontools-extras/s6-setlock.o s6-setsid: EXTRA_LIBS := -lskarnet s6-setsid: src/daemontools-extras/s6-setsid.o s6-setuidgid: EXTRA_LIBS := -lskarnet s6-setuidgid: src/daemontools-extras/s6-setuidgid.o s6-socklog: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-socklog: src/daemontools-extras/s6-socklog.o src/daemontools-extras/lolsyslog.o s6-softlimit: EXTRA_LIBS := -lskarnet s6-softlimit: src/daemontools-extras/s6-softlimit.o s6-tai64n: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB} s6-tai64n: src/daemontools-extras/s6-tai64n.o s6-tai64nlocal: EXTRA_LIBS := -lskarnet s6-tai64nlocal: src/daemontools-extras/s6-tai64nlocal.o ucspilogd: EXTRA_LIBS := -lskarnet ucspilogd: src/daemontools-extras/ucspilogd.o src/daemontools-extras/lolsyslog.o s6-fdholder-daemon: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-fdholder-daemon: src/fdholder/s6-fdholder-daemon.o ${LIBS6} s6-fdholder-delete: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-fdholder-delete: src/fdholder/s6-fdholder-delete.o ${LIBS6} s6-fdholder-getdump: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-fdholder-getdump: src/fdholder/s6-fdholder-getdump.o ${LIBS6} s6-fdholder-list: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-fdholder-list: src/fdholder/s6-fdholder-list.o ${LIBS6} s6-fdholder-retrieve: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-fdholder-retrieve: src/fdholder/s6-fdholder-retrieve.o ${LIBS6} s6-fdholder-setdump: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-fdholder-setdump: src/fdholder/s6-fdholder-setdump.o ${LIBS6} s6-fdholder-store: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-fdholder-store: src/fdholder/s6-fdholder-store.o ${LIBS6} s6-fdholder-transferdump: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-fdholder-transferdump: src/fdholder/s6-fdholder-transferdump.o ${LIBS6} s6-fdholderd: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-fdholderd: src/fdholder/s6-fdholderd.o ${LIBS6} s6-instance-control: EXTRA_LIBS := -lskarnet s6-instance-control: src/instance/s6-instance-control.o s6-instance-create: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB} s6-instance-create: src/instance/s6-instance-create.o ${LIBS6} s6-instance-delete: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB} s6-instance-delete: src/instance/s6-instance-delete.o ${LIBS6} s6-instance-list: EXTRA_LIBS := -lskarnet s6-instance-list: src/instance/s6-instance-list.o ${LIBS6} s6-instance-maker: EXTRA_LIBS := -lskarnet s6-instance-maker: src/instance/s6-instance-maker.o libs6auto.a.xyzzy s6-instance-status: EXTRA_LIBS := -lskarnet s6-instance-status: src/instance/s6-instance-status.o ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),) libs6.a.xyzzy: src/libs6/ftrigr1_zero.o src/libs6/ftrigr_check.o src/libs6/ftrigr_checksa.o src/libs6/ftrigr_ack.o src/libs6/ftrigr_end.o src/libs6/ftrigr_start.o src/libs6/ftrigr_startf.o src/libs6/ftrigr_subscribe.o src/libs6/ftrigr_unsubscribe.o src/libs6/ftrigr_update.o src/libs6/ftrigr_updateb.o src/libs6/ftrigr_wait_and.o src/libs6/ftrigr_wait_or.o src/libs6/ftrigr_zero.o src/libs6/ftrigw_clean.o src/libs6/ftrigw_fifodir_make.o src/libs6/ftrigw_notify.o src/libs6/ftrigw_notifyb.o src/libs6/ftrigw_notifyb_nosig.o src/libs6/s6_accessrules_backend_cdb.o src/libs6/s6_accessrules_backend_fs.o src/libs6/s6_accessrules_keycheck_ip4.o src/libs6/s6_accessrules_keycheck_ip6.o src/libs6/s6_accessrules_keycheck_reversedns.o src/libs6/s6_accessrules_keycheck_uidgid.o src/libs6/s6_accessrules_params_free.o src/libs6/s6_accessrules_uidgid_cdb.o src/libs6/s6_accessrules_uidgid_fs.o src/libs6/s6_compat_el_semicolon.o src/libs6/s6_dtally_pack.o src/libs6/s6_dtally_unpack.o src/libs6/s6_dtally_read.o src/libs6/s6_dtally_write.o src/libs6/s6_instance_chdirservice.o src/libs6/s6_servicedir_file_list.o src/libs6/s6_servicedir_instances_recreate_offline.o src/libs6/s6_servicedir_instances_recreate_offline_tmp.o src/libs6/s6_svc_ok.o src/libs6/s6_svc_write.o src/libs6/s6_svc_writectl.o src/libs6/s6_svstatus_pack.o src/libs6/s6_svstatus_read.o src/libs6/s6_svstatus_unpack.o src/libs6/s6_svstatus_write.o src/libs6/s6_fdholder_delete.o src/libs6/s6_fdholder_delete_async.o src/libs6/s6_fdholder_end.o src/libs6/s6_fdholder_getdump.o src/libs6/s6_fdholder_list.o src/libs6/s6_fdholder_list_async.o src/libs6/s6_fdholder_list_cb.o src/libs6/s6_fdholder_retrieve.o src/libs6/s6_fdholder_retrieve_async.o src/libs6/s6_fdholder_retrieve_cb.o src/libs6/s6_fdholder_setdump.o src/libs6/s6_fdholder_start.o src/libs6/s6_fdholder_store.o src/libs6/s6_fdholder_store_async.o src/libs6/s6_supervise_link.o src/libs6/s6_supervise_link_names.o src/libs6/s6_supervise_unlink.o src/libs6/s6_supervise_unlink_names.o else libs6.a.xyzzy: src/libs6/ftrigr1_zero.lo src/libs6/ftrigr_check.lo src/libs6/ftrigr_checksa.lo src/libs6/ftrigr_ack.lo src/libs6/ftrigr_end.lo src/libs6/ftrigr_start.lo src/libs6/ftrigr_startf.lo src/libs6/ftrigr_subscribe.lo src/libs6/ftrigr_unsubscribe.lo src/libs6/ftrigr_update.lo src/libs6/ftrigr_updateb.lo src/libs6/ftrigr_wait_and.lo src/libs6/ftrigr_wait_or.lo src/libs6/ftrigr_zero.lo src/libs6/ftrigw_clean.lo src/libs6/ftrigw_fifodir_make.lo src/libs6/ftrigw_notify.lo src/libs6/ftrigw_notifyb.lo src/libs6/ftrigw_notifyb_nosig.lo src/libs6/s6_accessrules_backend_cdb.lo src/libs6/s6_accessrules_backend_fs.lo src/libs6/s6_accessrules_keycheck_ip4.lo src/libs6/s6_accessrules_keycheck_ip6.lo src/libs6/s6_accessrules_keycheck_reversedns.lo src/libs6/s6_accessrules_keycheck_uidgid.lo src/libs6/s6_accessrules_params_free.lo src/libs6/s6_accessrules_uidgid_cdb.lo src/libs6/s6_accessrules_uidgid_fs.lo src/libs6/s6_compat_el_semicolon.lo src/libs6/s6_dtally_pack.lo src/libs6/s6_dtally_unpack.lo src/libs6/s6_dtally_read.lo src/libs6/s6_dtally_write.lo src/libs6/s6_instance_chdirservice.lo src/libs6/s6_servicedir_file_list.lo src/libs6/s6_servicedir_instances_recreate_offline.lo src/libs6/s6_servicedir_instances_recreate_offline_tmp.lo src/libs6/s6_svc_ok.lo src/libs6/s6_svc_write.lo src/libs6/s6_svc_writectl.lo src/libs6/s6_svstatus_pack.lo src/libs6/s6_svstatus_read.lo src/libs6/s6_svstatus_unpack.lo src/libs6/s6_svstatus_write.lo src/libs6/s6_fdholder_delete.lo src/libs6/s6_fdholder_delete_async.lo src/libs6/s6_fdholder_end.lo src/libs6/s6_fdholder_getdump.lo src/libs6/s6_fdholder_list.lo src/libs6/s6_fdholder_list_async.lo src/libs6/s6_fdholder_list_cb.lo src/libs6/s6_fdholder_retrieve.lo src/libs6/s6_fdholder_retrieve_async.lo src/libs6/s6_fdholder_retrieve_cb.lo src/libs6/s6_fdholder_setdump.lo src/libs6/s6_fdholder_start.lo src/libs6/s6_fdholder_store.lo src/libs6/s6_fdholder_store_async.lo src/libs6/s6_supervise_link.lo src/libs6/s6_supervise_link_names.lo src/libs6/s6_supervise_unlink.lo src/libs6/s6_supervise_unlink_names.lo endif libs6.so.xyzzy: EXTRA_LIBS := -lskarnet libs6.so.xyzzy: src/libs6/ftrigr1_zero.lo src/libs6/ftrigr_check.lo src/libs6/ftrigr_checksa.lo src/libs6/ftrigr_ack.lo src/libs6/ftrigr_end.lo src/libs6/ftrigr_start.lo src/libs6/ftrigr_startf.lo src/libs6/ftrigr_subscribe.lo src/libs6/ftrigr_unsubscribe.lo src/libs6/ftrigr_update.lo src/libs6/ftrigr_updateb.lo src/libs6/ftrigr_wait_and.lo src/libs6/ftrigr_wait_or.lo src/libs6/ftrigr_zero.lo src/libs6/ftrigw_clean.lo src/libs6/ftrigw_fifodir_make.lo src/libs6/ftrigw_notify.lo src/libs6/ftrigw_notifyb.lo src/libs6/ftrigw_notifyb_nosig.lo src/libs6/s6_accessrules_backend_cdb.lo src/libs6/s6_accessrules_backend_fs.lo src/libs6/s6_accessrules_keycheck_ip4.lo src/libs6/s6_accessrules_keycheck_ip6.lo src/libs6/s6_accessrules_keycheck_reversedns.lo src/libs6/s6_accessrules_keycheck_uidgid.lo src/libs6/s6_accessrules_params_free.lo src/libs6/s6_accessrules_uidgid_cdb.lo src/libs6/s6_accessrules_uidgid_fs.lo src/libs6/s6_compat_el_semicolon.lo src/libs6/s6_dtally_pack.lo src/libs6/s6_dtally_unpack.lo src/libs6/s6_dtally_read.lo src/libs6/s6_dtally_write.lo src/libs6/s6_instance_chdirservice.lo src/libs6/s6_servicedir_file_list.lo src/libs6/s6_servicedir_instances_recreate_offline.lo src/libs6/s6_servicedir_instances_recreate_offline_tmp.lo src/libs6/s6_svc_ok.lo src/libs6/s6_svc_write.lo src/libs6/s6_svc_writectl.lo src/libs6/s6_svstatus_pack.lo src/libs6/s6_svstatus_read.lo src/libs6/s6_svstatus_unpack.lo src/libs6/s6_svstatus_write.lo src/libs6/s6_fdholder_delete.lo src/libs6/s6_fdholder_delete_async.lo src/libs6/s6_fdholder_end.lo src/libs6/s6_fdholder_getdump.lo src/libs6/s6_fdholder_list.lo src/libs6/s6_fdholder_list_async.lo src/libs6/s6_fdholder_list_cb.lo src/libs6/s6_fdholder_retrieve.lo src/libs6/s6_fdholder_retrieve_async.lo src/libs6/s6_fdholder_retrieve_cb.lo src/libs6/s6_fdholder_setdump.lo src/libs6/s6_fdholder_start.lo src/libs6/s6_fdholder_store.lo src/libs6/s6_fdholder_store_async.lo src/libs6/s6_supervise_link.lo src/libs6/s6_supervise_link_names.lo src/libs6/s6_supervise_unlink.lo src/libs6/s6_supervise_unlink_names.lo ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),) libs6auto.a.xyzzy: src/libs6/s6_auto_write_logger.o src/libs6/s6_auto_write_logger_tmp.o src/libs6/s6_auto_write_logrun.o src/libs6/s6_auto_write_logrun_tmp.o src/libs6/s6_auto_write_service.o else libs6auto.a.xyzzy: src/libs6/s6_auto_write_logger.lo src/libs6/s6_auto_write_logger_tmp.lo src/libs6/s6_auto_write_logrun.lo src/libs6/s6_auto_write_logrun_tmp.lo src/libs6/s6_auto_write_service.lo endif libs6auto.so.xyzzy: EXTRA_LIBS := libs6auto.so.xyzzy: src/libs6/s6_auto_write_logger.lo src/libs6/s6_auto_write_logger_tmp.lo src/libs6/s6_auto_write_logrun.lo src/libs6/s6_auto_write_logrun_tmp.lo src/libs6/s6_auto_write_service.lo s6-ftrigrd: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-ftrigrd: src/libs6/s6-ftrigrd.o src/libs6/ftrig1_free.o src/libs6/ftrig1_make.o s6lockd: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6lockd: src/libs6/s6lockd.o s6lockd-helper: EXTRA_LIBS := -lskarnet s6lockd-helper: src/libs6/s6lockd-helper.o libs6lockd.a.xyzzy s6-cleanfifodir: EXTRA_LIBS := -lskarnet s6-cleanfifodir: src/pipe-tools/s6-cleanfifodir.o ${LIBS6} s6-ftrig-listen: EXTRA_LIBS := ${EXECLINE_LIB} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-ftrig-listen: src/pipe-tools/s6-ftrig-listen.o ${LIBS6} s6-ftrig-listen1: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-ftrig-listen1: src/pipe-tools/s6-ftrig-listen1.o ${LIBS6} s6-ftrig-notify: EXTRA_LIBS := -lskarnet s6-ftrig-notify: src/pipe-tools/s6-ftrig-notify.o ${LIBS6} s6-ftrig-wait: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-ftrig-wait: src/pipe-tools/s6-ftrig-wait.o ${LIBS6} s6-mkfifodir: EXTRA_LIBS := -lskarnet s6-mkfifodir: src/pipe-tools/s6-mkfifodir.o ${LIBS6} s6-notifyoncheck: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-notifyoncheck: src/supervision/s6-notifyoncheck.o ${LIBS6} s6-permafailon: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB} s6-permafailon: src/supervision/s6-permafailon.o ${LIBS6} s6-supervise: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB} s6-supervise: src/supervision/s6-supervise.o libs6.a.xyzzy s6-svc: EXTRA_LIBS := -lskarnet s6-svc: src/supervision/s6-svc.o ${LIBS6} s6-svdt: EXTRA_LIBS := -lskarnet s6-svdt: src/supervision/s6-svdt.o ${LIBS6} s6-svdt-clear: EXTRA_LIBS := -lskarnet s6-svdt-clear: src/supervision/s6-svdt-clear.o ${LIBS6} s6-svlink: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-svlink: src/supervision/s6-svlink.o ${LIBS6} s6-svlisten: EXTRA_LIBS := ${EXECLINE_LIB} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-svlisten: src/supervision/s6-svlisten.o src/supervision/s6_svlisten_signal_handler.o src/supervision/s6_svlisten_loop.o ${LIBS6} s6-svlisten1: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-svlisten1: src/supervision/s6-svlisten1.o src/supervision/s6_svlisten_signal_handler.o src/supervision/s6_svlisten_loop.o ${LIBS6} s6-svok: EXTRA_LIBS := -lskarnet s6-svok: src/supervision/s6-svok.o ${LIBS6} s6-svperms: EXTRA_LIBS := -lskarnet s6-svperms: src/supervision/s6-svperms.o ${LIBNSSS} s6-svscan: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-svscan: src/supervision/s6-svscan.o s6-svscanctl: EXTRA_LIBS := -lskarnet s6-svscanctl: src/supervision/s6-svscanctl.o ${LIBS6} s6-svstat: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB} s6-svstat: src/supervision/s6-svstat.o ${LIBS6} s6-svunlink: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-svunlink: src/supervision/s6-svunlink.o ${LIBS6} s6-svwait: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-svwait: src/supervision/s6-svwait.o src/supervision/s6_svlisten_loop.o ${LIBS6} s6-usertree-maker: EXTRA_LIBS := -lskarnet s6-usertree-maker: src/usertree/s6-usertree-maker.o libs6auto.a.xyzzy INTERNAL_LIBS := s6-2.13.1.0/package/info000066400000000000000000000001011470151141200145210ustar00rootroot00000000000000package=s6 version=2.13.1.0 category=admin package_macro_name=S6 s6-2.13.1.0/package/modes000066400000000000000000000030101470151141200146770ustar00rootroot00000000000000ucspilogd 0755 s6-ftrigrd 0755 s6-ftrig-listen1 0755 s6-ftrig-listen 0755 s6-ftrig-notify 0755 s6-ftrig-wait 0755 s6-cleanfifodir 0755 s6-mkfifodir 0755 s6-svscan 0755 s6-supervise 0755 s6-svc 0755 s6-svlink 0755 s6-svunlink 0755 s6-svscanctl 0755 s6-svok 0755 s6-svstat 0755 s6-svdt 0755 s6-svdt-clear 0755 s6-svwait 0755 s6-svlisten1 0755 s6-svlisten 0755 s6-svperms 0755 s6-notifyoncheck 0755 s6-permafailon 0755 s6-applyuidgid 0755 s6-envdir 0755 s6-envuidgid 0755 s6-fghack 0755 s6-log 0755 s6-setlock 0755 s6-setsid 0755 s6-setuidgid 0755 s6-softlimit 0755 s6-socklog 0755 s6-tai64n 0755 s6-tai64nlocal 0755 s6-accessrules-cdb-from-fs 0755 s6-accessrules-fs-from-cdb 0755 s6-connlimit 0755 s6-ioconnect 0755 s6-ipcclient 0755 s6-ipcserver-access 0755 s6-ipcserver-socketbinder 0755 s6-ipcserver 0755 s6-ipcserverd 0755 s6-sudo 0755 s6-sudoc 0755 s6-sudod 0755 s6-fdholder-daemon 0755 s6-fdholderd 0755 s6-fdholder-delete 0755 s6-fdholder-deletec 0755 s6-fdholder-store 0755 s6-fdholder-storec 0755 s6-fdholder-retrieve 0755 s6-fdholder-retrievec 0755 s6-fdholder-list 0755 s6-fdholder-listc 0755 s6-fdholder-getdump 0755 s6-fdholder-getdumpc 0755 s6-fdholder-setdump 0755 s6-fdholder-setdumpc 0755 s6-fdholder-transferdump 0755 s6-fdholder-transferdumpc 0755 s6-usertree-maker 0755 s6-instance-maker 0755 s6-instance-create 0755 s6-instance-delete 0755 s6-instance-control 0755 s6-instance-status 0755 s6-instance-list 0755 s6-2.13.1.0/package/targets.mak000066400000000000000000000022071470151141200160170ustar00rootroot00000000000000BIN_TARGETS := \ ucspilogd \ s6-ftrigrd \ s6-ftrig-listen1 \ s6-ftrig-listen \ s6-ftrig-notify \ s6-ftrig-wait \ s6-cleanfifodir \ s6-mkfifodir \ s6-svscan \ s6-supervise \ s6-svc \ s6-svscanctl \ s6-svok \ s6-svstat \ s6-svdt \ s6-svdt-clear \ s6-permafailon \ s6-svwait \ s6-svlisten1 \ s6-svlisten \ s6-svperms \ s6-svlink \ s6-svunlink \ s6-notifyoncheck \ s6-envdir \ s6-envuidgid \ s6-fghack \ s6-log \ s6-setlock \ s6-setsid \ s6-softlimit \ s6-socklog \ s6-tai64n \ s6-tai64nlocal \ s6-accessrules-cdb-from-fs \ s6-accessrules-fs-from-cdb \ s6-connlimit \ s6-ioconnect \ s6-ipcclient \ s6-ipcserver-access \ s6-ipcserver-socketbinder \ s6-ipcserver \ s6-ipcserverd \ s6-sudo \ s6-sudoc \ s6-sudod \ s6-fdholder-daemon \ s6-fdholderd \ s6-fdholder-delete \ s6-fdholder-store \ s6-fdholder-retrieve \ s6-fdholder-list \ s6-fdholder-getdump \ s6-fdholder-setdump \ s6-fdholder-transferdump \ s6-applyuidgid \ s6-setuidgid \ s6-instance-create \ s6-instance-delete \ s6-instance-control \ s6-instance-status \ s6-instance-list LIB_DEFS := S6=s6 ifneq ($(EXECLINE_LIB),) LIB_DEFS += S6AUTO=s6auto BIN_TARGETS += s6-usertree-maker s6-instance-maker endif s6-2.13.1.0/patch-for-solaris000077500000000000000000000007551470151141200155520ustar00rootroot00000000000000#!/usr/xpg4/bin/sh -e patchit () { echo '#!/usr/xpg4/bin/sh' > $1.tmp tail -n +2 $1 >> $1.tmp mv -f $1.tmp $1 chmod 0755 $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 s6-2.13.1.0/src/000077500000000000000000000000001470151141200130475ustar00rootroot00000000000000s6-2.13.1.0/src/conn-tools/000077500000000000000000000000001470151141200151425ustar00rootroot00000000000000s6-2.13.1.0/src/conn-tools/deps-exe/000077500000000000000000000000001470151141200166545ustar00rootroot00000000000000s6-2.13.1.0/src/conn-tools/deps-exe/s6-accessrules-cdb-from-fs000066400000000000000000000000501470151141200235310ustar00rootroot00000000000000-lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/conn-tools/deps-exe/s6-accessrules-fs-from-cdb000066400000000000000000000000121470151141200235270ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/conn-tools/deps-exe/s6-connlimit000066400000000000000000000000121470151141200211120ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/conn-tools/deps-exe/s6-ioconnect000066400000000000000000000000501470151141200211010ustar00rootroot00000000000000-lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/conn-tools/deps-exe/s6-ipcclient000066400000000000000000000000301470151141200210700ustar00rootroot00000000000000-lskarnet ${SOCKET_LIB} s6-2.13.1.0/src/conn-tools/deps-exe/s6-ipcserver000066400000000000000000000000121470151141200211200ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/conn-tools/deps-exe/s6-ipcserver-access000066400000000000000000000000411470151141200223610ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} s6-2.13.1.0/src/conn-tools/deps-exe/s6-ipcserver-socketbinder000066400000000000000000000000301470151141200235720ustar00rootroot00000000000000-lskarnet ${SOCKET_LIB} s6-2.13.1.0/src/conn-tools/deps-exe/s6-ipcserverd000066400000000000000000000000651470151141200212740ustar00rootroot00000000000000-lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/conn-tools/deps-exe/s6-sudo000066400000000000000000000000121470151141200200700ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/conn-tools/deps-exe/s6-sudoc000066400000000000000000000000501470151141200202350ustar00rootroot00000000000000-lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/conn-tools/deps-exe/s6-sudod000066400000000000000000000000651470151141200202440ustar00rootroot00000000000000-lskarnet ${SOCKET_LIB} ${SPAWN_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/conn-tools/s6-accessrules-cdb-from-fs.c000066400000000000000000000110461470151141200222470ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-accessrules-cdb-from-fs cdbfile dir" #define SUFFIX ":s6-accessrules-cdb-from-fs:XXXXXX" static stralloc tmp = STRALLOC_ZERO ; static void cleanup (void) { unlink_void(tmp.s) ; } static void dienomem (void) { cleanup() ; strerr_diefu1sys(111, "stralloc_catb") ; } static void doit (cdbmaker *c, stralloc *sa, size_t start) { size_t tmpbase = tmp.len ; unsigned int k = sa->len ; if (!stralloc_readyplus(sa, 10)) dienomem() ; stralloc_catb(sa, "/allow", 7) ; tmp.s[tmpbase] = 0 ; if (access(sa->s, F_OK) < 0) { if ((errno != ENOENT) && (errno != EACCES)) { cleanup() ; strerr_diefu2sys(111, "access ", sa->s) ; } sa->len = k+1 ; stralloc_catb(sa, "deny", 5) ; if (access(sa->s, F_OK) < 0) if ((errno != ENOENT) && (errno != EACCES)) { cleanup() ; strerr_diefu2sys(111, "access ", sa->s) ; } else return ; else if (!cdbmake_add(c, sa->s + start, k - start, "D", 1)) { cleanup() ; strerr_diefu1sys(111, "cdbmake_add") ; } } else { uint16_t envlen = 0 ; uint16_t execlen = 0 ; ssize_t r ; tmp.s[tmpbase] = 'A' ; sa->len = k+1 ; stralloc_catb(sa, "env", 4) ; tmp.len = tmpbase + 3 ; if ((envdir(sa->s, &tmp) < 0) && (errno != ENOENT)) { cleanup() ; strerr_diefu2sys(111, "envdir ", sa->s) ; } if (tmp.len > tmpbase + 4103) { cleanup() ; strerr_dief2sys(100, sa->s, " too big") ; } envlen = tmp.len - tmpbase - 3 ; tmp.len = tmpbase ; uint16_pack_big(tmp.s + tmpbase + 1, envlen) ; sa->len = k+1 ; stralloc_catb(sa, "exec", 5) ; r = openreadnclose(sa->s, tmp.s + tmpbase + 5 + envlen, 4096) ; if ((r == -1) && (errno != ENOENT)) { cleanup() ; strerr_diefu2sys(111, "openreadnclose ", sa->s) ; } if (r > 0) execlen = r ; if (execlen == 4096) strerr_warnw2x("possibly truncated file ", sa->s) ; uint16_pack_big(tmp.s + tmpbase + 3 + envlen, execlen) ; if (!cdbmake_add(c, sa->s + start, k - start, tmp.s + tmpbase, 5 + envlen + execlen)) { cleanup() ; strerr_diefu1sys(111, "cdbmake_add") ; } } } int main (int argc, char const *const *argv) { stralloc sa = STRALLOC_ZERO ; cdbmaker c = CDBMAKER_ZERO ; DIR *dir ; size_t start ; int fd ; PROG = "s6-accessrules-cdb-from-fs" ; if (argc < 3) strerr_dieusage(100, USAGE) ; if (!stralloc_cats(&tmp, argv[1])) return 0 ; if (!stralloc_readyplus(&tmp, 8210)) strerr_diefu1sys(111, "stralloc_catb") ; stralloc_catb(&tmp, SUFFIX, sizeof(SUFFIX)) ; fd = mkstemp(tmp.s) ; if (fd < 0) strerr_diefu2sys(111, "mkstemp ", tmp.s) ; if (!cdbmake_start(&c, fd)) { cleanup() ; strerr_diefu1sys(111, "cdbmake_start") ; } dir = opendir(argv[2]) ; if (!dir) { cleanup() ; strerr_diefu2sys(111, "opendir ", argv[2]) ; } if (!stralloc_cats(&sa, argv[2]) || !stralloc_catb(&sa, "/", 1)) dienomem() ; start = sa.len ; for (;;) { DIR *subdir ; direntry *d ; size_t base ; errno = 0 ; d = readdir(dir) ; if (!d) break ; if (d->d_name[0] == '.') continue ; sa.len = start ; if (!stralloc_cats(&sa, d->d_name) || !stralloc_0(&sa)) dienomem() ; base = sa.len ; subdir = opendir(sa.s) ; if (!subdir) { cleanup() ; strerr_diefu2sys(111, "opendir ", sa.s) ; } sa.s[base-1] = '/' ; for (;;) { errno = 0 ; d = readdir(subdir) ; if (!d) break ; if (d->d_name[0] == '.') continue ; sa.len = base ; if (!stralloc_cats(&sa, d->d_name)) dienomem() ; doit(&c, &sa, start) ; } if (errno) { sa.s[base-1] = 0 ; cleanup() ; strerr_diefu2sys(111, "readdir ", sa.s) ; } dir_close(subdir) ; } if (errno) { cleanup() ; strerr_diefu2sys(111, "readdir ", argv[2]) ; } dir_close(dir) ; if (!cdbmake_finish(&c)) { cleanup() ; strerr_diefu1sys(111, "cdb_make_finish") ; } if (fd_sync(fd) < 0) { cleanup() ; strerr_diefu1sys(111, "fd_sync") ; } fd_close(fd) ; if (rename(tmp.s, argv[1]) < 0) { cleanup() ; strerr_diefu4sys(111, "rename ", tmp.s, " to ", argv[1]) ; } return 0 ; } s6-2.13.1.0/src/conn-tools/s6-accessrules-fs-from-cdb.c000066400000000000000000000102471470151141200222510ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-accessrules-fs-from-cdb dir cdbfile" static char const *basedir ; size_t basedirlen ; static void cleanup () { int e = errno ; rm_rf(basedir) ; errno = e ; } static int domkdir (char const *s) { return mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISGID) < 0 ? errno == EEXIST : 1 ; } static void mkdirp (char *s) { mode_t m = umask(0) ; size_t len = strlen(s) ; size_t i = basedirlen + 1 ; for (; i < len ; i++) if (s[i] == '/') { s[i] = 0 ; if (!domkdir(s)) goto err ; s[i] = '/' ; } if (!domkdir(s)) goto err ; umask(m) ; return ; err: cleanup() ; strerr_diefu2sys(111, "mkdir ", s) ; } static void touchtrunc (char const *file) { int fd = open_trunc(file) ; if (fd < 0) { cleanup() ; strerr_diefu2sys(111, "open_trunc ", file) ; } fd_close(fd) ; } static int doenv (char const *dir, size_t dirlen, char const *env, uint32_t envlen) { mode_t m = umask(0) ; size_t i = 0 ; if (!domkdir(dir)) { cleanup() ; strerr_diefu2sys(111, "mkdir ", dir) ; } umask(m) ; while (i < envlen) { size_t n = byte_chr(env + i, envlen - i, 0) ; if (i + n >= envlen) return 0 ; { size_t p = byte_chr(env + i, n, '=') ; char tmp[dirlen + p + 2] ; memcpy(tmp, dir, dirlen) ; tmp[dirlen] = '/' ; memcpy(tmp + dirlen + 1, env + i, p) ; tmp[dirlen + p + 1] = 0 ; if (p < n) { struct iovec v[2] = { { .iov_base = (char *)env + i + p + 1, .iov_len = n - p - 1 }, { .iov_base = "\n", .iov_len = 1 } } ; if (!openwritevnclose_unsafe(tmp, v, 2)) { cleanup() ; strerr_diefu2sys(111, "openwritenclose_unsafe ", tmp) ; } } else touchtrunc(tmp) ; } i += n + 1 ; } return 1 ; } static int doit (char const *key, uint32_t klen, char const *data, uint32_t dlen) { uint16_t envlen, execlen ; char name[basedirlen + klen + 8] ; if (!dlen || (dlen > 8201)) return 0 ; memcpy(name, basedir, basedirlen) ; name[basedirlen] = '/' ; memcpy(name + basedirlen + 1, key, klen) ; name[basedirlen + 1 + klen] = 0 ; mkdirp(name) ; name[basedirlen + 1 + klen] = '/' ; if (data[0] == 'A') { memcpy(name + basedirlen + klen + 2, "allow", 6) ; touchtrunc(name) ; } else if (data[0] == 'D') { memcpy(name + basedirlen + klen + 2, "deny", 5) ; touchtrunc(name) ; } if (dlen < 3) return 1 ; uint16_unpack_big(data + 1, &envlen) ; if ((envlen > 4096U) || (3U + envlen > dlen)) return 0 ; uint16_unpack_big(data + 3 + envlen, &execlen) ; if ((execlen > 4096U) || (5U + envlen + execlen != dlen)) return 0 ; if (envlen) { memcpy(name + basedirlen + klen + 2, "env", 4) ; if (!doenv(name, basedirlen + klen + 5, data + 3, envlen)) return 0 ; } if (execlen) { memcpy(name + basedirlen + klen + 2, "exec", 5) ; if (!openwritenclose_unsafe(name, data + 5 + envlen, execlen)) { cleanup() ; strerr_diefu2sys(111, "openwritenclose_unsafe ", name) ; } } return 1 ; } int main (int argc, char const *const *argv) { cdb c = CDB_ZERO ; uint32_t pos = CDB_TRAVERSE_INIT() ; PROG = "s6-accessrules-fs-from-cdb" ; if (argc < 3) strerr_dieusage(100, USAGE) ; if (!cdb_init(&c, argv[2])) strerr_diefu1sys(111, "cdb_init") ; basedir = argv[1] ; basedirlen = strlen(argv[1]) ; { mode_t m = umask(0) ; if (mkdir(basedir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISGID) < 0) strerr_diefu2sys(111, "mkdir ", basedir) ; umask(m) ; } for (;;) { cdb_data key, data ; int r = cdb_traverse_next(&c, &key, &data, &pos) ; if (r < 0) { cleanup() ; strerr_diefu1x(111, "cdb_traverse_next: invalid cdb") ; } else if (!r) break ; else if (!doit(key.s, key.len, data.s, data.len)) { cleanup() ; strerr_diefu3x(111, "handle cdb record: ", argv[2], " does not contain valid accessrules data") ; } } return 0 ; } s6-2.13.1.0/src/conn-tools/s6-connlimit.c000066400000000000000000000017121470151141200176310ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include int main (int argc, char const *const *argv) { char const *x ; size_t protolen ; PROG = "s6-connlimit" ; x = getenv("PROTO") ; if (!x) strerr_dienotset(100, "PROTO") ; protolen = strlen(x) ; if (!protolen) strerr_dief1x(100, "empty PROTO") ; { unsigned int num ; char s[protolen + 8] ; memcpy(s, x, protolen) ; memcpy(s + protolen, "CONNNUM", 8) ; x = getenv(s) ; if (!x) strerr_dienotset(100, s) ; if (!uint0_scan(x, &num)) strerr_dief2x(100, "invalid ", s) ; memcpy(s + protolen + 4, "MAX", 4) ; x = getenv(s) ; if (x) { unsigned int max ; if (!uint0_scan(x, &max)) strerr_dief2x(100, "invalid ", s) ; if (num > max) strerr_dief2x(1, "number of connections from this client limited to ", x) ; } } (void)argc ; xexec0(argv+1) ; } s6-2.13.1.0/src/conn-tools/s6-ioconnect.c000066400000000000000000000137631470151141200176270ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-ioconnect [ -t timeout ] [ -r fdr ] [ -w fdw ]" #define dieusage() strerr_dieusage(100, USAGE) #define BSIZE 8192 typedef struct ioblah_s ioblah, *ioblah_ref ; struct ioblah_s { buffer b ; unsigned int xindex ; unsigned int flagsocket : 1 ; } ; static char buf[2][BSIZE] = { { '\0' }, { '\0' } } ; static ioblah a[2][2] = { { { .b = BUFFER_INIT(&buffer_read, 0, buf[0], BSIZE), .xindex = 5 }, { .b = BUFFER_INIT(&buffer_write, 7, buf[0], BSIZE), .xindex = 5 } }, { { .b = BUFFER_INIT(&buffer_read, 6, buf[1], BSIZE), .xindex = 5 }, { .b = BUFFER_INIT(&buffer_write, 1, buf[1], BSIZE), .xindex = 5 } } } ; static iopause_fd x[5] = { [0] = { .fd = -1, .events = IOPAUSE_READ } } ; static void closeit (unsigned int i, unsigned int j) { int fd = buffer_fd(&a[i][j].b) ; if (a[i][j].flagsocket) fd_shutdown(fd, j) ; fd_close(fd) ; buffer_fd(&a[i][j].b) = -1 ; a[i][j].xindex = 5 ; } static void handle_signals (void) { for (;;) { switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "selfpipe_read") ; case 0 : return ; case SIGTERM : { if (a[0][0].xindex < 5) x[a[0][0].xindex].revents |= IOPAUSE_EXCEPT ; if (a[1][0].xindex < 5) x[a[1][0].xindex].revents |= IOPAUSE_EXCEPT ; break ; } default : strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ; } } } static int flushit (unsigned int i) { int r = buffer_flush(&a[i][1].b) ; a[i][0].b.c.p = a[i][1].b.c.p ; /* XXX: abstraction leak */ return r ; } static int fillit (unsigned int i) { ssize_t r = sanitize_read(buffer_fill(&a[i][0].b)) ; a[i][1].b.c.n = a[i][0].b.c.n ; /* XXX: abstraction leak */ return r >= 0 ; } int main (int argc, char const *const *argv) { tain tto ; unsigned int i, j ; PROG = "s6-ioconnect" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int t = 0 ; for (;;) { int opt = subgetopt_r(argc, argv, "0167t:r:w:", &l) ; if (opt < 0) break ; switch (opt) { case '0' : break ; /* these options are deprecated */ case '1' : break ; /* only there for compatibility */ case '6' : break ; /* flagsocket is autodetected now */ case '7' : break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; case 'r' : if (!int0_scan(l.arg, &buffer_fd(&a[1][0].b))) dieusage() ; break ; case 'w' : if (!int0_scan(l.arg, &buffer_fd(&a[0][1].b))) dieusage() ; break ; default : dieusage() ; } } if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; argc -= l.ind ; argv += l.ind ; } if ((buffer_fd(&a[0][1].b) < 3) || (buffer_fd(&a[1][0].b) < 3)) dieusage() ; for (i = 0 ; i < 2 ; i++) for (j = 0 ; j < 2 ; j++) { int fd = buffer_fd(&a[i][j].b) ; struct stat st ; if (fstat(fd, &st) == -1) { char fmt[INT_FMT] ; fmt[int_fmt(fmt, fd)] = 0 ; strerr_diefu2sys(111, "fstat fd ", fmt) ; } a[i][j].flagsocket = !!S_ISSOCK(st.st_mode) ; if (ndelay_on(fd) == -1) { char fmt[INT_FMT] ; fmt[int_fmt(fmt, fd)] = 0 ; strerr_diefu3sys(111, "set fd ", fmt, " non-blocking") ; } } if (!sig_ignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; tain_now_set_stopwatch_g() ; x[0].fd = selfpipe_init() ; if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; if (!selfpipe_trap(SIGTERM)) strerr_diefu1sys(111, "trap SIGTERM") ; for (;;) { tain deadline ; unsigned int xlen = 1 ; tain_add_g(&deadline, buffer_isempty(&a[0][1].b) && buffer_isempty(&a[1][1].b) ? &tto : &tain_infinite_relative) ; for (i = 0 ; i < 2 ; i++) { if (buffer_fd(&a[i][0].b) >= 0 && buffer_isreadable(&a[i][0].b)) { x[xlen].fd = buffer_fd(&a[i][0].b) ; x[xlen].events = IOPAUSE_READ ; a[i][0].xindex = xlen++ ; } else a[i][0].xindex = 5 ; if (buffer_fd(&a[i][1].b) >= 0) { x[xlen].fd = buffer_fd(&a[i][1].b) ; x[xlen].events = IOPAUSE_EXCEPT | (buffer_iswritable(&a[i][1].b) ? IOPAUSE_WRITE : 0) ; a[i][1].xindex = xlen++ ; } else a[i][1].xindex = 5 ; } if (xlen == 1) break ; { int r = iopause_g(x, xlen, &deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; else if (!r) return 1 ; } if (x[0].revents & IOPAUSE_READ) handle_signals() ; for (i = 0 ; i < 2 ; i++) if (a[i][1].xindex < 5) { int dead = 0 ; if (x[a[i][1].xindex].revents & IOPAUSE_EXCEPT) { if (!buffer_isempty(&a[i][1].b)) { char fmt[INT_FMT] ; fmt[int_fmt(fmt, buffer_fd(&a[i][1].b))] = 0 ; flushit(i) ; /* sets errno */ strerr_warnwu2sys("write to fd ", fmt) ; } dead = 1 ; } else if (x[a[i][1].xindex].revents & IOPAUSE_WRITE) { if (!flushit(i)) { if (!error_isagain(errno)) dead = 1 ; } else if (buffer_fd(&a[i][0].b) == -1) dead = 1 ; } if (dead) { if (buffer_fd(&a[i][0].b) >= 0) closeit(i, 0) ; closeit(i, 1) ; } } for (i = 0 ; i < 2 ; i++) if (a[i][0].xindex < 5) { if (x[a[i][0].xindex].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT)) { if (!fillit(i)) { if (errno != EPIPE) { char fmt[INT_FMT] ; fmt[int_fmt(fmt, buffer_fd(&a[i][0].b))] = 0 ; strerr_warnwu2sys("read from fd ", fmt) ; } closeit(i, 0) ; if (buffer_isempty(&a[i][1].b)) closeit(i, 1) ; } } } } return 0 ; } s6-2.13.1.0/src/conn-tools/s6-ipcclient.c000066400000000000000000000036631470151141200176160ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #define USAGE "s6-ipcclient [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] path prog..." int main (int argc, char const *const *argv) { char const *bindpath = 0 ; char const *localname = 0 ; unsigned int verbosity = 1 ; PROG = "s6-ipcclient" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "qQvp:l:", &l) ; if (opt == -1) break ; switch (opt) { case 'q' : if (verbosity) verbosity-- ; break ; case 'Q' : verbosity = 1 ; break ; case 'v' : verbosity++ ; break ; case 'p' : bindpath = l.arg ; break ; case 'l' : localname = l.arg ; break ; default : strerr_dieusage(100, USAGE) ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 2) strerr_dieusage(100, USAGE) ; { char modif[24 + IPCPATH_MAX] = "PROTO=IPC\0IPCLOCALPATH=" ; size_t i = 23 ; int s = ipc_stream_b() ; if (s < 0) strerr_diefu1sys(111, "create socket") ; if (bindpath && (ipc_bind(s, bindpath) == -1)) strerr_diefu2sys(111, "bind socket to ", bindpath) ; if (!ipc_connect(s, argv[0])) strerr_diefu2sys(111, "connect to ", argv[0]) ; if (verbosity >= 2) strerr_warni3x(PROG, ": connected to ", argv[0]) ; if (localname) { size_t n = strlen(localname) ; if (n > IPCPATH_MAX) n = IPCPATH_MAX ; memcpy(modif + i, localname, n) ; i += n ; modif[i++] = 0 ; } else { int dummy ; if (ipc_local(s, modif + i, IPCPATH_MAX, &dummy) < 0) modif[--i] = 0 ; else i += strlen(modif + i) + 1 ; } if (fd_move(6, s) < 0) strerr_diefu2sys(111, "set up fd ", "6") ; if (fd_copy(7, 6) < 0) strerr_diefu2sys(111, "set up fd ", "7") ; xmexec_n(argv+1, modif, i, 2) ; } } s6-2.13.1.0/src/conn-tools/s6-ipcserver-access.c000066400000000000000000000136171470151141200211050ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef S6_USE_EXECLINE #include #endif #define USAGE "s6-ipcserver-access [ -v verbosity ] [ -e | -E ] [ -l localname ] [ -i rulesdir | -x rulesfile ] prog..." static unsigned int verbosity = 1 ; /* Utility functions */ static inline void dieusage (void) gccattr_noreturn ; static inline void dieusage () { strerr_dieusage(100, USAGE) ; } static inline void dienomem (void) gccattr_noreturn ; static inline void dienomem () { strerr_diefu1sys(111, "update environment") ; } static inline void X (void) gccattr_noreturn ; static inline void X () { strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ; } /* Logging */ static void logit (pid_t pid, uid_t uid, gid_t gid, int h) { char fmtpid[PID_FMT] ; char fmtuid[UID_FMT] ; char fmtgid[GID_FMT] ; fmtpid[pid_fmt(fmtpid, pid)] = 0 ; fmtuid[uid_fmt(fmtuid, uid)] = 0 ; fmtgid[gid_fmt(fmtgid, gid)] = 0 ; if (h) strerr_warni7x("allow", " pid ", fmtpid, " uid ", fmtuid, " gid ", fmtgid) ; else strerr_warni7sys("deny", " pid ", fmtpid, " uid ", fmtuid, " gid ", fmtgid) ; } static inline void log_accept (pid_t pid, uid_t uid, gid_t gid) { logit(pid, uid, gid, 1) ; } static inline void log_deny (pid_t pid, uid_t uid, gid_t gid) { logit(pid, uid, gid, 0) ; } /* Checking */ static s6_accessrules_result_t check_cdb (uid_t uid, gid_t gid, char const *file, s6_accessrules_params_t *params) { cdb c = CDB_ZERO ; s6_accessrules_result_t r ; if (!cdb_init(&c, file)) strerr_diefu2sys(111, "cdb_init ", file) ; r = s6_accessrules_uidgid_cdb(uid, gid, &c, params) ; cdb_free(&c) ; return r ; } static inline int check (s6_accessrules_params_t *params, char const *rules, unsigned int rulestype, uid_t uid, gid_t gid) { char const *x = "" ; s6_accessrules_result_t r ; switch (rulestype) { case 0 : if (verbosity >= 2) strerr_warnw1x("invoked without a ruleset!") ; return 1 ; case 1 : r = s6_accessrules_uidgid_fs(uid, gid, rules, params) ; x = "fs" ; break ; case 2 : r = check_cdb(uid, gid, rules, params) ; x = "cdb" ; break ; default : X() ; } switch (r) { case S6_ACCESSRULES_ERROR : strerr_diefu4sys(111, "check ", x, " ruleset in ", rules) ; case S6_ACCESSRULES_ALLOW : return 1 ; case S6_ACCESSRULES_DENY : return (errno = EACCES, 0) ; case S6_ACCESSRULES_NOTFOUND : return (errno = ENOENT, 0) ; default : X() ; } } int main (int argc, char const *const *argv) { s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ; char const *rules = 0 ; char const *localname = 0 ; char const *proto ; size_t protolen ; uid_t uid = 0 ; gid_t gid = 0 ; unsigned int rulestype = 0 ; int doenv = 1 ; PROG = "s6-ipcserver-access" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "v:Eel:i:x:", &l) ; if (opt == -1) break ; switch (opt) { case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; case 'E' : doenv = 0 ; break ; case 'e' : doenv = 1 ; break ; case 'l' : localname = l.arg ; break ; case 'i' : rules = l.arg ; rulestype = 1 ; break ; case 'x' : rules = l.arg ; rulestype = 2 ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; if (!*argv[0]) dieusage() ; proto = getenv("PROTO") ; if (!proto) strerr_dienotset(100, "PROTO") ; protolen = strlen(proto) ; { char const *x ; char tmp[protolen + 11] ; memcpy(tmp, proto, protolen) ; memcpy(tmp + protolen, "REMOTEEUID", 11) ; x = getenv(tmp) ; if (!x) strerr_dienotset(100, tmp) ; if (!uid0_scan(x, &uid)) strerr_dieinvalid(100, tmp) ; tmp[protolen + 7] = 'G' ; x = getenv(tmp) ; if (!x) strerr_dienotset(100, tmp) ; if (!gid0_scan(x, &gid)) strerr_dieinvalid(100, tmp) ; } if (check(¶ms, rules, rulestype, uid, gid)) goto accepted ; if (verbosity >= 2) log_deny(getpid(), uid, gid) ; return 1 ; accepted: if (verbosity) log_accept(getpid(), uid, gid) ; if (doenv) { char tmp[protolen + 10] ; memcpy(tmp, proto, protolen) ; memcpy(tmp + protolen, "LOCALPATH", 10) ; if (localname) { if (!env_addmodif(¶ms.env, tmp, localname)) dienomem() ; } else { char curname[IPCPATH_MAX+1] ; int dummy ; if (ipc_local(0, curname, IPCPATH_MAX+1, &dummy) < 0) strerr_diefu1sys(111, "ipc_local") ; if (!env_addmodif(¶ms.env, tmp, curname)) dienomem() ; } } else { char tmp[protolen + 11] ; memcpy(tmp, proto, protolen) ; memcpy(tmp + protolen, "REMOTEEUID", 11) ; if (!env_addmodif(¶ms.env, "PROTO", 0)) dienomem() ; if (!env_addmodif(¶ms.env, tmp, 0)) dienomem() ; tmp[protolen + 7] = 'G' ; if (!env_addmodif(¶ms.env, tmp, 0)) dienomem() ; memcpy(tmp + protolen + 6, "PATH", 5) ; if (!env_addmodif(¶ms.env, tmp, 0)) dienomem() ; memcpy(tmp + protolen, "LOCALPATH", 10) ; if (!env_addmodif(¶ms.env, tmp, 0)) dienomem() ; memcpy(tmp + protolen, "CONNNUM", 8) ; if (!env_addmodif(¶ms.env, tmp, 0)) dienomem() ; } if (params.exec.len) #ifdef S6_USE_EXECLINE { char *specialargv[4] = { EXECLINE_EXTBINPREFIX "execlineb", "-Pc", params.exec.s, 0 } ; xmexec_m((char const *const *)specialargv, params.env.s, params.env.len) ; } #else strerr_warnw1x("exec file found but ignored because s6 was compiled without execline support!") ; #endif xmexec_m(argv, params.env.s, params.env.len) ; } s6-2.13.1.0/src/conn-tools/s6-ipcserver-socketbinder.c000066400000000000000000000033351470151141200223140ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-ipcserver-socketbinder [ -d | -D ] [ -b backlog ] [ -M | -m ] [ -a perms ] [ -B ] path prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { unsigned int backlog = SOMAXCONN ; int flagreuse = 1 ; int flagdgram = 0 ; unsigned int flags = O_NONBLOCK ; unsigned int perms = 0777 ; PROG = "s6-ipcserver-socketbinder" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "DdMmBb:a:", &l) ; if (opt == -1) break ; switch (opt) { case 'D' : flagreuse = 0 ; break ; case 'd' : flagreuse = 1 ; break ; case 'M' : flagdgram = 0 ; break ; case 'm' : flagdgram = 1 ; break ; case 'B' : flags = 0 ; break ; case 'b' : if (!uint0_scan(l.arg, &backlog)) dieusage() ; break ; case 'a' : if (!uint0_oscan(l.arg, &perms)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 2) dieusage() ; close(0) ; if (flagdgram ? ipc_datagram_internal(flags) : ipc_stream_internal(flags)) strerr_diefu1sys(111, "create socket") ; { mode_t m = umask(~perms & 0777) ; if ((flagreuse ? ipc_bind_reuse(0, argv[0]) : ipc_bind(0, argv[0])) < 0) strerr_diefu2sys(111, "bind to ", argv[0]) ; umask(m) ; } if (backlog && ipc_listen(0, backlog) < 0) strerr_diefu2sys(111, "listen to ", argv[0]) ; xexec(argv+1) ; } s6-2.13.1.0/src/conn-tools/s6-ipcserver.c000066400000000000000000000103261470151141200176400ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-ipcserver [ -q | -Q | -v ] [ -d | -D ] [ -P | -p ] [ -1 ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -a socketperms ] [ -G gid,gid,... ] [ -g gid ] [ -u uid ] [ -U ] path prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { unsigned int verbosity = 1 ; int flag1 = 0 ; int flagU = 0 ; int flaglookup = 1 ; int flagreuse = 1 ; uid_t uid = 0 ; gid_t gid = 0 ; gid_t gids[NGROUPS_MAX] ; size_t gidn = (size_t)-1 ; unsigned int maxconn = 0 ; unsigned int localmaxconn = 0 ; unsigned int backlog = (unsigned int)-1 ; unsigned int socketperms = 0777 ; PROG = "s6-ipcserver" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "qQvDd1UPpc:C:b:a:u:g:G:", &l) ; if (opt == -1) break ; switch (opt) { case 'q' : verbosity = 0 ; break ; case 'Q' : verbosity = 1 ; break ; case 'v' : verbosity = 2 ; break ; case 'D' : flagreuse = 0 ; break ; case 'd' : flagreuse = 1 ; break ; case 'P' : flaglookup = 0 ; break ; case 'p' : flaglookup = 1 ; break ; case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; if (!maxconn) maxconn = 1 ; break ; case 'C' : if (!uint0_scan(l.arg, &localmaxconn)) dieusage() ; if (!localmaxconn) localmaxconn = 1 ; break ; case 'b' : if (!uint0_scan(l.arg, &backlog)) dieusage() ; break ; case 'a' : if (!uint0_oscan(l.arg, &socketperms)) dieusage() ; break ; case 'u' : if (!uid0_scan(l.arg, &uid)) dieusage() ; break ; case 'g' : if (!gid0_scan(l.arg, &gid)) dieusage() ; break ; case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn) && *l.arg) dieusage() ; break ; case '1' : flag1 = 1 ; break ; case 'U' : flagU = 1 ; uid = 0 ; gid = 0 ; gidn = (size_t)-1 ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (argc < 2) dieusage() ; } { size_t pos = 0 ; unsigned int m = 0 ; char fmt[UINT_FMT * 3 + 5 + UID_FMT + GID_FMT * (NGROUPS_MAX+1)] ; char const *newargv[26 + argc] ; newargv[m++] = S6_BINPREFIX "s6-ipcserver-socketbinder" ; if (!flagreuse) newargv[m++] = "-D" ; if (backlog != (unsigned int)-1) { if (!backlog) backlog = 1 ; newargv[m++] = "-b" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, backlog) ; fmt[pos++] = 0 ; } if (socketperms != 0777) { newargv[m++] = "-a" ; newargv[m++] = fmt + pos ; fmt[pos++] = '0' ; pos += uint_ofmt(fmt + pos, socketperms & 0777) ; fmt[pos++] = 0 ; } newargv[m++] = "--" ; newargv[m++] = *argv++ ; if (flagU || uid || gid || gidn != (size_t)-1) { newargv[m++] = S6_BINPREFIX "s6-applyuidgid" ; if (flagU) newargv[m++] = "-Uz" ; if (uid) { newargv[m++] = "-u" ; newargv[m++] = fmt + pos ; pos += uid_fmt(fmt + pos, uid) ; fmt[pos++] = 0 ; } if (gid) { newargv[m++] = "-g" ; newargv[m++] = fmt + pos ; pos += gid_fmt(fmt + pos, gid) ; fmt[pos++] = 0 ; } if (gidn != (size_t)-1) { newargv[m++] = "-G" ; newargv[m++] = fmt + pos ; pos += gid_fmtlist(fmt + pos, gids, gidn) ; fmt[pos++] = 0 ; } newargv[m++] = "--" ; } newargv[m++] = S6_BINPREFIX "s6-ipcserverd" ; if (!verbosity) newargv[m++] = "-v0" ; else if (verbosity == 2) newargv[m++] = "-v2" ; if (flag1) newargv[m++] = "-1" ; if (!flaglookup) newargv[m++] = "-P" ; if (maxconn) { newargv[m++] = "-c" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, maxconn) ; fmt[pos++] = 0 ; } if (localmaxconn) { newargv[m++] = "-C" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, localmaxconn) ; fmt[pos++] = 0 ; } newargv[m++] = "--" ; while (*argv) newargv[m++] = *argv++ ; newargv[m++] = 0 ; xexec(newargv) ; } } s6-2.13.1.0/src/conn-tools/s6-ipcserverd.c000066400000000000000000000242761470151141200200150ustar00rootroot00000000000000/* 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-ipcserverd [ -v verbosity ] [ -1 ] [ -P | -p ] [ -c maxconn ] [ -C localmaxconn ] prog..." #define ABSOLUTE_MAXCONN 16384 static unsigned int maxconn = 40 ; static unsigned int localmaxconn = 40 ; static char fmtmaxconn[UINT_FMT+1] = "/" ; static char fmtlocalmaxconn[UINT_FMT+1] = "/" ; static int flaglookup = 1 ; static unsigned int verbosity = 1 ; static int cont = 1 ; typedef struct piduid_s piduid_t, *piduid_t_ref ; struct piduid_s { pid_t left ; uid_t right ; } ; typedef struct uidnum_s uidnum_t, *uidnum_t_ref ; struct uidnum_s { uid_t left ; unsigned int right ; } ; static piduid_t *piduid ; static unsigned int numconn = 0 ; static uidnum_t *uidnum ; static unsigned int uidlen = 0 ; static inline void dieusage () { strerr_dieusage(100, USAGE) ; } static inline void X (void) { strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ; } static unsigned int lookup_pid (pid_t pid) { unsigned int i = 0 ; for (; i < numconn ; i++) if (pid == piduid[i].left) break ; return i ; } static inline unsigned int lookup_uid (uid_t uid) { unsigned int i = 0 ; for (; i < uidlen ; i++) if (uid == uidnum[i].left) break ; return i ; } static inline void log_start (void) { strerr_warni1x("starting") ; } static inline void log_exit (void) { strerr_warni1x("exiting") ; } static void log_status (void) { char fmt[UINT_FMT] ; fmt[uint_fmt(fmt, numconn)] = 0 ; strerr_warni3x("status: ", fmt, fmtmaxconn) ; } static inline void log_deny (uid_t uid, gid_t gid, unsigned int num) { char fmtuid[UID_FMT] = "?" ; char fmtgid[GID_FMT] = "?" ; char fmtnum[UINT_FMT] = "?" ; if (flaglookup) { fmtuid[uid_fmt(fmtuid, uid)] = 0 ; fmtgid[gid_fmt(fmtgid, gid)] = 0 ; fmtnum[uint_fmt(fmtnum, num)] = 0 ; } strerr_warni7sys("deny ", fmtuid, ":", fmtgid, " count ", fmtnum, fmtlocalmaxconn) ; } static inline void log_accept (pid_t pid, uid_t uid, gid_t gid, unsigned int num) { char fmtuidgid[UID_FMT + GID_FMT + 1] = "?:?" ; char fmtpid[UINT_FMT] ; char fmtnum[UINT_FMT] = "?" ; if (flaglookup) { size_t n = uid_fmt(fmtuidgid, uid) ; fmtuidgid[n++] = ':' ; n += gid_fmt(fmtuidgid + n, gid) ; fmtuidgid[n] = 0 ; fmtnum[uint_fmt(fmtnum, num)] = 0 ; } fmtpid[pid_fmt(fmtpid, pid)] = 0 ; strerr_warni7x("allow ", fmtuidgid, " pid ", fmtpid, " count ", fmtnum, fmtlocalmaxconn) ; } static inline void log_close (pid_t pid, uid_t uid, int w) { char fmtpid[PID_FMT] ; char fmtuid[UID_FMT] = "?" ; char fmtw[UINT_FMT] ; fmtpid[pid_fmt(fmtpid, pid)] = 0 ; if (flaglookup) fmtuid[uid_fmt(fmtuid, uid)] = 0 ; fmtw[uint_fmt(fmtw, WIFSIGNALED(w) ? WTERMSIG(w) : WEXITSTATUS(w))] = 0 ; strerr_warni6x("end pid ", fmtpid, " uid ", fmtuid, WIFSIGNALED(w) ? " signal " : " exitcode ", fmtw) ; } static void killthem (int sig) { unsigned int i = 0 ; for (; i < numconn ; i++) kill(piduid[i].left, sig) ; } static inline void wait_children (void) { for (;;) { unsigned int i ; int w ; pid_t pid = wait_nohang(&w) ; if (pid < 0) if (errno != ECHILD) strerr_diefu1sys(111, "wait_nohang") ; else break ; else if (!pid) break ; i = lookup_pid(pid) ; if (i < numconn) { uid_t uid = piduid[i].right ; unsigned int j = lookup_uid(uid) ; if (j >= uidlen) X() ; if (!--uidnum[j].right) uidnum[j] = uidnum[--uidlen] ; piduid[i] = piduid[--numconn] ; if (verbosity >= 2) { log_close(pid, uid, w) ; log_status() ; } } } } static inline void handle_signals (void) { for (;;) switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "read selfpipe") ; case 0 : return ; case SIGCHLD : wait_children() ; break ; case SIGTERM : { if (verbosity >= 2) strerr_warni3x("received ", "SIGTERM,", " quitting") ; cont = 0 ; break ; } case SIGHUP : { if (verbosity >= 2) strerr_warni5x("received ", "SIGHUP,", " sending ", "SIGTERM+SIGCONT", " to all connections") ; killthem(SIGTERM) ; killthem(SIGCONT) ; break ; } case SIGQUIT : { if (verbosity >= 2) strerr_warni6x("received ", "SIGQUIT,", " sending ", "SIGTERM+SIGCONT", " to all connections", " and quitting") ; cont = 0 ; killthem(SIGTERM) ; killthem(SIGCONT) ; break ; } case SIGABRT : { if (verbosity >= 2) strerr_warni6x("received ", "SIGABRT,", " sending ", "SIGKILL", " to all connections", " and quitting") ; cont = 0 ; killthem(SIGKILL) ; break ; } default : X() ; } } static void new_connection (int s, char const *remotepath, char const *const *argv, char const *const *envp, size_t envlen) { uid_t uid = 0 ; gid_t gid = 0 ; size_t m = 0 ; size_t rplen = strlen(remotepath) + 1 ; pid_t pid ; unsigned int num, i ; cspawn_fileaction fa[2] = { [0] = { .type = CSPAWN_FA_MOVE, .x = { .fd2 = { [0] = 0, [1] = s } } }, [1] = { .type = CSPAWN_FA_COPY, .x = { .fd2 = { [0] = 1, [1] = 0 } } } } ; char const *newenvp[envlen + 6] ; char fmt[65 + UID_FMT + GID_FMT + UINT_FMT + rplen] ; if (flaglookup && (getpeereid(s, &uid, &gid) < 0)) { if (verbosity) strerr_warnwu1sys("getpeereid") ; return ; } i = lookup_uid(uid) ; num = (i < uidlen) ? uidnum[i].right : 0 ; if (num >= localmaxconn) { log_deny(uid, gid, num) ; return ; } memcpy(fmt + m, "PROTO=IPC\0IPCREMOTEEUID", 23) ; m += 23 ; if (flaglookup) { fmt[m++] = '=' ; m += uid_fmt(fmt + m, uid) ; } fmt[m++] = 0 ; memcpy(fmt + m, "IPCREMOTEEGID", 13) ; m += 13 ; if (flaglookup) { fmt[m++] = '=' ; m += gid_fmt(fmt + m, gid) ; } fmt[m++] = 0 ; memcpy(fmt + m, "IPCCONNNUM=", 11) ; m += 11 ; if (flaglookup) m += uint_fmt(fmt + m, num) ; fmt[m++] = 0 ; memcpy(fmt + m, "IPCREMOTEPATH=", 14) ; m += 14 ; memcpy(fmt + m, remotepath, rplen) ; m += rplen ; env_mergen(newenvp, envlen + 6, envp, envlen, fmt, m, 5) ; pid = cspawn(argv[0], argv, newenvp, CSPAWN_FLAGS_SELFPIPE_FINISH, fa, 2) ; if (!pid) { if (verbosity) strerr_warnwu2sys("spawn ", argv[0]) ; return ; } if (i < uidlen) uidnum[i].right = num + 1 ; else { uidnum[uidlen].left = uid ; uidnum[uidlen++].right = 1 ; } piduid[numconn].left = pid ; piduid[numconn++].right = uid ; if (verbosity >= 2) { log_accept(pid, uid, gid, uidnum[i].right) ; log_status() ; } } int main (int argc, char const *const *argv) { iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .fd = 0, .events = IOPAUSE_READ | IOPAUSE_EXCEPT } } ; PROG = "s6-ipcserverd" ; { subgetopt l = SUBGETOPT_ZERO ; int flag1 = 0 ; for (;;) { int opt = subgetopt_r(argc, argv, "Pp1c:C:v:", &l) ; if (opt == -1) break ; switch (opt) { case 'P' : flaglookup = 0 ; break ; case 'p' : flaglookup = 1 ; break ; case '1' : flag1 = 1 ; break ; case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ; case 'C' : if (!uint0_scan(l.arg, &localmaxconn)) dieusage() ; break ; case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (!argc || !*argv[0]) dieusage() ; { struct stat st ; if (fstat(0, &st) < 0) strerr_diefu1sys(111, "fstat stdin") ; if (!S_ISSOCK(st.st_mode)) strerr_dief1x(100, "stdin is not a socket") ; } if (coe(0) < 0) strerr_diefu1sys(111, "make socket close-on-exec") ; if (flag1) { if (fcntl(1, F_GETFD) < 0) strerr_dief1sys(100, "called with option -1 but stdout said") ; } else close(1) ; if (!maxconn) maxconn = 1 ; if (maxconn > ABSOLUTE_MAXCONN) maxconn = ABSOLUTE_MAXCONN ; if (!flaglookup || (localmaxconn > maxconn)) localmaxconn = maxconn ; x[0].fd = selfpipe_init() ; if (x[0].fd == -1) strerr_diefu1sys(111, "create selfpipe") ; if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; { sigset_t set ; sigemptyset(&set) ; sigaddset(&set, SIGCHLD) ; sigaddset(&set, SIGTERM) ; sigaddset(&set, SIGHUP) ; sigaddset(&set, SIGQUIT) ; sigaddset(&set, SIGABRT) ; if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ; } fmtlocalmaxconn[1+uint_fmt(fmtlocalmaxconn+1, localmaxconn)] = 0 ; if (verbosity >= 2) { fmtmaxconn[1+uint_fmt(fmtmaxconn+1, maxconn)] = 0 ; log_start() ; log_status() ; } if (flag1) { fd_write(1, "\n", 1) ; fd_close(1) ; } } { piduid_t inyostack0[maxconn] ; uidnum_t inyostack1[flaglookup ? maxconn : 1] ; size_t envlen = env_len((char const *const *)environ) ; piduid = inyostack0 ; uidnum = inyostack1 ; while (cont) { if (iopause_g(x, 1 + (numconn < maxconn), 0) < 0) strerr_diefu1sys(111, "iopause") ; if (x[0].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with selfpipe") ; if (x[0].revents & IOPAUSE_READ) { handle_signals() ; continue ; } if (numconn < maxconn) { if (x[1].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with socket") ; if (x[1].revents & IOPAUSE_READ) { int dummy ; char remotepath[IPCPATH_MAX+1] ; int sock = ipc_accept(x[1].fd, remotepath, IPCPATH_MAX+1, &dummy) ; if (sock < 0) { if (verbosity) strerr_warnwu1sys("accept") ; } else { new_connection(sock, remotepath, argv, (char const *const *)environ, envlen) ; fd_close(sock) ; } } } } } if (verbosity >= 2) log_exit() ; return 0 ; } s6-2.13.1.0/src/conn-tools/s6-sudo.c000066400000000000000000000041421470151141200166070ustar00rootroot00000000000000 /* ISC license. */ #include #include #include #include #include #define USAGE "s6-sudo [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] [ -e ] [ -t timeout ] [ -T timeoutrun ] path [ args... ]" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { unsigned int verbosity = 1, t = 0, T = 0 ; char const *bindpath = 0 ; char const *localname = 0 ; int nodoenv = 0 ; PROG = "s6-sudo" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "qQvp:l:et:T:", &l) ; if (opt == -1) break ; switch (opt) { case 'q' : if (verbosity) verbosity-- ; break ; case 'Q' : verbosity = 1 ; break ; case 'v' : verbosity++ ; break ; case 'p' : bindpath = l.arg ; break ; case 'l' : localname = l.arg ; break ; case 'e' : nodoenv = 1 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; if (verbosity > 4) verbosity = 4 ; { char const *eargv[9 + argc + ((verbosity < 2 ? 1 : verbosity-1)) + ((!!bindpath + !!localname) << 1) + nodoenv] ; char fmt1[UINT_FMT] ; char fmt2[UINT_FMT] ; unsigned int n = 0 ; eargv[n++] = S6_BINPREFIX "s6-ipcclient" ; if (!verbosity) eargv[n++] = "-Q" ; else while (--verbosity) eargv[n++] = "-v" ; if (bindpath) { eargv[n++] = "-p" ; eargv[n++] = bindpath ; } if (localname) { eargv[n++] = "-l" ; eargv[n++] = localname ; } eargv[n++] = "--" ; eargv[n++] = *argv++ ; argc-- ; eargv[n++] = S6_BINPREFIX "s6-sudoc" ; if (nodoenv) eargv[n++] = "-e" ; eargv[n++] = "-t" ; fmt1[uint_fmt(fmt1, t)] = 0 ; eargv[n++] = fmt1 ; eargv[n++] = "-T" ; fmt2[uint_fmt(fmt2, T)] = 0 ; eargv[n++] = fmt2 ; eargv[n++] = "--" ; while (argc--) eargv[n++] = *argv++ ; eargv[n++] = 0 ; xexec(eargv) ; } } s6-2.13.1.0/src/conn-tools/s6-sudo.h000066400000000000000000000004131470151141200166110ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_SUDO_H #define S6_SUDO_H #define S6_SUDO_BANNERB "s6-sudo b v1.0\n" #define S6_SUDO_BANNERB_LEN (sizeof(S6_SUDO_BANNERB) - 1) #define S6_SUDO_BANNERA "s6-sudo a v1.0\n" #define S6_SUDO_BANNERA_LEN (sizeof(S6_SUDO_BANNERA) - 1) #endif s6-2.13.1.0/src/conn-tools/s6-sudoc.c000066400000000000000000000076451470151141200167650ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "s6-sudo.h" #define USAGE "s6-sudoc [ -e ] [ -t timeoutconn ] [ -T timeoutrun ] [ args... ]" #define dieusage() strerr_dieusage(100, USAGE) #define dienomem() strerr_diefu1sys(111, "stralloc_catb") int main (int argc, char const *const *argv, char const *const *envp) { char buf6[64] ; buffer b6 = BUFFER_INIT(&buffer_read, 6, buf6, 64) ; unixmessage_sender b7 = UNIXMESSAGE_SENDER_INIT(7) ; subgetopt l = SUBGETOPT_ZERO ; unsigned int t = 0, T = 0 ; int doenv = 1 ; tain deadline = TAIN_INFINITE_RELATIVE ; PROG = "s6-sudoc" ; for (;;) { int opt = subgetopt_r(argc, argv, "et:T:", &l) ; if (opt < 0) break ; switch (opt) { case 'e' : doenv = 0 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&deadline, t) ; if ((ndelay_on(6) < 0) || (ndelay_on(7) < 0)) strerr_diefu1sys(111, "make socket non-blocking") ; if (!fd_sanitize() || !fd_ensure_open(2, 1)) strerr_diefu1sys(111, "sanitize stdin/stdout/stderr") ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; { size_t r ; char tmp[S6_SUDO_BANNERB_LEN] ; r = buffer_timed_get_g(&b6, tmp, S6_SUDO_BANNERB_LEN, &deadline) ; if (!r) strerr_diefu1x(111, "connect to the s6-sudod server - check that you have appropriate permissions") ; if (r < S6_SUDO_BANNERB_LEN) strerr_diefu1sys(111, "read banner from s6-sudod") ; if (strncmp(tmp, S6_SUDO_BANNERB, S6_SUDO_BANNERB_LEN)) strerr_dief1x(100, "wrong banner - check that you are connecting to a s6-sudod server") ; } { int fds[3] = { 0, 1, 2 } ; char pack[16] ; struct iovec v[4] = { { .iov_base = S6_SUDO_BANNERA, .iov_len = S6_SUDO_BANNERA_LEN }, { .iov_base = pack, .iov_len = 16 }, { .iov_base = 0, .iov_len = 0 }, { .iov_base = 0, .iov_len = 0 } } ; unixmessagev mv = { .v = v, .vlen = 4, .fds = fds, .nfds = 3 } ; stralloc sa = STRALLOC_ZERO ; size_t envlen = doenv ? env_len(envp) : 0 ; uint32_pack_big(pack, (uint32_t)argc) ; uint32_pack_big(pack + 4, (uint32_t)envlen) ; if (!env_string(&sa, argv, argc)) dienomem() ; v[2].iov_len = sa.len ; uint32_pack_big(pack + 8, (uint32_t)v[2].iov_len) ; if (doenv) { if (!env_string(&sa, envp, envlen)) dienomem() ; v[3].iov_len = sa.len - v[2].iov_len ; } uint32_pack_big(pack + 12, (uint32_t)v[3].iov_len) ; v[2].iov_base = sa.s ; v[3].iov_base = sa.s + v[2].iov_len ; if (!unixmessage_putv_and_close(&b7, &mv, (unsigned char const *)"\003")) strerr_diefu1sys(111, "unixmessage_putv") ; stralloc_free(&sa) ; } if (!unixmessage_sender_timed_flush_g(&b7, &deadline)) strerr_diefu1sys(111, "send args to server") ; unixmessage_sender_free(&b7) ; { char c ; if (buffer_timed_get_g(&b6, &c, 1, &deadline) < 1) strerr_diefu1sys(111, "get confirmation from server") ; if (c) { errno = c ; strerr_diefu1sys(111, "start privileged program: server answered: ") ; } } if (T) tain_from_millisecs(&deadline, T) ; else deadline = tain_infinite_relative ; tain_add_g(&deadline, &deadline) ; { char pack[UINT_PACK] ; if (buffer_timed_get_g(&b6, pack, UINT_PACK, &deadline) < UINT_PACK) strerr_diefu1sys(111, "get exit status from server") ; uint_unpack_big(pack, &t) ; } return wait_estatus(t) ; } s6-2.13.1.0/src/conn-tools/s6-sudod.c000066400000000000000000000155401470151141200167570ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "s6-sudo.h" #define USAGE "s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -d ] [ -t timeout ] args..." #define dieusage() strerr_dieusage(100, USAGE) #define dienomem() strerr_diefu1sys(111, "stralloc_catb") static int handle_signals (pid_t pid, int *wstat) { int done = 0 ; for (;;) { int sig = selfpipe_read() ; switch (sig) { case -1 : strerr_diefu1sys(111, "read from selfpipe") ; case 0 : return done ; case SIGCHLD : { int w ; pid_t r = wait_pid_nohang(pid, &w) ; if ((r < 0) && (errno != ECHILD)) strerr_diefu1sys(111, "wait_pid_nohang") ; else if (r > 0) { done = 1 ; *wstat = w ; } break ; } default : strerr_dief1sys(101, "internal inconsistency, please submit a bug-report") ; } } } int main (int argc, char const *const *argv, char const *const *envp) { iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .fd = 0, .events = 0, .revents = 0 } } ; unixmessage m ; unsigned int nullfds = 0 ; pid_t pid ; int wstat ; size_t envc = env_len(envp) ; uint32_t cargc, cenvc, carglen, cenvlen ; tain deadline = TAIN_INFINITE_RELATIVE ; PROG = "s6-sudod" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int t = 0 ; for (;;) { int opt = subgetopt_r(argc, argv, "012dt:", &l) ; if (opt < 0) break ; switch (opt) { case '0' : nullfds |= 1 ; break ; case '1' : nullfds |= 2 ; break ; case '2' : nullfds |= 4 ; break ; case 'd' : nullfds |= 15 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&deadline, t) ; } if ((ndelay_on(0) < 0) || (ndelay_on(1) < 0)) strerr_diefu1sys(111, "make socket non-blocking") ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; buffer_putnoflush(buffer_1small, S6_SUDO_BANNERB, S6_SUDO_BANNERB_LEN) ; if (!buffer_timed_flush_g(buffer_1small, &deadline)) strerr_diefu1sys(111, "write banner to client") ; if (unixmessage_timed_receive_g(unixmessage_receiver_0, &m, &deadline) <= 0) strerr_diefu1sys(111, "read message from client") ; if (m.nfds != 3) strerr_dief1x(100, "client did not send 3 fds") ; if (m.len < 16 + S6_SUDO_BANNERA_LEN) strerr_dief1x(100, "wrong client message") ; if (strncmp(m.s, S6_SUDO_BANNERA, S6_SUDO_BANNERA_LEN)) strerr_dief1x(100, "wrong client banner") ; uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN, &cargc) ; uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 4, &cenvc) ; uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 8, &carglen) ; uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 12, &cenvlen) ; if (S6_SUDO_BANNERA_LEN + 16 + carglen + cenvlen != m.len) strerr_dief1x(100, "wrong client argc/envlen") ; if ((cargc > 131072) || (cenvc > 131072)) strerr_dief1x(100, "too many args/envvars from client") ; if (argc + cargc == 0) strerr_dief1x(100, "client and server args both empty") ; if (nullfds & 1) { close(m.fds[0]) ; m.fds[0] = open2("/dev/null", O_RDONLY) ; if (m.fds[0] < 0) strerr_diefu2sys(111, "open /dev/null for ", "reading") ; } if (nullfds & 2) { close(m.fds[1]) ; m.fds[1] = open2("/dev/null", O_WRONLY) ; if (m.fds[1] < 0) strerr_diefu2sys(111, "open /dev/null for ", "writing") ; } if (nullfds & 4) { close(m.fds[2]) ; m.fds[2] = 2 ; } { cspawn_fileaction fa[3] = { [0] = { .type = CSPAWN_FA_MOVE, .x = { .fd2 = { [0] = 0, [1] = m.fds[0] } } }, [1] = { .type = CSPAWN_FA_MOVE, .x = { .fd2 = { [0] = 1, [1] = m.fds[1] } } }, [2] = { .type = CSPAWN_FA_MOVE, .x = { .fd2 = { [0] = 2, [1] = m.fds[2] } } } } ; char const *targv[argc + 1 + cargc] ; char const *tenvp[envc + 1 + cenvc] ; unsigned int i = 0 ; for (; i < (unsigned int)argc ; i++) targv[i] = argv[i] ; for (i = 0 ; i <= envc ; i++) tenvp[i] = envp[i] ; if (!env_make(targv + argc, cargc, m.s + S6_SUDO_BANNERA_LEN + 16, carglen)) { char c = errno ; buffer_putnoflush(buffer_1small, &c, 1) ; buffer_timed_flush_g(buffer_1small, &deadline) ; errno = c ; strerr_diefu1sys(111, "make child argv") ; } if (!env_make(tenvp + envc + 1, cenvc, m.s + S6_SUDO_BANNERA_LEN + 16 + carglen, cenvlen)) { char c = errno ; buffer_putnoflush(buffer_1small, &c, 1) ; buffer_timed_flush_g(buffer_1small, &deadline) ; errno = c ; strerr_diefu1sys(111, "make child envp") ; } targv[argc + cargc] = 0 ; for (i = 0 ; i < cenvc ; i++) { char const *var = tenvp[envc + 1 + i] ; unsigned int j = 0 ; size_t len = str_chr(var, '=') ; if (!var[len]) { char c = EINVAL ; buffer_putnoflush(buffer_1small, &c, 1) ; buffer_timed_flush_g(buffer_1small, &deadline) ; strerr_dief1x(100, "bad environment from client") ; } for (; j < envc ; j++) if (!strncmp(var, tenvp[j], len+1)) break ; if ((j < envc) && !tenvp[j][len+1]) tenvp[j] = var ; } x[0].fd = selfpipe_init() ; if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; if (!selfpipe_trap(SIGCHLD)) strerr_diefu1sys(111, "trap SIGCHLD") ; pid = cspawn(targv[0], targv, tenvp, CSPAWN_FLAGS_SELFPIPE_FINISH, fa, 3) ; if (!pid) strerr_diefu2sys(111, "spawn ", targv[0]) ; } fd_close(m.fds[0]) ; fd_close(m.fds[1]) ; if (!(nullfds & 4)) fd_close(m.fds[2]) ; unixmessage_receiver_free(unixmessage_receiver_0) ; buffer_putnoflush(buffer_1small, "", 1) ; if (!buffer_timed_flush_g(buffer_1small, &deadline)) strerr_diefu1sys(111, "send confirmation to client") ; for (;;) { if (iopause_g(x, 1 + !x[1].revents, 0) < 0) strerr_diefu1sys(111, "iopause") ; if (x[0].revents && handle_signals(pid, &wstat)) break ; if (x[1].revents && !(nullfds & 8)) { kill(pid, SIGTERM) ; kill(pid, SIGCONT) ; return 1 ; } } if (!x[1].revents) { char pack[UINT_PACK] ; uint_pack_big(pack, (unsigned int)wstat) ; buffer_putnoflush(buffer_1small, pack, UINT_PACK) ; if (ndelay_off(1) < 0) strerr_diefu1sys(111, "set stdout blocking") ; if (!buffer_flush(buffer_1small)) strerr_diefu1sys(111, "write status to client") ; } return 0 ; } s6-2.13.1.0/src/daemontools-extras/000077500000000000000000000000001470151141200166775ustar00rootroot00000000000000s6-2.13.1.0/src/daemontools-extras/deps-exe/000077500000000000000000000000001470151141200204115ustar00rootroot00000000000000s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-applyuidgid000066400000000000000000000000121470151141200231660ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-envdir000066400000000000000000000000121470151141200221420ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-envuidgid000066400000000000000000000000511470151141200226340ustar00rootroot00000000000000${LIBNSSS} -lskarnet ${MAYBEPTHREAD_LIB} s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-fghack000066400000000000000000000000271470151141200221040ustar00rootroot00000000000000-lskarnet ${SPAWN_LIB} s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-log000066400000000000000000000000471470151141200214440ustar00rootroot00000000000000-lskarnet ${SPAWN_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-setlock000066400000000000000000000000271470151141200223250ustar00rootroot00000000000000-lskarnet ${TIMER_LIB} s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-setsid000066400000000000000000000000121470151141200221460ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-setuidgid000066400000000000000000000000121470151141200226340ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-socklog000066400000000000000000000000641470151141200223230ustar00rootroot00000000000000lolsyslog.o -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-softlimit000066400000000000000000000000121470151141200226650ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-tai64n000066400000000000000000000000321470151141200217620ustar00rootroot00000000000000-lskarnet ${SYSCLOCK_LIB} s6-2.13.1.0/src/daemontools-extras/deps-exe/s6-tai64nlocal000066400000000000000000000000121470151141200227730ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/daemontools-extras/deps-exe/ucspilogd000066400000000000000000000000261470151141200223230ustar00rootroot00000000000000lolsyslog.o -lskarnet s6-2.13.1.0/src/daemontools-extras/lolsyslog.c000066400000000000000000000036511470151141200210770ustar00rootroot00000000000000/* ISC license. */ #undef INTERNAL_MARK #ifndef SYSLOG_NAMES #define SYSLOG_NAMES #endif #include #include #include #include #include "lolsyslog.h" #ifndef INTERNAL_MARK typedef struct CODE_s CODE, *CODE_ref ; struct CODE_s { char *c_name ; unsigned int c_val ; } ; #define LOG_PRI(p) ((p) & LOG_PRIMASK) #define LOG_FAC(p) (((p) & LOG_FACMASK) / (LOG_PRIMASK + 1)) static CODE const facilitynames[] = { { "kern", LOG_KERN }, { "user", LOG_USER }, { "mail", LOG_MAIL }, { "news", LOG_NEWS }, { "uucp", LOG_UUCP }, { "daemon", LOG_DAEMON }, { "auth", LOG_AUTH }, { "cron", LOG_CRON }, { "lpr", LOG_LPR }, #ifdef LOG_SYSLOG { "syslog", LOG_SYSLOG }, #endif #ifdef LOG_AUDIT { "audit", LOG_AUDIT }, #endif { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { 0, -1 } } ; static CODE const prioritynames[] = { { "emerg", LOG_EMERG }, { "alert", LOG_ALERT }, { "crit", LOG_CRIT }, { "err", LOG_ERR }, { "warning", LOG_WARNING }, { "notice", LOG_NOTICE }, { "info", LOG_INFO }, { "debug", LOG_DEBUG }, { 0, -1 } } ; #endif size_t lolsyslog_string (char *out, char const *in) { size_t i ; unsigned int fpr ; int fp ; CODE const *p = facilitynames ; char const *x ; if (in[0] != '<' || !(i = uint_scan(in+1, &fpr)) || in[1+i] != '>') return 0 ; fp = LOG_FAC(fpr) << 3 ; for (; p->c_name ; p++) if (p->c_val == fp) break ; x = p->c_name ? p->c_name : "unknown" ; strcpy(out, x) ; out += strlen(x) ; *out++ = '.' ; p = prioritynames ; fp = LOG_PRI(fpr) ; for (; p->c_name ; p++) if (p->c_val == fp) break ; x = p->c_name ? p->c_name : "unknown" ; strcpy(out, x) ; out += strlen(x) ; *out++ = ':' ; *out++ = ' ' ; *out++ = 0 ; return i+2 ; } s6-2.13.1.0/src/daemontools-extras/lolsyslog.h000066400000000000000000000002321470151141200210740ustar00rootroot00000000000000/* ISC license. */ #ifndef LOLSYSLOG_H #define LOLSYSLOG_H #define LOLSYSLOG_STRING 32 extern size_t lolsyslog_string (char *, char const *) ; #endif s6-2.13.1.0/src/daemontools-extras/s6-applyuidgid.c000066400000000000000000000040551470151141200217100ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-applyuidgid [ -z ] [ -u uid ] [ -g gid ] [ -G gidlist ] [ -U ] prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { uid_t uid = 0 ; gid_t gid = 0 ; gid_t gids[NGROUPS_MAX+1] ; size_t gidn = (size_t)-1 ; int unexport = 0 ; PROG = "s6-applyuidgid" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "zUu:g:G:", &l) ; if (opt == -1) break ; switch (opt) { case 'z' : unexport = 1 ; break ; case 'u' : if (!uid0_scan(l.arg, &uid)) dieusage() ; break ; case 'g' : if (!gid0_scan(l.arg, &gid)) dieusage() ; break ; case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn) && *l.arg) dieusage() ; break ; case 'U' : { char const *x = getenv("UID") ; if (!x) strerr_dienotset(100, "UID") ; if (!uid0_scan(x, &uid)) strerr_dieinvalid(100, "UID") ; x = getenv("GID") ; if (!x) strerr_dienotset(100, "GID") ; if (!gid0_scan(x, &gid)) strerr_dieinvalid(100, "GID") ; x = getenv("GIDLIST") ; if (!x) strerr_dienotset(100, "GIDLIST") ; if (!gid_scanlist(gids, NGROUPS_MAX+1, x, &gidn) && *x) strerr_dieinvalid(100, "GIDLIST") ; break ; } default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; if (gidn != (size_t)-1 && setgroups_and_gid(gid ? gid : getegid(), gidn, gids) < 0) strerr_diefu1sys(111, "set supplementary group list") ; if (gid && setgid(gid) < 0) strerr_diefu1sys(111, "setgid") ; if (uid && setuid(uid) < 0) strerr_diefu1sys(111, "setuid") ; if (unexport) xmexec_n(argv, "UID\0GID\0GIDLIST", 16, 3) ; else xexec(argv) ; } s6-2.13.1.0/src/daemontools-extras/s6-envdir.c000066400000000000000000000023111470151141200206550ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #define USAGE "s6-envdir [ -I | -i ] [ -n ] [ -f ] [ -L ] [ -c nullchar ] dir prog..." int main (int argc, char const *const *argv) { stralloc modifs = STRALLOC_ZERO ; subgetopt l = SUBGETOPT_ZERO ; int insist = 1 ; unsigned int options = 0 ; char nullis = '\n' ; PROG = "s6-envdir" ; for (;;) { int opt = subgetopt_r(argc, argv, "IinfLc:", &l) ; if (opt == -1) break ; switch (opt) { case 'I' : insist = 0 ; break ; case 'i' : insist = 1 ; break ; case 'n' : options |= SKALIBS_ENVDIR_NOCHOMP ; break ; case 'f' : options |= SKALIBS_ENVDIR_VERBATIM ; break ; case 'L' : options |= SKALIBS_ENVDIR_NOCLAMP ; break ; case 'c' : nullis = *l.arg ; break ; default : strerr_dieusage(100, USAGE) ; } } argc -= l.ind ; argv += l.ind ; if (argc < 2) strerr_dieusage(100, USAGE) ; if ((envdir_internal(*argv++, &modifs, options, nullis) < 0) && (insist || (errno != ENOENT))) strerr_diefu2sys(111, "envdir ", argv[-1]) ; xmexec_m(argv, modifs.s, modifs.len) ; } s6-2.13.1.0/src/daemontools-extras/s6-envuidgid.c000066400000000000000000000100271470151141200213470ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-envuidgid [ -i | -D defaultuid:defaultgid:defaultgidlist ] [ -u | -g | -B ] [ -n ] account prog..." #define dieusage() strerr_dieusage(100, USAGE) static inline size_t scan_defaults (char const *s, uid_t *uid, gid_t *gid, size_t *n, gid_t *tab) { size_t pos = uid_scan(s, uid) ; if (!pos) { if (*s != ':') return 0 ; *uid = 0 ; } s += pos ; if (!*s) goto zgid ; if (*s++ != ':') return 0 ; if (!*s) goto zgid ; pos = gid_scan(s, gid) ; if (!pos) { if (*s != ':') return 0 ; *gid = 0 ; } s += pos ; if (!*s) goto zn ; if (*s++ != ':') return 0 ; if (!*s) goto zn ; return gid_scanlist(tab, NGROUPS_MAX, s, n) ; zgid: *gid = 0 ; zn: *n = 0 ; return 1 ; } static int prot_readgroups (char const *name, gid_t *tab, unsigned int max) { unsigned int n = 0 ; for (;;) { struct group *gr ; char **member ; errno = 0 ; if (n >= max) break ; gr = getgrent() ; if (!gr) break ; for (member = gr->gr_mem ; *member ; member++) if (!strcmp(name, *member)) break ; if (*member) tab[n++] = gr->gr_gid ; } endgrent() ; return errno ? -1 : n ; } int main (int argc, char *const *argv) { char const *user = 0 ; char const *group = 0 ; unsigned int what = 7 ; int numfallback = 0 ; int insist = 1 ; uid_t uid ; gid_t gid ; size_t n ; gid_t tab[NGROUPS_MAX] ; PROG = "s6-envuidgid" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, (char const *const *)argv, "ugBniD:", &l) ; if (opt == -1) break ; switch (opt) { case 'u' : what = 1 ; break ; case 'g' : what = 2 ; break ; case 'B' : what = 3 ; break ; case 'n' : what &= 3 ; numfallback = 1 ; break ; case 'i' : insist = 1 ; break ; case 'D' : if (!scan_defaults(l.arg, &uid, &gid, &n, tab)) dieusage() ; insist = 0 ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 2) dieusage() ; switch (what) { case 7 : /* account */ case 1 : /* user */ user = argv[0] ; break ; case 2 : /* group */ group = argv[0] ; break ; case 3 : /* both */ { char *colon = strchr(argv[0], ':') ; user = argv[0] ; if (colon) { *colon = 0 ; group = colon + 1 ; if (colon == argv[0]) user = 0 ; if (!group[0]) group = 0 ; } break ; } default : strerr_dief1x(101, "inconsistent option management - please submit a bug-report") ; } if (group) { struct group *gr = getgrnam(group) ; if (gr) gid = gr->gr_gid ; else if (numfallback && gid_scan(group, &gid)) ; else if (insist) strerr_dief2x(1, "unknown group: ", group) ; } if (user) { struct passwd *pw = getpwnam(user) ; if (pw) { uid = pw->pw_uid ; if (what == 7) { int r = prot_readgroups(argv[0], tab, NGROUPS_MAX) ; if (r < 0) strerr_diefu2sys(111, "get supplementary groups for ", argv[0]) ; n = r ; gid = pw->pw_gid ; } } else if (numfallback && uid_scan(user, &uid)) ; else if (insist) strerr_dief2x(1, "unknown user: ", user) ; } { size_t pos = 0 ; char fmt[19 + UID_FMT + (NGROUPS_MAX+1) * GID_FMT] ; if (what & 1) { memcpy(fmt + pos, "UID=", 4) ; pos += 4 ; pos += uid_fmt(fmt + pos, uid) ; fmt[pos++] = 0 ; } if (what & 2) { memcpy(fmt + pos, "GID=", 4) ; pos += 4 ; pos += gid_fmt(fmt + pos, gid) ; fmt[pos++] = 0 ; } if (what & 4) { memcpy(fmt + pos, "GIDLIST=", 8) ; pos += 8 ; pos += gid_fmtlist(fmt + pos, tab, n) ; fmt[pos++] = 0 ; } xmexec_m((char const *const *)argv + 1, fmt, pos) ; } } s6-2.13.1.0/src/daemontools-extras/s6-fghack.c000066400000000000000000000020661470151141200206200ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-fghack prog..." #define N 30 int main (int argc, char const *const *argv, char const *const *envp) { int p[2] ; int fds[N] ; pid_t pid ; cspawn_fileaction fa = { .type = CSPAWN_FA_CLOSE } ; char c ; PROG = "s6-fghack" ; if (argc < 2) strerr_dieusage(100, USAGE) ; if (pipe(p) == -1) strerr_diefu1sys(111, "create hackpipe") ; for (size_t i = 0 ; i < N ; i++) fds[i] = dup(p[1]) ; fa.x.fd = p[0] ; pid = cspawn(argv[1], argv + 1, envp, 0, &fa, 1) ; if (!pid) strerr_diefu2sys(111, "spawn ", argv[1]) ; close(p[1]) ; for (size_t i = 0 ; i < N ; i++) close(fds[i]) ; p[1] = fd_read(p[0], &c, 1) ; if (p[1] == -1) strerr_diefu1sys(111, "read on hackpipe") ; if (p[1]) strerr_dief2x(102, argv[1], " wrote on hackpipe") ; if (wait_pid(pid, &p[1]) < 0) strerr_diefu1sys(111, "wait_pid") ; return wait_estatus(p[1]) ; } s6-2.13.1.0/src/daemontools-extras/s6-log.c000066400000000000000000001066551470151141200201670ustar00rootroot00000000000000/* 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 #include #include #include #include #include #include #include #include #include #ifdef S6_USE_EXECLINE #include #endif #define USAGE "s6-log [ -d notif ] [ -q | -v ] [ -b ] [ -p ] [ -l linelimit ] [ -t lastlinetimeout ] [ -- ] logging_script" #define dieusage() strerr_dieusage(100, USAGE) #define dienomem() strerr_diefu1sys(111, "stralloc_catb") #define LINELIMIT_MIN 48 static mode_t mask ; static int flagprotect = 0 ; static int flagexiting = 0 ; static unsigned int verbosity = 1 ; static tain lastlinetto = TAIN_INFINITE_RELATIVE ; static tain exit_deadline = TAIN_INFINITE ; static stralloc indata = STRALLOC_ZERO ; /* Data types */ typedef int qcmp_func (void const *, void const *) ; typedef qcmp_func *qcmp_func_ref ; typedef enum rotstate_e rotstate_t, *rotstate_t_ref ; enum rotstate_e { ROTSTATE_WRITABLE, ROTSTATE_START, ROTSTATE_RENAME, ROTSTATE_NEWCURRENT, ROTSTATE_CHMODPREVIOUS, ROTSTATE_FINISHPREVIOUS, ROTSTATE_RUNPROCESSOR, ROTSTATE_WAITPROCESSOR, ROTSTATE_SYNCPROCESSED, ROTSTATE_SYNCNEWSTATE, ROTSTATE_UNLINKPREVIOUS, ROTSTATE_RENAMESTATE, ROTSTATE_FINISHPROCESSED, ROTSTATE_ENDFCHMOD, ROTSTATE_END } ; typedef enum seltype_e seltype_t, *seltype_t_ref ; enum seltype_e { SELTYPE_DEFAULT, SELTYPE_PLUS, SELTYPE_MINUS, SELTYPE_PHAIL } ; typedef struct sel_s sel_t, *sel_t_ref ; struct sel_s { seltype_t type ; regex_t re ; } ; #define SEL_ZERO { .type = SELTYPE_PHAIL } typedef enum acttype_e acttype_t, *acttype_t_ref ; enum acttype_e { ACTTYPE_NOTHING, ACTTYPE_FD1, ACTTYPE_FD2, ACTTYPE_STATUS, ACTTYPE_DIR, ACTTYPE_PHAIL } ; typedef struct as_status_s as_status_t, *as_status_t_ref ; struct as_status_s { char const *file ; size_t filelen ; } ; typedef union actstuff_u actstuff_t, *actstuff_t_ref ; union actstuff_u { size_t fd2_size ; as_status_t status ; unsigned int ld ; } ; typedef struct act_s act_t, *act_t_ref ; struct act_s { acttype_t type ; actstuff_t data ; unsigned int flags ; char const *prefix ; size_t prefixlen ; } ; typedef struct scriptelem_s scriptelem_t, *scriptelem_t_ref ; struct scriptelem_s { sel_t const *sels ; unsigned int sellen ; act_t const *acts ; unsigned int actlen ; } ; typedef void inputproc_func (scriptelem_t const *, unsigned int, size_t, unsigned int) ; typedef inputproc_func *inputproc_func_ref ; typedef struct logdir_s logdir_t, *logdir_t_ref ; struct logdir_s { bufalloc out ; unsigned int xindex ; tain retrytto ; tain deadline ; uint64_t maxdirsize ; uint32_t b ; uint32_t n ; uint32_t s ; uint32_t tolerance ; pid_t pid ; char const *dir ; char const *processor ; unsigned int flags ; int fd ; int fdlock ; rotstate_t rstate ; } ; #define LOGDIR_ZERO { \ .out = BUFALLOC_ZERO, \ .xindex = 0, \ .retrytto = TAIN_ZERO, \ .deadline = TAIN_ZERO, \ .maxdirsize = 0, \ .b = 0, \ .n = 0, \ .s = 0, \ .tolerance = 0, \ .pid = 0, \ .dir = 0, \ .processor = 0, \ .fd = -1, \ .fdlock = -1, \ .rstate = ROTSTATE_WRITABLE \ } struct filedesc_s { off_t size ; char name[28] ; } ; /* Logdirs */ static logdir_t *logdirs ; static unsigned int llen = 0 ; static int filedesc_cmp (struct filedesc_s const *a, struct filedesc_s const *b) { return memcmp(a->name+1, b->name+1, 26) ; } static int name_is_relevant (char const *name) { tain dummy ; if (strlen(name) != 27) return 0 ; if (!timestamp_scan(name, &dummy)) return 0 ; if (name[25] != '.') return 0 ; if ((name[26] != 's') && (name[26] != 'u')) return 0 ; return 1 ; } static inline int logdir_trim (logdir_t *ldp) { unsigned int n = 0 ; DIR *dir = opendir(ldp->dir) ; if (!dir) return -1 ; for (;;) { direntry *d ; errno = 0 ; d = readdir(dir) ; if (!d) break ; if (name_is_relevant(d->d_name)) n++ ; } if (errno) { dir_close(dir) ; return -1 ; } if (!n) { dir_close(dir) ; return 0 ; } rewinddir(dir) ; { uint64_t totalsize = 0 ; size_t dirlen = strlen(ldp->dir) ; unsigned int i = 0 ; struct filedesc_s archive[n] ; char fullname[dirlen + 29] ; memcpy(fullname, ldp->dir, dirlen) ; fullname[dirlen] = '/' ; for (;;) { struct stat st ; direntry *d ; errno = 0 ; d = readdir(dir) ; if (!d) break ; if (!name_is_relevant(d->d_name)) continue ; if (i >= n) { errno = EBUSY ; break ; } memcpy(fullname + dirlen + 1, d->d_name, 28) ; if (stat(fullname, &st) < 0) { if (verbosity) strerr_warnwu2sys("stat ", fullname) ; continue ; } memcpy(archive[i].name, d->d_name, 28) ; archive[i].size = st.st_size ; totalsize += st.st_size ; i++ ; } if (errno) { dir_close(dir) ; return -1 ; } dir_close(dir) ; if ((i <= ldp->n) && (!ldp->maxdirsize || (totalsize <= ldp->maxdirsize))) return 0 ; qsort(archive, i, sizeof(struct filedesc_s), (qcmp_func_ref)&filedesc_cmp) ; n = 0 ; while ((i > ldp->n + n) || (ldp->maxdirsize && (totalsize > ldp->maxdirsize))) { memcpy(fullname + dirlen + 1, archive[n].name, 28) ; if (unlink(fullname) < 0) { if (errno == ENOENT) totalsize -= archive[n].size ; if (verbosity) strerr_warnwu2sys("unlink ", fullname) ; } else totalsize -= archive[n].size ; n++ ; } } return n ; } static int finish (logdir_t *ldp, char const *name, char suffix) { struct stat st ; size_t dirlen = strlen(ldp->dir) ; size_t namelen = strlen(name) ; char x[dirlen + namelen + 2] ; memcpy(x, ldp->dir, dirlen) ; x[dirlen] = '/' ; memcpy(x + dirlen + 1, name, namelen + 1) ; if (stat(x, &st) < 0) return errno == ENOENT ? 0 : -1 ; if (st.st_nlink == 1) { char y[dirlen + 29] ; memcpy(y, ldp->dir, dirlen) ; y[dirlen] = '/' ; timestamp_g(y + dirlen + 1) ; y[dirlen + 26] = '.' ; y[dirlen + 27] = suffix ; y[dirlen + 28] = 0 ; if (link(x, y) < 0) return -1 ; } if (unlink(x) < 0) return -1 ; return logdir_trim(ldp) ; } static int rotator (logdir_t *ldp) { size_t dirlen = strlen(ldp->dir) ; switch (ldp->rstate) { case ROTSTATE_START : if (fd_sync(ldp->fd) < 0) { if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ; goto fail ; } tain_now_g() ; ldp->rstate = ROTSTATE_RENAME ; case ROTSTATE_RENAME : { char current[dirlen + 9] ; char previous[dirlen + 10] ; memcpy(current, ldp->dir, dirlen) ; memcpy(current + dirlen, "/current", 9) ; memcpy(previous, ldp->dir, dirlen) ; memcpy(previous + dirlen, "/previous", 10) ; if (rename(current, previous) < 0) { if (verbosity) strerr_warnwu4sys("rename ", current, " to ", previous) ; goto fail ; } ldp->rstate = ROTSTATE_NEWCURRENT ; } case ROTSTATE_NEWCURRENT : { int fd ; char x[dirlen + 9] ; memcpy(x, ldp->dir, dirlen) ; memcpy(x + dirlen, "/current", 9) ; fd = openc_append(x) ; if (fd < 0) { if (verbosity) strerr_warnwu2sys("open_append ", x) ; goto fail ; } fd_close(ldp->fd) ; ldp->fd = fd ; ldp->b = 0 ; ldp->rstate = ROTSTATE_CHMODPREVIOUS ; } case ROTSTATE_CHMODPREVIOUS : { char x[dirlen + 10] ; memcpy(x, ldp->dir, dirlen) ; memcpy(x + dirlen, "/previous", 10) ; if (chmod(x, mask | S_IXUSR) < 0) { if (verbosity) strerr_warnwu2sys("chmod ", x) ; goto fail ; } if (ldp->processor) goto runprocessor ; ldp->rstate = ROTSTATE_FINISHPREVIOUS ; } case ROTSTATE_FINISHPREVIOUS : if (finish(ldp, "previous", 's') < 0) { if (verbosity) strerr_warnwu2sys("finish previous .s to logdir ", ldp->dir) ; goto fail ; } tain_copynow(&ldp->deadline) ; ldp->rstate = ROTSTATE_WRITABLE ; break ; runprocessor: ldp->rstate = ROTSTATE_RUNPROCESSOR ; case ROTSTATE_RUNPROCESSOR : { #ifdef S6_USE_EXECLINE char const *cargv[4] = { ldp->flags & 4 ? "/bin/sh" : EXECLINE_EXTBINPREFIX "execlineb", ldp->flags & 4 ? "-c" : "-Pc", ldp->processor, 0 } ; #else char const *cargv[4] = { "/bin/sh", "-c", ldp->processor, 0 } ; #endif cspawn_fileaction fa[5] = { [0] = { .type = CSPAWN_FA_CHDIR, .x = { .path = ldp->dir } }, [1] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 0, .file = "previous", .oflag = O_RDONLY, .mode = 0644 } } }, [2] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 1, .file = "processed", .oflag = O_WRONLY | O_CREAT | O_TRUNC, .mode = 0666 } } }, [3] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 4, .file = "state", .oflag = O_RDONLY, .mode = 0644 } } }, [4] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 5, .file = "newstate", .oflag = O_WRONLY | O_CREAT | O_TRUNC, .mode = 0666 } } } } ; pid_t pid = cspawn(cargv[0], cargv, (char const *const *)environ, CSPAWN_FLAGS_SELFPIPE_FINISH, fa, 5) ; if (!pid) { if (verbosity) strerr_warnwu2sys("spawn processor for logdir ", ldp->dir) ; goto fail ; } ldp->pid = pid ; tain_add_g(&ldp->deadline, &tain_infinite_relative) ; ldp->rstate = ROTSTATE_WAITPROCESSOR ; } case ROTSTATE_WAITPROCESSOR : return (errno = EAGAIN, 0) ; case ROTSTATE_SYNCPROCESSED : { int fd ; char x[dirlen + 11] ; memcpy(x, ldp->dir, dirlen) ; memcpy(x + dirlen, "/processed", 11) ; fd = open_append(x) ; if (fd < 0) { if (verbosity) strerr_warnwu2sys("open_append ", x) ; goto fail ; } if (fd_sync(fd) < 0) { fd_close(fd) ; if (verbosity) strerr_warnwu2sys("fd_sync ", x) ; goto fail ; } tain_now_g() ; if (fd_chmod(fd, mask | S_IXUSR) < 0) { fd_close(fd) ; if (verbosity) strerr_warnwu2sys("fd_chmod ", x) ; goto fail ; } fd_close(fd) ; ldp->rstate = ROTSTATE_SYNCNEWSTATE ; } case ROTSTATE_SYNCNEWSTATE : { int fd ; char x[dirlen + 10] ; memcpy(x, ldp->dir, dirlen) ; memcpy(x + dirlen, "/newstate", 10) ; fd = open_append(x) ; if (fd < 0) { if (verbosity) strerr_warnwu2sys("open_append ", x) ; goto fail ; } if (fd_sync(fd) < 0) { if (verbosity) strerr_warnwu2sys("fd_sync ", x) ; goto fail ; } tain_now_g() ; fd_close(fd) ; ldp->rstate = ROTSTATE_UNLINKPREVIOUS ; } case ROTSTATE_UNLINKPREVIOUS : { char x[dirlen + 10] ; memcpy(x, ldp->dir, dirlen) ; memcpy(x + dirlen, "/previous", 10) ; if ((unlink(x) < 0) && (errno != ENOENT)) { if (verbosity) strerr_warnwu2sys("open_append ", x) ; goto fail ; } ldp->rstate = ROTSTATE_RENAMESTATE ; } case ROTSTATE_RENAMESTATE : { char newstate[dirlen + 10] ; char state[dirlen + 7] ; memcpy(newstate, ldp->dir, dirlen) ; memcpy(state, ldp->dir, dirlen) ; memcpy(newstate + dirlen, "/newstate", 10) ; memcpy(state + dirlen, "/state", 7) ; if (rename(newstate, state) < 0) { if (verbosity) strerr_warnwu4sys("rename ", newstate, " to ", state) ; goto fail ; } ldp->rstate = ROTSTATE_FINISHPROCESSED ; } case ROTSTATE_FINISHPROCESSED : if (finish(ldp, "processed", 's') < 0) { if (verbosity) strerr_warnwu2sys("finish processed .s to logdir ", ldp->dir) ; goto fail ; } tain_copynow(&ldp->deadline) ; ldp->rstate = ROTSTATE_WRITABLE ; break ; default : strerr_dief1x(101, "inconsistent state in rotator()") ; } return 1 ; fail: tain_add_g(&ldp->deadline, &ldp->retrytto) ; return 0 ; } static ssize_t logdir_write (int i, char const *s, size_t len) { logdir_t *ldp = logdirs + i ; ssize_t r ; size_t n = len ; { size_t m = byte_rchr(s, n, '\n') ; if (m < n) n = m+1 ; } r = fd_write(ldp->fd, s, n) ; if (r < 0) { if (!error_isagain(errno)) { tain_add_g(&ldp->deadline, &ldp->retrytto) ; if (verbosity) strerr_warnwu3sys("write to ", ldp->dir, "/current") ; } return r ; } ldp->b += r ; if ((ldp->b + ldp->tolerance >= ldp->s) && (s[r-1] == '\n')) { ldp->rstate = ROTSTATE_START ; rotator(ldp) ; } return r ; } static inline void rotate_or_flush (logdir_t *ldp) { if ((ldp->rstate != ROTSTATE_WRITABLE) && !rotator(ldp)) return ; if (ldp->b >= ldp->s) { ldp->rstate = ROTSTATE_START ; if (!rotator(ldp)) return ; } bufalloc_flush(&ldp->out) ; } static inline void logdir_init (unsigned int index, uint32_t s, uint32_t n, uint32_t tolerance, uint64_t maxdirsize, tain const *retrytto, char const *processor, char const *name, unsigned int flags) { logdir_t *ldp = logdirs + index ; struct stat st ; size_t dirlen = strlen(name) ; int r ; char x[dirlen + 11] ; ldp->s = s ; ldp->n = n ; ldp->pid = 0 ; ldp->tolerance = tolerance ; ldp->maxdirsize = maxdirsize ; ldp->retrytto = *retrytto ; ldp->processor = processor ; ldp->flags = flags ; ldp->dir = name ; ldp->fd = -1 ; ldp->rstate = ROTSTATE_WRITABLE ; r = mkdir(ldp->dir, S_IRWXU | S_ISGID) ; if (r < 0 && errno != EEXIST) strerr_diefu2sys(111, "mkdir ", name) ; memcpy(x, name, dirlen) ; memcpy(x + dirlen, "/lock", 6) ; ldp->fdlock = openc_create(x) ; if (ldp->fdlock < 0) strerr_diefu2sys(111, "open ", x) ; r = fd_lock(ldp->fdlock, 1, 1) ; if (!r) errno = EBUSY ; if (r < 1) strerr_diefu2sys(111, "lock ", x) ; memcpy(x + dirlen + 1, "current", 8) ; if (stat(x, &st) < 0) { if (errno != ENOENT) strerr_diefu2sys(111, "stat ", x) ; } else if (st.st_mode & S_IXUSR) goto opencurrent ; memcpy(x + dirlen + 1, "state", 6) ; unlink_void(x) ; memcpy(x + dirlen + 1, "newstate", 9) ; unlink_void(x) ; { int flagprocessed = 0 ; memcpy(x + dirlen + 1, "processed", 10) ; if (stat(x, &st) < 0) { if (errno != ENOENT) strerr_diefu2sys(111, "stat ", x) ; } else if (st.st_mode & S_IXUSR) flagprocessed = 1 ; if (flagprocessed) { memcpy(x + dirlen + 1, "previous", 9) ; unlink_void(x) ; if (finish(ldp, "processed", 's') < 0) strerr_diefu2sys(111, "finish processed .s for logdir ", ldp->dir) ; } else { unlink_void(x) ; if (finish(ldp, "previous", 'u') < 0) strerr_diefu2sys(111, "finish previous .u for logdir ", ldp->dir) ; } } if (finish(ldp, "current", 'u') < 0) strerr_diefu2sys(111, "finish current .u for logdir ", ldp->dir) ; memcpy(x + dirlen + 1, "state", 6) ; r = open_trunc(x) ; if (r == -1) strerr_diefu2sys(111, "open_trunc ", x) ; fd_close(r) ; st.st_size = 0 ; memcpy(x + dirlen + 1, "current", 8) ; opencurrent: ldp->fd = openc_append(x) ; if (ldp->fd < 0) strerr_diefu2sys(111, "open_append ", x) ; if (fd_chmod(ldp->fd, mask) == -1) strerr_diefu2sys(111, "fd_chmod ", x) ; ldp->b = st.st_size ; tain_copynow(&ldp->deadline) ; bufalloc_init(&ldp->out, &logdir_write, index) ; } static inline int logdir_finalize (logdir_t *ldp) { switch (ldp->rstate) { case ROTSTATE_WRITABLE : { if (fd_sync(ldp->fd) < 0) { if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ; goto fail ; } tain_now_g() ; ldp->rstate = ROTSTATE_ENDFCHMOD ; } case ROTSTATE_ENDFCHMOD : { if (fd_chmod(ldp->fd, mask | S_IXUSR) < 0) { if (verbosity) strerr_warnwu3sys("fd_chmod ", ldp->dir, "/current") ; goto fail ; } ldp->rstate = ROTSTATE_END ; break ; } default : strerr_dief1x(101, "inconsistent state in logdir_finalize()") ; } return 1 ; fail: tain_add_g(&ldp->deadline, &ldp->retrytto) ; return 0 ; } static inline void finalize (void) { unsigned int n = llen ; for (;;) { unsigned int i = 0 ; tain deadline ; tain_addsec_g(&deadline, 2) ; for (; i < llen ; i++) if (logdirs[i].rstate != ROTSTATE_END) { if (logdir_finalize(logdirs + i)) n-- ; else if (tain_less(&logdirs[i].deadline, &deadline)) deadline = logdirs[i].deadline ; } if (!n) break ; { iopause_fd x ; iopause_g(&x, 0, &deadline) ; } } } /* Script */ static inline void script_firstpass (char const *const *argv, unsigned int *sellen, unsigned int *actlen, unsigned int *scriptlen, unsigned int *gflags) { unsigned int se = 0, ac = 0, sc = 0, gf = *gflags ; int flagacted = 0 ; for (; *argv ; argv++) { switch ((*argv)[0]) { case 'f' : if ((*argv)[1]) goto fail ; case '+' : case '-' : if (flagacted) { sc++ ; flagacted = 0 ; } se++ ; case 'n' : case 's' : case 'S' : case 'l' : case 'r' : case 'E' : case '^' : case 'p' : #ifdef S6_USE_EXECLINE case '!' : #endif case '?' : break ; case 't' : if ((*argv)[1]) goto fail ; gf |= 1 ; break ; case 'T' : if ((*argv)[1]) goto fail ; gf |= 2 ; break ; case '1' : case '2' : if ((*argv)[1]) goto fail ; flagacted = 1 ; ac++ ; break ; case '.' : case '/' : llen++ ; flagacted = 1 ; ac++ ; break ; case '=' : if (!(*argv)[1]) goto fail ; flagacted = 1 ; ac++ ; break ; default : strerr_dief2x(100, "unrecognized directive: ", *argv) ; } } if (flagacted) sc++ ; else if (sc) { if (verbosity) strerr_warnw1x("ignoring extraneous non-action directives") ; } else strerr_dief1x(100, "no action directive specified") ; *sellen = se ; *actlen = ac ; *scriptlen = sc ; *gflags = gf ; return ; fail : strerr_dief2x(100, "syntax error at directive: ", *argv) ; } static inline void script_secondpass (char const *const *argv, scriptelem_t *script, sel_t *selections, act_t *actions) { tain retrytto ; unsigned int fd2_size = 200 ; unsigned int status_size = 1001 ; uint32_t s = 99999 ; uint32_t n = 10 ; uint32_t tolerance = 2000 ; uint64_t maxdirsize = 0 ; char const *processor = 0 ; char const *prefix = 0 ; size_t prefixlen = 0 ; unsigned int sel = 0, act = 0, lidx = 0, flags = 0 ; int flagacted = 0 ; tain_uint(&retrytto, 2) ; for (; *argv ; argv++) { switch (**argv) { case 'f' : case '+' : case '-' : { sel_t selitem = { .type = (*argv)[0] != 'f' ? (*argv)[0] == '+' ? SELTYPE_PLUS : SELTYPE_MINUS : SELTYPE_DEFAULT } ; if ((*argv)[0] != 'f') { int r = skalibs_regcomp(&selitem.re, *argv + 1, REG_EXTENDED | REG_NOSUB | REG_NEWLINE) ; if (r == REG_ESPACE) { errno = ENOMEM ; strerr_diefu1sys(111, "initialize script") ; } if (r) goto fail ; } if (flagacted) { flagacted = 0 ; script->sels = selections ; script->sellen = sel ; script->acts = actions ; script->actlen = act ; selections += sel ; sel = 0 ; actions += act ; act = 0 ; script++ ; } selections[sel++] = selitem ; break ; } case 'n' : if (!uint320_scan(*argv + 1, &n)) goto fail ; break ; case 's' : if (!uint320_scan(*argv + 1, &s)) goto fail ; if (s < 4096) s = 4096 ; if (s > 268435455) s = 268435455 ; break ; case 'S' : if (!uint640_scan(*argv + 1, &maxdirsize)) goto fail ; break ; case 'l' : if (!uint320_scan(*argv + 1, &tolerance)) goto fail ; if (tolerance > (s >> 1)) strerr_dief3x(100, "directive ", *argv, " conflicts with previous s directive") ; break ; case 'r' : { uint32_t t ; if (!uint320_scan(*argv + 1, &t)) goto fail ; if (!tain_from_millisecs(&retrytto, t)) goto fail ; break ; } case 'E' : if (!uint0_scan(*argv + 1, &fd2_size)) goto fail ; break ; case '^' : if (!uint0_scan(*argv + 1, &status_size)) goto fail ; break ; case 'p' : if ((*argv)[1]) { prefix = *argv + 1 ; prefixlen = strlen(prefix) ; } else { prefix = 0 ; prefixlen = 0 ; } break ; #ifdef S6_USE_EXECLINE case '!' : processor = (*argv)[1] ? *argv + 1 : 0 ; flags &= ~4 ; break ; #endif case '?' : processor = (*argv)[1] ? *argv + 1 : 0 ; flags |= 4 ; break ; case 't' : flags |= 1 ; break ; case 'T' : flags |= 2 ; break ; case '1' : { act_t a = { .type = ACTTYPE_FD1, .flags = flags, .prefix = prefix, .prefixlen = prefixlen } ; actions[act++] = a ; flagacted = 1 ; flags = 0 ; break ; } case '2' : { act_t a = { .type = ACTTYPE_FD2, .flags = flags, .prefix = prefix, .prefixlen = prefixlen, .data = { .fd2_size = fd2_size } } ; actions[act++] = a ; flagacted = 1 ; flags = 0 ; break ; } case '=' : { act_t a = { .type = ACTTYPE_STATUS, .flags = flags, .prefix = prefix, .prefixlen = prefixlen, .data = { .status = { .file = *argv + 1, .filelen = status_size } } } ; actions[act++] = a ; flagacted = 1 ; flags = 0 ; break ; } case '.' : case '/' : { act_t a = { .type = ACTTYPE_DIR, .flags = flags, .prefix = prefix, .prefixlen = prefixlen, .data = { .ld = lidx } } ; logdir_init(lidx, s, n, tolerance, maxdirsize, &retrytto, processor, *argv, flags) ; lidx++ ; actions[act++] = a ; flagacted = 1 ; flags = 0 ; break ; } default : goto fail ; } } if (flagacted) { script->sels = selections ; script->sellen = sel ; script->acts = actions ; script->actlen = act ; } return ; fail: strerr_dief2x(100, "unrecognized directive: ", *argv) ; } static void script_run (scriptelem_t const *script, unsigned int scriptlen, char const *s, size_t len, unsigned int gflags) { int flagselected = 1, flagacted = 0 ; unsigned int i = 0 ; size_t hlen = 0 ; char hstamp[32] ; char tstamp[TIMESTAMP+1] ; if (gflags & 3) { tain now ; tain_wallclock_read(&now) ; if (gflags & 1) { timestamp_fmt(tstamp, &now) ; tstamp[TIMESTAMP] = ' ' ; } if (gflags & 2) { localtmn l ; localtmn_from_tain(&l, &now, 1) ; hlen = localtmn_fmt(hstamp, &l) ; hstamp[hlen++] = ' ' ; hstamp[hlen++] = ' ' ; } } for (; i < scriptlen ; i++) { unsigned int j = 0 ; for (; j < script[i].sellen ; j++) { switch (script[i].sels[j].type) { case SELTYPE_DEFAULT : flagselected = !flagacted ; break ; case SELTYPE_PLUS : if (!flagselected && !regexec(&script[i].sels[j].re, s, 0, 0, 0)) flagselected = 1 ; break ; case SELTYPE_MINUS : if (flagselected && !regexec(&script[i].sels[j].re, s, 0, 0, 0)) flagselected = 0 ; break ; default : strerr_dief2x(101, "internal consistency error in ", "selection type") ; } } if (flagselected) { flagacted = 1 ; for (j = 0 ; j < script[i].actlen ; j++) { act_t const *act = script[i].acts + j ; unsigned int m = 0 ; struct iovec v[6] ; if (act->flags & 1) { v[m].iov_base = tstamp ; v[m++].iov_len = TIMESTAMP+1 ; } if (act->flags & 2) { v[m].iov_base = hstamp ; v[m++].iov_len = hlen ; } if (act->prefix) { v[m].iov_base = (char *)act->prefix ; v[m++].iov_len = act->prefixlen ; v[m].iov_base = " " ; v[m++].iov_len = 1 ; } v[m].iov_base = (char *)s ; v[m++].iov_len = len ; v[m].iov_base = "\n" ; v[m++].iov_len = 1 ; switch (act->type) { case ACTTYPE_FD1 : if (!bufalloc_putv(bufalloc_1, v, m)) dienomem() ; case ACTTYPE_NOTHING : break ; case ACTTYPE_FD2 : buffer_puts(buffer_2, PROG) ; buffer_puts(buffer_2, ": alert: ") ; if (act->data.fd2_size && act->data.fd2_size + 3 < len) { v[m-2].iov_len = act->data.fd2_size ; v[m-1].iov_base = "...\n" ; v[m-1].iov_len = 4 ; } buffer_putv(buffer_2, v, m) ; buffer_flush(buffer_2) ; /* if it blocks, too bad */ break ; case ACTTYPE_STATUS : if (act->data.status.filelen) { size_t reallen = siovec_len(v, m) ; if (reallen > act->data.status.filelen) siovec_trunc(v, m, act->data.status.filelen) ; else { size_t k = act->data.status.filelen - reallen + 1 ; char pad[k] ; v[m-1].iov_base = pad ; v[m-1].iov_len = k ; while (k--) pad[k] = '\n' ; if (!openwritevnclose_suffix(act->data.status.file, v, m, ".new") && verbosity) strerr_warnwu2sys("write status file ", act->data.status.file) ; break ; } } if (!openwritevnclose_suffix(act->data.status.file, v, m, ".new") && verbosity) strerr_warnwu2sys("write status file ", act->data.status.file) ; break ; case ACTTYPE_DIR : if (!bufalloc_putv(&logdirs[act->data.ld].out, v, m)) dienomem() ; break ; default : strerr_dief2x(101, "internal consistency error in ", "action type") ; } } } } if (gflags & 3) tain_now_g() ; } /* Input */ static void prepare_to_exit (void) { fd_close(0) ; flagexiting = 1 ; } static inline int getchunk (buffer *b, stralloc *sa, size_t linelimit) { struct iovec v[2] ; size_t pos ; int r ; buffer_rpeek(b, v) ; pos = siovec_bytein(v, 2, "\n", 2) ; if (linelimit && sa->len + pos > linelimit) { r = 2 ; pos = linelimit - sa->len ; } else { r = pos < buffer_len(b) ; pos += r ; } if (!stralloc_readyplus(sa, pos + (r == 2))) return -1 ; buffer_getnofill(b, sa->s + sa->len, pos) ; sa->len += pos ; if (r == 2) sa->s[sa->len++] = 0 ; return r ; } static void normal_stdin (scriptelem_t const *script, unsigned int scriptlen, size_t linelimit, unsigned int gflags) { ssize_t r = sanitize_read(buffer_fill(buffer_0)) ; if (r < 0) { if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ; prepare_to_exit() ; } else if (r) for (;;) { r = getchunk(buffer_0, &indata, linelimit) ; if (r < 0) dienomem() ; else if (!r) break ; indata.s[indata.len - 1] = 0 ; script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ; indata.len = 0 ; } } static void process_partial_line (scriptelem_t const *script, unsigned int scriptlen, unsigned int gflags) { if (!stralloc_0(&indata)) dienomem() ; script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ; indata.len = 0 ; } static void last_stdin (scriptelem_t const *script, unsigned int scriptlen, size_t linelimit, unsigned int gflags) { for (;;) { char c ; switch (sanitize_read(fd_read(0, &c, 1))) { case 0 : return ; case -1 : if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ; if (indata.len) goto lastline ; goto end ; case 1 : if (c == '\n' || !c) goto lastline ; if (!stralloc_catb(&indata, &c, 1)) dienomem() ; if (linelimit && indata.len >= linelimit) { if (verbosity) strerr_warnw2x("input line too long, ", "stopping before the end") ; goto lastline ; } else break ; } } lastline: process_partial_line(script, scriptlen, gflags) ; end: prepare_to_exit() ; } static inputproc_func_ref handle_stdin = &normal_stdin ; /* Signals */ static inline void processor_died (logdir_t *ldp, int wstat) { ldp->pid = 0 ; if (WIFSIGNALED(wstat)) { if (verbosity) strerr_warnw2x("processor crashed in ", ldp->dir) ; tain_add_g(&ldp->deadline, &ldp->retrytto) ; ldp->rstate = ROTSTATE_RUNPROCESSOR ; } else if (WEXITSTATUS(wstat)) { if (verbosity) strerr_warnw2x("processor failed in ", ldp->dir) ; tain_add_g(&ldp->deadline, &ldp->retrytto) ; ldp->rstate = ROTSTATE_RUNPROCESSOR ; } else { ldp->rstate = ROTSTATE_SYNCPROCESSED ; rotator(ldp) ; } } static inline int handle_signals (void) { int e = 0 ; for (;;) { switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "selfpipe_read") ; case 0 : return e ; case SIGALRM : { unsigned int i = 0 ; for (; i < llen ; i++) if ((logdirs[i].rstate == ROTSTATE_WRITABLE) && logdirs[i].b) { logdirs[i].rstate = ROTSTATE_START ; rotator(logdirs + i) ; } break ; } case SIGTERM : if (flagprotect) break ; case SIGHUP : handle_stdin = &last_stdin ; tain_add_g(&exit_deadline, &lastlinetto) ; if (!indata.len) { prepare_to_exit() ; e = 1 ; } break ; case SIGCHLD : { for (;;) { int wstat ; unsigned int i = 0 ; pid_t r = wait_nohang(&wstat) ; if (r <= 0) break ; for (; i < llen ; i++) if (r == logdirs[i].pid) break ; if (i < llen) processor_died(logdirs + i, wstat) ; } break ; } default : strerr_dief1x(101, "internal consistency error with signal handling") ; } } } /* Main */ int main (int argc, char const *const *argv) { unsigned int sellen, actlen, scriptlen ; unsigned int linelimit = 8192 ; unsigned int notif = 0 ; unsigned int gflags = 0 ; int flagblock = 0 ; PROG = "s6-log" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int t = 2000 ; for (;;) { int opt = subgetopt_r(argc, argv, "qvbpl:d:t:", &l) ; if (opt == -1) break ; switch (opt) { case 'q' : if (verbosity) verbosity-- ; break ; case 'v' : verbosity++ ; break ; case 'b' : flagblock = 1 ; break ; case 'p' : flagprotect = 1 ; break ; case 'l' : if (!uint0_scan(l.arg, &linelimit)) dieusage() ; break ; case 'd' : if (!uint0_scan(l.arg, ¬if)) dieusage() ; if (notif < 3) strerr_dief1x(100, "notification fd must be 3 or more") ; if (fcntl(notif, F_GETFD) < 0) strerr_dief1sys(100, "invalid notification fd") ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&lastlinetto, t) ; else lastlinetto = tain_infinite_relative ; } if (!argc) dieusage() ; if (linelimit && linelimit < LINELIMIT_MIN) linelimit = LINELIMIT_MIN ; if (!fd_sanitize()) strerr_diefu1sys(111, "ensure stdin/stdout/stderr are open") ; if (!tain_now_set_stopwatch_g() && verbosity) strerr_warnwu1sys("set monotonic clock and read current time - timestamps may be wrong for a while") ; if (ndelay_on(0) < 0) strerr_diefu3sys(111, "set std", "in", " non-blocking") ; if (ndelay_on(1) < 0) strerr_diefu3sys(111, "set std", "out", " non-blocking") ; mask = umask(0) ; umask(mask) ; mask = ~mask & 0666 ; script_firstpass(argv, &sellen, &actlen, &scriptlen, &gflags) ; { sel_t selections[sellen ? sellen : 1] ; act_t actions[actlen] ; scriptelem_t script[scriptlen] ; logdir_t logdirblob[llen] ; iopause_fd x[3 + llen] ; logdirs = logdirblob ; script_secondpass(argv, script, selections, actions) ; x[0].fd = selfpipe_init() ; if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "sig_ignore(SIGPIPE)") ; { sigset_t set ; sigemptyset(&set) ; sigaddset(&set, SIGTERM) ; sigaddset(&set, SIGHUP) ; sigaddset(&set, SIGALRM) ; sigaddset(&set, SIGCHLD) ; if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "selfpipe_trapset") ; } x[0].events = IOPAUSE_READ ; if (notif) { fd_write(notif, "\n", 1) ; fd_close(notif) ; } for (;;) { tain deadline = exit_deadline ; int r = 0 ; unsigned int xindex0, xindex1 ; unsigned int i = 0, j = 1 ; if (bufalloc_1->fd == 1 && bufalloc_len(bufalloc_1)) { r = 1 ; x[j].fd = 1 ; x[j].events = IOPAUSE_EXCEPT | (bufalloc_len(bufalloc_1) ? IOPAUSE_WRITE : 0) ; xindex1 = j++ ; } else xindex1 = 0 ; for (; i < llen ; i++) { logdirs[i].xindex = 0 ; if (bufalloc_len(&logdirs[i].out) || (logdirs[i].rstate != ROTSTATE_WRITABLE)) { r = 1 ; if (tain_less(&logdirs[i].deadline, &deadline)) deadline = logdirs[i].deadline ; } } if (!flagexiting && !(flagblock && r)) { x[j].fd = 0 ; x[j].events = IOPAUSE_READ ; xindex0 = j++ ; } else xindex0 = 0 ; if (flagexiting && !r) break ; r = iopause_g(x, j, &deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; else if (!r) { if (!tain_future(&exit_deadline)) { if (indata.len) process_partial_line(script, scriptlen, gflags) ; prepare_to_exit() ; } for (i = 0 ; i < llen ; i++) if (!tain_future(&logdirs[i].deadline)) rotate_or_flush(logdirs + i) ; continue ; } if (x[0].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT) && handle_signals()) continue ; if (xindex1 && x[xindex1].revents) { if (!bufalloc_flush(bufalloc_1) && !error_isagain(errno)) { unsigned int i = actlen ; strerr_warnwu1sys("write to stdout, closing the stream - error was") ; fd_close(1) ; bufalloc_1->fd = -1 ; bufalloc_free(bufalloc_1) ; while (i--) if (actions[i].type == ACTTYPE_FD1) actions[i].type = ACTTYPE_NOTHING ; } } for (i = 0 ; i < llen ; i++) if (logdirs[i].xindex && x[logdirs[i].xindex].revents & IOPAUSE_WRITE) rotate_or_flush(logdirs + i) ; if (xindex0 && x[xindex0].revents) { if (x[xindex0].revents & IOPAUSE_READ) (*handle_stdin)(script, scriptlen, linelimit, gflags) ; else { if (indata.len) process_partial_line(script, scriptlen, gflags) ; prepare_to_exit() ; } } } finalize() ; } return 0 ; } s6-2.13.1.0/src/daemontools-extras/s6-setlock.c000066400000000000000000000044731470151141200210450ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-setlock [ -r | -w ] [ -n | -N ] [ -t timeout ] [ -d fd ] lockfile prog..." #define dieusage() strerr_dieusage(100, USAGE) static char const *file ; static void sigalrm_handler (int sig) { (void)sig ; strerr_dief3x(1, "lock ", file, ": timed out") ; } int main (int argc, char const *const *argv) { unsigned int nb = 0, ex = 1 ; unsigned int timeout = 0 ; int dest = -1 ; int fd ; int r ; PROG = "s6-setlock" ; for (;;) { int opt = lgetopt(argc, argv, "nNrwt:d:") ; if (opt == -1) break ; switch (opt) { case 'n' : nb = 1 ; break ; case 'N' : nb = 0 ; break ; case 'r' : ex = 0 ; break ; case 'w' : ex = 1 ; break ; case 't' : if (!uint0_scan(subgetopt_here.arg, &timeout)) dieusage() ; break ; case 'd' : { unsigned int u ; if (!uint0_scan(subgetopt_here.arg, &u)) dieusage() ; dest = u ; break ; } default : dieusage() ; } } argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ; if (argc < 2) dieusage() ; file = argv[0] ; if (ex) { fd = open_create(file) ; if (fd == -1) strerr_diefu3sys(111, "open ", file, " for writing") ; } else { fd = open_read(file) ; if (fd == -1) { if (errno != ENOENT) strerr_diefu3sys(111, "open ", file, " for reading") ; fd = open_create(file) ; if (fd == -1) strerr_diefu2sys(111, "create ", file) ; fd_close(fd) ; fd = open_read(file) ; if (fd == -1) strerr_diefu3sys(111, "open ", file, " for reading") ; } } if (timeout) { tain tto ; tain_from_millisecs(&tto, timeout) ; if (!sig_catch(SIGALRM, &sigalrm_handler)) strerr_diefu1sys(111, "set SIGALRM handler") ; if (!alarm_timeout(&tto)) strerr_diefu1sys(111, "set timer") ; } r = fd_lock(fd, ex, nb) ; if (timeout) alarm_disable() ; if (!r) errno = EBUSY ; if (r < 1) strerr_diefu2sys(1, "lock ", file) ; if (dest >= 0 && fd_move(dest, fd) == -1) strerr_diefu1sys(111, "move lock descriptor") ; xexec(argv+1) ; } s6-2.13.1.0/src/daemontools-extras/s6-setsid.c000066400000000000000000000033341470151141200206670ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-setsid [ -s | -b | -f | -g ] [ -i | -I | -q ] [ -d ctty ] prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { unsigned int ctty = 0, what = 0, insist = 1 ; PROG = "s6-setsid" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "sbfgiIqd:", &l) ; if (opt == -1) break ; switch (opt) { case 's' : what = 0 ; break ; case 'b' : what = 1 ; break ; case 'f' : what = 2 ; break ; case 'g' : what = 3 ; break ; case 'i' : insist = 2 ; break ; case 'I' : insist = 1 ; break ; case 'q' : insist = 0 ; break ; case 'd' : if (!uint0_scan(l.arg, &ctty)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; if (what) { if (setpgid(0, 0) < 0) switch (insist) { case 2 : strerr_diefu1sys(111, "setpgid") ; case 1 : strerr_warnwu1sys("setpgid") ; break ; default : break ; } if (what >= 2) { if (what == 3) sig_altignore(SIGTTOU) ; if (tcsetpgrp(ctty, getpid()) < 0) switch (insist) { case 2 : strerr_diefu1sys(111, "tcsetpgrp") ; case 1 : strerr_warnwu1sys("tcsetpgrp") ; break ; default : break ; } } } else if (setsid() < 0) switch (insist) { case 2 : strerr_diefu1sys(111, "setsid") ; case 1 : strerr_warnwu1sys("setsid") ; break ; default : break ; } xexec(argv) ; } s6-2.13.1.0/src/daemontools-extras/s6-setuidgid.c000066400000000000000000000017621470151141200213600ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #define USAGE "s6-setuidgid username prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { char const *newargv[argc + 7] ; char *colon ; unsigned int m = 0 ; PROG = "s6-setuidgid" ; if (argc < 3) dieusage() ; argv++ ; if (!argv[0][0]) xexec(argv+1) ; colon = strchr(argv[0], ':') ; if (colon) { *colon = 0 ; newargv[m++] = S6_BINPREFIX "s6-applyuidgid" ; newargv[m++] = "-u" ; newargv[m++] = argv[0] ; newargv[m++] = "-g" ; newargv[m++] = colon + 1 ; newargv[m++] = "-G" ; newargv[m++] = "" ; argv++ ; } else { newargv[m++] = S6_BINPREFIX "s6-envuidgid" ; newargv[m++] = *argv++ ; newargv[m++] = S6_BINPREFIX "s6-applyuidgid" ; newargv[m++] = "-Uz" ; } newargv[m++] = "--" ; while (*argv) newargv[m++] = *argv++ ; newargv[m++] = 0 ; xexec(newargv) ; } s6-2.13.1.0/src/daemontools-extras/s6-socklog.c000066400000000000000000000160631470151141200210400ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lolsyslog.h" #define USAGE "s6-socklog [ -d notif ] [ -r ] [ -U | -u uid -g gid -G gidlist ] [ -l linelen ] [ -t lameducktimeout ] [ -x socket | -i ip_port ]" #define dieusage() strerr_dieusage(100, USAGE) static tain lameducktto = TAIN_INFINITE_RELATIVE ; static int cont = 1 ; static inline void handle_signals (void) { for (;;) switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "selfpipe_read()") ; case 0 : return ; case SIGTERM : cont = 0 ; tain_add_g(&lameducktto, &lameducktto) ; break ; default : break ; } } int main (int argc, char const *const *argv) { iopause_fd x[3] = { { .events = IOPAUSE_READ }, { .fd = 1 } } ; int flagraw = 0 ; int is6 = 0 ; char const *usock = "/dev/log" ; unsigned int linelen = 1024 ; PROG = "s6-socklog" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int notif = 0 ; unsigned int t = 0 ; uid_t uid = 0 ; gid_t gid = 0 ; gid_t gids[NGROUPS_MAX + 1] ; size_t gidn = (size_t)-1 ; ip46 ip ; uint16_t port = 514 ; for (;;) { int opt = subgetopt_r(argc, argv, "rd:l:t:u:g:G:Ux:i:", &l) ; if (opt == -1) break ; switch (opt) { case 'r' : flagraw = 1 ; break ; case 'd' : if (!uint0_scan(l.arg, ¬if)) dieusage() ; break ; case 'l' : if (!uint0_scan(l.arg, &linelen)) dieusage() ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; case 'u' : if (!uid0_scan(l.arg, &uid)) dieusage() ; break ; case 'g' : if (!gid0_scan(l.arg, &gid)) dieusage() ; break ; case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn) && *l.arg) dieusage() ; break ; case 'U' : { char const *x = getenv("UID") ; if (!x) strerr_dienotset(100, "UID") ; if (!uid0_scan(x, &uid)) strerr_dieinvalid(100, "UID") ; x = getenv("GID") ; if (!x) strerr_dienotset(100, "GID") ; if (!gid0_scan(x, &gid)) strerr_dieinvalid(100, "GID") ; x = getenv("GIDLIST") ; if (!x) strerr_dienotset(100, "GIDLIST") ; if (!gid_scanlist(gids, NGROUPS_MAX+1, x, &gidn) && *x) strerr_dieinvalid(100, "GIDLIST") ; break ; } case 'x' : usock = l.arg ; break ; case 'i' : { size_t pos = ip46_scan(l.arg, &ip) ; if (!pos) dieusage() ; if (l.arg[pos] == '_' || (!ip46_is6(&ip) && l.arg[pos] == ':')) if (!uint160_scan(l.arg + pos + 1, &port)) dieusage() ; usock = 0 ; break ; } default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (linelen < 76) linelen = 76 ; if (linelen > 1048576) linelen = 1048576 ; if (t) tain_from_millisecs(&lameducktto, t) ; if (notif) { if (notif < 3) strerr_dief1x(100, "notification fd must be 3 or more") ; if (fcntl(notif, F_GETFD) < 0) strerr_dief1sys(100, "invalid notification fd") ; } close(0) ; if (fcntl(1, F_GETFD) < 0) strerr_dief2sys(100, "invalid std", "out") ; if (fcntl(2, F_GETFD) < 0) strerr_dief2sys(100, "invalid std", "err") ; if (usock) { x[2].fd = ipc_datagram_nbcoe() ; if (x[2].fd == -1) strerr_diefu1sys(111, "create socket") ; if (ipc_bind_reuse_perms(x[2].fd, usock, 0777) == -1) strerr_diefu2sys(111, "bind socket to ", usock) ; } else { x[2].fd = socket_udp46_nbcoe(ip46_is6(&ip)) ; if (x[2].fd == -1) strerr_diefu1sys(111, "create socket") ; if (socket_bind46_reuse(x[2].fd, &ip, port) == -1) { char fmti[IP46_FMT] ; char fmtp[UINT16_FMT] ; fmti[ip46_fmt(fmti, &ip)] = 0 ; fmtp[uint16_fmt(fmtp, port)] = 0 ; strerr_diefu5sys(111, "bind socket to ", "ip ", fmti, " port ", fmtp) ; } is6 = ip46_is6(&ip) ; } if (gidn != (size_t)-1 && setgroups_and_gid(gid ? gid : getegid(), gidn, gids) < 0) strerr_diefu1sys(111, "set supplementary group list") ; if (gid && setgid(gid) < 0) strerr_diefu1sys(111, "setgid") ; if (uid && setuid(uid) < 0) strerr_diefu1sys(111, "setuid") ; x[0].fd = selfpipe_init() ; if (x[0].fd == -1) strerr_diefu1sys(111, "init selfpipe") ; if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; if (!selfpipe_trap(SIGTERM)) strerr_diefu1sys(111, "trap signals") ; tain_now_set_stopwatch_g() ; if (notif) { fd_write(notif, "\n", 1) ; fd_close(notif) ; } } { char outbuf[linelen << 2] ; buffer b1 = BUFFER_INIT(&buffer_write, 1, outbuf, linelen << 2) ; char line[linelen + 1] ; while (cont || buffer_len(&b1)) { ssize_t r ; x[1].events = buffer_len(&b1) ? IOPAUSE_WRITE : 0 ; x[2].events = cont && buffer_available(&b1) >= linelen + 80 ? IOPAUSE_READ : 0 ; r = iopause_g(x, 3, cont ? &tain_infinite : &lameducktto) ; if (r == -1) strerr_diefu1sys(111, "iopause") ; if (!r) return 99 ; if (x[0].revents & IOPAUSE_READ) handle_signals() ; if (x[1].events & x[1].revents & IOPAUSE_WRITE) if (!buffer_flush(&b1) && !error_isagain(errno)) strerr_diefu1sys(111, "write to stdout") ; if (x[2].events & x[2].revents & IOPAUSE_READ) { if (usock) { r = sanitize_read(fd_recv(x[2].fd, line, linelen + 1, 0)) ; if (r == -1) strerr_diefu1sys(111, "recv") ; } else { ip46 ip ; uint16_t port ; r = sanitize_read(socket_recv46(x[2].fd, line, linelen + 1, &ip, &port, is6)) ; if (r == -1) strerr_diefu1sys(111, "recv") ; if (r) { char fmt[IP46_FMT + UINT16_FMT + 3] ; size_t m = ip46_fmt(fmt, &ip) ; fmt[m++] = '_' ; m += uint16_fmt(fmt + m, port) ; fmt[m++] = ':' ; fmt[m++] = ' ' ; buffer_putnoflush(&b1, fmt, m) ; } } if (r) { size_t len = r ; size_t pos = 0 ; while (r && (!line[r-1] || line[r-1] == '\n')) r-- ; for (size_t i = 0 ; i < r ; i++) if (!line[i] || line[i] == '\n') line[i] = '~' ; if (!flagraw) { char sbuf[LOLSYSLOG_STRING] ; pos = lolsyslog_string(sbuf, line) ; if (pos) buffer_putsnoflush(&b1, sbuf) ; } buffer_putnoflush(&b1, line + pos, r - pos) ; if (len == linelen+1) buffer_putnoflush(&b1, "...", 3) ; buffer_putnoflush(&b1, "\n", 1) ; } } } } return 0 ; } s6-2.13.1.0/src/daemontools-extras/s6-softlimit.c000066400000000000000000000054031470151141200214050ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #define USAGE "s6-softlimit [ -a allbytes ] [ -c corebytes ] [ -d databytes ] [ -f filebytes ] [ -l lockbytes ] [ -m membytes ] [ -o openfiles ] [ -p processes ] [ -r residentbytes ] [ -s stackbytes ] [ -t cpusecs ] prog..." static int what = 1 ; static void doit (int res, char const *arg) { struct rlimit r ; if (getrlimit(res, &r) < 0) strerr_diefu1sys(111, "getrlimit") ; if ((arg[0] == '=') && !arg[1]) { if (what & 2) r.rlim_max = RLIM_INFINITY ; if (what & 1) r.rlim_cur = r.rlim_max ; } else { uint64_t n ; if (!uint640_scan(arg, &n)) strerr_dieusage(100, USAGE) ; if (what & 2) r.rlim_max = n ; if (what & 1) { if (n > r.rlim_max) n = r.rlim_max ; r.rlim_cur = n ; } } if (setrlimit(res, &r) < 0) strerr_diefu1sys(111, "setrlimit") ; } int main (int argc, char const *const *argv) { subgetopt l = SUBGETOPT_ZERO ; PROG = "s6-softlimit" ; for (;;) { int opt = subgetopt_r(argc, argv, "hHa:c:d:f:l:m:o:p:r:s:t:", &l) ; if (opt == -1) break ; switch (opt) { case 'h' : what = 2 ; break ; case 'H' : what = 3 ; break ; case 'a' : #ifdef RLIMIT_AS doit(RLIMIT_AS, l.arg) ; #endif #ifdef RLIMIT_VMEM doit(RLIMIT_VMEM, l.arg) ; #endif break ; case 'c' : #ifdef RLIMIT_CORE doit(RLIMIT_CORE, l.arg) ; #endif break ; case 'd' : #ifdef RLIMIT_DATA doit(RLIMIT_DATA, l.arg) ; #endif break ; case 'f' : #ifdef RLIMIT_FSIZE doit(RLIMIT_FSIZE, l.arg) ; #endif break ; case 'l' : #ifdef RLIMIT_MEMLOCK doit(RLIMIT_MEMLOCK, l.arg) ; #endif break ; case 'm' : #ifdef RLIMIT_DATA doit(RLIMIT_DATA, l.arg) ; #endif #ifdef RLIMIT_STACK doit(RLIMIT_STACK, l.arg) ; #endif #ifdef RLIMIT_MEMLOCK doit(RLIMIT_MEMLOCK, l.arg) ; #endif #ifdef RLIMIT_VMEM doit(RLIMIT_VMEM, l.arg) ; #endif #ifdef RLIMIT_AS doit(RLIMIT_AS, l.arg) ; #endif break ; case 'o' : #ifdef RLIMIT_NOFILE doit(RLIMIT_NOFILE, l.arg) ; #endif #ifdef RLIMIT_OFILE doit(RLIMIT_OFILE, l.arg) ; #endif break ; case 'p' : #ifdef RLIMIT_NPROC doit(RLIMIT_NPROC, l.arg) ; #endif break ; case 'r' : #ifdef RLIMIT_RSS doit(RLIMIT_RSS, l.arg) ; #endif break ; case 's' : #ifdef RLIMIT_STACK doit(RLIMIT_STACK, l.arg) ; #endif break ; case 't' : #ifdef RLIMIT_CPU doit(RLIMIT_CPU, l.arg) ; #endif break ; } } argc -= l.ind ; argv += l.ind ; if (!argc) strerr_dieusage(100, USAGE) ; xexec(argv) ; } s6-2.13.1.0/src/daemontools-extras/s6-tai64n.c000066400000000000000000000015421470151141200205000ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include int main (void) { char stamp[TIMESTAMP+1] ; PROG = "s6-tai64n" ; stamp[TIMESTAMP] = ' ' ; for (;;) { int r = skagetln(buffer_0f1, &satmp, '\n') ; if (r < 0) if (errno != EPIPE) strerr_diefu1sys(111, "read from stdin") ; else { r = 1 ; if (!stralloc_catb(&satmp, "\n", 1)) strerr_diefu1sys(111, "add newline") ; } else if (!r) break ; timestamp(stamp) ; if ((buffer_put(buffer_1, stamp, TIMESTAMP+1) < TIMESTAMP+1) || (buffer_put(buffer_1, satmp.s, satmp.len) < (ssize_t)satmp.len)) strerr_diefu1sys(111, "write to stdout") ; satmp.len = 0 ; } return 0 ; } s6-2.13.1.0/src/daemontools-extras/s6-tai64nlocal.c000066400000000000000000000030221470151141200215060ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #define USAGE "s6-tai64nlocal [ -g ]" int main (int argc, char const *const *argv) { int islocal = 1 ; PROG = "s6-tai64nlocal" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "g", &l) ; if (opt == -1) break ; switch (opt) { case 'g' : islocal = 0 ; break ; default : strerr_dieusage(100, USAGE) ; } } argc -= l.ind ; argv += l.ind ; } for (;;) { unsigned int p = 0 ; int r = skagetln(buffer_0f1, &satmp, '\n') ; if (r == -1) if (errno != EPIPE) strerr_diefu1sys(111, "read from stdin") ; else r = 1 ; else if (!r) break ; if (satmp.len > TIMESTAMP) { tain a ; p = timestamp_scan(satmp.s, &a) ; if (p) { localtmn local ; if (localtmn_from_tain(&local, &a, islocal)) { char fmt[LOCALTMN_FMT+1] ; size_t len = localtmn_fmt(fmt, &local) ; if (buffer_put(buffer_1, fmt, len) < (ssize_t)len) strerr_diefu1sys(111, "write to stdout") ; } else p = 0 ; } } if (buffer_put(buffer_1, satmp.s + p, satmp.len - p) < (ssize_t)(satmp.len - p)) strerr_diefu1sys(111, "write to stdout") ; satmp.len = 0 ; } return 0 ; } s6-2.13.1.0/src/daemontools-extras/ucspilogd.c000066400000000000000000000037721470151141200210450ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include "lolsyslog.h" #define USAGE "ucspilogd [ -D default ] [ var... ]" #define dieusage() strerr_dieusage(100, USAGE) static inline void die (void) { strerr_diefu1sys(111, "write to stdout") ; } int main (int argc, char const *const *argv) { char const *d = "" ; PROG = "ucspilogd" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "D:", &l) ; if (opt == -1) break ; switch (opt) { case 'D' : d = l.arg ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } { unsigned int i = 0 ; stralloc sa = STRALLOC_ZERO ; char buf[LOLSYSLOG_STRING] ; char const *envs[argc] ; for (; i < (unsigned int)argc ; i++) { envs[i] = getenv(argv[i]) ; if (!envs[i]) envs[i] = d ; } for (;;) { size_t pos = 0 ; size_t j ; sa.len = 0 ; { int r = skagetlnsep(buffer_0f1, &sa, "\n", 2) ; if (r < 0) { if (errno != EPIPE || !stralloc_0(&sa)) strerr_diefu1sys(111, "read from stdin") ; } if (!r) break ; } if (!sa.len) continue ; sa.s[sa.len-1] = 0 ; if ((sa.s[0] == '@') && (sa.len > 26) && (byte_chr(sa.s, 26, ' ') == 25)) { if (buffer_put(buffer_1, sa.s, 26) < 26) die() ; pos += 26 ; } for (i = 0 ; i < (unsigned int)argc ; i++) if ((buffer_puts(buffer_1, envs[i]) < 0) || (buffer_put(buffer_1, ": ", 2) < 2)) die() ; j = lolsyslog_string(buf, sa.s + pos) ; pos += j ; if (j && buffer_puts(buffer_1, buf) < 0) die() ; sa.s[sa.len-1] = '\n' ; if (buffer_put(buffer_1, sa.s + pos, sa.len - pos) < 0) die() ; } } return 0 ; } s6-2.13.1.0/src/fdholder/000077500000000000000000000000001470151141200146365ustar00rootroot00000000000000s6-2.13.1.0/src/fdholder/deps-exe/000077500000000000000000000000001470151141200163505ustar00rootroot00000000000000s6-2.13.1.0/src/fdholder/deps-exe/s6-fdholder-daemon000066400000000000000000000000611470151141200216460ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/fdholder/deps-exe/s6-fdholder-delete000066400000000000000000000000611470151141200216450ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/fdholder/deps-exe/s6-fdholder-getdump000066400000000000000000000000611470151141200220500ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/fdholder/deps-exe/s6-fdholder-list000066400000000000000000000000611470151141200213560ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/fdholder/deps-exe/s6-fdholder-retrieve000066400000000000000000000000611470151141200222300ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/fdholder/deps-exe/s6-fdholder-setdump000066400000000000000000000000611470151141200220640ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/fdholder/deps-exe/s6-fdholder-store000066400000000000000000000000611470151141200215370ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/fdholder/deps-exe/s6-fdholder-transferdump000066400000000000000000000000611470151141200231150ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/fdholder/deps-exe/s6-fdholderd000066400000000000000000000000611470151141200205510ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/fdholder/s6-fdholder-daemon.c000066400000000000000000000111341470151141200203600ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-fdholder-daemon [ -v verbosity ] [ -d | -D ] [ -1 ] [ -c maxconn ] [ -n maxfds ] [ -b backlog ] [ -G gid,gid,... ] [ -g gid ] [ -u uid ] [ -U ] [ -t timeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ] path" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { unsigned int verbosity = 1 ; int flag1 = 0 ; int flagU = 0 ; int flagreuse = 1 ; unsigned int uid = 0, gid = 0 ; gid_t gids[NGROUPS_MAX] ; size_t gidn = (size_t)-1 ; unsigned int maxconn = 0 ; unsigned int maxfds = 0 ; unsigned int backlog = (unsigned int)-1 ; unsigned int timeout = 0 ; unsigned int ltimeout = 0 ; char const *rulesdir = 0 ; char const *rulesfile = 0 ; PROG = "s6-fdholder-daemon" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "Dd1Uv:c:n:b:u:g:G:t:T:i:x:", &l) ; if (opt == -1) break ; switch (opt) { case 'D' : flagreuse = 0 ; break ; case 'd' : flagreuse = 1 ; break ; case '1' : flag1 = 1 ; break ; case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; if (!maxconn) maxconn = 1 ; break ; case 'n' : if (!uint0_scan(l.arg, &maxfds)) dieusage() ; if (!maxfds) maxfds = 1 ; break ; case 'b' : if (!uint0_scan(l.arg, &backlog)) dieusage() ; break ; case 'u' : if (!uint0_scan(l.arg, &uid)) dieusage() ; break ; case 'g' : if (!uint0_scan(l.arg, &gid)) dieusage() ; break ; case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn) && *l.arg) dieusage() ; break ; case 'U' : flagU = 1 ; uid = 0 ; gid = 0 ; gidn = (size_t)-1 ; break ; case 't' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ; case 'T' : if (!uint0_scan(l.arg, <imeout)) dieusage() ; break ; case 'i' : rulesdir = l.arg ; rulesfile = 0 ; break ; case 'x' : rulesfile = l.arg ; rulesdir = 0 ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (!argc) dieusage() ; } if (!rulesdir && !rulesfile) strerr_dief1x(100, "no access rights specified!") ; { size_t pos = 0 ; unsigned int m = 0 ; char const *newargv[30] ; char fmt[UINT_FMT * 8 + GID_FMT * NGROUPS_MAX] ; newargv[m++] = S6_BINPREFIX "s6-ipcserver-socketbinder" ; if (!flagreuse) newargv[m++] = "-D" ; if (backlog != (unsigned int)-1) { newargv[m++] = "-b" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, backlog) ; fmt[pos++] = 0 ; } newargv[m++] = "--" ; newargv[m++] = *argv++ ; if (flagU || uid || gid || gidn != (size_t)-1) { newargv[m++] = S6_BINPREFIX "s6-applyuidgid" ; if (flagU) newargv[m++] = "-Uz" ; if (uid) { newargv[m++] = "-u" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, uid) ; fmt[pos++] = 0 ; } if (gid) { newargv[m++] = "-g" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, gid) ; fmt[pos++] = 0 ; } if (gidn != (size_t)-1) { newargv[m++] = "-G" ; newargv[m++] = fmt + pos ; pos += gid_fmtlist(fmt + pos, gids, gidn) ; fmt[pos++] = 0 ; } newargv[m++] = "--" ; } newargv[m++] = S6_BINPREFIX "s6-fdholderd" ; if (verbosity != 1) { newargv[m++] = "-v" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, verbosity) ; fmt[pos++] = 0 ; } if (flag1) newargv[m++] = "-1" ; if (maxconn) { newargv[m++] = "-c" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, maxconn) ; fmt[pos++] = 0 ; } if (maxfds) { newargv[m++] = "-n" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, maxfds) ; fmt[pos++] = 0 ; } if (timeout) { newargv[m++] = "-t" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, timeout) ; fmt[pos++] = 0 ; } if (ltimeout) { newargv[m++] = "-T" ; newargv[m++] = fmt + pos ; pos += uint_fmt(fmt + pos, ltimeout) ; fmt[pos++] = 0 ; } if (rulesdir) { newargv[m++] = "-i" ; newargv[m++] = rulesdir ; } else if (rulesfile) { newargv[m++] = "-x" ; newargv[m++] = rulesfile ; } newargv[m++] = 0 ; xexec(newargv) ; } } s6-2.13.1.0/src/fdholder/s6-fdholder-delete.c000066400000000000000000000022361470151141200203620ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #define USAGE "s6-fdholder-delete [ -t timeout ] socket id" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { s6_fdholder_t a = S6_FDHOLDER_ZERO ; tain deadline ; PROG = "s6-fdholder-delete" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { 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 : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ; } if (argc < 2) dieusage() ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; if (!s6_fdholder_start_g(&a, argv[0], &deadline)) strerr_diefu2sys(111, "connect to a fd-holder daemon at ", argv[0]) ; if (!s6_fdholder_delete_g(&a, argv[1], &deadline)) strerr_diefu2sys(1, "delete fd for id ", argv[0]) ; return 0 ; } s6-2.13.1.0/src/fdholder/s6-fdholder-getdump.c000066400000000000000000000052771470151141200205750ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-fdholder-getdump [ -t timeout ] socket prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv, char const *const *envp) { s6_fdholder_t a = S6_FDHOLDER_ZERO ; genalloc dump = GENALLOC_ZERO ; tain deadline, halfinfinite ; PROG = "s6-fdholder-getdump" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { 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 : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ; } if (argc < 2) dieusage() ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; if (!s6_fdholder_start_g(&a, argv[0], &deadline)) strerr_diefu2sys(111, "connect to a fd-holder daemon at ", argv[0]) ; if (!s6_fdholder_getdump_g(&a, &dump, &deadline)) strerr_diefu1sys(1, "get dump") ; s6_fdholder_end(&a) ; tain_half(&halfinfinite, &tain_infinite_relative) ; tain_add_g(&halfinfinite, &halfinfinite) ; { size_t n = genalloc_len(s6_fdholder_fd_t, &dump) ; size_t pos = 0 ; unsigned int i = 0 ; char modifs[7 + UINT_FMT + (25 + TIMESTAMP + 4 * UINT_FMT) * n] ; if (n > UINT_MAX) strerr_dief1x(100, "dump exceeds maximum size") ; memcpy(modifs + pos, "S6_FD#=", 7) ; pos += 7 ; pos += uint_fmt(modifs + pos, n) ; modifs[pos++] = 0 ; for (; i < n ; i++) { s6_fdholder_fd_t *p = genalloc_s(s6_fdholder_fd_t, &dump) + i ; size_t len = strlen(p->id) + 1 ; if (uncoe(p->fd) < 0) strerr_diefu1sys(111, "uncoe") ; memcpy(modifs + pos, "S6_FD_", 6) ; pos += 6 ; pos += uint_fmt(modifs + pos, i) ; modifs[pos++] = '=' ; pos += uint_fmt(modifs + pos, p->fd) ; modifs[pos++] = 0 ; memcpy(modifs + pos, "S6_FDID_", 8) ; pos += 8 ; pos += uint_fmt(modifs + pos, i) ; modifs[pos++] = '=' ; memcpy(modifs + pos, p->id, len) ; pos += len ; memcpy(modifs + pos, "S6_FDLIMIT_", 11) ; pos += 11 ; pos += uint_fmt(modifs + pos, i) ; if (tain_less(&p->limit, &halfinfinite)) { modifs[pos++] = '=' ; pos += timestamp_fmt(modifs + pos, &p->limit) ; } modifs[pos++] = 0 ; } xmexec_n(argv+1, modifs, pos, 1 + 3*n) ; } } s6-2.13.1.0/src/fdholder/s6-fdholder-list.c000066400000000000000000000034261470151141200200750ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #define USAGE "s6-fdholder-list [ -t timeout ] socket" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { s6_fdholder_t a = S6_FDHOLDER_ZERO ; stralloc sa = STRALLOC_ZERO, sb = STRALLOC_ZERO ; size_t pos = 0 ; int n ; tain deadline ; PROG = "s6-fdholder-list" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { 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 : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ; } if (!argc) dieusage() ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; if (!s6_fdholder_start_g(&a, argv[0], &deadline)) strerr_diefu2sys(111, "connect to a fd-holder daemon at ", argv[0]) ; n = s6_fdholder_list_g(&a, &sa, &deadline) ; if (n < 0) strerr_diefu1sys(1, "get fd list") ; while (n--) { size_t len = strlen(sa.s + pos) ; sb.len = 0 ; if (!string_quote_nodelim_mustquote(&sb, sa.s + pos, len, 0, 0)) strerr_diefu1sys(111, "quote string") ; if (buffer_put(buffer_1, sb.s, sb.len) < sb.len || buffer_put(buffer_1, "\n", 1) < 1) strerr_diefu1sys(111, "buffer_put") ; pos += len+1 ; } stralloc_free(&sb) ; stralloc_free(&sa) ; if (!buffer_flush(buffer_1)) strerr_diefu1sys(111, "write to stdout") ; return 0 ; } s6-2.13.1.0/src/fdholder/s6-fdholder-retrieve.c000066400000000000000000000030121470151141200207360ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-fdholder-retrieve [ -D ] [ -t timeout ] socket id prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { s6_fdholder_t a = S6_FDHOLDER_ZERO ; tain deadline ; int fd ; int dodelete = 0 ; PROG = "s6-fdholder-retrieve" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "Dt:", &l) ; if (opt == -1) break ; switch (opt) { case 'D' : dodelete = 1 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ; } if (argc < 3) dieusage() ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; if (!s6_fdholder_start_g(&a, argv[0], &deadline)) strerr_diefu2sys(111, "connect to a fd-holder daemon at ", argv[0]) ; fd = s6_fdholder_retrieve_maybe_delete_g(&a, argv[1], dodelete, &deadline) ; if (fd < 0) strerr_diefu2sys(1, "retrieve fd for id ", argv[1]) ; s6_fdholder_end(&a) ; if (!fd) { if (uncoe(0) < 0) strerr_diefu1sys(111, "uncoe stdin") ; } else if (fd_move(0, fd) < 0) strerr_diefu1sys(111, "move fd") ; xexec(argv+2) ; } s6-2.13.1.0/src/fdholder/s6-fdholder-setdump.c000066400000000000000000000044101470151141200205750ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-fdholder-setdump [ -t timeout ] socket" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { s6_fdholder_t a = S6_FDHOLDER_ZERO ; tain deadline ; unsigned int dumplen ; char const *x ; PROG = "s6-fdholder-setdump" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { 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 : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ; } if (!argc) dieusage() ; x = getenv("S6_FD#") ; if (!x) strerr_dienotset(100, "S6_FD#") ; if (!uint0_scan(x, &dumplen)) strerr_dieinvalid(100, "S6_FD#") ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; if (!s6_fdholder_start_g(&a, argv[0], &deadline)) strerr_diefu2sys(111, "connect to a fd-holder daemon at ", argv[0]) ; if (dumplen) { unsigned int i = 0 ; s6_fdholder_fd_t dump[dumplen] ; char s[11 + UINT_FMT] ; for (; i < dumplen ; i++) { size_t len ; unsigned int fd ; memcpy(s, "S6_FD_", 6) ; s[6 + uint_fmt(s+6, i)] = 0 ; x = getenv(s) ; if (!x) strerr_dienotset(100, s) ; if (!uint0_scan(x, &fd)) strerr_dieinvalid(100, s) ; dump[i].fd = fd ; memcpy(s, "S6_FDID_", 8) ; s[8 + uint_fmt(s+8, i)] = 0 ; x = getenv(s) ; if (!x) strerr_dienotset(100, s) ; len = strlen(x) ; if (!len || len > S6_FDHOLDER_ID_SIZE) strerr_dieinvalid(100, s) ; memcpy(dump[i].id, x, len+1) ; memcpy(s, "S6_FDLIMIT_", 11) ; s[11 + uint_fmt(s+11, i)] = 0 ; x = getenv(s) ; if (!x) tain_add_g(&dump[i].limit, &tain_infinite_relative) ; else if (!timestamp_scan(x, &dump[i].limit)) strerr_dieinvalid(100, s) ; } if (!s6_fdholder_setdump_g(&a, dump, dumplen, &deadline)) strerr_diefu1sys(1, "set dump") ; } return 0 ; } s6-2.13.1.0/src/fdholder/s6-fdholder-store.c000066400000000000000000000027311470151141200202540ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #define USAGE "s6-fdholder-store [ -d fd ] [ -t timeout ] [ -T fdtimeout ] socket id" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { s6_fdholder_t a = S6_FDHOLDER_ZERO ; tain deadline, limit ; unsigned int fd = 0 ; PROG = "s6-fdholder-store" ; { unsigned int t = 0, T = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "d:t:T:", &l) ; if (opt == -1) break ; switch (opt) { case 'd' : if (!uint0_scan(l.arg, &fd)) dieusage() ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ; if (T) tain_from_millisecs(&limit, T) ; else limit = tain_infinite_relative ; } if (argc < 2) dieusage() ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; tain_add_g(&limit, &limit) ; if (!s6_fdholder_start_g(&a, argv[0], &deadline)) strerr_diefu2sys(111, "connect to a fd-holder daemon at ", argv[0]) ; if (!s6_fdholder_store_g(&a, fd, argv[1], &limit, &deadline)) strerr_diefu1sys(1, "store fd") ; return 0 ; } s6-2.13.1.0/src/fdholder/s6-fdholder-transferdump.c000066400000000000000000000042641470151141200216350ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #define USAGE "s6-fdholder-transferdump [ -t timeoutfrom:timeoutto ] socketfrom socketto" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { s6_fdholder_t a = S6_FDHOLDER_ZERO ; genalloc dump = GENALLOC_ZERO ; /* array of s6_fdholder_fd_t */ tain deadline, totto ; PROG = "s6-fdholder-transferdump" ; { unsigned int timeoutfrom = 0, timeoutto = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "t:", &l) ; if (opt == -1) break ; switch (opt) { case 't' : { size_t pos = uint_scan(l.arg, &timeoutfrom) ; if (!pos) { if (l.arg[pos] != ':') dieusage() ; timeoutfrom = 0 ; } if (!l.arg[pos]) timeoutto = 0 ; else { if (l.arg[pos++] != ':') dieusage() ; if (!l.arg[pos]) timeoutto = 0 ; else if (!uint0_scan(l.arg + pos, &timeoutto)) dieusage() ; } break ; } default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (timeoutfrom) tain_from_millisecs(&deadline, timeoutfrom) ; else deadline = tain_infinite_relative ; if (timeoutto) tain_from_millisecs(&totto, timeoutto) ; else totto = tain_infinite_relative ; } if (argc < 2) dieusage() ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; if (!s6_fdholder_start_g(&a, argv[0], &deadline)) strerr_diefu2sys(111, "connect to a source fd-holder daemon at ", argv[0]) ; if (!s6_fdholder_getdump_g(&a, &dump, &deadline)) strerr_diefu1sys(1, "get dump") ; s6_fdholder_end(&a) ; tain_add_g(&deadline, &totto) ; if (!s6_fdholder_start_g(&a, argv[1], &deadline)) strerr_diefu2sys(111, "connect to a destination fd-holder daemon at ", argv[1]) ; if (!s6_fdholder_setdump_g(&a, genalloc_s(s6_fdholder_fd_t, &dump), genalloc_len(s6_fdholder_fd_t, &dump), &deadline)) strerr_diefu1sys(1, "set dump") ; return 0 ; } s6-2.13.1.0/src/fdholder/s6-fdholderd.c000066400000000000000000000560241470151141200172720ustar00rootroot00000000000000/* 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 #include #include #include #include #include #include #include #include #include #define USAGE "s6-fdholderd [ -v verbosity ] [ -1 ] [ -c maxconn ] [ -n maxfds ] [ -t timeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ]" #define dieusage() strerr_dieusage(100, USAGE) ; static unsigned int verbosity = 1 ; static int cont = 1 ; static tain answertto = TAIN_INFINITE_RELATIVE ; static tain lameduckdeadline = TAIN_INFINITE_RELATIVE ; static tain const nano1 = { .sec = TAI_ZERO, .nano = 1 } ; static unsigned int rulestype = 0 ; static char const *rules = 0 ; static cdb cdbmap = CDB_ZERO ; static void handle_signals (void) { for (;;) switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "selfpipe_read()") ; case 0 : return ; case SIGTERM : { if (cont) { cont = 0 ; tain_add_g(&lameduckdeadline, &lameduckdeadline) ; } break ; } case SIGHUP : { cdb c = CDB_ZERO ; if (rulestype != 2) break ; if (!cdb_init(&c, rules)) break ; cdb_free(&cdbmap) ; cdbmap = c ; } break ; default : break ; } } /* fd store */ static genset *fdstore ; #define FD(i) genset_p(s6_fdholder_fd_t, fdstore, (i)) static unsigned int maxfds = 1000 ; #define numfds genset_n(fdstore) static avltreen *fds_by_id ; static avltreen *fds_by_deadline ; static void *fds_id_dtok (uint32_t d, void *x) { (void)x ; return FD(d)->id ; } static int fds_id_cmp (void const *a, void const *b, void *x) { (void)x ; return strcmp((char const *)a, (char const *)b) ; } static void *fds_deadline_dtok (uint32_t d, void *x) { (void)x ; return &FD(d)->limit ; } static int fds_deadline_cmp (void const *a, void const *b, void *x) { tain const *aa = (tain const *)a ; tain const *bb = (tain const *)b ; (void)x ; return tain_less(aa, bb) ? -1 : tain_less(bb, aa) ; } static void fds_delete (uint32_t pp) { avltreen_delete(fds_by_id, fds_id_dtok(pp, 0)) ; avltreen_delete(fds_by_deadline, fds_deadline_dtok(pp, 0)) ; genset_delete(fdstore, pp) ; } static void fds_close_and_delete (uint32_t pp) { fd_close(FD(pp)->fd) ; fds_delete(pp) ; } /* client connection */ typedef struct client_s client_t, *client_t_ref ; struct client_s { uint32_t next ; uint32_t xindex ; tain deadline ; regex_t rre ; regex_t wre ; unsigned int dumping ; unsigned int flags ; unixconnection connection ; } ; static genset *clients ; static uint32_t sentinel ; #define CLIENT(i) genset_p(client_t, clients, (i)) #define numconn (genset_n(clients) - 1) static inline void client_free (client_t *c) { fd_close(unixmessage_sender_fd(&c->connection.out)) ; unixconnection_free(&c->connection) ; regfree(&c->rre) ; regfree(&c->wre) ; } static inline void client_delete (uint32_t cc, uint32_t prev) { client_t *c = CLIENT(cc) ; CLIENT(prev)->next = c->next ; client_free(c) ; genset_delete(clients, cc) ; } static void removeclient (uint32_t *i, uint32_t j) { client_delete(*i, j) ; *i = j ; } static void client_setdeadline (client_t *c) { tain blah ; tain_half(&blah, &tain_infinite_relative) ; tain_add_g(&blah, &blah) ; if (tain_less(&blah, &c->deadline)) tain_add_g(&c->deadline, &answertto) ; } static inline int client_prepare_iopause (uint32_t i, tain *deadline, iopause_fd *x, uint32_t *j) { client_t *c = CLIENT(i) ; if (tain_less(&c->deadline, deadline)) *deadline = c->deadline ; if (!unixmessage_sender_isempty(&c->connection.out) || !unixmessage_receiver_isempty(&c->connection.in) || (cont && !unixmessage_receiver_isfull(&c->connection.in))) { x[*j].fd = unixmessage_sender_fd(&c->connection.out) ; x[*j].events = (!unixmessage_receiver_isempty(&c->connection.in) || (cont && !unixmessage_receiver_isfull(&c->connection.in)) ? IOPAUSE_READ : 0) | (!unixmessage_sender_isempty(&c->connection.out) ? IOPAUSE_WRITE : 0) ; c->xindex = (*j)++ ; } else c->xindex = 0 ; return !!c->xindex ; } static inline void client_add (uint32_t *cc, int fd, regex_t const *rre, regex_t const *wre, unsigned int flags) { uint32_t i ; client_t *c ; i = genset_new(clients) ; c = CLIENT(i) ; tain_add_g(&c->deadline, &answertto) ; c->rre = *rre ; c->wre = *wre ; c->dumping = 0 ; c->flags = flags ; unixconnection_init(&c->connection, fd, fd) ; c->next = CLIENT(sentinel)->next ; CLIENT(sentinel)->next = i ; *cc = i ; } static inline int client_flush (uint32_t i, iopause_fd const *x) { client_t *c = CLIENT(i) ; if (c->xindex && (x[c->xindex].revents & IOPAUSE_WRITE)) { if (unixconnection_flush(&c->connection)) tain_add_g(&c->deadline, &tain_infinite_relative) ; else if (!error_isagain(errno)) return 0 ; } return 1 ; } static int answer (client_t *c, char e) { unixmessage m = { .s = &e, .len = 1, .fds = 0, .nfds = 0 } ; if (!unixmessage_put(&c->connection.out, &m)) return 0 ; client_setdeadline(c) ; return 1 ; } static int do_store (uint32_t cc, unixmessage const *m) { uint32_t pp, idlen ; client_t *c = CLIENT(cc) ; s6_fdholder_fd_t *p ; if (c->dumping || m->len < TAIN_PACK + 3 || m->nfds != 1) return (errno = EPROTO, 0) ; idlen = (unsigned char)m->s[TAIN_PACK] ; if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 2 + TAIN_PACK != m->len || m->s[1 + TAIN_PACK + idlen]) return (errno = EPROTO, 0) ; if (regexec(&c->wre, m->s + TAIN_PACK + 1, 0, 0, 0)) { unixmessage_drop(m) ; return answer(c, EPERM) ; } if (numfds >= maxfds) { unixmessage_drop(m) ; return answer(c, ENFILE) ; } if (avltreen_search(fds_by_id, m->s + TAIN_PACK + 1, &pp)) { unixmessage_drop(m) ; return answer(c, EBUSY) ; } if (!answer(c, 0)) return 0 ; pp = genset_new(fdstore) ; p = FD(pp) ; tain_unpack(m->s, &p->limit) ; p->fd = m->fds[0] ; memcpy(p->id, m->s + TAIN_PACK + 1, idlen) ; memset(p->id + idlen, 0, S6_FDHOLDER_ID_SIZE + 1 - idlen) ; for (;;) { uint32_t dummy ; if (!avltreen_search(fds_by_deadline, &p->limit, &dummy)) break ; tain_add(&p->limit, &p->limit, &nano1) ; } avltreen_insert(fds_by_id, pp) ; avltreen_insert(fds_by_deadline, pp) ; return 1 ; } static int do_delete (uint32_t cc, unixmessage const *m) { uint32_t pp, idlen ; client_t *c = CLIENT(cc) ; if (c->dumping || m->len < 3 || m->nfds) return (errno = EPROTO, 0) ; idlen = (unsigned char)m->s[0] ; if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 2 != m->len || m->s[idlen + 1]) return (errno = EPROTO, 0) ; if (regexec(&c->wre, m->s + 1, 0, 0, 0)) return answer(c, EPERM) ; if (!avltreen_search(fds_by_id, m->s + 1, &pp)) return answer(c, ENOENT) ; if (!answer(c, 0)) return 0 ; fds_close_and_delete(pp) ; return 1 ; } static int do_retrieve (uint32_t cc, unixmessage const *m) { int fd ; unixmessage ans = { .s = "", .len = 1, .fds = &fd, .nfds = 1 } ; uint32_t pp, idlen ; client_t *c = CLIENT(cc) ; if (c->dumping || m->len < 4 || m->nfds) return (errno = EPROTO, 0) ; idlen = (unsigned char)m->s[1] ; if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 3 != m->len || m->s[idlen + 2]) return (errno = EPROTO, 0) ; if (regexec(&c->rre, m->s + 2, 0, 0, 0)) return answer(c, EPERM) ; if (m->s[0] && regexec(&c->wre, m->s + 2, 0, 0, 0)) return answer(c, EPERM) ; if (!avltreen_search(fds_by_id, m->s + 2, &pp)) return answer(c, ENOENT) ; fd = FD(pp)->fd ; if (!unixmessage_put_and_close(&c->connection.out, &ans, m->s[0] ? unixmessage_bits_closeall : unixmessage_bits_closenone)) return 0 ; if (m->s[0]) fds_delete(pp) ; return 1 ; } static int fill_siovec_with_ids_iter (void *thing, void *data) { struct iovec *v = (*(struct iovec **)data)++ ; s6_fdholder_fd_t *p = thing ; v->iov_base = p->id ; v->iov_len = strlen(p->id) + 1 ; return 1 ; } static int do_list (uint32_t cc, unixmessage const *m) { client_t *c = CLIENT(cc) ; struct iovec v[1+numfds] ; unixmessagev ans = { .v = v, .vlen = 1+numfds, .fds = 0, .nfds = 0 } ; struct iovec *vp = v + 1 ; char pack[5] = "" ; if (c->dumping || m->len || m->nfds) return (errno = EPROTO, 0) ; if (!(c->flags & 4)) return answer(c, EPERM) ; uint32_pack_big(pack + 1, (uint32_t)numfds) ; v[0].iov_base = pack ; v[0].iov_len = 5 ; genset_iter(fdstore, &fill_siovec_with_ids_iter, &vp) ; if (!unixmessage_putv(&c->connection.out, &ans)) return answer(c, errno) ; client_setdeadline(c) ; return 1 ; } typedef struct getdumpiter_s getdumpiter_t, *getdumpiter_t_ref ; struct getdumpiter_s { struct iovec *v ; int *fd ; char *limit ; } ; static int getdump_iter (void *thing, void *stuff) { s6_fdholder_fd_t *p = thing ; getdumpiter_t *blah = stuff ; unsigned char len = strlen(p->id) ; tain_pack(blah->limit, &p->limit) ; blah->limit[TAIN_PACK] = len ; blah->v->iov_base = p->id ; blah->v->iov_len = len + 1 ; *blah->fd++ = p->fd ; blah->v += 2 ; blah->limit += TAIN_PACK + 1 ; return 1 ; } static int do_getdump (uint32_t cc, unixmessage const *m) { uint32_t n = numfds ? 1 + ((numfds-1) / UNIXMESSAGE_MAXFDS) : 0 ; client_t *c = CLIENT(cc) ; if (c->dumping || m->len || m->nfds) return (errno = EPROTO, 0) ; if (!(c->flags & 1)) return answer(c, EPERM) ; { char pack[9] = "" ; unixmessage ans = { .s = pack, .len = 9, .fds = 0, .nfds = 0 } ; uint32_pack_big(pack+1, n) ; uint32_pack_big(pack+5, numfds) ; if (!unixmessage_put(&c->connection.out, &ans)) return answer(c, errno) ; } if (n) { uint32_t i = 0 ; unixmessagev ans[n] ; struct iovec v[numfds << 1] ; int fds[numfds] ; char limits[(TAIN_PACK+1) * numfds] ; for (; i < numfds ; i++) { v[i<<1].iov_base = limits + i * (TAIN_PACK+1) ; v[i<<1].iov_len = TAIN_PACK+1 ; } { getdumpiter_t state = { .v = v+1, .fd = fds, .limit = limits } ; genset_iter(fdstore, &getdump_iter, &state) ; } for (i = 0 ; i < n-1 ; i++) { ans[i].v = v + (i<<1) * UNIXMESSAGE_MAXFDS ; ans[i].vlen = UNIXMESSAGE_MAXFDS << 1 ; ans[i].fds = fds + i * UNIXMESSAGE_MAXFDS ; ans[i].nfds = UNIXMESSAGE_MAXFDS ; } ans[n-1].v = v + ((n-1)<<1) * UNIXMESSAGE_MAXFDS ; ans[n-1].vlen = (1 + (numfds-1) % UNIXMESSAGE_MAXFDS) << 1 ; ans[n-1].fds = fds + (n-1) * UNIXMESSAGE_MAXFDS ; ans[n-1].nfds = 1 + (numfds - 1) % UNIXMESSAGE_MAXFDS ; for (i = 0 ; i < n ; i++) { if (!unixmessage_putv(&c->connection.out, ans + i)) { int e = errno ; ++i ; while (i--) unixmessage_unput(&c->connection.out) ; return answer(c, e) ; } } } client_setdeadline(c) ; return 1 ; } static int do_setdump (uint32_t cc, unixmessage const *m) { char pack[5] = "" ; uint32_t n ; unixmessage ans = { .s = pack, .len = 5, .fds = 0, .nfds = 0 } ; client_t *c = CLIENT(cc) ; if (c->dumping || m->len != 4 || m->nfds) return (errno = EPROTO, 0) ; if (!(c->flags & 2)) return answer(c, EPERM) ; uint32_unpack_big(m->s, &n) ; if (n > maxfds || n + numfds > maxfds) return answer(c, ENFILE) ; c->dumping = n ; n = n ? 1 + (n-1) / UNIXMESSAGE_MAXFDS : 0 ; uint32_pack_big(pack+1, n) ; if (!unixmessage_put(&c->connection.out, &ans)) return answer(c, errno) ; return 1 ; } static int do_setdump_data (uint32_t cc, unixmessage const *m) { client_t *c = CLIENT(cc) ; if (!m->nfds || m->nfds > c->dumping || m->len < m->nfds * (TAIN_PACK + 3)) return (errno = EPROTO, 0) ; if (!(c->flags & 2)) { unixmessage_drop(m) ; return answer(c, EPERM) ; } if (numfds + m->nfds > maxfds) { unixmessage_drop(m) ; return answer(c, ENFILE) ; } { char const *s = m->s ; size_t len = m->len ; uint32_t i = 0 ; uint32_t indices[m->nfds] ; for (; i < m->nfds ; i++) { s6_fdholder_fd_t *p ; uint32_t oldid, idlen ; if (len < TAIN_PACK + 3) break ; idlen = (unsigned char)s[TAIN_PACK] ; if (idlen > S6_FDHOLDER_ID_SIZE || len < TAIN_PACK + 2 + idlen || s[TAIN_PACK + 1 + idlen]) break ; indices[i] = genset_new(fdstore) ; p = FD(indices[i]) ; tain_unpack(s, &p->limit) ; memcpy(p->id, s + TAIN_PACK + 1, idlen+1) ; p->fd = m->fds[i] ; if (avltreen_search(fds_by_id, p->id, &oldid)) fds_close_and_delete(oldid) ; avltreen_insert(fds_by_id, indices[i]) ; avltreen_insert(fds_by_deadline, indices[i]) ; s += TAIN_PACK + 2 + idlen ; len -= TAIN_PACK + 2 + idlen ; } if (i < m->nfds || len) { while (i--) fds_delete(indices[i]) ; return (errno = EPROTO, 0) ; } } c->dumping -= m->nfds ; return answer(c, c->dumping ? EAGAIN : 0) ; } static int do_error (uint32_t cc, unixmessage const *m) { (void)cc ; (void)m ; return (errno = EPROTO, 0) ; } typedef int parse_func (uint32_t, unixmessage const *) ; typedef parse_func *parse_func_ref ; static inline int parse_protocol (unixmessage const *m, void *p) { static parse_func_ref const f[8] = { &do_store, &do_delete, &do_retrieve, &do_list, &do_getdump, &do_setdump, &do_setdump_data, &do_error } ; unixmessage mcopy = { .s = m->s + 1, .len = m->len - 1, .fds = m->fds, .nfds = m->nfds } ; if (!m->len) { unixmessage_drop(m) ; return (errno = EPROTO, 0) ; } if (!(*f[byte_chr("SDRL?!.", 7, m->s[0])])(*(uint32_t *)p, &mcopy)) { unixmessage_drop(m) ; return 0 ; } return 1 ; } static inline int client_read (uint32_t cc, iopause_fd const *x) { client_t *c = CLIENT(cc) ; return !unixmessage_receiver_isempty(&c->connection.in) || (c->xindex && (x[c->xindex].revents & IOPAUSE_READ)) ? unixmessage_handle(&c->connection.in, &parse_protocol, &cc) > 0 : 1 ; } /* Environment on new connections */ static int makere (regex_t *re, char const *s, char const *var) { size_t varlen = strlen(var) ; if (str_start(s, var) && (s[varlen] == '=')) { int r = skalibs_regcomp(re, s + varlen + 1, REG_EXTENDED | REG_NOSUB) ; if (r) { if (verbosity) { char buf[256] ; regerror(r, re, buf, 256) ; strerr_warnw6x("invalid ", var, " value: ", s + varlen + 1, ": ", buf) ; } return -1 ; } else return 1 ; } return 0 ; } static void defaultre (regex_t *re) { int r = skalibs_regcomp(re, ".^", REG_EXTENDED | REG_NOSUB) ; if (r) { char buf[256] ; regerror(r, re, buf, 256) ; strerr_diefu2x(100, "compile .^ into a regular expression: ", buf) ; } } static inline int parse_env (char const *const *envp, regex_t *rre, regex_t *wre, unsigned int *flags, unsigned int *donep) { unsigned int done = 0 ; unsigned int fl = 0 ; for (; *envp ; envp++) { if (str_start(*envp, "S6_FDHOLDER_GETDUMP=")) fl |= 1 ; if (str_start(*envp, "S6_FDHOLDER_SETDUMP=")) fl |= 2 ; if (str_start(*envp, "S6_FDHOLDER_LIST=")) fl |= 4 ; if (!(done & 1)) { int r = makere(rre, *envp, "S6_FDHOLDER_RETRIEVE_REGEX") ; if (r < 0) { if (done & 2) regfree(wre) ; return 0 ; } else if (r) done |= 1 ; } if (!(done & 2)) { int r = makere(wre, *envp, "S6_FDHOLDER_STORE_REGEX") ; if (r < 0) { if (done & 1) regfree(rre) ; return 0 ; } else if (r) done |= 2 ; } } *flags = fl ; *donep = done ; return 1 ; } static inline int new_connection (int fd, regex_t *rre, regex_t *wre, unsigned int *flags) { s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ; s6_accessrules_result_t result = S6_ACCESSRULES_ERROR ; uid_t uid ; gid_t gid ; unsigned int done = 0 ; if (getpeereid(fd, &uid, &gid) < 0) { if (verbosity) strerr_warnwu1sys("getpeereid") ; return 0 ; } switch (rulestype) { case 1 : result = s6_accessrules_uidgid_fs(uid, gid, rules, ¶ms) ; break ; case 2 : result = s6_accessrules_uidgid_cdb(uid, gid, &cdbmap, ¶ms) ; break ; default : break ; } if (result != S6_ACCESSRULES_ALLOW) { if (verbosity && (result == S6_ACCESSRULES_ERROR)) strerr_warnw1sys("error while checking rules") ; return 0 ; } if (params.exec.len && verbosity) { char fmtuid[UID_FMT] ; char fmtgid[GID_FMT] ; fmtuid[uid_fmt(fmtuid, uid)] = 0 ; fmtgid[gid_fmt(fmtgid, gid)] = 0 ; strerr_warnw4x("unused exec string in rules for uid ", fmtuid, " gid ", fmtgid) ; } if (params.env.s) { size_t n = byte_count(params.env.s, params.env.len, '\0') ; char const *envp[n+1] ; if (!env_make(envp, n, params.env.s, params.env.len)) { if (verbosity) strerr_warnwu1sys("env_make") ; s6_accessrules_params_free(¶ms) ; return 0 ; } envp[n] = 0 ; if (!parse_env(envp, rre, wre, flags, &done)) { if (verbosity) strerr_warnwu1sys("parse_env") ; s6_accessrules_params_free(¶ms) ; return 0 ; } } s6_accessrules_params_free(¶ms) ; if (!(done & 1)) defaultre(rre) ; if (!(done & 2)) defaultre(wre) ; return 1 ; } int main (int argc, char const *const *argv, char const *const *envp) { int flag1 = 0 ; uint32_t maxconn = 16 ; PROG = "s6-fdholderd" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int t = 0, T = 0 ; for (;;) { int opt = subgetopt_r(argc, argv, "v:1c:n:i:x:t:T:", &l) ; if (opt == -1) break ; switch (opt) { case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; case '1' : flag1 = 1 ; break ; case 'i' : rules = l.arg ; rulestype = 1 ; break ; case 'x' : rules = l.arg ; rulestype = 2 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ; case 'n' : if (!uint0_scan(l.arg, &maxfds)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&answertto, t) ; if (T) tain_from_millisecs(&lameduckdeadline, T) ; } if (!rulestype) strerr_dief1x(100, "no access rights specified!") ; if (maxconn > S6_FDHOLDER_MAX) maxconn = S6_FDHOLDER_MAX ; if (!maxconn) maxconn = 1 ; { struct rlimit fdlimit ; if (getrlimit(RLIMIT_NOFILE, &fdlimit) < 0) strerr_diefu1sys(111, "getrlimit") ; if (fdlimit.rlim_cur != RLIM_INFINITY) { if (fdlimit.rlim_cur < 7) strerr_dief1x(111, "open file limit too low") ; if (maxfds > fdlimit.rlim_cur) maxfds = fdlimit.rlim_cur - 6 ; } } if (!maxfds) maxfds = 1 ; { struct stat st ; if (fstat(0, &st) < 0) strerr_diefu1sys(111, "fstat stdin") ; if (!S_ISSOCK(st.st_mode)) strerr_dief1x(100, "stdin is not a socket") ; } if (flag1) { if (fcntl(1, F_GETFD) < 0) strerr_dief1sys(100, "called with option -1 but stdout said") ; } else close(1) ; if (selfpipe_init() == -1) strerr_diefu1sys(111, "selfpipe_init") ; if (!sig_ignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; { sigset_t set ; sigemptyset(&set) ; sigaddset(&set, SIGTERM) ; sigaddset(&set, SIGHUP) ; if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ; } if (rulestype == 2) { if (!cdb_init(&cdbmap, rules)) strerr_diefu2sys(111, "cdb_init ", rules) ; } { /* Hello, stack. I have a present for you. */ genset clientinfo, fdinfo ; avltreen fdidinfo, fddeadlineinfo ; iopause_fd x[2 + maxconn] ; client_t clientstorage[1+maxconn] ; uint32_t clientfreelist[1+maxconn] ; s6_fdholder_fd_t fdstorage[maxfds] ; uint32_t fdfreelist[maxfds] ; avlnode fdidstorage[maxfds] ; uint32_t fdidfreelist[maxfds] ; avlnode fddeadlinestorage[maxfds] ; uint32_t fddeadlinefreelist[maxfds] ; /* Hope you enjoyed it! Have a nice day! */ GENSET_init(&clientinfo, client_t, clientstorage, clientfreelist, 1+maxconn) ; clients = &clientinfo ; sentinel = genset_new(clients) ; clientstorage[sentinel].next = sentinel ; GENSET_init(&fdinfo, s6_fdholder_fd_t, fdstorage, fdfreelist, maxfds) ; fdstore = &fdinfo ; avltreen_init(&fdidinfo, fdidstorage, fdidfreelist, maxfds, &fds_id_dtok, &fds_id_cmp, 0) ; fds_by_id = &fdidinfo ; avltreen_init(&fddeadlineinfo, fddeadlinestorage, fddeadlinefreelist, maxfds, &fds_deadline_dtok, &fds_deadline_cmp, 0) ; fds_by_deadline = &fddeadlineinfo ; x[0].fd = selfpipe_fd() ; x[0].events = IOPAUSE_READ ; x[1].fd = 0 ; if (flag1) { fd_write(1, "\n", 1) ; fd_close(1) ; } /* We are long-lived and have to check absolute fd deadlines, so we purposefully remain in wallclock mode. */ tain_now_g() ; for (;;) { tain deadline ; uint32_t j = 2 ; uint32_t i ; int r = 1 ; if (cont) tain_add_g(&deadline, &tain_infinite_relative) ; else deadline = lameduckdeadline ; if (avltreen_min(fds_by_deadline, &i) && tain_less(&FD(i)->limit, &deadline)) deadline = FD(i)->limit ; x[1].events = (cont && (numconn < maxconn)) ? IOPAUSE_READ : 0 ; for (i = clientstorage[sentinel].next ; i != sentinel ; i = clientstorage[i].next) if (client_prepare_iopause(i, &deadline, x, &j)) r = 0 ; if (!cont && r) break ; r = iopause_g(x, j, &deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; if (!r) { if (!cont && !tain_future(&lameduckdeadline)) break ; for (;;) { if (!avltreen_min(fds_by_deadline, &i)) break ; if (tain_future(&FD(i)->limit)) break ; fds_close_and_delete(i) ; } for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next) if (!tain_future(&clientstorage[i].deadline)) removeclient(&i, j) ; continue ; } if (x[0].revents & IOPAUSE_READ) handle_signals() ; for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next) if (!client_flush(i, x)) removeclient(&i, j) ; for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next) if (!client_read(i, x)) removeclient(&i, j) ; if (x[1].revents & IOPAUSE_READ) { regex_t rre, wre ; unsigned int flags = 0 ; int dummy ; int fd = ipc_accept_nb(x[1].fd, 0, 0, &dummy) ; if (fd < 0) if (!error_isagain(errno)) strerr_diefu1sys(111, "accept") ; else continue ; else if (!new_connection(fd, &rre, &wre, &flags)) fd_close(fd) ; else client_add(&i, fd, &rre, &wre, flags) ; } } return ((!!numfds) | (!!numconn << 1)) ; } } s6-2.13.1.0/src/include-local/000077500000000000000000000000001470151141200155625ustar00rootroot00000000000000s6-2.13.1.0/src/include-local/s6lockd.h000066400000000000000000000001731470151141200173010ustar00rootroot00000000000000/* ISC license. */ #ifndef S6LOCKD_H #define S6LOCKD_H extern int s6lockd_openandlock (char const *, int, int) ; #endif s6-2.13.1.0/src/include/000077500000000000000000000000001470151141200144725ustar00rootroot00000000000000s6-2.13.1.0/src/include/s6/000077500000000000000000000000001470151141200150225ustar00rootroot00000000000000s6-2.13.1.0/src/include/s6/accessrules.h000066400000000000000000000061651470151141200175170ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_ACCESSRULES_H #define S6_ACCESSRULES_H #include #include #include #include typedef struct uidgid_s uidgid_t, *uidgid_t_ref ; struct uidgid_s { uid_t left ; gid_t right ; } ; typedef struct s6_accessrules_params_s s6_accessrules_params_t, *s6_accessrules_params_t_ref ; struct s6_accessrules_params_s { stralloc env ; stralloc exec ; } ; #define S6_ACCESSRULES_PARAMS_ZERO { .env = STRALLOC_ZERO, .exec = STRALLOC_ZERO } extern void s6_accessrules_params_free (s6_accessrules_params_t *) ; typedef enum s6_accessrules_result_e s6_accessrules_result_t, *s6_accessrules_result_t_ref ; enum s6_accessrules_result_e { S6_ACCESSRULES_ERROR = -1, S6_ACCESSRULES_DENY = 0, S6_ACCESSRULES_ALLOW = 1, S6_ACCESSRULES_NOTFOUND = 2 } ; typedef s6_accessrules_result_t s6_accessrules_backend_func (char const *, size_t, void const *, s6_accessrules_params_t *) ; typedef s6_accessrules_backend_func *s6_accessrules_backend_func_ref ; extern s6_accessrules_backend_func s6_accessrules_backend_fs ; extern s6_accessrules_backend_func s6_accessrules_backend_cdb ; typedef s6_accessrules_result_t s6_accessrules_keycheck_func (void const *, void const *, s6_accessrules_params_t *, s6_accessrules_backend_func_ref) ; typedef s6_accessrules_keycheck_func *s6_accessrules_keycheck_func_ref ; extern s6_accessrules_keycheck_func s6_accessrules_keycheck_uidgid ; extern s6_accessrules_keycheck_func s6_accessrules_keycheck_ip4 ; extern s6_accessrules_keycheck_func s6_accessrules_keycheck_ip6 ; extern s6_accessrules_keycheck_func s6_accessrules_keycheck_reversedns ; #define s6_accessrules_keycheck_ip46(key, data, params, f) (ip46_is6((ip46 const *)(key)) ? s6_accessrules_keycheck_ip6(((ip46 const *)(key))->ip, data, params, f) : s6_accessrules_keycheck_ip4(((ip46 const *)(key))->ip, data, params, f)) extern s6_accessrules_result_t s6_accessrules_uidgid_cdb (uid_t, gid_t, cdb const *, s6_accessrules_params_t *) ; extern s6_accessrules_result_t s6_accessrules_uidgid_fs (uid_t, gid_t, char const *, s6_accessrules_params_t *) ; #define s6_accessrules_ip4_cdb(ip4, c, params) s6_accessrules_keycheck_ip4(ip4, c, (params), &s6_accessrules_backend_cdb) #define s6_accessrules_ip4_fs(ip4, rulesdir, params) s6_accessrules_keycheck_ip4(ip4, rulesdir, (params), &s6_accessrules_backend_fs) #define s6_accessrules_ip6_cdb(ip6, c, params) s6_accessrules_keycheck_ip6(ip6, c, (params), &s6_accessrules_backend_cdb) #define s6_accessrules_ip6_fs(ip6, rulesdir, params) s6_accessrules_keycheck_ip6(ip6, rulesdir, (params), &s6_accessrules_backend_fs) #define s6_accessrules_ip46_cdb(ip, c, params) s6_accessrules_keycheck_ip46(ip, c, (params), &s6_accessrules_backend_cdb) #define s6_accessrules_ip46_fs(ip, rulesdir, params) s6_accessrules_keycheck_ip46(ip, rulesdir, (params), &s6_accessrules_backend_fs) #define s6_accessrules_reversedns_cdb(name, c, params) s6_accessrules_keycheck_reversedns(name, c, (params), &s6_accessrules_backend_cdb) #define s6_accessrules_reversedns_fs(name, c, params) s6_accessrules_keycheck_reversedns(name, c, (params), &s6_accessrules_backend_fs) #endif s6-2.13.1.0/src/include/s6/auto.h000066400000000000000000000020071470151141200161420ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_AUTO_H #define S6_AUTO_H #include #include #include typedef int s6_buffer_writer_func (buffer *, void *) ; typedef s6_buffer_writer_func *s6_buffer_writer_func_ref ; extern int s6_auto_write_logrun (char const *, char const *, char const *, unsigned int, unsigned int, uint64_t, uint64_t, char const *) ; extern int s6_auto_write_logrun_tmp (char const *, char const *, char const *, unsigned int, unsigned int, uint64_t, uint64_t, char const *, stralloc *) ; extern void s6_auto_write_logger (char const *, char const *, char const *, unsigned int, unsigned int, uint64_t, uint64_t, char const *, char const *, char const *) ; extern void s6_auto_write_logger_tmp (char const *, char const *, char const *, unsigned int, unsigned int, uint64_t, uint64_t, char const *, char const *, char const *, stralloc *sa) ; extern void s6_auto_write_service (char const *, unsigned int, s6_buffer_writer_func_ref, void *, char const *) ; #endif s6-2.13.1.0/src/include/s6/compat.h000066400000000000000000000005041470151141200164550ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_COMPAT_H #define S6_COMPAT_H #include #ifdef S6_USE_EXECLINE #include #define s6_el_semicolon(argv) el_semicolon(argv) #else extern int s6_compat_el_semicolon (char const **) ; #define s6_el_semicolon(argv) s6_compat_el_semicolon(argv) #endif #endif s6-2.13.1.0/src/include/s6/fdholder.h000066400000000000000000000067061470151141200167730ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_FDHOLDER_H #define S6_FDHOLDER_H #include #include #include #include #include #define S6_FDHOLDER_ID_SIZE 255 #define S6_FDHOLDER_MAX 256 typedef struct s6_fdholder_s s6_fdholder_t, *s6_fdholder_t_ref ; struct s6_fdholder_s { unixconnection connection ; } ; #define S6_FDHOLDER_ZERO { .connection = UNIXCONNECTION_ZERO } ; /* Starting and ending */ #define s6_fdholder_init(a, fd) unixconnection_init(&(a)->connection, fd, fd) #define s6_fdholder_free(a) unixconnection_free(&(a)->connection) extern int s6_fdholder_start (s6_fdholder_t *, char const *, tain const *, tain *) ; #define s6_fdholder_start_g(a, path, deadline) s6_fdholder_start(a, path, (deadline), &STAMP) extern void s6_fdholder_end (s6_fdholder_t *) ; /* Individual fds */ extern int s6_fdholder_store_async (s6_fdholder_t *, int, char const *, tain const *) ; extern int s6_fdholder_store (s6_fdholder_t *, int, char const *, tain const *, tain const *, tain *) ; #define s6_fdholder_store_g(a, fd, id, limit, deadline) s6_fdholder_store(a, fd, id, limit, (deadline), &STAMP) extern int s6_fdholder_delete_async (s6_fdholder_t *, char const *) ; extern int s6_fdholder_delete (s6_fdholder_t *, char const *, tain const *, tain *) ; #define s6_fdholder_delete_g(a, id, deadline) s6_fdholder_delete(a, id, (deadline), &STAMP) typedef struct s6_fdholder_retrieve_result_s s6_fdholder_retrieve_result_t, *s6_fdholder_retrieve_result_t_ref ; struct s6_fdholder_retrieve_result_s { int fd ; unsigned char err ; } ; extern int s6_fdholder_retrieve_maybe_delete_async (s6_fdholder_t *, char const *, int) ; extern unixmessage_handler_func s6_fdholder_retrieve_cb ; extern int s6_fdholder_retrieve_maybe_delete (s6_fdholder_t *, char const *, int, tain const *, tain *) ; #define s6_fdholder_retrieve_maybe_delete_g(a, id, h, deadline) s6_fdholder_retrieve_maybe_delete(a, id, h, (deadline), &STAMP) #define s6_fdholder_retrieve(a, id, deadline, stamp) s6_fdholder_retrieve_maybe_delete(a, id, 0, deadline, stamp) #define s6_fdholder_retrieve_g(a, id, deadline) s6_fdholder_retrieve(a, id, (deadline), &STAMP) #define s6_fdholder_retrieve_delete(a, id, deadline, stamp) s6_fdholder_retrieve_maybe_delete(a, id, 1, deadline, stamp) #define s6_fdholder_retrieve_delete_g(a, id, deadline) s6_fdholder_retrieve_delete(a, id, (deadline), &STAMP) typedef struct s6_fdholder_list_result_s s6_fdholder_list_result_t, *s6_fdholder_list_result_t_ref ; struct s6_fdholder_list_result_s { stralloc *sa ; unsigned int n ; unsigned char err ; } ; extern int s6_fdholder_list_async (s6_fdholder_t *) ; extern unixmessage_handler_func s6_fdholder_list_cb ; extern int s6_fdholder_list (s6_fdholder_t *, stralloc *, tain const *, tain *) ; #define s6_fdholder_list_g(a, sa, deadline) s6_fdholder_list(a, sa, (deadline), &STAMP) /* Dumps */ typedef struct s6_fdholder_fd_s s6_fdholder_fd_t, *s6_fdholder_fd_t_ref ; struct s6_fdholder_fd_s { char id[S6_FDHOLDER_ID_SIZE + 1] ; int fd ; tain limit ; } ; extern int s6_fdholder_getdump (s6_fdholder_t *, genalloc *, tain const *, tain *) ; #define s6_fdholder_getdump_g(a, g, deadline) s6_fdholder_getdump(a, g, (deadline), &STAMP) extern int s6_fdholder_setdump (s6_fdholder_t *, s6_fdholder_fd_t const *, unsigned int, tain const *, tain *) ; #define s6_fdholder_setdump_g(a, list, n, deadline) s6_fdholder_setdump(a, list, n, (deadline), &STAMP) #endif s6-2.13.1.0/src/include/s6/ftrigr.h000066400000000000000000000061041470151141200164710ustar00rootroot00000000000000/* ISC license. */ #ifndef FTRIGR_H #define FTRIGR_H #include #include #include #include #include #include #include #include #include /* Constants */ #define FTRIGR_IPCPATH SKALIBS_SPROOT "/run/service/ftrigrd/s" #define FTRIGRD_PROG S6_EXTBINPREFIX "s6-ftrigrd" #define FTRIGR_BANNER1 "ftrigr v1.0 (b)\n" #define FTRIGR_BANNER1_LEN (sizeof FTRIGR_BANNER1 - 1) #define FTRIGR_BANNER2 "ftrigr v1.0 (a)\n" #define FTRIGR_BANNER2_LEN (sizeof FTRIGR_BANNER2 - 1) /* Internals of the ftrigr_t */ typedef enum fr1state_e fr1state_t, *fr1state_t_ref ; enum fr1state_e { FR1STATE_WAITACK, FR1STATE_WAITACKDATA, FR1STATE_LISTENING, FR1STATE_ERROR } ; typedef struct ftrigr1_s ftrigr1_t, *ftrigr1_t_ref ; struct ftrigr1_s { uint32_t options ; fr1state_t state ; stralloc what ; } ; #define FTRIGR1_ZERO { .options = 0, .state = FR1STATE_ERROR, .what = STRALLOC_ZERO } extern ftrigr1_t const ftrigr1_zero ; /* The ftrigr_t itself */ typedef struct ftrigr_s ftrigr, ftrigr_t, *ftrigr_ref, *ftrigr_t_ref ; struct ftrigr_s { textclient connection ; genalloc list ; /* array of uint16_t */ size_t head ; gensetdyn data ; /* set of ftrigr1_t */ } ; #define FTRIGR_ZERO { .connection = TEXTCLIENT_ZERO, .list = GENALLOC_ZERO, .head = 0, .data = GENSETDYN_INIT(ftrigr1_t, 2, 0, 1) } extern ftrigr_t const ftrigr_zero ; /* Starting and ending a session */ extern int ftrigr_start (ftrigr_t *, char const *, tain const *, tain *) ; #define ftrigr_start_g(a, path, deadline) ftrigr_start(a, path, (deadline), &STAMP) extern int ftrigr_startf (ftrigr_t *, tain const *, tain *) ; #define ftrigr_startf_g(a, deadline) ftrigr_startf(a, (deadline), &STAMP) extern void ftrigr_end (ftrigr_t *) ; /* Instant primitives for async programming */ #define ftrigr_fd(a) textclient_fd(&(a)->connection) extern int ftrigr_updateb (ftrigr_t *) ; extern int ftrigr_update (ftrigr_t *) ; extern int ftrigr_check (ftrigr_t *, uint16_t, char *) ; extern int ftrigr_checksa (ftrigr_t *, uint16_t, stralloc *) ; extern void ftrigr_ack (ftrigr_t *, size_t) ; /* Synchronous functions with timeouts */ #define FTRIGR_REPEAT 0x0001 extern uint16_t ftrigr_subscribe (ftrigr_t *, char const *, char const *, uint32_t, tain const *, tain *) ; #define ftrigr_subscribe_g(a, path, re, options, deadline) ftrigr_subscribe(a, path, re, options, (deadline), &STAMP) extern int ftrigr_unsubscribe (ftrigr_t *, uint16_t, tain const *, tain *) ; #define ftrigr_unsubscribe_g(a, id, deadline) ftrigr_unsubscribe(a, id, (deadline), &STAMP) extern int ftrigr_wait_and (ftrigr_t *, uint16_t const *, unsigned int, tain const *, tain *) ; #define ftrigr_wait_and_g(a, list, len, deadline) ftrigr_wait_and(a, list, len, (deadline), &STAMP) extern int ftrigr_wait_or (ftrigr_t *, uint16_t const *, unsigned int, tain const *, tain *, char *) ; #define ftrigr_wait_or_g(a, list, len, deadline, what) ftrigr_wait_or(a, list, len, deadline, &STAMP, what) #endif s6-2.13.1.0/src/include/s6/ftrigw.h000066400000000000000000000007061470151141200165000ustar00rootroot00000000000000/* ISC license. */ #ifndef FTRIGW_H #define FTRIGW_H #include #include extern int ftrigw_fifodir_make (char const *, gid_t, int) ; extern int ftrigw_notify (char const *, char) ; extern int ftrigw_notifyb (char const *, char const *, size_t) ; extern int ftrigw_notifyb_nosig (char const *, char const *, size_t) ; #define ftrigw_notifys(f, s) ftrigw_notifyb(f, (s), strlen(s)) extern int ftrigw_clean (char const *) ; #endif s6-2.13.1.0/src/include/s6/lock.h000066400000000000000000000055421470151141200161310ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_LOCK_H #define S6_LOCK_H #include #include #include #include #include #include /* Constants */ #define S6LOCKD_PROG S6_EXTBINPREFIX "s6lockd" #define S6LOCKD_HELPER_PROG S6_LIBEXECPREFIX "s6lockd-helper" #define S6LOCK_BANNER1 "s6lock v1.0 (b)\n" #define S6LOCK_BANNER1_LEN (sizeof S6LOCK_BANNER1 - 1) #define S6LOCK_BANNER2 "s6lock v1.0 (a)\n" #define S6LOCK_BANNER2_LEN (sizeof S6LOCK_BANNER2 - 1) /* The client handle */ typedef struct s6lock_s s6lock_t, *s6lock_t_ref ; struct s6lock_s { textclient connection ; genalloc list ; /* array of uint16_t */ gensetdyn data ; /* set of unsigned char */ } ; #define S6LOCK_ZERO { .connection = TEXTCLIENT_ZERO, .list = GENALLOC_ZERO, .data = GENSETDYN_INIT(int, 2, 0, 1) } extern s6lock_t const s6lock_zero ; /* Starting and ending a session */ extern int s6lock_start (s6lock_t *, char const *, tain const *, tain *) ; #define s6lock_start_g(a, ipcpath, deadline) s6lock_start(a, ipcpath, (deadline), &STAMP) extern int s6lock_startf (s6lock_t *, char const *, tain const *, tain *) ; #define s6lock_startf_g(a, lockdir, deadline) s6lock_startf(a, lockdir, (deadline), &STAMP) extern void s6lock_end (s6lock_t *) ; /* Asynchronous primitives */ #define s6lock_fd(a) textclient_fd(&(a)->connection) extern int s6lock_update (s6lock_t *) ; extern int s6lock_check (s6lock_t *, uint16_t) ; /* Synchronous functions */ #define S6LOCK_OPTIONS_SH 0x0000U #define S6LOCK_OPTIONS_EX 0x0001U extern int s6lock_acquire (s6lock_t *, uint16_t *, char const *, uint32_t, tain const *, tain const *, tain *) ; #define s6lock_acquire_g(a, id, path, options, limit, deadline) s6lock_acquire(a, id, path, options, limit, (deadline), &STAMP) #define s6lock_acquire_sh(a, id, path, limit, deadline, stamp) s6lock_aquire(a, id, path, S6LOCK_OPTIONS_SH, limit, deadline, stamp) #define s6lock_acquire_ex(a, id, path, limit, deadline, stamp) s6lock_aquire(a, id, path, S6LOCK_OPTIONS_EX, limit, deadline, stamp) #define s6lock_acquire_sh_g(a, id, path, limit, deadline) s6lock_acquire_sh(a, id, path, limit, (deadline), &STAMP) #define s6lock_acquire_ex_g(a, id, path, limit, deadline) s6lock_acquire_ex(a, id, path, limit, (deadline), &STAMP) extern int s6lock_release (s6lock_t *, uint16_t, tain const *, tain *) ; #define s6lock_release_g(a, id, deadline) s6lock_release(a, id, (deadline), &STAMP) extern int s6lock_wait_and (s6lock_t *, uint16_t const *, unsigned int, tain const *, tain *) ; #define s6lock_wait_and_g(a, list, len, deadline) s6lock_wait_and(a, list, len, (deadline), &STAMP) extern int s6lock_wait_or (s6lock_t *, uint16_t const *, unsigned int, tain const *, tain *) ; #define s6lock_wait_or_g(a, list, len, deadline) s6lock_wait_or(a, list, len, (deadline), &STAMP) #endif s6-2.13.1.0/src/include/s6/s6.h000066400000000000000000000003341470151141200155230ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_H #define S6_H #include #include #include #include #include #include #include #endif s6-2.13.1.0/src/include/s6/servicedir.h000066400000000000000000000014471470151141200173400ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_SERVICEDIR_H #define S6_SERVICEDIR_H #include #include #define S6_SERVICEDIR_FILE_MAXLEN 16 #define S6_FILETYPE_NORMAL 0 #define S6_FILETYPE_EMPTY 1 #define S6_FILETYPE_UINT 2 #define S6_FILETYPE_DIR 3 #define S6_SVFILE_EXECUTABLE 0x01 #define S6_SVFILE_MANDATORY 0x02 #define S6_SVFILE_ATOMIC 0x04 typedef struct s6_servicedir_desc_s s6_servicedir_desc, *s6_servicedir_desc_ref ; struct s6_servicedir_desc_s { char const *name ; uint8_t type : 3 ; uint8_t options : 5 ; } ; extern s6_servicedir_desc const *const s6_servicedir_file_list ; extern int s6_servicedir_instances_recreate_offline (char const *, char const *) ; extern int s6_servicedir_instances_recreate_offline_tmp (char const *, char const *, stralloc *) ; #endif s6-2.13.1.0/src/include/s6/supervise.h000066400000000000000000000055471470151141200172330ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_SUPERVISE_H #define S6_SUPERVISE_H #include #include #include #define S6_SUPERVISE_CTLDIR "supervise" #define S6_SUPERVISE_EVENTDIR "event" #define S6_SVSCAN_CTLDIR ".s6-svscan" #define S6_SVSTATUS_FILENAME S6_SUPERVISE_CTLDIR "/status" #define S6_SVSTATUS_SIZE 43 #define S6_DTALLY_FILENAME S6_SUPERVISE_CTLDIR "/death_tally" #define S6_MAX_DEATH_TALLY 4096 extern int s6_svc_ok (char const *) ; extern int s6_svc_write (char const *, char const *, size_t) ; extern int s6_svc_writectl (char const *, char const *, char const *, size_t) ; typedef struct s6_svstatus_s s6_svstatus_t, *s6_svstatus_t_ref ; struct s6_svstatus_s { tain stamp ; tain readystamp ; pid_t pid ; pid_t pgid ; int wstat ; unsigned int flagpaused : 1 ; unsigned int flagfinishing : 1 ; unsigned int flagwantup : 1 ; unsigned int flagready : 1 ; } ; #define S6_SVSTATUS_ZERO \ { \ .stamp = TAIN_ZERO, \ .readystamp = TAIN_ZERO, \ .pid = 0, \ .pgid = 0, \ .wstat = 0, \ .flagpaused = 0, \ .flagfinishing = 0, \ .flagwantup = 1, \ .flagready = 1, \ } extern void s6_svstatus_pack (char *, s6_svstatus_t const *) ; extern void s6_svstatus_unpack (char const *, s6_svstatus_t *) ; extern int s6_svstatus_read (char const *, s6_svstatus_t *) ; extern int s6_svstatus_write (char const *, s6_svstatus_t const *) ; extern int s6_supervise_link (char const *, char const *const *, size_t, char const *, uint32_t, tain const *, tain *) ; #define s6_supervise_link_g(scdir, servicedirs, n, prefix, options, deadline) s6_supervise_link(scdir, servicedirs, n, prefix, options, (deadline), &STAMP) extern int s6_supervise_link_names (char const *, char const *const *, char const *const *, size_t, uint32_t, tain const *, tain *) ; #define s6_supervise_link_names_g(scdir, servicedirs, names, n, options, deadline) s6_supervise_link_names(scdir, servicedirs, names, n, options, (deadline), &STAMP) extern void s6_supervise_unlink (char const *, char const *, uint32_t) ; extern int s6_supervise_unlink_names (char const *, char const *const *, size_t, uint32_t, tain const *, tain *) ; #define s6_supervise_unlink_names_g(scdir, names, n, options, deadline) s6_supervise_unlink_names(scdir, names, n, options, (deadline), &STAMP) typedef struct s6_dtally_s s6_dtally_t, *s6_dtally_ref ; struct s6_dtally_s { tain stamp ; unsigned char exitcode ; unsigned char sig ; } ; #define S6_DTALLY_ZERO { .stamp = TAIN_ZERO, .exitcode = 0, .sig = 0 } #define S6_DTALLY_PACK (TAIN_PACK + 2) extern void s6_dtally_pack (char *, s6_dtally_t const *) ; extern void s6_dtally_unpack (char const *, s6_dtally_t *) ; extern ssize_t s6_dtally_read (char const *, s6_dtally_t *, size_t) ; extern int s6_dtally_write (char const *, s6_dtally_t const *, size_t) ; extern void s6_instance_chdirservice (char const *s) ; /* chdirs to s/instance */ #endif s6-2.13.1.0/src/instance/000077500000000000000000000000001470151141200146535ustar00rootroot00000000000000s6-2.13.1.0/src/instance/deps-exe/000077500000000000000000000000001470151141200163655ustar00rootroot00000000000000s6-2.13.1.0/src/instance/deps-exe/s6-instance-control000066400000000000000000000000121470151141200221110ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/instance/deps-exe/s6-instance-create000066400000000000000000000000431470151141200217000ustar00rootroot00000000000000${LIBS6} -lskarnet ${SYSCLOCK_LIB} s6-2.13.1.0/src/instance/deps-exe/s6-instance-delete000066400000000000000000000000431470151141200216770ustar00rootroot00000000000000${LIBS6} -lskarnet ${SYSCLOCK_LIB} s6-2.13.1.0/src/instance/deps-exe/s6-instance-list000066400000000000000000000000231470151141200214060ustar00rootroot00000000000000${LIBS6} -lskarnet s6-2.13.1.0/src/instance/deps-exe/s6-instance-maker000066400000000000000000000000341470151141200215340ustar00rootroot00000000000000libs6auto.a.xyzzy -lskarnet s6-2.13.1.0/src/instance/deps-exe/s6-instance-status000066400000000000000000000000121470151141200217540ustar00rootroot00000000000000-lskarnet s6-2.13.1.0/src/instance/s6-instance-control.c000066400000000000000000000040151470151141200206270ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-instance-control [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -abqhkti12pcyroduDUxO ] service instancename" #define dieusage() strerr_dieusage(100, USAGE) #define DATASIZE 63 int main (int argc, char const **argv) { char const **fullargv = argv ; size_t namelen ; PROG = "s6-instance-control" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int datalen = 1 ; unsigned int timeout = 0 ; for (;;) { int opt = subgetopt_r(argc, argv, "abqhkti12pcyroduDUxOT:w:", &l) ; if (opt == -1) break ; switch (opt) { case 'a' : case 'b' : case 'q' : case 'h' : case 'k' : case 't' : case 'i' : case '1' : case '2' : case 'p' : case 'c' : case 'y' : case 'r' : case 'o' : case 'd' : case 'u' : case 'D' : case 'U' : case 'x' : case 'O' : if (datalen++ >= DATASIZE) strerr_dief1x(100, "too many commands") ; break ; case 'T' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ; case 'w' : if (!memchr("dDuUrR", l.arg[0], 6)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 2) dieusage() ; namelen = strlen(argv[1]) ; if (!argv[0][0]) strerr_dief1x(100, "invalid service name") ; if (!argv[1][0] || argv[1][0] == '.' || byte_in(argv[1], namelen, " \t\f\r\n", 5) < namelen) strerr_dief1x(100, "invalid instance name") ; { size_t svlen = strlen(argv[0]) ; char fn[svlen + 11 + namelen] ; memcpy(fn, argv[0], svlen) ; memcpy(fn + svlen, "/instance/", 10) ; memcpy(fn + svlen + 10, argv[1], namelen + 1) ; argv[0] = fn ; argv[1] = 0 ; fullargv[0] = S6_BINPREFIX "s6-svc" ; xexec(fullargv) ; } } s6-2.13.1.0/src/instance/s6-instance-create.c000066400000000000000000000055021470151141200204140ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-instance-create [ -d | -D ] [ -f ] [ -P ] [ -t timeout ] service instancename" #define dieusage() strerr_dieusage(100, USAGE) static void cleanup (char const *s) { int e = errno ; rm_rf(s) ; errno = e ; } int main (int argc, char const *const *argv) { tain tto = TAIN_INFINITE_RELATIVE ; size_t namelen ; uint32_t options = 16 ; PROG = "s6-instance-create" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "dDfPt:", &l) ; if (opt == -1) break ; switch (opt) { case 'd' : options |= 12 ; break ; case 'D' : options |= 4 ; options &= ~8U ; break ; case 'f' : options |= 1 ; break ; case 'P' : options |= 2 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&tto, t) ; } if (argc < 2) dieusage() ; namelen = strlen(argv[1]) ; if (!argv[1][0] || argv[1][0] == '.' || byte_in(argv[1], namelen, " \t\f\r\n", 5) < namelen) strerr_dief1x(100, "invalid instance name") ; { mode_t m = umask(0) ; size_t svlen = strlen(argv[0]) ; char fn[svlen + 11] ; memcpy(fn, argv[0], svlen) ; memcpy(fn + svlen, "/instances", 11) ; if (mkdir(fn, 0755) == -1 && errno != EEXIST) strerr_diefu2sys(111, "mkdir ", fn) ; fn[svlen + 9] = 0 ; if (mkdir(fn, 0755) == -1 && errno != EEXIST) strerr_diefu2sys(111, "mkdir ", fn) ; umask(m) ; } s6_instance_chdirservice(argv[0]) ; tain_now_set_stopwatch_g() ; tain_add_g(&tto, &tto) ; { char sv[namelen + 14] ; char const *p = sv ; memcpy(sv, "../instances/", 13) ; memcpy(sv + 13, argv[1], namelen + 1) ; { struct stat st ; if (stat(sv, &st) == 0) if (!S_ISDIR(st.st_mode)) strerr_dief3x(100, "unexpected file preventing instance creation at ", argv[0], sv+2) ; else strerr_dief3x(100, "instance appears to already exist at ", argv[0], sv+2) ; else if (errno != ENOENT) strerr_diefu3sys(111, "stat ", argv[0], sv+2) ; } if (!hiercopy("../template", sv)) { cleanup(sv) ; strerr_diefu5sys(111, "copy ", argv[0], "/template to ", argv[0], sv+2) ; } if (s6_supervise_link_names_g(".", &p, argv + 1, 1, options, &tto) == -1) { cleanup(sv) ; strerr_diefu4sys(errno == ETIMEDOUT ? 99 : 111, "create instance of ", argv[0], " named ", argv[1]) ; } } return 0 ; } s6-2.13.1.0/src/instance/s6-instance-delete.c000066400000000000000000000030751470151141200204160ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #define USAGE "s6-instance-delete [ -X ] [ -t timeout ] service instancename" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { tain tto = TAIN_INFINITE_RELATIVE ; size_t namelen ; uint32_t options = 1 ; PROG = "s6-instance-delete" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "Xt:", &l) ; if (opt == -1) break ; switch (opt) { case 'X' : options &= ~1U ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&tto, t) ; } if (argc < 2) dieusage() ; namelen = strlen(argv[1]) ; if (!argv[1][0] || argv[1][0] == '.' || byte_in(argv[1], namelen, " \t\f\r\n", 5) < namelen) strerr_dief1x(100, "invalid instance name") ; s6_instance_chdirservice(argv[0]) ; tain_now_set_stopwatch_g() ; tain_add_g(&tto, &tto) ; if (s6_supervise_unlink_names_g(".", argv + 1, 1, options, &tto) == -1) strerr_diefu4sys(111, "prepare deletion of instance ", argv[1], " of service ", argv[0]) ; { char fn[14 + namelen] ; memcpy(fn, "../instances/", 13) ; memcpy(fn + 13, argv[1], namelen + 1) ; rm_rf(fn) ; } return 0 ; } s6-2.13.1.0/src/instance/s6-instance-list.c000066400000000000000000000022231470151141200201210ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-instance-list service" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { DIR *dir ; PROG = "s6-instance-list" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "", &l) ; if (opt == -1) break ; switch (opt) { default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; s6_instance_chdirservice(argv[0]) ; dir = opendir(".") ; for (;;) { direntry *d ; errno = 0 ; d = readdir(dir) ; if (!d) break ; if (d->d_name[0] == '.') continue ; if (buffer_puts(buffer_1, d->d_name) < 0 || buffer_put(buffer_1, "\n", 1) < 0) strerr_diefu1sys(111, "write to stdout") ; } if (errno) strerr_diefu3sys(111, "readdir ", argv[0], "/instance") ; dir_close(dir) ; if (!buffer_flush(buffer_1)) strerr_diefu1sys(111, "write to stdout") ; return 0 ; } s6-2.13.1.0/src/instance/s6-instance-maker.c000066400000000000000000000140011470151141200202420ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-instance-maker [ -c max ] [ -u user ] [ -l loguser ] [ -L logdir ] [ -t stamptype ] [ -n nfiles ] [ -s filesize ] [ -S maxsize ] [ -P prefix ] [ -r service[/logger[/pipeline]] ] template dir" #define dieusage() strerr_dieusage(100, USAGE) typedef struct svinfo_s svinfo, *svinfo_ref ; struct svinfo_s { char const *user ; unsigned int maxinstances ; } ; static stralloc sa = STRALLOC_ZERO ; static int write_run (buffer *b, void *data) { svinfo *t = data ; size_t l ; char fmt[UINT_FMT] ; l = uint_fmt(fmt, t->maxinstances) ; if (buffer_puts(b, "#!" EXECLINE_SHEBANGPREFIX "execlineb -P\n\n" EXECLINE_EXTBINPREFIX "fdmove -c 2 1\n") < 0) return 0 ; if (t->user[0]) { if (!string_quote(&sa, t->user, strlen(t->user))) return 0 ; if (buffer_puts(b, S6_EXTBINPREFIX "s6-setuidgid ") < 0 || buffer_put(b, sa.s, sa.len) < 0 || buffer_put(b, "\n", 1) < 0) goto err ; sa.len = 0 ; } if (buffer_puts(b, S6_EXTBINPREFIX "s6-svscan -d3 -c") < 0 || buffer_put(b, fmt, l) < 0 || buffer_putsflush(b, " -- instance\n") < 0) return 0 ; return 1 ; err: sa.len = 0 ; return 0 ; } static void write_service (char const *dir, char const *template, char const *user, unsigned int maxinstances, char const *logger) { svinfo data = { .user = user, .maxinstances = maxinstances } ; size_t dirlen = strlen(dir) ; char fn[dirlen + 11] ; memcpy(fn, dir, dirlen) ; s6_auto_write_service(dir, 3, &write_run, &data, logger) ; if (!logger) { mode_t m = umask(0) ; memcpy(fn + dirlen, "/instance", 10) ; if (mkdir(fn, 0755) == -1) strerr_diefu2sys(111, "mkdir ", fn) ; memcpy(fn + dirlen + 9, "s", 2) ; if (mkdir(fn, 0755) == -1) strerr_diefu2sys(111, "mkdir ", fn) ; umask(m) ; } memcpy(fn + dirlen, "/template", 10) ; if (!hiercopy_tmp(template, fn, &sa)) strerr_diefu4sys(111, "copy file hierarchy from ", template, " to ", fn) ; } int main (int argc, char const *const *argv) { char const *user = "" ; char const *loguser = 0 ; char const *logdir = 0 ; unsigned int maxinstances = 500 ; unsigned int stamptype = 1 ; unsigned int nfiles = 10 ; uint64_t filesize = 1000000 ; uint64_t maxsize = 0 ; char const *prefix = 0 ; char *rcinfo[3] = { 0, 0, 0 } ; size_t dirlen ; PROG = "s6-instance-maker" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "u:l:L:c:t:n:s:S:P:r:", &l) ; if (opt == -1) break ; switch (opt) { case 'u' : user = l.arg ; break ; case 'l' : loguser = l.arg ; break ; case 'L' : logdir = l.arg ; break ; case 'c' : if (!uint0_scan(l.arg, &maxinstances)) dieusage() ; break ; case 't' : if (!uint0_scan(l.arg, &stamptype)) dieusage() ; break ; case 'n' : if (!uint0_scan(l.arg, &nfiles)) dieusage() ; break ; case 's' : if (!uint640_scan(l.arg, &filesize)) dieusage() ; break ; case 'S' : if (!uint640_scan(l.arg, &maxsize)) dieusage() ; break ; case 'P' : prefix = l.arg ; break ; case 'r' : rcinfo[0] = (char *)l.arg ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 2) dieusage() ; if (logdir && logdir[0] != '/') strerr_dief1x(100, "logdir must be absolute") ; if (stamptype > 3) strerr_dief1x(100, "stamptype must be 0, 1, 2 or 3") ; if (strchr(user, ' ') || strchr(user, '\t') || strchr(user, '\n')) strerr_dief1x(100, "invalid user") ; if (maxinstances < 1) maxinstances = 1 ; if (maxinstances > 90000) maxinstances = 90000 ; if (rcinfo[0]) { if (strchr(rcinfo[0], '\n')) strerr_dief2x(100, "newlines", " are forbidden in s6-rc names") ; if (rcinfo[0][0] == '/') strerr_dief2x(100, "service", " name cannot be empty") ; rcinfo[1] = strchr(rcinfo[0], '/') ; if (rcinfo[1]) { *rcinfo[1]++ = 0 ; if (!rcinfo[1][0]) strerr_dief1x(100, "argument to -r must be: service or service/logger or service/logger/pipeline") ; if (rcinfo[1][0] == '/') strerr_dief2x(100, "logger", " name cannot be empty") ; if (!logdir) strerr_dief1x(100, "logger specifiec (-r) but logdir not specified (-L)") ; rcinfo[2] = strchr(rcinfo[1], '/') ; if (rcinfo[2]) { *rcinfo[2]++ = 0 ; if (!rcinfo[2][0]) strerr_dief2x(100, "pipeline", " name cannot be empty") ; if (strchr(rcinfo[2], '/')) strerr_dief2x(100, "slashes", " are forbidden in s6-rc names") ; } } else if (logdir) strerr_dief1x(100, "logdir specified (-L) but logger name not specified (-r)") ; } dirlen = strlen(argv[1]) ; if (rcinfo[0]) { mode_t m = umask(0) ; size_t svclen = strlen(rcinfo[0]) ; size_t loglen = rcinfo[1] ? strlen(rcinfo[1]) : 0 ; char dir[dirlen + 2 + (svclen > loglen ? svclen : loglen)] ; memcpy(dir, argv[1], dirlen) ; dir[dirlen] = '/' ; if (mkdir(argv[1], 0755) < 0 && errno != EEXIST) strerr_diefu2sys(111, "mkdir ", argv[1]) ; umask(m) ; memcpy(dir + dirlen + 1, rcinfo[0], svclen + 1) ; write_service(dir, argv[0], user, maxinstances, rcinfo[1] ? rcinfo[1] : "") ; if (rcinfo[1]) { memcpy(dir + dirlen + 1, rcinfo[1], loglen + 1) ; s6_auto_write_logger_tmp(dir, loguser, logdir, stamptype, nfiles, filesize, maxsize, prefix, rcinfo[0], rcinfo[2], &sa) ; } } else { write_service(argv[1], argv[0], user, maxinstances, 0) ; if (logdir) { char dir[dirlen + 5] ; memcpy(dir, argv[1], dirlen) ; memcpy(dir + dirlen, "/log", 5) ; s6_auto_write_logger_tmp(dir, loguser, logdir, stamptype, nfiles, filesize, maxsize, prefix, 0, 0, &sa) ; } } return 0 ; } s6-2.13.1.0/src/instance/s6-instance-status.c000066400000000000000000000050261470151141200204750ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #define USAGE "s6-instance-status [ -uwNrpest | -o up,wantedup,normallyup,ready,paused,pid,exitcode,signal,signum,updownsince,readysince,updownfor,readyfor ] [ -n ] service name" #define dieusage() strerr_dieusage(100, USAGE) #define MAXFIELDS 16 #define checkfields() if (n++ >= MAXFIELDS) strerr_dief1x(100, "too many option fields") static unsigned int check_options (char const *arg, unsigned int n) { static char const *table[] = { "up", "wantedup", "normallyup", "ready", "paused", "pid", "exitcode", "signal", "signum", "updownsince", "readysince", "updownfor", "readyfor", 0 } ; while (*arg) { size_t pos = str_chr(arg, ',') ; char const *const *p = table ; if (!pos) strerr_dief1x(100, "invalid null option field") ; for (; *p ; p++) if (!strncmp(arg, *p, pos)) break ; if (!p) { char blah[pos+1] ; memcpy(blah, arg, pos) ; blah[pos] = 0 ; strerr_dief2x(100, "invalid option field: ", blah) ; } checkfields() ; arg += pos ; if (*arg) arg++ ; } return n ; } int main (int argc, char const **argv) { char const **fullargv = argv ; size_t namelen ; unsigned int n = 0 ; PROG = "s6-instance-status" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "no:uwNrpest", &l) ; if (opt == -1) break ; switch (opt) { case 'n' : break ; case 'o' : n = check_options(l.arg, n) ; break ; case 'u' : case 'w' : case 'N' : case 'r' : case 'p' : case 'e' : case 's' : case 't' : if (n++ >= MAXFIELDS) strerr_dief1x(100, "too many option fields") ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 2) dieusage() ; namelen = strlen(argv[1]) ; if (!argv[0][0]) strerr_dief1x(100, "invalid service name") ; if (!argv[1][0] || argv[1][0] == '.' || byte_in(argv[1], namelen, " \t\f\r\n", 5) < namelen) strerr_dief1x(100, "invalid instance name") ; { size_t svlen = strlen(argv[0]) ; char fn[svlen + 11 + namelen] ; memcpy(fn, argv[0], svlen) ; memcpy(fn + svlen, "/instance/", 10) ; memcpy(fn + svlen + 10, argv[1], namelen + 1) ; argv[0] = fn ; argv[1] = 0 ; fullargv[0] = S6_BINPREFIX "s6-svstat" ; xexec(fullargv) ; } } s6-2.13.1.0/src/libs6/000077500000000000000000000000001470151141200140665ustar00rootroot00000000000000s6-2.13.1.0/src/libs6/deps-exe/000077500000000000000000000000001470151141200156005ustar00rootroot00000000000000s6-2.13.1.0/src/libs6/deps-exe/s6-ftrigrd000077500000000000000000000001041470151141200175100ustar00rootroot00000000000000ftrig1_free.o ftrig1_make.o -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-2.13.1.0/src/libs6/deps-exe/s6lockd000077500000000000000000000000651470151141200170740ustar00rootroot00000000000000-lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/libs6/deps-exe/s6lockd-helper000077500000000000000000000000351470151141200203460ustar00rootroot00000000000000libs6lockd.a.xyzzy -lskarnet s6-2.13.1.0/src/libs6/deps-lib/000077500000000000000000000000001470151141200155655ustar00rootroot00000000000000s6-2.13.1.0/src/libs6/deps-lib/s6000066400000000000000000000025431470151141200160440ustar00rootroot00000000000000ftrigr1_zero.o ftrigr_check.o ftrigr_checksa.o ftrigr_ack.o ftrigr_end.o ftrigr_start.o ftrigr_startf.o ftrigr_subscribe.o ftrigr_unsubscribe.o ftrigr_update.o ftrigr_updateb.o ftrigr_wait_and.o ftrigr_wait_or.o ftrigr_zero.o ftrigw_clean.o ftrigw_fifodir_make.o ftrigw_notify.o ftrigw_notifyb.o ftrigw_notifyb_nosig.o s6_accessrules_backend_cdb.o s6_accessrules_backend_fs.o s6_accessrules_keycheck_ip4.o s6_accessrules_keycheck_ip6.o s6_accessrules_keycheck_reversedns.o s6_accessrules_keycheck_uidgid.o s6_accessrules_params_free.o s6_accessrules_uidgid_cdb.o s6_accessrules_uidgid_fs.o s6_compat_el_semicolon.o s6_dtally_pack.o s6_dtally_unpack.o s6_dtally_read.o s6_dtally_write.o s6_instance_chdirservice.o s6_servicedir_file_list.o s6_servicedir_instances_recreate_offline.o s6_servicedir_instances_recreate_offline_tmp.o s6_svc_ok.o s6_svc_write.o s6_svc_writectl.o s6_svstatus_pack.o s6_svstatus_read.o s6_svstatus_unpack.o s6_svstatus_write.o s6_fdholder_delete.o s6_fdholder_delete_async.o s6_fdholder_end.o s6_fdholder_getdump.o s6_fdholder_list.o s6_fdholder_list_async.o s6_fdholder_list_cb.o s6_fdholder_retrieve.o s6_fdholder_retrieve_async.o s6_fdholder_retrieve_cb.o s6_fdholder_setdump.o s6_fdholder_start.o s6_fdholder_store.o s6_fdholder_store_async.o s6_supervise_link.o s6_supervise_link_names.o s6_supervise_unlink.o s6_supervise_unlink_names.o -lskarnet s6-2.13.1.0/src/libs6/deps-lib/s6auto000066400000000000000000000001741470151141200167330ustar00rootroot00000000000000s6_auto_write_logger.o s6_auto_write_logger_tmp.o s6_auto_write_logrun.o s6_auto_write_logrun_tmp.o s6_auto_write_service.o s6-2.13.1.0/src/libs6/ftrig1.h000066400000000000000000000007021470151141200154320ustar00rootroot00000000000000/* ISC license. */ #ifndef FTRIG1_H #define FTRIG1_H #include #define FTRIG1_PREFIX "ftrig1" #define FTRIG1_PREFIXLEN (sizeof FTRIG1_PREFIX - 1) typedef struct ftrig1_s ftrig1_t, *ftrig1_t_ref ; struct ftrig1_s { int fd ; int fdw ; stralloc name ; } ; #define FTRIG1_ZERO { .fd = -1, .fdw = -1, .name = STRALLOC_ZERO } extern int ftrig1_make (ftrig1_t *, char const *) ; extern void ftrig1_free (ftrig1_t *) ; #endif s6-2.13.1.0/src/libs6/ftrig1_free.c000066400000000000000000000005761470151141200164370ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include "ftrig1.h" void ftrig1_free (ftrig1_t *p) { if (p->name.s) { unlink_void(p->name.s) ; stralloc_free(&p->name) ; } if (p->fd >= 0) { fd_close(p->fd) ; p->fd = -1 ; } if (p->fdw >= 0) { fd_close(p->fdw) ; p->fdw = -1 ; } } s6-2.13.1.0/src/libs6/ftrig1_make.c000066400000000000000000000023231470151141200164230ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include "ftrig1.h" int ftrig1_make (ftrig1_t *f, char const *path) { ftrig1_t ff = FTRIG1_ZERO ; size_t pathlen = strlen(path) ; char tmp[pathlen + FTRIG1_PREFIXLEN + 36] ; memcpy(tmp, path, pathlen) ; tmp[pathlen] = '/' ; tmp[pathlen+1] = '.' ; memcpy(tmp + pathlen + 2, FTRIG1_PREFIX, FTRIG1_PREFIXLEN) ; tmp[pathlen + 2 + FTRIG1_PREFIXLEN] = ':' ; if (!timestamp(tmp + pathlen + 3 + FTRIG1_PREFIXLEN)) return 0 ; memcpy(tmp + pathlen + FTRIG1_PREFIXLEN + 28, ":XXXXXX", 8) ; ff.fd = mkptemp3(tmp, 0622, O_NONBLOCK|O_CLOEXEC) ; if (ff.fd == -1) return 0 ; ff.fdw = open_write(tmp) ; if (ff.fdw == -1) goto err1 ; if (!stralloc_ready(&ff.name, pathlen + FTRIG1_PREFIXLEN + 36)) goto err2 ; stralloc_copyb(&ff.name, tmp, pathlen + 1) ; stralloc_catb(&ff.name, tmp + pathlen + 2, FTRIG1_PREFIXLEN + 34) ; if (rename(tmp, ff.name.s) == -1) goto err3 ; *f = ff ; return 1 ; err3: stralloc_free(&ff.name) ; err2: fd_close(ff.fdw) ; err1: fd_close(ff.fd) ; unlink_void(tmp) ; return 0 ; } s6-2.13.1.0/src/libs6/ftrigr1_zero.c000066400000000000000000000001321470151141200166430ustar00rootroot00000000000000/* ISC license. */ #include ftrigr1_t const ftrigr1_zero = FTRIGR1_ZERO ; s6-2.13.1.0/src/libs6/ftrigr_ack.c000066400000000000000000000005171470151141200163500ustar00rootroot00000000000000/* ISC license. */ #include #include #include void ftrigr_ack (ftrigr_t *a, size_t n) { size_t len = genalloc_len(uint16_t, &a->list) ; a->head += n ; if (a->head > len) a->head = len ; if (a->head == len) { a->head = 0 ; genalloc_setlen(uint16_t, &a->list, 0) ; } } s6-2.13.1.0/src/libs6/ftrigr_check.c000066400000000000000000000005371470151141200166710ustar00rootroot00000000000000/* ISC license. */ #include #include #include int ftrigr_check (ftrigr_t *a, uint16_t id, char *c) { stralloc sa = STRALLOC_ZERO ; int r = ftrigr_checksa(a, id, &sa) ; if (r && sa.len) { int e = errno ; *c = sa.s[sa.len - 1] ; stralloc_free(&sa) ; errno = e ; } return r ; } s6-2.13.1.0/src/libs6/ftrigr_checksa.c000066400000000000000000000021361470151141200172120ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include int ftrigr_checksa (ftrigr_t *a, uint16_t id, stralloc *sa) { ftrigr1_t *p ; if (!id--) return (errno = EINVAL, -1) ; p = GENSETDYN_P(ftrigr1_t, &a->data, id) ; if (!p) return (errno = EINVAL, -1) ; switch (p->state) { case FR1STATE_WAITACKDATA : { if (!stralloc_catb(sa, p->what.s, p->what.len)) return -1 ; stralloc_free(&p->what) ; *p = ftrigr1_zero ; gensetdyn_delete(&a->data, id) ; return 1 ; } case FR1STATE_LISTENING : { if (!p->what.len) break ; if (!stralloc_catb(sa, p->what.s, p->what.len)) return -1 ; p->what.len = 0 ; return 1 ; } case FR1STATE_WAITACK : { int e ; if (!stralloc_catb(sa, p->what.s, p->what.len - 1)) return -1 ; e = p->what.s[p->what.len - 1] ; stralloc_free(&p->what) ; *p = ftrigr1_zero ; gensetdyn_delete(&a->data, id) ; errno = e ; return -1 ; } default: return (errno = EINVAL, -1) ; } return 0 ; } s6-2.13.1.0/src/libs6/ftrigr_end.c000066400000000000000000000004731470151141200163610ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include void ftrigr_end (ftrigr_ref a) { gensetdyn_free(&a->data) ; genalloc_free(uint16_t, &a->list) ; textclient_end(&a->connection) ; *a = ftrigr_zero ; } s6-2.13.1.0/src/libs6/ftrigr_start.c000066400000000000000000000004621470151141200167460ustar00rootroot00000000000000/* ISC license. */ #include #include int ftrigr_start (ftrigr_t *a, char const *path, tain const *deadline, tain *stamp) { return textclient_start(&a->connection, path, 0, FTRIGR_BANNER1, FTRIGR_BANNER1_LEN, FTRIGR_BANNER2, FTRIGR_BANNER2_LEN, deadline, stamp) ; } s6-2.13.1.0/src/libs6/ftrigr_startf.c000066400000000000000000000006541470151141200171170ustar00rootroot00000000000000/* ISC license. */ #include #include int ftrigr_startf (ftrigr_t *a, tain const *deadline, tain *stamp) { static char const *const cargv[2] = { FTRIGRD_PROG, 0 } ; static char const *const cenvp[1] = { 0 } ; return textclient_startf(&a->connection, cargv, cenvp, TEXTCLIENT_OPTION_WAITPID, FTRIGR_BANNER1, FTRIGR_BANNER1_LEN, FTRIGR_BANNER2, FTRIGR_BANNER2_LEN, deadline, stamp) ; } s6-2.13.1.0/src/libs6/ftrigr_subscribe.c000066400000000000000000000024751470151141200176000ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include uint16_t ftrigr_subscribe (ftrigr_t *a, char const *path, char const *re, uint32_t options, tain const *deadline, tain *stamp) { size_t pathlen = strlen(path) ; size_t relen = strlen(re) ; ftrigr1_t ft = { .options = options, .state = FR1STATE_LISTENING, .what = STRALLOC_ZERO } ; uint32_t i ; char tmp[15] = "--L" ; struct iovec v[3] = { { .iov_base = tmp, .iov_len = 15 }, { .iov_base = (char *)path, .iov_len = pathlen + 1 }, { .iov_base = (char *)re, .iov_len = relen + 1 } } ; if (!gensetdyn_new(&a->data, &i)) return 0 ; if (i >= UINT16_MAX) { gensetdyn_delete(&a->data, i) ; return (errno = EMFILE, 0) ; } uint16_pack_big(tmp, (uint16_t)i) ; uint32_pack_big(tmp+3, options) ; uint32_pack_big(tmp+7, (uint32_t)pathlen) ; uint32_pack_big(tmp+11, (uint32_t)relen) ; if (!textclient_commandv(&a->connection, v, 3, deadline, stamp)) { int e = errno ; gensetdyn_delete(&a->data, i) ; errno = e ; return 0 ; } *GENSETDYN_P(ftrigr1_t, &a->data, i) = ft ; return (uint16_t)(i+1) ; } s6-2.13.1.0/src/libs6/ftrigr_unsubscribe.c000066400000000000000000000015011470151141200201300ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include int ftrigr_unsubscribe (ftrigr_t *a, uint16_t i, tain const *deadline, tain *stamp) { ftrigr1_t *p ; char pack[3] = "--U" ; if (!i--) return (errno = EINVAL, 0) ; p = GENSETDYN_P(ftrigr1_t, &a->data, i) ; if (!p) return (errno = EINVAL, 0) ; switch (p->state) { case FR1STATE_WAITACK : case FR1STATE_WAITACKDATA : { char dummy ; ftrigr_check(a, i+1, &dummy) ; return 1 ; } default : break ; } uint16_pack_big(pack, i) ; if (!textclient_command(&a->connection, pack, 3, deadline, stamp)) return 0 ; stralloc_free(&p->what) ; *p = ftrigr1_zero ; return gensetdyn_delete(&a->data, i) ; } s6-2.13.1.0/src/libs6/ftrigr_update.c000066400000000000000000000003121470151141200170650ustar00rootroot00000000000000/* ISC license. */ #include #include #include int ftrigr_update (ftrigr_t *a) { genalloc_setlen(uint16_t, &a->list, 0) ; return ftrigr_updateb(a) ; } s6-2.13.1.0/src/libs6/ftrigr_updateb.c000066400000000000000000000034451470151141200172410ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include static inline int appears (uint16_t, uint16_t const *, size_t) gccattr_pure ; static inline int appears (uint16_t id, uint16_t const *list, size_t len) { while (len) if (id == list[--len]) return 1 ; return 0 ; } static int msghandler (struct iovec const *v, void *context) { ftrigr_t *a = (ftrigr_t *)context ; ftrigr1_t *p ; int addit = 1 ; char const *s = v->iov_base ; uint16_t id ; if (v->iov_len != 4) return (errno = EPROTO, 0) ; uint16_unpack_big(s, &id) ; p = GENSETDYN_P(ftrigr1_t, &a->data, id) ; if (!p) return 1 ; if (p->state != FR1STATE_LISTENING) return (errno = EINVAL, 0) ; if (!genalloc_readyplus(uint16_t, &a->list, 1)) return 0 ; switch (s[2]) { case 'd' : if (!stralloc_catb(&p->what, s + 3, 1)) return 0 ; p->state = FR1STATE_WAITACK ; break ; case '!' : if (!stralloc_catb(&p->what, s + 3, 1)) return 0 ; if (p->options & FTRIGR_REPEAT) { if (p->what.len > 1 && appears(id+1, genalloc_s(uint16_t, &a->list), genalloc_len(uint16_t, &a->list))) addit = 0 ; } else p->state = FR1STATE_WAITACKDATA ; break ; default : return (errno = EPROTO, 0) ; } if (addit) { id++ ; genalloc_append(uint16_t, &a->list, &id) ; } return 1 ; } int ftrigr_updateb (ftrigr_t *a) { size_t curlen = genalloc_len(uint16_t, &a->list) ; int r = textclient_update(&a->connection, &msghandler, a) ; return r < 0 ? r : (int)(genalloc_len(uint16_t, &a->list) - curlen) ; } s6-2.13.1.0/src/libs6/ftrigr_wait_and.c000066400000000000000000000012111470151141200173700ustar00rootroot00000000000000/* ISC license. */ #include #include #include int ftrigr_wait_and (ftrigr_t *a, uint16_t const *idlist, unsigned int n, tain const *deadline, tain *stamp) { iopause_fd x = { -1, IOPAUSE_READ, 0 } ; x.fd = ftrigr_fd(a) ; for (; n ; n--, idlist++) { for (;;) { char dummy ; int r = ftrigr_check(a, *idlist, &dummy) ; if (r < 0) return r ; else if (r) break ; r = iopause_stamp(&x, 1, deadline, stamp) ; if (r < 0) return r ; else if (!r) return (errno = ETIMEDOUT, -1) ; else if (ftrigr_updateb(a) < 0) return -1 ; } } return 1 ; } s6-2.13.1.0/src/libs6/ftrigr_wait_or.c000066400000000000000000000014061470151141200172540ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include int ftrigr_wait_or (ftrigr_t *a, uint16_t const *idlist, unsigned int n, tain const *deadline, tain *stamp, char *c) { iopause_fd x = { -1, IOPAUSE_READ | IOPAUSE_EXCEPT, 0 } ; x.fd = ftrigr_fd(a) ; if (x.fd < 0) return -1 ; for (;;) { unsigned int i = 0 ; int r ; for (; i < n ; i++) { r = ftrigr_check(a, idlist[i], c) ; if (r < 0) return r ; else if (r) return i ; } r = iopause_stamp(&x, 1, deadline, stamp) ; if (r < 0) return 0 ; else if (!r) return (errno = ETIMEDOUT, -1) ; else if (ftrigr_update(a) < 0) return -1 ; } return (errno = EPROTO, -1) ; /* can't happen */ } s6-2.13.1.0/src/libs6/ftrigr_zero.c000066400000000000000000000001271470151141200165660ustar00rootroot00000000000000/* ISC license. */ #include ftrigr_t const ftrigr_zero = FTRIGR_ZERO ; s6-2.13.1.0/src/libs6/ftrigw_clean.c000066400000000000000000000017721470151141200167050ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include "ftrig1.h" #include int ftrigw_clean (char const *path) { size_t pathlen = strlen(path) ; int e = 0 ; DIR *dir = opendir(path) ; if (!dir) return 0 ; { char tmp[pathlen + FTRIG1_PREFIXLEN + 35] ; memcpy(tmp, path, pathlen) ; tmp[pathlen] = '/' ; tmp[pathlen + FTRIG1_PREFIXLEN + 34] = 0 ; for (;;) { direntry *d ; int fd ; errno = 0 ; d = readdir(dir) ; if (!d) break ; if (strncmp(d->d_name, FTRIG1_PREFIX, FTRIG1_PREFIXLEN)) continue ; if (strlen(d->d_name) != FTRIG1_PREFIXLEN + 33) continue ; memcpy(tmp + pathlen + 1, d->d_name, FTRIG1_PREFIXLEN + 33) ; fd = open_write(tmp) ; if (fd >= 0) fd_close(fd) ; else if ((errno == ENXIO) && (unlink(tmp) < 0)) e = errno ; } } dir_close(dir) ; if (errno) e = errno ; return e ? (errno = e, 0) : 1 ; } s6-2.13.1.0/src/libs6/ftrigw_fifodir_make.c000066400000000000000000000012261470151141200202340ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include int ftrigw_fifodir_make (char const *path, gid_t gid, int force) { mode_t m = umask(0) ; if (mkdir(path, 0700) < 0) { struct stat st ; umask(m) ; if (errno != EEXIST) return 0 ; if (stat(path, &st) == -1) return 0 ; if (st.st_uid != getuid()) return (errno = EACCES, 0) ; if (!S_ISDIR(st.st_mode)) return (errno = ENOTDIR, 0) ; if (!force) return 1 ; } else umask(m) ; if ((gid != (gid_t)-1) && (chown(path, -1, gid) < 0)) return 0 ; if (chmod(path, gid != (gid_t)-1 ? 03730 : 01733) < 0) return 0 ; return 1 ; } s6-2.13.1.0/src/libs6/ftrigw_notify.c000066400000000000000000000002041470151141200171200ustar00rootroot00000000000000/* ISC license. */ #include int ftrigw_notify (char const *path, char c) { return ftrigw_notifyb(path, &c, 1) ; } s6-2.13.1.0/src/libs6/ftrigw_notifyb.c000066400000000000000000000007741470151141200172760ustar00rootroot00000000000000/* ISC license. */ #include #include #include int ftrigw_notifyb (char const *path, char const *s, size_t len) { struct sigaction old ; struct sigaction action = { .sa_handler = SIG_IGN, .sa_flags = SA_RESTART | SA_NOCLDSTOP } ; int r ; sigfillset(&action.sa_mask) ; if (sigaction(SIGPIPE, &action, &old) == -1) return -1 ; r = ftrigw_notifyb_nosig(path, s, len) ; { int e = errno ; sigaction(SIGPIPE, &old, 0) ; errno = e ; } return r ; } s6-2.13.1.0/src/libs6/ftrigw_notifyb_nosig.c000066400000000000000000000031531470151141200204670ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include "ftrig1.h" int ftrigw_notifyb_nosig (char const *path, char const *s, size_t len) { unsigned int i = 0 ; DIR *dir = opendir(path) ; if (!dir) return -1 ; { size_t pathlen = strlen(path) ; char tmp[pathlen + FTRIG1_PREFIXLEN + 35] ; memcpy(tmp, path, pathlen) ; tmp[pathlen] = '/' ; tmp[pathlen + FTRIG1_PREFIXLEN + 34] = 0 ; for (;;) { direntry *d ; int fd ; errno = 0 ; d = readdir(dir) ; if (!d) break ; if (strncmp(d->d_name, FTRIG1_PREFIX ":@", FTRIG1_PREFIXLEN + 2)) continue ; if (strlen(d->d_name) != FTRIG1_PREFIXLEN + 33) continue ; memcpy(tmp + pathlen + 1, d->d_name, FTRIG1_PREFIXLEN + 33) ; fd = open_write(tmp) ; if (fd == -1) { if (errno == ENXIO) unlink_void(tmp) ; } else { ssize_t r = fd_write(fd, s, len) ; if ((r < 0) || (size_t)r < len) { if (errno == EPIPE) unlink_void(tmp) ; /* what to do if EAGAIN ? full fifo -> fix the reader ! There's a race condition in extreme cases though ; but it's still better to be nonblocking - the writer shouldn't get in trouble because of a bad reader. */ fd_close(fd) ; } else { fd_close(fd) ; i++ ; } } } } dir_close(dir) ; return errno ? -1 : (int)i ; } s6-2.13.1.0/src/libs6/s6-ftrigrd.c000066400000000000000000000163651470151141200162340ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ftrig1.h" #include #include #define FTRIGRD_MAXREADS 32 #define FTRIGRD_BUFSIZE 16 #define dienomem() strerr_diefu1sys(111, "stralloc_catb") typedef struct ftrigio_s ftrigio, *ftrigio_ref ; struct ftrigio_s { unsigned int xindex ; ftrig1_t trig ; buffer b ; regex_t re ; stralloc sa ; uint32_t options ; uint16_t id ; /* given by client */ } ; #define FTRIGIO_ZERO { .xindex = 0, .trig = FTRIG1_ZERO, .b = BUFFER_INIT(0, -1, 0, 0), .buf = "", .sa = STRALLOC_ZERO, .options = 0, .id = 0 } static genalloc g = GENALLOC_ZERO ; /* ftrigio */ static void ftrigio_free (ftrigio *p) { alloc_free(p->b.c.x) ; ftrig1_free(&p->trig) ; stralloc_free(&p->sa) ; regfree(&p->re) ; } static void cleanup (void) { size_t n = genalloc_len(ftrigio, &g) ; ftrigio *a = genalloc_s(ftrigio, &g) ; for (size_t i = 0 ; i < n ; i++) ftrigio_free(a + i) ; genalloc_setlen(ftrigio, &g, 0) ; } static void trig (uint16_t id, char what, char info) { char pack[4] ; uint16_pack_big(pack, id) ; pack[2] = what ; pack[3] = info ; if (!textmessage_put(textmessage_sender_x, pack, 4)) { cleanup() ; strerr_diefu1sys(111, "build answer") ; } } static void answer (unsigned char c) { if (!textmessage_put(textmessage_sender_1, (char *)&c, 1)) { cleanup() ; strerr_diefu1sys(111, "textmessage_put") ; } } static void remove (size_t i) { size_t n = genalloc_len(ftrigio, &g) ; ftrigio *a = genalloc_s(ftrigio, &g) ; ftrigio_free(a + i) ; a[i] = a[--n] ; genalloc_setlen(ftrigio, &g, n) ; } static inline int ftrigio_read (ftrigio *p) { unsigned int i = FTRIGRD_MAXREADS ; while (i--) { regmatch_t pmatch ; size_t blen ; ssize_t r = sanitize_read(buffer_fill(&p->b)) ; if (!r) break ; if (r < 0) return (trig(p->id, 'd', errno), 0) ; blen = buffer_len(&p->b) ; if (!stralloc_readyplus(&p->sa, blen+1)) { cleanup() ; dienomem() ; } buffer_getnofill(&p->b, p->sa.s + p->sa.len, blen) ; p->sa.len += blen ; p->sa.s[p->sa.len] = 0 ; while (!regexec(&p->re, p->sa.s, 1, &pmatch, REG_NOTBOL | REG_NOTEOL)) { trig(p->id, '!', p->sa.s[pmatch.rm_eo - 1]) ; if (!(p->options & FTRIGR_REPEAT)) return 0 ; memmove(p->sa.s, p->sa.s + pmatch.rm_eo, p->sa.len + 1 - pmatch.rm_eo) ; p->sa.len -= pmatch.rm_eo ; } } return 1 ; } static int parse_protocol (struct iovec const *v, void *context) { char const *s = v->iov_base ; uint16_t id ; if (v->iov_len < 3) { cleanup() ; strerr_dief1x(100, "invalid client request") ; } uint16_unpack_big(s, &id) ; switch (s[2]) { case 'U' : /* unsubscribe */ { size_t n = genalloc_len(ftrigio, &g) ; size_t i = 0 ; for (; i < n ; i++) if (genalloc_s(ftrigio, &g)[i].id == id) break ; if (i < n) { remove(i) ; answer(0) ; } else answer(EINVAL) ; break ; } case 'L' : /* subscribe to path and match re */ { size_t n = genalloc_len(ftrigio, &g) ; ftrigio *p ; char *x ; uint32_t options, pathlen, relen ; int r ; if (v->iov_len < 19) { answer(EPROTO) ; break ; } uint32_unpack_big(s + 3, &options) ; uint32_unpack_big(s + 7, &pathlen) ; uint32_unpack_big(s + 11, &relen) ; if (((pathlen + relen + 17) != v->iov_len) || s[15 + pathlen] || s[v->iov_len - 1]) { answer(EPROTO) ; break ; } if (!genalloc_readyplus(ftrigio, &g, 1)) { answer(ENOMEM) ; break ; } x = alloc(FTRIGRD_BUFSIZE) ; if (!x) { answer(ENOMEM) ; break ; } p = genalloc_s(ftrigio, &g) + n ; r = skalibs_regcomp(&p->re, s + 16 + pathlen, REG_EXTENDED) ; if (r) { alloc_free(x) ; answer(r == REG_ESPACE ? ENOMEM : EINVAL) ; break ; } if (!ftrig1_make(&p->trig, s + 15)) { regfree(&p->re) ; alloc_free(x) ; answer(errno) ; break ; } buffer_init(&p->b, &buffer_read, p->trig.fd, x, FTRIGRD_BUFSIZE) ; p->options = options ; p->id = id ; p->sa = stralloc_zero ; genalloc_setlen(ftrigio, &g, n+1) ; answer(0) ; break ; } default : cleanup() ; strerr_dief1x(100, "invalid client request") ; } (void)context ; return 1 ; } int main (void) { PROG = "s6-ftrigrd" ; if (ndelay_on(0) == -1 || ndelay_on(1) == -1) strerr_diefu1sys(111, "make fds nonblocking") ; if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; { tain deadline ; tain_now_set_stopwatch_g() ; tain_addsec_g(&deadline, 2) ; if (!textclient_server_01x_init_g(FTRIGR_BANNER1, FTRIGR_BANNER1_LEN, FTRIGR_BANNER2, FTRIGR_BANNER2_LEN, &deadline)) strerr_diefu1sys(111, "sync with client") ; } for (;;) { size_t n = genalloc_len(ftrigio, &g) ; size_t i = 0 ; iopause_fd x[3 + n] ; x[0].fd = 0 ; x[0].events = IOPAUSE_EXCEPT | IOPAUSE_READ ; x[1].fd = 1 ; x[1].events = IOPAUSE_EXCEPT | (textmessage_sender_isempty(textmessage_sender_1) ? 0 : IOPAUSE_WRITE) ; x[2].fd = textmessage_sender_fd(textmessage_sender_x) ; x[2].events = IOPAUSE_EXCEPT | (textmessage_sender_isempty(textmessage_sender_x) ? 0 : IOPAUSE_WRITE) ; for (; i < n ; i++) { ftrigio *p = genalloc_s(ftrigio, &g) + i ; p->xindex = 3+i ; x[3+i].fd = p->trig.fd ; x[3+i].events = IOPAUSE_READ ; } if (iopause(x, 3 + n, 0, 0) < 0) { cleanup() ; strerr_diefu1sys(111, "iopause") ; } /* client closed */ if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) break ; /* client is reading */ if (x[1].revents & IOPAUSE_WRITE) if (!textmessage_sender_flush(textmessage_sender_1) && !error_isagain(errno)) { cleanup() ; strerr_diefu1sys(111, "flush stdout") ; } if (x[2].revents & IOPAUSE_WRITE) if (!textmessage_sender_flush(textmessage_sender_x) && !error_isagain(errno)) { cleanup() ; return 1 ; } /* scan listening ftrigs */ for (i = 0 ; i < genalloc_len(ftrigio, &g) ; i++) { ftrigio *p = genalloc_s(ftrigio, &g) + i ; if (x[p->xindex].revents & IOPAUSE_READ) if (!ftrigio_read(p)) remove(i--) ; } /* client is writing */ if (!textmessage_receiver_isempty(textmessage_receiver_0) || x[0].revents & IOPAUSE_READ) { if (textmessage_handle(textmessage_receiver_0, &parse_protocol, 0) < 0) { if (errno == EPIPE) break ; /* normal exit */ cleanup() ; strerr_diefu1sys(111, "handle messages from client") ; } } } cleanup() ; return 0 ; } s6-2.13.1.0/src/libs6/s6_accessrules_backend_cdb.c000066400000000000000000000031161470151141200214360ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include s6_accessrules_result_t s6_accessrules_backend_cdb (char const *key, size_t keylen, void const *arg, s6_accessrules_params_t *params) { cdb_data data ; int wasnull = !params->env.s ; uint16_t envlen, execlen ; cdb const *c = arg ; int r = cdb_find(c, &data, key, keylen) ; if (r < 0) return S6_ACCESSRULES_ERROR ; else if (!r) return S6_ACCESSRULES_NOTFOUND ; if (!data.len || data.len > 8197) return (errno = EINVAL, S6_ACCESSRULES_ERROR) ; if (data.s[0] == 'D') return S6_ACCESSRULES_DENY ; if (data.s[0] != 'A') return S6_ACCESSRULES_NOTFOUND ; if (data.len < 5) return (errno = EINVAL, S6_ACCESSRULES_ERROR) ; uint16_unpack_big(data.s + 1, &envlen) ; if ((envlen > 4096) || (data.len - 5 < envlen)) return (errno = EINVAL, S6_ACCESSRULES_ERROR) ; uint16_unpack_big(data.s + 3 + envlen, &execlen) ; if ((execlen > 4096) || (5 + envlen + execlen != data.len)) return (errno = EINVAL, S6_ACCESSRULES_ERROR) ; if (envlen && !stralloc_catb(¶ms->env, data.s + 3, envlen)) return S6_ACCESSRULES_ERROR ; if (execlen) { if (!stralloc_readyplus(¶ms->exec, execlen + 1)) { if (envlen) { if (wasnull) stralloc_free(¶ms->env) ; else params->env.len -= envlen ; } return S6_ACCESSRULES_ERROR ; } stralloc_catb(¶ms->exec, data.s + 5 + envlen, execlen) ; stralloc_0(¶ms->exec) ; } return S6_ACCESSRULES_ALLOW ; } s6-2.13.1.0/src/libs6/s6_accessrules_backend_fs.c000066400000000000000000000034571470151141200213260ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include s6_accessrules_result_t s6_accessrules_backend_fs (char const *key, size_t keylen, void const *data, s6_accessrules_params_t *params) { char const *dir = data ; size_t dirlen = strlen(dir) ; size_t envbase = params->env.len ; int wasnull = !params->env.s ; { char tmp[dirlen + keylen + 10] ; memcpy(tmp, dir, dirlen) ; tmp[dirlen] = '/' ; memcpy(tmp + dirlen + 1, key, keylen) ; memcpy(tmp + dirlen + keylen + 1, "/allow", 7) ; if (access(tmp, R_OK) < 0) { if ((errno != EACCES) && (errno != ENOENT)) return S6_ACCESSRULES_ERROR ; memcpy(tmp + dirlen + keylen + 2, "deny", 5) ; return (access(tmp, R_OK) == 0) ? S6_ACCESSRULES_DENY : (errno != EACCES) && (errno != ENOENT) ? S6_ACCESSRULES_ERROR : S6_ACCESSRULES_NOTFOUND ; } memcpy(tmp + dirlen + keylen + 2, "env", 4) ; if ((envdir(tmp, ¶ms->env) < 0) && (errno != ENOENT)) return S6_ACCESSRULES_ERROR ; if (!stralloc_readyplus(¶ms->exec, 4097)) { if (wasnull) stralloc_free(¶ms->env) ; else params->env.len = envbase ; return S6_ACCESSRULES_ERROR ; } memcpy(tmp + dirlen + keylen + 2, "exec", 5) ; { ssize_t r = openreadnclose(tmp, params->exec.s + params->exec.len, 4096) ; if ((r == -1) && (errno != EACCES) && (errno != ENOENT)) { if (wasnull) stralloc_free(¶ms->env) ; else params->env.len = envbase ; return S6_ACCESSRULES_ERROR ; } if (r > 0) { params->exec.len += r ; params->exec.s[params->exec.len++] = 0 ; } } } return S6_ACCESSRULES_ALLOW ; } s6-2.13.1.0/src/libs6/s6_accessrules_keycheck_ip4.c000066400000000000000000000013651470151141200216050ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include s6_accessrules_result_t s6_accessrules_keycheck_ip4 (void const *key, void const *data, s6_accessrules_params_t *params, s6_accessrules_backend_func_ref check1) { char fmt[IP4_FMT + UINT_FMT + 6] = "ip4/" ; uint32_t ip ; unsigned int i = 0 ; uint32_unpack_big((char const *)key, &ip) ; for (; i <= 32 ; i++) { s6_accessrules_result_t r ; size_t len = 4 + ip4_fmtu32(fmt+4, (i == 32) ? 0 : ip & ~((1U << i) - 1)) ; fmt[len++] = '_' ; len += uint_fmt(fmt + len, 32 - i) ; r = (*check1)(fmt, len, data, params) ; if (r != S6_ACCESSRULES_NOTFOUND) return r ; } return S6_ACCESSRULES_NOTFOUND ; } s6-2.13.1.0/src/libs6/s6_accessrules_keycheck_ip6.c000066400000000000000000000015651470151141200216110ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include s6_accessrules_result_t s6_accessrules_keycheck_ip6 (void const *key, void const *data, s6_accessrules_params_t *params, s6_accessrules_backend_func_ref check1) { char fmt[IP6_FMT + UINT_FMT + 6] = "ip6/" ; char ip6[16] ; unsigned int i = 0 ; memcpy(ip6, (char const *)key, 16) ; for (; i <= 128 ; i++) { size_t len ; s6_accessrules_result_t r ; if (i) ip6[(128-i) >> 3] &= ~(1 << ((i-1)&7)) ; len = 4 + ip6_fmt(fmt+4, ip6) ; fmt[len++] = '_' ; len += uint_fmt(fmt + len, 128 - i) ; LOLDEBUG("s6_accessrules_keycheck_ip6: checking %.*s", (int)len, fmt) ; r = (*check1)(fmt, len, data, params) ; if (r != S6_ACCESSRULES_NOTFOUND) return r ; } return S6_ACCESSRULES_NOTFOUND ; } s6-2.13.1.0/src/libs6/s6_accessrules_keycheck_reversedns.c000066400000000000000000000015041470151141200232640ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include s6_accessrules_result_t s6_accessrules_keycheck_reversedns (void const *key, void const *data, s6_accessrules_params_t *params, s6_accessrules_backend_func_ref check1) { char const *name = key ; size_t len = strlen(name) ; if (!len) return (errno = EINVAL, S6_ACCESSRULES_ERROR) ; if (name[len-1] == '.') len-- ; { size_t i = 0 ; char tmp[len + 11] ; memcpy(tmp, "reversedns/", 11) ; while (i < len) { s6_accessrules_result_t r ; memcpy(tmp+11, name+i, len-i) ; r = (*check1)(tmp, 11+len-i, data, params) ; if (r != S6_ACCESSRULES_NOTFOUND) return r ; i += byte_chr(name+i, len-i, '.') + 1 ; } } return (*check1)("reversedns/@", 12, data, params) ; } s6-2.13.1.0/src/libs6/s6_accessrules_keycheck_uidgid.c000066400000000000000000000017251470151141200223560ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include s6_accessrules_result_t s6_accessrules_keycheck_uidgid (void const *key, void const *data, s6_accessrules_params_t *params, s6_accessrules_backend_func_ref check1) { uidgid_t const *uidgid = key ; char fmt[4 + UINT64_FMT] = "uid/" ; s6_accessrules_result_t r ; if (uidgid->left == geteuid()) { r = (*check1)("uid/self", 8, data, params) ; if (r != S6_ACCESSRULES_NOTFOUND) return r ; } r = (*check1)(fmt, 4 + uid_fmt(fmt+4, uidgid->left), data, params) ; if (r != S6_ACCESSRULES_NOTFOUND) return r ; if (uidgid->right == getegid()) { r = (*check1)("gid/self", 8, data, params) ; if (r != S6_ACCESSRULES_NOTFOUND) return r ; } fmt[0] = 'g' ; r = (*check1)(fmt, 4 + gid_fmt(fmt+4, uidgid->right), data, params) ; if (r != S6_ACCESSRULES_NOTFOUND) return r ; return (*check1)("uid/default", 11, data, params) ; } s6-2.13.1.0/src/libs6/s6_accessrules_params_free.c000066400000000000000000000003261470151141200215230ustar00rootroot00000000000000/* ISC license. */ #include #include void s6_accessrules_params_free (s6_accessrules_params_t *params) { stralloc_free(¶ms->env) ; stralloc_free(¶ms->exec) ; } s6-2.13.1.0/src/libs6/s6_accessrules_uidgid_cdb.c000066400000000000000000000004741470151141200213200ustar00rootroot00000000000000/* ISC license. */ #include s6_accessrules_result_t s6_accessrules_uidgid_cdb (uid_t uid, gid_t gid, cdb const *c, s6_accessrules_params_t *params) { uidgid_t uidgid = { .left = uid, .right = gid } ; return s6_accessrules_keycheck_uidgid(&uidgid, c, params, &s6_accessrules_backend_cdb) ; } s6-2.13.1.0/src/libs6/s6_accessrules_uidgid_fs.c000066400000000000000000000005211470151141200211710ustar00rootroot00000000000000/* ISC license. */ #include s6_accessrules_result_t s6_accessrules_uidgid_fs (uid_t uid, gid_t gid, char const *rulesdir, s6_accessrules_params_t *params) { uidgid_t uidgid = { .left = uid, .right = gid } ; return s6_accessrules_keycheck_uidgid(&uidgid, (void *)rulesdir, params, &s6_accessrules_backend_fs) ; } s6-2.13.1.0/src/libs6/s6_auto_write_logger.c000066400000000000000000000006751470151141200203730ustar00rootroot00000000000000/* ISC license. */ #include #include void s6_auto_write_logger (char const *dir, char const *loguser, char const *logdir, unsigned int stamptype, unsigned int nfiles, uint64_t filesize, uint64_t maxsize, char const *prefix, char const *service, char const *pipelinename) { return s6_auto_write_logger_tmp(dir, loguser, logdir, stamptype, nfiles, filesize, maxsize, prefix, service, pipelinename, &satmp) ; } s6-2.13.1.0/src/libs6/s6_auto_write_logger_tmp.c000066400000000000000000000034321470151141200212450ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include void s6_auto_write_logger_tmp (char const *dir, char const *loguser, char const *logdir, unsigned int stamptype, unsigned int nfiles, uint64_t filesize, uint64_t maxsize, char const *prefix, char const *service, char const *pipelinename, stralloc *sa) { mode_t m = umask(0) ; size_t dirlen = strlen(dir) ; char fn[dirlen + 17] ; memcpy(fn, dir, dirlen) ; memcpy(fn + dirlen, "/notification-fd", 17) ; if (mkdir(dir, 0755) == -1) strerr_diefu2sys(111, "mkdir ", dir) ; umask(m) ; m = ~m & 0666 ; if (!openwritenclose_unsafe(fn, "3\n", 2)) goto err ; memcpy(fn + dirlen + 1, "run", 4) ; if (!s6_auto_write_logrun_tmp(fn, loguser, logdir, stamptype, nfiles, filesize, maxsize, prefix, sa)) goto err ; if (service) { struct iovec v[2] = { { .iov_base = (char *)service, .iov_len = strlen(service) }, { .iov_base = "\n", .iov_len = 1 } } ; memcpy(fn + dirlen + 1, "type", 5) ; if (!openwritenclose_unsafe(fn, "longrun\n", 8)) goto err ; memcpy(fn + dirlen + 1, "consumer-for", 13) ; if (!openwritevnclose_unsafe(fn, v, 2)) goto err ; if (pipelinename) { v[0].iov_base = (char *)pipelinename ; v[0].iov_len = strlen(pipelinename) ; memcpy(fn + dirlen + 1, "pipeline-name", 14) ; if (!openwritevnclose_unsafe(fn, v, 2)) goto err ; } } else { if (chmod(fn, m | ((m >> 2) & 0111)) == -1) strerr_diefu2sys(111, "chmod ", fn) ; if (!(m & 0400)) strerr_warnw2x("weird umask, check permissions manually on ", fn) ; } return ; err: strerr_diefu2sys(111, "write to ", fn) ; } s6-2.13.1.0/src/libs6/s6_auto_write_logrun.c000066400000000000000000000005761470151141200204220ustar00rootroot00000000000000/* ISC license. */ #include #include int s6_auto_write_logrun (char const *runfile, char const *loguser, char const *logdir, unsigned int stamptype, unsigned int nfiles, uint64_t filesize, uint64_t maxsize, char const *prefix) { return s6_auto_write_logrun_tmp(runfile, loguser, logdir, stamptype, nfiles, filesize, maxsize, prefix, &satmp) ; } s6-2.13.1.0/src/libs6/s6_auto_write_logrun_tmp.c000066400000000000000000000037141470151141200212770ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include int s6_auto_write_logrun_tmp (char const *runfile, char const *loguser, char const *logdir, unsigned int stamptype, unsigned int nfiles, uint64_t filesize, uint64_t maxsize, char const *prefix, stralloc *sa) { buffer b ; char buf[1024] ; char fmt[UINT64_FMT] ; int fd = open_trunc(runfile) ; if (fd < 0) return 0 ; buffer_init(&b, &buffer_write, fd, buf, 1024) ; if (buffer_puts(&b, "#!" EXECLINE_SHEBANGPREFIX "execlineb -P\n") < 0) goto err ; if (loguser) { if (buffer_puts(&b, S6_EXTBINPREFIX "s6-setuidgid ") < 0 || !string_quote(sa, loguser, strlen(loguser)) || buffer_put(&b, sa->s, sa->len) < 0 || buffer_put(&b, "\n", 1) < 0) goto err ; sa->len = 0 ; } if (buffer_puts(&b, S6_EXTBINPREFIX "s6-log -bd3 -- ") < 0 || (stamptype & 1 && buffer_put(&b, "t ", 2) < 0) || (stamptype & 2 && buffer_put(&b, "T ", 2) < 0) || buffer_put(&b, "n", 1) < 0 || buffer_put(&b, fmt, uint_fmt(fmt, nfiles)) < 0 || buffer_put(&b, " s", 2) < 0 || buffer_put(&b, fmt, uint64_fmt(fmt, filesize)) < 0 || buffer_put(&b, " ", 1) < 0) goto err ; if (maxsize) { if (buffer_put(&b, "S", 1) < 0 || buffer_put(&b, fmt, uint64_fmt(fmt, maxsize)) < 0 || buffer_put(&b, " ", 1) < 0) goto err ; } if (prefix) { if (buffer_put(&b, "p", 1) < 0 || buffer_puts(&b, prefix) < 0 || buffer_put(&b, " ", 1) < 0) goto err ; } if (!string_quote(sa, logdir, strlen(logdir)) || buffer_put(&b, sa->s, sa->len) < 0 || buffer_put(&b, "\n", 1) < 0) goto err ; sa->len = 0 ; if (!buffer_flush(&b)) goto err ; fd_close(fd) ; return 1 ; err: fd_close(fd) ; unlink_void(runfile) ; return 0 ; } s6-2.13.1.0/src/libs6/s6_auto_write_service.c000066400000000000000000000033231470151141200205450ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include void s6_auto_write_service (char const *dir, unsigned int nfd, s6_buffer_writer_func_ref f, void *data, char const *logger) { int fd ; buffer b ; size_t dirlen = strlen(dir) ; mode_t m = umask(0) ; char buf[4096] ; char fn[dirlen + 17] ; if (mkdir(dir, 0755) == -1) strerr_diefu2sys(111, "mkdir ", dir) ; umask(m) ; memcpy(fn, dir, dirlen) ; if (nfd) { char fmt[UINT_FMT] ; size_t l = uint_fmt(fmt, nfd) ; fmt[l++] = '\n' ; memcpy(fn + dirlen, "/notification-fd", 17) ; if (!openwritenclose_unsafe(fn, fmt, l)) strerr_diefu2sys(111, "write to ", fn) ; } memcpy(fn + dirlen, "/run", 5) ; fd = open_trunc(fn) ; if (fd == -1) strerr_diefu2sys(111, "open ", fn) ; buffer_init(&b, &buffer_write, fd, buf, 4096) ; if (!(*f)(&b, data)) strerr_diefu2sys(111, "write to ", fn) ; fd_close(fd) ; if (logger) { memcpy(fn + dirlen + 1, "type", 5) ; if (!openwritenclose_unsafe(fn, "longrun\n", 8)) strerr_diefu2sys(111, "write to ", fn) ; if (logger[0]) { struct iovec v[2] = { { .iov_base = (char *)logger, .iov_len = strlen(logger) }, { .iov_base = "\n", .iov_len = 1 } } ; memcpy(fn + dirlen + 1, "producer-for", 13) ; if (!openwritevnclose_unsafe(fn, v, 2)) strerr_diefu2sys(111, "write to ", fn) ; } } else { if (chmod(fn, (~m & 0666) | ((~m >> 2) & 0111)) < 0) strerr_diefu2sys(111, "chmod ", fn) ; if (!(~m & 0400)) strerr_warnw2x("weird umask, check permissions manually on ", fn) ; } } s6-2.13.1.0/src/libs6/s6_compat_el_semicolon.c000066400000000000000000000022241470151141200206550ustar00rootroot00000000000000/* ISC license. */ #include #ifndef S6_USE_EXECLINE #include #include #include static unsigned int el_getstrict (void) { static unsigned int strict = 0 ; static int first = 1 ; if (first) { char const *x = getenv("EXECLINE_STRICT") ; first = 0 ; if (x) uint0_scan(x, &strict) ; } return strict ; } int s6_compat_el_semicolon (char const **argv) { static unsigned int nblock = 0 ; int argc1 = 0 ; nblock++ ; for (;; argc1++, argv++) { char const *arg = *argv ; if (!arg) return argc1 + 1 ; if (!arg[0]) return argc1 ; else if (arg[0] == ' ') ++*argv ; else { unsigned int strict = el_getstrict() ; if (strict) { char fmt1[UINT_FMT] ; char fmt2[UINT_FMT] ; fmt1[uint_fmt(fmt1, nblock)] = 0 ; fmt2[uint_fmt(fmt2, (unsigned int)argc1)] = 0 ; if (strict >= 2) strerr_dief6x(100, "unquoted argument ", arg, " at block ", fmt1, " position ", fmt2) ; else strerr_warnw6x("unquoted argument ", arg, " at block ", fmt1, " position ", fmt2) ; } } } } #endif s6-2.13.1.0/src/libs6/s6_dtally_pack.c000066400000000000000000000003451470151141200171330ustar00rootroot00000000000000/* ISC license. */ #include #include void s6_dtally_pack (char *pack, s6_dtally_t const *d) { tain_pack(pack, &d->stamp) ; pack[TAIN_PACK] = d->exitcode ; pack[TAIN_PACK + 1] = d->sig ; } s6-2.13.1.0/src/libs6/s6_dtally_read.c000066400000000000000000000024461470151141200171340ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include static int truncit (char const *s) { int fd = open_trunc(s) ; if (fd < 0) return -1 ; fd_close(fd) ; return 0 ; } ssize_t s6_dtally_read (char const *sv, s6_dtally_t *tab, size_t max) { int e = errno ; size_t len = strlen(sv) ; size_t n ; int fd ; struct stat st ; char fn[len + sizeof(S6_DTALLY_FILENAME) + 1] ; memcpy(fn, sv, len) ; memcpy(fn + len, "/" S6_DTALLY_FILENAME, sizeof(S6_DTALLY_FILENAME) + 1) ; fd = open_read(fn) ; if (fd < 0) return errno == ENOENT ? truncit(fn) : -1 ; if (fstat(fd, &st) < 0) goto err ; if (st.st_size % S6_DTALLY_PACK) { fd_close(fd) ; return truncit(fn) ; } n = st.st_size / S6_DTALLY_PACK ; if (n > max) n = max ; { char tmp[n ? S6_DTALLY_PACK * n : 1] ; if (lseek(fd, -(off_t)(n * S6_DTALLY_PACK), SEEK_END) < 0) goto err ; errno = EPIPE ; if (allread(fd, tmp, n * S6_DTALLY_PACK) < n * S6_DTALLY_PACK) goto err ; fd_close(fd) ; for (size_t i = 0 ; i < n ; i++) s6_dtally_unpack(tmp + i * S6_DTALLY_PACK, tab + i) ; } errno = e ; return n ; err: fd_close(fd) ; return -1 ; } s6-2.13.1.0/src/libs6/s6_dtally_unpack.c000066400000000000000000000003511470151141200174730ustar00rootroot00000000000000/* ISC license. */ #include #include void s6_dtally_unpack (char const *pack, s6_dtally_t *d) { tain_unpack(pack, &d->stamp) ; d->exitcode = pack[TAIN_PACK] ; d->sig = pack[TAIN_PACK + 1] ; } s6-2.13.1.0/src/libs6/s6_dtally_write.c000066400000000000000000000010451470151141200173450ustar00rootroot00000000000000/* ISC license. */ #include #include #include int s6_dtally_write (char const *sv, s6_dtally_t const *tab, size_t n) { size_t len = strlen(sv) ; char fn[len + sizeof(S6_DTALLY_FILENAME) + 1] ; char tmp[n ? S6_DTALLY_PACK * n : 1] ; memcpy(fn, sv, len) ; memcpy(fn + len, "/" S6_DTALLY_FILENAME, sizeof(S6_DTALLY_FILENAME) + 1) ; for (size_t i = 0 ; i < n ; i++) s6_dtally_pack(tmp + i * S6_DTALLY_PACK, tab + i) ; return openwritenclose_suffix(fn, tmp, n * S6_DTALLY_PACK, ".new") ; } s6-2.13.1.0/src/libs6/s6_fdholder_delete.c000066400000000000000000000013001470151141200177450ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include int s6_fdholder_delete (s6_fdholder_t *a, char const *id, tain const *deadline, tain *stamp) { unixmessage m ; if (!s6_fdholder_delete_async(a, id)) return 0 ; if (!unixmessage_sender_timed_flush(&a->connection.out, deadline, stamp)) return 0 ; if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) return 0 ; if (m.len != 1 || m.nfds) { unixmessage_drop(&m) ; return (errno = EPROTO, 0) ; } return m.s[0] ? (errno = (unsigned char)m.s[0], 0) : 1 ; } s6-2.13.1.0/src/libs6/s6_fdholder_delete_async.c000066400000000000000000000011221470151141200211440ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include int s6_fdholder_delete_async (s6_fdholder_t *a, char const *id) { size_t idlen = strlen(id) ; char pack[2] = "D" ; struct iovec v[2] = { { .iov_base = pack, .iov_len = 2 }, { .iov_base = (char *)id, .iov_len = idlen + 1 } } ; unixmessagev m = { .v = v, .vlen = 2, .fds = 0, .nfds = 0 } ; if (idlen > S6_FDHOLDER_ID_SIZE) return (errno = ENAMETOOLONG, 0) ; pack[1] = (unsigned char)idlen ; return unixmessage_putv(&a->connection.out, &m) ; } s6-2.13.1.0/src/libs6/s6_fdholder_end.c000066400000000000000000000003501470151141200172550ustar00rootroot00000000000000/* ISC license. */ #include #include #include void s6_fdholder_end (s6_fdholder_t *a) { fd_close(unixmessage_sender_fd(&a->connection.out)) ; s6_fdholder_free(a) ; } s6-2.13.1.0/src/libs6/s6_fdholder_getdump.c000066400000000000000000000047711470151141200201670ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include int s6_fdholder_getdump (s6_fdholder_t *a, genalloc *g, tain const *deadline, tain *stamp) { unixmessage m = { .s = "?", .len = 1, .fds = 0, .nfds = 0 } ; uint32_t ntot, n ; size_t oldlen = genalloc_len(s6_fdholder_fd_t, g) ; unsigned int i = 0 ; int ok ; if (!unixmessage_put(&a->connection.out, &m)) return 0 ; if (!unixmessage_sender_timed_flush(&a->connection.out, deadline, stamp)) return 0 ; if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) return 0 ; if (!m.len || m.nfds) return (errno = EPROTO, 0) ; if (m.s[0]) return (errno = (unsigned char)m.s[0], 0) ; if (m.len != 9) return (errno = EPROTO, 0) ; uint32_unpack_big(m.s + 1, &n) ; uint32_unpack_big(m.s + 5, &ntot) ; if (!ntot) return 1 ; if (n != 1 + (ntot-1) / UNIXMESSAGE_MAXFDS) return (errno = EPROTO, 0) ; ok = genalloc_readyplus(s6_fdholder_fd_t, g, ntot) ; for (; i < n ; i++) { if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) goto err ; if (genalloc_len(s6_fdholder_fd_t, g) + m.nfds > ntot) goto droperr ; if (ok) { s6_fdholder_fd_t *tab = genalloc_s(s6_fdholder_fd_t, g) + genalloc_len(s6_fdholder_fd_t, g) ; unsigned int j = 0 ; for (; j < m.nfds ; j++) { unsigned char thislen ; if (m.len < TAIN_PACK + 3) goto droperr ; tain_unpack(m.s, &tab[j].limit) ; m.s += TAIN_PACK ; m.len -= TAIN_PACK + 1 ; thislen = *m.s++ ; if (thislen > m.len - 1 || m.s[thislen]) goto droperr ; memcpy(tab[j].id, m.s, thislen) ; memset(tab[j].id + thislen, 0, S6_FDHOLDER_ID_SIZE + 1 - thislen) ; m.s += (size_t)thislen + 1 ; m.len -= (size_t)thislen + 1 ; tab[j].fd = m.fds[j] ; } genalloc_setlen(s6_fdholder_fd_t, g, genalloc_len(s6_fdholder_fd_t, g) + m.nfds) ; } else unixmessage_drop(&m) ; } if (!ok) return (errno = ENOMEM, 0) ; return 1 ; droperr: unixmessage_drop(&m) ; errno = EPROTO ; err: { size_t j = genalloc_len(s6_fdholder_fd_t, g) ; while (j-- > oldlen) fd_close(genalloc_s(s6_fdholder_fd_t, g)[j].fd) ; genalloc_setlen(s6_fdholder_fd_t, g, oldlen) ; } return 0 ; } s6-2.13.1.0/src/libs6/s6_fdholder_list.c000066400000000000000000000012741470151141200174700ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include int s6_fdholder_list (s6_fdholder_t *a, stralloc *sa, tain const *deadline, tain *stamp) { s6_fdholder_list_result_t res = { .sa = sa } ; unixmessage m ; if (!s6_fdholder_list_async(a)) return -1 ; if (!unixmessage_sender_timed_flush(&a->connection.out, deadline, stamp)) return -1 ; if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) return -1 ; if (!s6_fdholder_list_cb(&m, &res)) return -1 ; if (res.err) return (errno = res.err, -1) ; return (int)res.n ; } s6-2.13.1.0/src/libs6/s6_fdholder_list_async.c000066400000000000000000000003641470151141200206640ustar00rootroot00000000000000/* ISC license. */ #include #include int s6_fdholder_list_async (s6_fdholder_t *a) { unixmessage m = { .s = "L", .len = 1, .fds = 0, .nfds = 0 } ; return unixmessage_put(&a->connection.out, &m) ; } s6-2.13.1.0/src/libs6/s6_fdholder_list_cb.c000066400000000000000000000014121470151141200201260ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include int s6_fdholder_list_cb (unixmessage const *m, void *p) { uint32_t n ; s6_fdholder_list_result_t *res = p ; if (m->nfds) goto droperr ; if (!m->len) goto err ; if (m->s[0]) { res->err = m->s[0] ; return 1 ; } if (m->len < 5) goto err ; uint32_unpack_big(m->s + 1, &n) ; if (byte_count(m->s + 5, m->len - 5, 0) != n) goto err ; if (!stralloc_catb(res->sa, m->s + 5, m->len - 5)) return 0 ; res->n = n ; res->err = 0 ; return 1 ; droperr: unixmessage_drop(m) ; err: return (errno = EPROTO, 0) ; } s6-2.13.1.0/src/libs6/s6_fdholder_retrieve.c000066400000000000000000000013261470151141200203400ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include int s6_fdholder_retrieve_maybe_delete (s6_fdholder_t *a, char const *id, int dodelete, tain const *deadline, tain *stamp) { unixmessage m ; s6_fdholder_retrieve_result_t res ; if (!s6_fdholder_retrieve_maybe_delete_async(a, id, dodelete)) return -1 ; if (!unixmessage_sender_timed_flush(&a->connection.out, deadline, stamp)) return -1 ; if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) return -1 ; if (!s6_fdholder_retrieve_cb(&m, &res)) return -1 ; if (res.err) return (errno = res.err, -1) ; return res.fd ; } s6-2.13.1.0/src/libs6/s6_fdholder_retrieve_async.c000066400000000000000000000012101470151141200215250ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include int s6_fdholder_retrieve_maybe_delete_async (s6_fdholder_t *a, char const *id, int dodelete) { size_t idlen = strlen(id) ; char pack[3] = "R" ; struct iovec v[2] = { { .iov_base = pack, .iov_len = 3 }, { .iov_base = (char *)id, .iov_len = idlen + 1 } } ; unixmessagev m = { .v = v, .vlen = 2, .fds = 0, .nfds = 0 } ; if (idlen > S6_FDHOLDER_ID_SIZE) return (errno = ENAMETOOLONG, 0) ; pack[1] = !!dodelete ; pack[2] = (unsigned char)idlen ; return unixmessage_putv(&a->connection.out, &m) ; } s6-2.13.1.0/src/libs6/s6_fdholder_retrieve_cb.c000066400000000000000000000007751470151141200210130ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include int s6_fdholder_retrieve_cb (unixmessage const *m, void *p) { s6_fdholder_retrieve_result_t *res = p ; if (m->len != 1) goto err ; if (m->s[0]) { if (m->nfds) goto err ; res->err = m->s[0] ; return 1 ; } if (m->nfds != 1) goto err ; res->fd = m->fds[0] ; res->err = 0 ; return 1 ; err: unixmessage_drop(m) ; return (errno = EPROTO, 0) ; } s6-2.13.1.0/src/libs6/s6_fdholder_setdump.c000066400000000000000000000060001470151141200201660ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include int s6_fdholder_setdump (s6_fdholder_t *a, s6_fdholder_fd_t const *list, unsigned int ntot, tain const *deadline, tain *stamp) { uint32_t trips ; if (!ntot) return 1 ; for (unsigned int i = 0 ; i < ntot ; i++) { size_t zpos = byte_chr(list[i].id, S6_FDHOLDER_ID_SIZE + 1, 0) ; if (!zpos || zpos >= S6_FDHOLDER_ID_SIZE + 1) return (errno = EINVAL, 0) ; } { char pack[5] = "!" ; unixmessage m = { .s = pack, .len = 5, .fds = 0, .nfds = 0 } ; uint32_pack_big(pack+1, ntot) ; if (!unixmessage_put(&a->connection.out, &m)) return 0 ; if (!unixmessage_sender_timed_flush(&a->connection.out, deadline, stamp)) return 0 ; if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) return 0 ; if (!m.len || m.nfds) { unixmessage_drop(&m) ; return (errno = EPROTO, 0) ; } if (m.s[0]) return (errno = (unsigned char)m.s[0], 0) ; if (m.len != 5) return (errno = EPROTO, 0) ; uint32_unpack_big(m.s + 1, &trips) ; if (trips != 1 + (ntot-1) / UNIXMESSAGE_MAXFDS) return (errno = EPROTO, 0) ; } { struct iovec v[1 + (UNIXMESSAGE_MAXFDS << 1)] = { [0] = { .iov_base = ".", .iov_len = 1 } } ; int fds[UNIXMESSAGE_MAXFDS] ; char pack[UNIXMESSAGE_MAXFDS][TAIN_PACK + 1] ; for (unsigned int j = 0 ; j < UNIXMESSAGE_MAXFDS ; j++) { v[1 + (j<<1)].iov_base = pack[j] ; v[1 + (j<<1)].iov_len = TAIN_PACK + 1 ; } for (unsigned int i = 0 ; i < trips ; i++) { { unsigned int n = ntot > UNIXMESSAGE_MAXFDS ? UNIXMESSAGE_MAXFDS : ntot ; unixmessagev m = { .v = v, .vlen = 1 + (n<<1), .fds = fds, .nfds = n } ; for (unsigned int j = 0 ; j < n ; j++, list++, ntot--) { size_t len = strlen(list->id) ; tain_pack(pack[j], &list->limit) ; pack[j][TAIN_PACK] = (unsigned char)len ; v[2 + (j<<1)].iov_base = (char *)list->id ; v[2 + (j<<1)].iov_len = len + 1 ; fds[j] = list->fd ; } if (!unixmessage_putv(&a->connection.out, &m)) return 0 ; } if (!unixmessage_sender_timed_flush(&a->connection.out, deadline, stamp)) return 0 ; { unixmessage m ; if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) return 0 ; if (m.len != 1 || m.nfds) { unixmessage_drop(&m) ; return (errno = EPROTO, 0) ; } if (!error_isagain((unsigned char)m.s[0]) && i < trips-1) return errno = m.s[0] ? (unsigned char)m.s[0] : EPROTO, 0 ; if (i == trips - 1 && m.s[0]) return errno = error_isagain((unsigned char)m.s[0]) ? EPROTO : (unsigned char)m.s[0], 0 ; } } } return 1 ; } s6-2.13.1.0/src/libs6/s6_fdholder_start.c000066400000000000000000000006101470151141200176430ustar00rootroot00000000000000/* ISC license. */ #include #include #include int s6_fdholder_start (s6_fdholder_t *a, char const *path, tain const *deadline, tain *stamp) { int fd = ipc_stream_nb() ; if (fd < 0) return 0 ; if (!ipc_timed_connect(fd, path, deadline, stamp)) { fd_close(fd) ; return 0 ; } s6_fdholder_init(a, fd) ; return 1 ; } s6-2.13.1.0/src/libs6/s6_fdholder_store.c000066400000000000000000000013441470151141200176470ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include int s6_fdholder_store (s6_fdholder_t *a, int fd, char const *id, tain const *limit, tain const *deadline, tain *stamp) { unixmessage m ; if (!s6_fdholder_store_async(a, fd, id, limit)) return 0 ; if (!unixmessage_sender_timed_flush(&a->connection.out, deadline, stamp)) return 0 ; if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) return 0 ; if (m.len != 1 || m.nfds) { unixmessage_drop(&m) ; return (errno = EPROTO, 0) ; } return m.s[0] ? (errno = (unsigned char)m.s[0], 0) : 1 ; } s6-2.13.1.0/src/libs6/s6_fdholder_store_async.c000066400000000000000000000013101470151141200210350ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include int s6_fdholder_store_async (s6_fdholder_t *a, int fd, char const *id, tain const *limit) { size_t idlen = strlen(id) ; char pack[2 + TAIN_PACK] = "S" ; struct iovec v[2] = { { .iov_base = pack, .iov_len = 2 + TAIN_PACK }, { .iov_base = (char *)id, .iov_len = idlen + 1 } } ; unixmessagev m = { .v = v, .vlen = 2, .fds = &fd, .nfds = 1 } ; if (idlen > S6_FDHOLDER_ID_SIZE) return (errno = ENAMETOOLONG, 0) ; tain_pack(pack + 1, limit) ; pack[1+TAIN_PACK] = (unsigned char)idlen ; return unixmessage_putv(&a->connection.out, &m) ; } s6-2.13.1.0/src/libs6/s6_instance_chdirservice.c000066400000000000000000000013751470151141200212060ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include void s6_instance_chdirservice (char const *s) { int fd, r ; size_t len = strlen(s) ; char fn[len + 10] ; if (!*s) strerr_dief1x(100, "invalid service path") ; memcpy(fn, s, len) ; memcpy(fn + len, "/instance", 10) ; if (chdir(fn) == -1) strerr_diefu2sys(111, "chdir to ", fn) ; fd = open_read(S6_SVSCAN_CTLDIR "/lock") ; if (fd < 0) strerr_diefu3sys(111, "open ", fn, "/" S6_SVSCAN_CTLDIR "/lock") ; r = fd_islocked(fd) ; if (r < 0) strerr_diefu3sys(111, "check lock on ", fn, "/" S6_SVSCAN_CTLDIR "/lock") ; if (!r) strerr_dief2x(1, "instanced service not running on ", s) ; fd_close(fd) ; } s6-2.13.1.0/src/libs6/s6_servicedir_file_list.c000066400000000000000000000021641470151141200210360ustar00rootroot00000000000000/* ISC license. */ #include static s6_servicedir_desc const s6_servicedir_file_list_[] = { { .name = "finish", .type = S6_FILETYPE_NORMAL, .options = S6_SVFILE_EXECUTABLE | S6_SVFILE_ATOMIC }, { .name = "run", .type = S6_FILETYPE_NORMAL, .options = S6_SVFILE_EXECUTABLE | S6_SVFILE_MANDATORY | S6_SVFILE_ATOMIC }, { .name = "notification-fd", .type = S6_FILETYPE_UINT, .options = 0 }, { .name = "lock-fd", .type = S6_FILETYPE_UINT, .options = 0 }, { .name = "timeout-kill", .type = S6_FILETYPE_UINT, .options = 0 }, { .name = "timeout-finish", .type = S6_FILETYPE_UINT, .options = 0 }, { .name = "max-death-tally", .type = S6_FILETYPE_UINT, .options = 0 }, { .name = "down-signal", .type = S6_FILETYPE_NORMAL, .options = 0 }, { .name = "flag-newpidns", .type = S6_FILETYPE_EMPTY, .options = 0 }, { .name = "template", .type = S6_FILETYPE_DIR, .options = 0 }, { .name = "data", .type = S6_FILETYPE_DIR, .options = 0 }, { .name = "env", .type = S6_FILETYPE_DIR, .options = 0 }, { .name = 0, .options = 0 } } ; s6_servicedir_desc const *const s6_servicedir_file_list = s6_servicedir_file_list_ ; s6-2.13.1.0/src/libs6/s6_servicedir_instances_recreate_offline.c000066400000000000000000000003541470151141200244260ustar00rootroot00000000000000/* ISC license. */ #include #include int s6_servicedir_instances_recreate_offline (char const *old, char const *new) { return s6_servicedir_instances_recreate_offline_tmp(old, new, &satmp) ; } s6-2.13.1.0/src/libs6/s6_servicedir_instances_recreate_offline_tmp.c000066400000000000000000000043251470151141200253100ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include int s6_servicedir_instances_recreate_offline_tmp (char const *old, char const *new, stralloc *sa) { int n ; mode_t m ; size_t maxlen = 0 ; size_t sabase = sa->len ; size_t oldlen = strlen(old) ; size_t newlen = strlen(new) ; { char olddir[oldlen + 11] ; memcpy(olddir, old, oldlen) ; memcpy(olddir + oldlen, "/instances", 11) ; n = sals(olddir, sa, &maxlen) ; } if (n == -1) return errno == ENOENT ? 0 : -1 ; { size_t pos = sabase ; char olddir[oldlen + 17 + maxlen] ; char templatedir[newlen + 10] ; char newsd[newlen + 11 + maxlen] ; char newdir[newlen + 17 + maxlen] ; char lnk[14 + maxlen] ; memcpy(olddir, old, oldlen) ; memcpy(olddir + oldlen, "/instances/", 11) ; memcpy(templatedir, new, newlen) ; memcpy(templatedir + newlen, "/template", 10) ; memcpy(newsd, new, newlen) ; memcpy(newsd + newlen, "/instance", 10) ; memcpy(newdir, newsd, newlen + 9) ; newdir[newlen + 9] = 's' ; newdir[newlen + 10] = 0 ; memcpy(lnk, "../instances/", 13) ; m = umask(0) ; if (mkdir(newsd, 0755) == -1 && errno != EEXIST) goto merr ; if (mkdir(newdir, 0755) == -1 && errno != EEXIST) goto merr ; umask(m) ; newsd[newlen+9] = '/' ; newdir[newlen+10] = '/' ; while (pos < sa->len) { size_t len = strlen(sa->s + pos) ; memcpy(olddir + oldlen + 11, sa->s + pos, len) ; memcpy(olddir + oldlen + 11 + len, "/down", 6) ; memcpy(newsd + newlen + 10, sa->s + pos, len + 1) ; memcpy(newdir + newlen + 11, sa->s + pos, len + 1) ; memcpy(lnk + 13, sa->s + pos, len + 1) ; if (!hiercopy_tmp(templatedir, newdir, sa)) goto err ; if (symlink(lnk, newsd) == -1 && errno != EEXIST) goto err ; if (access(olddir, F_OK) == 0) { memcpy(newdir + newlen + 11 + len, "/down", 6) ; if (!openwritenclose_unsafe(newdir, "", 0)) goto err ; } else if (errno != ENOENT) goto err ; pos += len + 1 ; } } return n ; merr: umask(m) ; err: sa->len = sabase ; return -1 ; } s6-2.13.1.0/src/libs6/s6_supervise_link.c000066400000000000000000000016161470151141200177100ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include int s6_supervise_link (char const *scdir, char const *const *servicedirs, size_t n, char const *prefix, uint32_t options, tain const *deadline, tain *stamp) { int r ; size_t prefixlen = strlen(prefix) ; stralloc sa = STRALLOC_ZERO ; char const *names[n ? n : 1] ; { size_t indices[n ? n : 1] ; for (size_t i = 0 ; i < n ; i++) { indices[i] = sa.len ; if (!stralloc_catb(&sa, prefix, prefixlen) || !sabasename(&sa, servicedirs[i], strlen(servicedirs[i])) || !stralloc_0(&sa)) goto err ; } for (size_t i = 0 ; i < n ; i++) names[i] = sa.s + indices[i] ; } r = s6_supervise_link_names(scdir, servicedirs, names, n, options, deadline, stamp) ; stralloc_free(&sa) ; return r ; err: stralloc_free(&sa) ; return -1 ; } s6-2.13.1.0/src/libs6/s6_supervise_link_names.c000066400000000000000000000107161470151141200210740ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include static inline void do_unlink (char const *scdir, char const *const *names, size_t n, uint32_t killopts) { for (size_t i = 0 ; i < n ; i++) s6_supervise_unlink(scdir, names[i], killopts) ; } static uint16_t registerit (ftrigr_t *a, char *fn, size_t len, gid_t gid, uint32_t options, tain const *deadline, tain *stamp) { if (options & 4) { int fd ; memcpy(fn + len, "/down", 6) ; fd = open_trunc(fn) ; if (fd < 0) return 0 ; fd_close(fd) ; } memcpy(fn + len, "/" S6_SUPERVISE_EVENTDIR, 1 + sizeof(S6_SUPERVISE_EVENTDIR)) ; if (!ftrigw_fifodir_make(fn, gid, options & 1)) return 0 ; return ftrigr_subscribe(a, fn, "s", 0, deadline, stamp) ; } /* options: bit 0: force event/ mode bit 1: make event/ public bit 2: don't start the service bit 3: remove down files after starting supervisors bit 4: allow links to relative paths */ int s6_supervise_link_names (char const *scdir, char const *const *servicedirs, char const *const *names, size_t n, uint32_t options, tain const *deadline, tain *stamp) { size_t maxnlen = 0, maxlen = 0 ; size_t ntotal = n ; unsigned char locked[bitarray_div8(n)] ; unsigned char logged[bitarray_div8(n)] ; if (!n) return 0 ; memset(locked, 0, bitarray_div8(n)) ; memset(logged, 0, bitarray_div8(n)) ; for (size_t i = 0 ; i < n ; i++) { struct stat st ; size_t len = strlen(servicedirs[i]) ; size_t nlen = strlen(names[i]) ; int h ; char subdir[len + 5] ; if (nlen > maxnlen) maxnlen = nlen ; if (len > maxlen) maxlen = len ; h = s6_svc_ok(servicedirs[i]) ; if (h < 0) return -1 ; if (h) bitarray_set(locked, i) ; memcpy(subdir, servicedirs[i], len) ; memcpy(subdir + len, "/log", 5) ; if (stat(subdir, &st) < 0) { if (errno != ENOENT) return -1 ; } else { int r ; if (!S_ISDIR(st.st_mode)) return (errno = ENOTDIR, -1) ; r = s6_svc_ok(subdir) ; if (r < 0) return -1 ; if (r != h) return (errno = EINVAL, -1) ; bitarray_set(logged, i) ; ntotal++ ; } } { ftrigr_t a = FTRIGR_ZERO ; stralloc rpsa = STRALLOC_ZERO ; gid_t gid = options & 2 ? -1 : getegid() ; size_t scdirlen = strlen(scdir) ; unsigned int m = 0 ; size_t i = 0 ; uint32_t killopts = 0 ; int r ; uint16_t ids[ntotal] ; char lname[scdirlen + maxnlen + 7] ; char fn[maxlen + 5 + (sizeof(S6_SUPERVISE_EVENTDIR) > 5 ? sizeof(S6_SUPERVISE_EVENTDIR) : 5)] ; if (!ftrigr_startf(&a, deadline, stamp)) return -1 ; memcpy(lname, scdir, scdirlen) ; lname[scdirlen] = '/' ; for (; i < n ; i++) { char const *src = servicedirs[i] ; size_t len = strlen(servicedirs[i]) ; int h = bitarray_peek(locked, i) ; memcpy(fn, servicedirs[i], len) ; if (!h) { ids[m] = registerit(&a, fn, len, gid, options, deadline, stamp) ; if (!ids[m++]) goto err ; if (bitarray_peek(logged, i)) { memcpy(fn + len, "/log", 4) ; ids[m] = registerit(&a, fn, len + 4, gid, options, deadline, stamp) ; if (!ids[m++]) goto err ; } } fn[len] = 0 ; strcpy(lname + scdirlen + 1, names[i]) ; if (!(options & 16) && servicedirs[i][0] != '/') { rpsa.len = 0 ; if (sarealpath(&rpsa, servicedirs[i]) < 0 || !stralloc_0(&rpsa)) goto err ; src = rpsa.s ; } if (symlink(src, lname) < 0 && (!h || errno != EEXIST)) goto err ; } stralloc_free(&rpsa) ; r = s6_svc_writectl(scdir, S6_SVSCAN_CTLDIR, "a", 1) ; if (!r) errno = ENXIO ; if (r <= 0) goto errsa ; killopts = 3 ; if (ftrigr_wait_and(&a, ids, m, deadline, stamp) < 0) goto errsa ; ftrigr_end(&a) ; if (options & 8) { for (size_t i = 0 ; i < n ; i++) { size_t nlen = strlen(names[i]) ; memcpy(lname + scdirlen + 1, names[i], nlen) ; memcpy(lname + scdirlen + 1 + nlen, "/down", 6) ; unlink_void(lname) ; } } return m ; err: stralloc_free(&rpsa) ; errsa: ftrigr_end(&a) ; do_unlink(scdir, names, i, killopts | (options & 4)) ; } return -1 ; } s6-2.13.1.0/src/libs6/s6_supervise_unlink.c000066400000000000000000000025771470151141200202620ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include void s6_supervise_unlink (char const *scdir, char const *name, uint32_t options) { int e = errno ; int fd = -1, fdlog = -1 ; size_t scdirlen = strlen(scdir) ; size_t namelen = strlen(name) ; char fn[scdirlen + namelen + sizeof(S6_SUPERVISE_CTLDIR) + 14] ; memcpy(fn, scdir, scdirlen) ; fn[scdirlen] = '/' ; memcpy(fn + scdirlen + 1, name, namelen) ; if (options & 4) { memcpy(fn + scdirlen + 1 + namelen, "/down", 6) ; unlink_void(fn) ; } if (options & 1) { memcpy(fn + scdirlen + 1 + namelen, "/" S6_SUPERVISE_CTLDIR, sizeof(S6_SUPERVISE_CTLDIR)) ; memcpy(fn + scdirlen + 1 + namelen + sizeof(S6_SUPERVISE_CTLDIR), "/control", 9) ; fd = open_write(fn) ; memcpy(fn + scdirlen + 1 + namelen, "/log/" S6_SUPERVISE_CTLDIR, 4 + sizeof(S6_SUPERVISE_CTLDIR)) ; memcpy(fn + scdirlen + 5 + namelen + sizeof(S6_SUPERVISE_CTLDIR), "/control", 9) ; fdlog = open_write(fn) ; } fn[scdirlen + 1 + namelen] = 0 ; unlink_void(fn) ; if (fd >= 0) { fd_write(fd, "xd", 1 + !!(options & 2)) ; fd_close(fd) ; } if (fdlog >= 0) { fd_write(fdlog, "xo", 1 + !!(options & 2)) ; fd_close(fdlog) ; } errno = e ; } s6-2.13.1.0/src/libs6/s6_supervise_unlink_names.c000066400000000000000000000052511470151141200214350ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include static uint16_t registerit (ftrigr_t *a, char *fn, size_t len, tain const *deadline, tain *stamp) { memcpy(fn + len, "/" S6_SUPERVISE_EVENTDIR, sizeof(S6_SUPERVISE_EVENTDIR) + 1) ; return ftrigr_subscribe(a, fn, "x", 0, deadline, stamp) ; } /* options: bit 0: wait for s6-supervise to exit */ int s6_supervise_unlink_names (char const *scdir, char const *const *names, size_t n, uint32_t options, tain const *deadline, tain *stamp) { size_t scdirlen = strlen(scdir) ; size_t ntotal = n ; unsigned char locked[bitarray_div8(n)] ; unsigned char logged[bitarray_div8(n)] ; if (!n) return 0 ; memset(locked, 0, bitarray_div8(n)) ; memset(logged, 0, bitarray_div8(n)) ; if (options & 1) for (size_t i = 0 ; i < n ; i++) { struct stat st ; size_t nlen = strlen(names[i]) ; int h ; char fn[scdirlen + nlen + 6] ; memcpy(fn, scdir, scdirlen) ; fn[scdirlen] = '/' ; memcpy(fn + scdirlen + 1, names[i], nlen + 1) ; h = s6_svc_ok(fn) ; if (h < 0) return -1 ; if (h) bitarray_set(locked, i) ; memcpy(fn + scdirlen + 1 + nlen, "/log", 5) ; if (stat(fn, &st) < 0) { if (errno != ENOENT) return -1 ; } else { int r ; if (!S_ISDIR(st.st_mode)) return (errno = ENOTDIR, -1) ; r = s6_svc_ok(fn) ; if (r < 0) return -1 ; if (r != h) return (errno = EINVAL, -1) ; bitarray_set(logged, i) ; ntotal++ ; } } { ftrigr_t a = FTRIGR_ZERO ; unsigned int m = 0 ; uint16_t ids[ntotal] ; if (options & 1 && !ftrigr_startf(&a, deadline, stamp)) return -1 ; for (size_t i = 0 ; i < n ; i++) { size_t nlen = strlen(names[i]) ; char fn[scdirlen + nlen + 6 + sizeof(S6_SUPERVISE_EVENTDIR)] ; memcpy(fn, scdir, scdirlen) ; fn[scdirlen] = '/' ; memcpy(fn + scdirlen + 1, names[i], nlen) ; if (options & 1 && bitarray_peek(locked, i)) { ids[m] = registerit(&a, fn, scdirlen + 1 + nlen, deadline, stamp) ; if (ids[m]) m++ ; if (bitarray_peek(logged, i)) { memcpy(fn + scdirlen + 1 + nlen, "/log", 4) ; ids[m] = registerit(&a, fn, scdirlen + 5 + nlen, deadline, stamp) ; if (ids[m]) m++ ; } } fn[scdirlen + 1 + nlen] = 0 ; unlink_void(fn) ; } s6_svc_writectl(scdir, S6_SVSCAN_CTLDIR, "an", 2) ; if (options & 1) { ftrigr_wait_and(&a, ids, m, deadline, stamp) ; ftrigr_end(&a) ; } return m ; } } s6-2.13.1.0/src/libs6/s6_svc_ok.c000066400000000000000000000010261470151141200161250ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include int s6_svc_ok (char const *dir) { int r ; int e = errno ; int fd ; size_t dirlen = strlen(dir) ; char fn[dirlen + 6 + sizeof(S6_SUPERVISE_CTLDIR)] ; memcpy(fn, dir, dirlen) ; memcpy(fn + dirlen, "/" S6_SUPERVISE_CTLDIR "/lock", 6 + sizeof(S6_SUPERVISE_CTLDIR)) ; fd = open_read(fn) ; if (fd < 0) return errno == ENOENT ? (errno = e, 0) : -1 ; r = fd_islocked(fd) ; fd_close(fd) ; return r ; } s6-2.13.1.0/src/libs6/s6_svc_write.c000066400000000000000000000010541470151141200166470ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include int s6_svc_write (char const *fifo, char const *data, size_t datalen) { int fd = open_write(fifo) ; if (fd < 0) switch (errno) { case ENXIO : return 0 ; case ENOENT : case ENOTDIR : case EISDIR : return -2 ; default : return -1 ; } if (ndelay_off(fd) == -1) return -1 ; if (datalen && fd_write(fd, data, datalen) == -1) { fd_close(fd) ; return -1 ; } fd_close(fd) ; return 1 ; } s6-2.13.1.0/src/libs6/s6_svc_writectl.c000066400000000000000000000020321470151141200173470ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include int s6_svc_writectl (char const *service, char const *subdir, char const *s, size_t len) { size_t svlen = strlen(service) ; size_t sublen = strlen(subdir) ; int r ; char fn[svlen + sublen + 10] ; memcpy(fn, service, svlen) ; fn[svlen] = '/' ; memcpy(fn + svlen + 1, subdir, sublen) ; memcpy(fn + svlen + 1 + sublen, "/control", 9) ; r = s6_svc_write(fn, s, len) ; if (r != -2) return r ; #ifdef SKALIBS_HASODIRECTORY /* Investigate what went wrong */ { int fdsub ; int fd = open2(service, O_RDONLY | O_DIRECTORY) ; if (fd == -1) return -1 ; fdsub = open2_at(fd, subdir, O_RDONLY | O_DIRECTORY) ; fd_close(fd) ; if (fdsub == -1) return (errno == ENOENT) ? 0 : -2 ; fd_close(fdsub) ; return -2 ; } #else /* Too bad, get a better system */ return -2 ; #endif } s6-2.13.1.0/src/libs6/s6_svstatus_pack.c000066400000000000000000000010571470151141200175370ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include void s6_svstatus_pack (char *pack, s6_svstatus_t const *sv) { tain_pack(pack, &sv->stamp) ; tain_pack(pack + 12, &sv->readystamp) ; uint64_pack_big(pack + 24, (uint64_t)sv->pid) ; uint64_pack_big(pack + 32, (uint64_t)sv->pgid) ; uint16_pack_big(pack + 40, (uint16_t)sv->wstat) ; pack[42] = sv->flagpaused | (sv->flagfinishing << 1) | (sv->flagwantup << 2) | (sv->flagready << 3) ; } s6-2.13.1.0/src/libs6/s6_svstatus_read.c000066400000000000000000000011221470151141200175250ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include int s6_svstatus_read (char const *dir, s6_svstatus_t *status) { ssize_t r ; size_t n = strlen(dir) ; char pack[S6_SVSTATUS_SIZE] ; char tmp[n + 1 + sizeof(S6_SVSTATUS_FILENAME)] ; memcpy(tmp, dir, n) ; memcpy(tmp + n, "/" S6_SVSTATUS_FILENAME, 1 + sizeof(S6_SVSTATUS_FILENAME)) ; r = openreadnclose(tmp, pack, S6_SVSTATUS_SIZE) ; if (r == -1) return 0 ; if (r < S6_SVSTATUS_SIZE) return (errno = EPIPE, 0) ; s6_svstatus_unpack(pack, status) ; return 1 ; } s6-2.13.1.0/src/libs6/s6_svstatus_unpack.c000066400000000000000000000012201470151141200200720ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include void s6_svstatus_unpack (char const *pack, s6_svstatus_t *sv) { uint64_t pid ; uint16_t wstat ; tain_unpack(pack, &sv->stamp) ; tain_unpack(pack + 12, &sv->readystamp) ; uint64_unpack_big(pack + 24, &pid) ; sv->pid = pid ; uint64_unpack_big(pack + 32, &pid) ; sv->pgid = pid ; uint16_unpack_big(pack + 40, &wstat) ; sv->wstat = wstat ; sv->flagpaused = pack[42] & 1 ; sv->flagfinishing = !!(pack[42] & 2) ; sv->flagwantup = !!(pack[42] & 4) ; sv->flagready = !!(pack[42] & 8) ; } s6-2.13.1.0/src/libs6/s6_svstatus_write.c000066400000000000000000000007511470151141200177530ustar00rootroot00000000000000/* ISC license. */ #include #include #include int s6_svstatus_write (char const *dir, s6_svstatus_t const *status) { size_t n = strlen(dir) ; char pack[S6_SVSTATUS_SIZE] ; char tmp[n + 1 + sizeof(S6_SVSTATUS_FILENAME)] ; memcpy(tmp, dir, n) ; memcpy(tmp + n, "/" S6_SVSTATUS_FILENAME, 1 + sizeof(S6_SVSTATUS_FILENAME)) ; s6_svstatus_pack(pack, status) ; return openwritenclose_suffix(tmp, pack, S6_SVSTATUS_SIZE, ".new") ; } s6-2.13.1.0/src/pipe-tools/000077500000000000000000000000001470151141200151425ustar00rootroot00000000000000s6-2.13.1.0/src/pipe-tools/deps-exe/000077500000000000000000000000001470151141200166545ustar00rootroot00000000000000s6-2.13.1.0/src/pipe-tools/deps-exe/s6-cleanfifodir000066400000000000000000000000231470151141200215450ustar00rootroot00000000000000${LIBS6} -lskarnet s6-2.13.1.0/src/pipe-tools/deps-exe/s6-ftrig-listen000066400000000000000000000001161470151141200215320ustar00rootroot00000000000000${LIBS6} ${EXECLINE_LIB} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/pipe-tools/deps-exe/s6-ftrig-listen1000066400000000000000000000000761470151141200216200ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/pipe-tools/deps-exe/s6-ftrig-notify000066400000000000000000000000231470151141200215410ustar00rootroot00000000000000${LIBS6} -lskarnet s6-2.13.1.0/src/pipe-tools/deps-exe/s6-ftrig-wait000066400000000000000000000000761470151141200212050ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/pipe-tools/deps-exe/s6-mkfifodir000066400000000000000000000000231470151141200210720ustar00rootroot00000000000000${LIBS6} -lskarnet s6-2.13.1.0/src/pipe-tools/s6-cleanfifodir.c000066400000000000000000000005251470151141200202630ustar00rootroot00000000000000/* ISC license. */ #include #include #define USAGE "s6-cleanfifodir fifodir" int main (int argc, char const *const *argv) { PROG = "s6-cleanfifodir" ; if (argc < 2) strerr_dieusage(100, USAGE) ; if (!ftrigw_clean(argv[1])) strerr_diefu2sys(111, "clean up fifodir at ", argv[1]) ; return 0 ; } s6-2.13.1.0/src/pipe-tools/s6-ftrig-listen.c000066400000000000000000000065201470151141200202460ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-ftrig-listen [ -a | -o ] [ -t timeout ] fifodir1 regexp1 ... \"\" prog..." #define dieusage() strerr_dieusage(100, USAGE) static void handle_signals (void) { for (;;) switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "selfpipe_read") ; case 0 : return ; case SIGCHLD : wait_reap() ; break ; default : strerr_dief1x(101, "unexpected data in selfpipe") ; } } int main (int argc, char const **argv, char const *const *envp) { iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ; tain deadline, tto ; ftrigr_t a = FTRIGR_ZERO ; int argc1 ; unsigned int i = 0 ; char or = 0 ; PROG = "s6-ftrig-listen" ; { unsigned int t = 0 ; for (;;) { int opt = lgetopt(argc, argv, "aot:") ; if (opt == -1) break ; switch (opt) { case 'a' : or = 0 ; break ; case 'o' : or = 1 ; break ; case 't' : if (uint0_scan(subgetopt_here.arg, &t)) break ; default : dieusage() ; } } if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ; } if (argc < 4) dieusage() ; argc1 = s6_el_semicolon(argv) ; if (!argc1 || (argc1 & 1) || (argc == argc1 + 1)) dieusage() ; if (argc1 >= argc) strerr_dief1x(100, "unterminated fifodir+regex block") ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &tto) ; x[0].fd = selfpipe_init() ; if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; if (!selfpipe_trap(SIGCHLD)) strerr_diefu1sys(111, "selfpipe_trap") ; if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; if (!ftrigr_startf_g(&a, &deadline)) strerr_diefu1sys(111, "ftrigr_startf") ; x[1].fd = ftrigr_fd(&a) ; { pid_t pid = 0 ; unsigned int idlen = argc1 >> 1 ; uint16_t ids[idlen] ; for (; i < idlen ; i++) { ids[i] = ftrigr_subscribe_g(&a, argv[i<<1], argv[(i<<1)+1], 0, &deadline) ; if (!ids[i]) strerr_diefu4sys(111, "subscribe to ", argv[i<<1], " with regexp ", argv[(i<<1)+1]) ; } pid = cspawn(argv[argc1 + 1], argv + argc1 + 1, envp, CSPAWN_FLAGS_SELFPIPE_FINISH, 0, 0) ; if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1 + 1]) ; for (;;) { int r ; i = 0 ; while (i < idlen) { char dummy ; r = ftrigr_check(&a, ids[i], &dummy) ; if (r < 0) strerr_diefu1sys(111, "ftrigr_check") ; else if (!r) i++ ; else if (or) idlen = 0 ; else ids[i] = ids[--idlen] ; } if (!idlen) break ; r = iopause_g(x, 2, &deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; else if (!r) { errno = ETIMEDOUT ; strerr_diefu1sys(1, "get expected event") ; } if (x[0].revents & IOPAUSE_READ) handle_signals() ; if (x[1].revents & IOPAUSE_READ) { if (ftrigr_update(&a) < 0) strerr_diefu1sys(111, "ftrigr_update") ; } } } return 0 ; } s6-2.13.1.0/src/pipe-tools/s6-ftrig-listen1.c000066400000000000000000000053471470151141200203350ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-ftrig-listen1 [ -t timeout ] fifodir regexp prog..." static void handle_signals (void) { for (;;) switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "selfpipe_read") ; case 0 : return ; case SIGCHLD : wait_reap() ; break ; default : strerr_dief1x(101, "unexpected data in selfpipe") ; } } int main (int argc, char const *const *argv, char const *const *envp) { iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ; tain deadline, tto ; ftrigr_t a = FTRIGR_ZERO ; pid_t pid ; uint16_t id ; char pack[2] = " \n" ; PROG = "s6-ftrig-listen1" ; { unsigned int t = 0 ; for (;;) { int opt = lgetopt(argc, argv, "t:") ; if (opt == -1) break ; switch (opt) { case 't' : if (uint0_scan(subgetopt_here.arg, &t)) break ; default : strerr_dieusage(100, USAGE) ; } } if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ; } if (argc < 3) strerr_dieusage(100, USAGE) ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &tto) ; if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "sig_ignore") ; if (!ftrigr_startf_g(&a, &deadline)) strerr_diefu1sys(111, "ftrigr_startf") ; id = ftrigr_subscribe_g(&a, argv[0], argv[1], 0, &deadline) ; if (!id) strerr_diefu4sys(111, "subscribe to ", argv[0], " with regexp ", argv[1]) ; x[0].fd = selfpipe_init() ; if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; if (!selfpipe_trap(SIGCHLD)) strerr_diefu1sys(111, "selfpipe_trap") ; x[1].fd = ftrigr_fd(&a) ; pid = cspawn(argv[2], argv+2, envp, CSPAWN_FLAGS_SELFPIPE_FINISH, 0, 0) ; if (!pid) strerr_diefu2sys(111, "spawn ", argv[2]) ; for (;;) { int r = ftrigr_check(&a, id, &pack[0]) ; if (r < 0) strerr_diefu1sys(111, "ftrigr_check") ; if (r) break ; r = iopause_g(x, 2, &deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; else if (!r) { errno = ETIMEDOUT ; strerr_diefu1sys(1, "get expected event") ; } if (x[0].revents & IOPAUSE_READ) handle_signals() ; if (x[1].revents & IOPAUSE_READ) { if (ftrigr_update(&a) < 0) strerr_diefu1sys(111, "ftrigr_update") ; } } if (allwrite(1, pack, 2) < 2) strerr_diefu1sys(111, "write to stdout") ; return 0 ; } s6-2.13.1.0/src/pipe-tools/s6-ftrig-notify.c000066400000000000000000000005361470151141200202610ustar00rootroot00000000000000/* ISC license. */ #include #include #define USAGE "s6-ftrig-notify fifodir message" int main (int argc, char const *const *argv) { PROG = "s6-ftrig-notify" ; if (argc < 3) strerr_dieusage(100, USAGE) ; if (ftrigw_notifys(argv[1], argv[2]) < 0) strerr_diefu2sys(111, "notify ", argv[1]) ; return 0 ; } s6-2.13.1.0/src/pipe-tools/s6-ftrig-wait.c000066400000000000000000000026621470151141200177170ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #define USAGE "s6-ftrig-wait [ -t timeout ] fifodir regexp" int main (int argc, char const *const *argv) { tain deadline, tto ; ftrigr_t a = FTRIGR_ZERO ; uint16_t id ; char pack[2] = " \n" ; PROG = "s6-ftrig-wait" ; { unsigned int t = 0 ; for (;;) { int opt = lgetopt(argc, argv, "t:") ; if (opt == -1) break ; switch (opt) { case 't' : if (uint0_scan(subgetopt_here.arg, &t)) break ; default : strerr_dieusage(100, USAGE) ; } } if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ; } if (argc < 2) strerr_dieusage(100, USAGE) ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &tto) ; if (!ftrigr_startf_g(&a, &deadline)) strerr_diefu1sys(111, "ftrigr_startf") ; id = ftrigr_subscribe_g(&a, argv[0], argv[1], 0, &deadline) ; if (!id) strerr_diefu4sys(111, "subscribe to ", argv[0], " with regexp ", argv[1]) ; if (ftrigr_wait_or_g(&a, &id, 1, &deadline, &pack[0]) == -1) strerr_diefu2sys((errno == ETIMEDOUT) ? 1 : 111, "match regexp on ", argv[1]) ; if (allwrite(1, pack, 2) < 2) strerr_diefu1sys(111, "write to stdout") ; return 0 ; } s6-2.13.1.0/src/pipe-tools/s6-mkfifodir.c000066400000000000000000000015131470151141200176060ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #define USAGE "s6-mkfifodir [ -f ] [ -g gid ] fifodir" int main (int argc, char const *const *argv) { subgetopt l = SUBGETOPT_ZERO ; gid_t gid = -1 ; int force = 0 ; PROG = "s6-mkfifodir" ; for (;;) { int opt = subgetopt_r(argc, argv, "fg:", &l) ; if (opt == -1) break ; switch (opt) { case 'f' : force = 1 ; break ; case 'g' : if (!gid0_scan(l.arg, &gid)) strerr_dieusage(100, USAGE) ; break ; default : strerr_dieusage(100, USAGE) ; } } argc -= l.ind ; argv += l.ind ; if (argc < 1) strerr_dieusage(100, USAGE) ; if (!ftrigw_fifodir_make(*argv, gid, force)) strerr_diefu2sys(111, "create fifodir at ", *argv) ; return 0 ; } s6-2.13.1.0/src/supervision/000077500000000000000000000000001470151141200154355ustar00rootroot00000000000000s6-2.13.1.0/src/supervision/deps-exe/000077500000000000000000000000001470151141200171475ustar00rootroot00000000000000s6-2.13.1.0/src/supervision/deps-exe/s6-notifyoncheck000066400000000000000000000000761470151141200222660ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/supervision/deps-exe/s6-permafailon000066400000000000000000000000431470151141200217120ustar00rootroot00000000000000${LIBS6} -lskarnet ${SYSCLOCK_LIB} s6-2.13.1.0/src/supervision/deps-exe/s6-supervise000066400000000000000000000000501470151141200214400ustar00rootroot00000000000000libs6.a.xyzzy -lskarnet ${SYSCLOCK_LIB} s6-2.13.1.0/src/supervision/deps-exe/s6-svc000066400000000000000000000000231470151141200202060ustar00rootroot00000000000000${LIBS6} -lskarnet s6-2.13.1.0/src/supervision/deps-exe/s6-svdt000066400000000000000000000000231470151141200203730ustar00rootroot00000000000000${LIBS6} -lskarnet s6-2.13.1.0/src/supervision/deps-exe/s6-svdt-clear000066400000000000000000000000231470151141200214570ustar00rootroot00000000000000${LIBS6} -lskarnet s6-2.13.1.0/src/supervision/deps-exe/s6-svlink000066400000000000000000000000761470151141200207310ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/supervision/deps-exe/s6-svlisten000066400000000000000000000001761470151141200212730ustar00rootroot00000000000000s6_svlisten_signal_handler.o s6_svlisten_loop.o ${LIBS6} ${EXECLINE_LIB} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/supervision/deps-exe/s6-svlisten1000066400000000000000000000001561470151141200213520ustar00rootroot00000000000000s6_svlisten_signal_handler.o s6_svlisten_loop.o ${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/supervision/deps-exe/s6-svok000066400000000000000000000000231470151141200203750ustar00rootroot00000000000000${LIBS6} -lskarnet s6-2.13.1.0/src/supervision/deps-exe/s6-svperms000066400000000000000000000000251470151141200211140ustar00rootroot00000000000000${LIBNSSS} -lskarnet s6-2.13.1.0/src/supervision/deps-exe/s6-svscan000066400000000000000000000000471470151141200207160ustar00rootroot00000000000000-lskarnet ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/supervision/deps-exe/s6-svscanctl000066400000000000000000000000231470151141200214130ustar00rootroot00000000000000${LIBS6} -lskarnet s6-2.13.1.0/src/supervision/deps-exe/s6-svstat000066400000000000000000000000431470151141200207410ustar00rootroot00000000000000${LIBS6} -lskarnet ${SYSCLOCK_LIB} s6-2.13.1.0/src/supervision/deps-exe/s6-svunlink000066400000000000000000000000761470151141200212740ustar00rootroot00000000000000${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/supervision/deps-exe/s6-svwait000066400000000000000000000001211470151141200207270ustar00rootroot00000000000000s6_svlisten_loop.o ${LIBS6} -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-2.13.1.0/src/supervision/s6-notifyoncheck.c000066400000000000000000000175211470151141200210000ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef S6_USE_EXECLINE #include #define USAGE "s6-notifyoncheck [ -d ] [ -3 fd ] [ -s initialsleep ] [ -T globaltimeout ] [ -t localtimeout ] [ -w waitingtime ] [ -n tries ] [ -c \"checkprog...\" ] prog..." #define OPTIONS "d3:s:T:t:w:n:c:" #else #define USAGE "s6-notifyoncheck [ -d ] [ -3 fd ] [ -s initialsleep ] [ -T globaltimeout ] [ -t localtimeout ] [ -w waitingtime ] [ -n tries ] prog..." #define OPTIONS "d3:s:T:t:w:n:" #endif #define dieusage() strerr_dieusage(100, USAGE) static inline int read_uint (char const *file, unsigned int *fd) { char buf[UINT_FMT + 1] ; ssize_t r = openreadnclose_nb(file, buf, UINT_FMT) ; if (r == -1) return -1 ; buf[byte_chr(buf, r, '\n')] = 0 ; return !!uint0_scan(buf, fd) ; } static inline int handle_signals (pid_t pid, int *w) { int gotit = 0 ; for (;;) { switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "selfpipe_read") ; case 0 : return gotit ; case SIGCHLD : { int wstat ; if (wait_pid_nohang(pid, &wstat) == pid) { *w = wstat ; gotit = 1 ; } break ; } } } } static int handle_event (ftrigr_t *a, uint16_t id, pid_t pid) { int r ; char what ; if (ftrigr_update(a) < 0) strerr_diefu1sys(111, "ftrigr_update") ; r = ftrigr_check(a, id, &what) ; if (r < 0) strerr_diefu1sys(111, "ftrigr_check") ; if (r && what == 'd') { if (pid) kill(pid, SIGTERM) ; return 1 ; } return 0 ; } int main (int argc, char const *const *argv, char const *const *envp) { ftrigr_t a = FTRIGR_ZERO ; iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .events = IOPAUSE_READ } } ; char const *childargv[4] = { "./data/check", 0, 0, 0 } ; #ifdef S6_USE_EXECLINE char const *checkprog = 0 ; #endif unsigned int fd ; int df = 0 ; int autodetect = 1 ; int p[2] ; tain globaldeadline, sleeptto, localtto, waittto ; unsigned int tries = 7 ; uint16_t id ; PROG = "s6-notifyoncheck" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int initialsleep = 10, globaltimeout = 0, localtimeout = 0, waitingtime = 1000 ; for (;;) { int opt = subgetopt_r(argc, argv, OPTIONS, &l) ; if (opt == -1) break ; switch (opt) { case 'd' : df = 1 ; break ; case '3' : if (!uint0_scan(l.arg, &fd)) dieusage() ; autodetect = 0 ; break ; case 's' : if (!uint0_scan(l.arg, &initialsleep)) dieusage() ; break ; case 'T' : if (!uint0_scan(l.arg, &globaltimeout)) dieusage() ; break ; case 't' : if (!uint0_scan(l.arg, &localtimeout)) dieusage() ; break ; case 'w' : if (!uint0_scan(l.arg, &waitingtime)) dieusage() ; break ; case 'n' : if (!uint0_scan(l.arg, &tries)) dieusage() ; break ; #ifdef S6_USE_EXECLINE case 'c' : checkprog = l.arg ; break ; #endif default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (!argc) dieusage() ; if (!tain_from_millisecs(&sleeptto, initialsleep)) dieusage() ; if (globaltimeout) tain_from_millisecs(&globaldeadline, globaltimeout) ; else globaldeadline = tain_infinite_relative ; if (localtimeout) tain_from_millisecs(&localtto, localtimeout) ; else localtto = tain_infinite_relative ; if (waitingtime) tain_from_millisecs(&waittto, waitingtime) ; else waittto = tain_infinite_relative ; if (!tries) tries = UINT_MAX ; } { int r = s6_svc_ok(".") ; if (r < 0) strerr_diefu1sys(111, "sanity-check current service directory") ; if (!r) strerr_dief1x(100, "s6-supervise not running.") ; } #ifdef S6_USE_EXECLINE if (checkprog) { childargv[0] = EXECLINE_EXTBINPREFIX "execlineb" ; childargv[1] = "-Pc" ; childargv[2] = checkprog ; } #endif if (autodetect) { int r = read_uint("notification-fd", &fd) ; if (r < 0) strerr_diefu2sys(111, "read ", "./notification-fd") ; if (!r) strerr_dief2x(100, "invalid ", "./notification-fd") ; } if (fcntl(fd, F_GETFD) < 0) strerr_dief2sys(111, "notification-fd", " sanity check failed") ; tain_now_set_stopwatch_g() ; tain_add_g(&globaldeadline, &globaldeadline) ; /* Fork, let the parent exec into the daemon, keep working in the child. We want the child to die if the parent dies, because no need to keep polling a dead service. And another child will be spawned next time the service is relaunched by s6-supervise. We could keep a pipe from the parent to the child, for death notification, but that's an additional fd forever open in the parent, which is not good (we need to be 100% transparent). So we're using ftrigr to listen to a 'd' event in the servicedir's fifodir. It's much heavier, but temporary - it doesn't use permanent resources in the daemon - and we're polling anyway, so the user doesn't care about being 100% lightweight. We need some voodoo synchronization so ftrigr_start can be started from the child without a race condition. */ if (pipecoe(p) < 0) strerr_diefu1sys(111, "pipe") ; switch (df ? doublefork() : fork()) { case -1: strerr_diefu1sys(111, df ? "doublefork" : "fork") ; case 0 : break ; default: { char c ; close((int)fd) ; if (read(p[0], &c, 1) < 1) strerr_diefu1x(111, "synchronize with child") ; xexec_e(argv, envp) ; } } PROG = "s6-notifyoncheck (child)" ; close(p[0]) ; if (!ftrigr_startf_g(&a, &globaldeadline)) strerr_diefu1sys(111, "ftrigr_startf") ; id = ftrigr_subscribe_g(&a, "event", "d", 0, &globaldeadline) ; if (!id) strerr_diefu1sys(111, "ftrigr_subscribe to event fifodir") ; x[0].fd = selfpipe_init() ; if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "trap SIGCHLD") ; x[1].fd = ftrigr_fd(&a) ; if (fd_write(p[1], "", 1) < 1) strerr_diefu1sys(2, "synchronize with parent") ; close(p[1]) ; /* Loop around a sleep and a ./data/check invocation */ while (tries == UINT_MAX || tries--) { tain deadline = globaldeadline ; tain localdeadline ; pid_t pid ; tain_add_g(&localdeadline, &sleeptto) ; sleeptto = waittto ; if (tain_less(&localdeadline, &deadline)) deadline = localdeadline ; for (;;) { int r = iopause_g(x+1, 1, &deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; if (!r) { if (!tain_future(&globaldeadline)) return 3 ; else break ; } if (handle_event(&a, id, 0)) return 2 ; } pid = cspawn(childargv[0], childargv, envp, CSPAWN_FLAGS_SELFPIPE_FINISH, 0, 0) ; if (!pid) { strerr_warnwu2sys("spawn ", childargv[0]) ; continue ; } deadline = globaldeadline ; tain_add_g(&localdeadline, &localtto) ; if (tain_less(&localdeadline, &deadline)) deadline = localdeadline ; for (;;) { int r = iopause_g(x, 2, &deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; if (!r) { if (!tain_future(&globaldeadline)) { kill(pid, SIGTERM) ; return 3 ; } else break ; } if (x[0].revents & IOPAUSE_READ) { int wstat ; if (handle_signals(pid, &wstat)) { if (WIFEXITED(wstat) && !WEXITSTATUS(wstat)) { fd_write((int)fd, "\n", 1) ; return 0 ; } else break ; } } if (x[1].revents & IOPAUSE_READ && handle_event(&a, id, pid)) return 2 ; } } return 1 ; } s6-2.13.1.0/src/supervision/s6-permafailon.c000066400000000000000000000067131470151141200204330ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-permafailon seconds deathcount statuslist prog..." #define dieusage() strerr_dieusage(100, USAGE) static inline void list_scan (char const *s, unsigned char *codes, sigset_t *sigs) { size_t pos = 0 ; memset(codes, 0, 32) ; sigemptyset(sigs) ; while (s[pos]) { unsigned int u ; size_t len = uint_scan(s + pos, &u) ; if (len) { if (u > 255) strerr_dief1x(100, "invalid exit code") ; pos += len ; if (s[pos] == '-') { unsigned int v ; pos++ ; len = uint_scan(s + pos, &v) ; if (!len) strerr_dief1x(100, "invalid interval specification") ; if (v > 255) strerr_dief1x(100, "invalid exit code") ; if (v < u) strerr_dief1x(100, "invalid interval") ; pos += len ; bitarray_setn(codes, u, v - u + 1) ; } else bitarray_set(codes, u) ; } else { int sig ; size_t next = pos ; while (!strchr(",; \n\r\t", s[next])) next++ ; char tmp[next - pos + 1] ; memcpy(tmp, s + pos, next - pos) ; tmp[next - pos] = 0 ; len = sig0_scan(tmp, &sig) ; if (!len) strerr_dief1x(100, "invalid status list specification") ; pos += len ; if (sigaddset(sigs, sig) < 0) strerr_dief1x(100, "invalid signal") ; } while (memchr(",; \n\r\t", s[pos], 6)) pos++ ; } } int main (int argc, char const *const *argv) { unsigned char codes[32] ; sigset_t sigs ; unsigned int total, seconds, n ; struct stat st ; PROG = "s6-permafailon" ; if (argc < 4) dieusage() ; if (!uint0_scan(argv[1], &seconds)) dieusage() ; if (!uint0_scan(argv[2], &n)) dieusage() ; if (!n) dieusage() ; if (n > S6_MAX_DEATH_TALLY) n = S6_MAX_DEATH_TALLY ; list_scan(argv[3], codes, &sigs) ; if (stat(S6_DTALLY_FILENAME, &st) < 0) { strerr_warnwu2sys("stat ", S6_DTALLY_FILENAME) ; goto cont ; } if (st.st_size % S6_DTALLY_PACK || st.st_size > S6_DTALLY_PACK * S6_MAX_DEATH_TALLY) { strerr_warnw2x("invalid ", S6_DTALLY_FILENAME) ; goto cont ; } total = st.st_size / S6_DTALLY_PACK ; { tain mintime ; unsigned int matches = 0 ; s6_dtally_t tab[total] ; ssize_t r = s6_dtally_read(".", tab, total) ; if (r <= 0) { if (r < 0) strerr_warnwu2sys("read ", S6_DTALLY_FILENAME) ; goto cont ; } if (r < n) goto cont ; tain_uint(&mintime, seconds) ; { tain now ; tain_wallclock_read(&now) ; tain_sub(&mintime, &now, &mintime) ; } for (unsigned int i = 0 ; i < r ; i++) { if (!tain_less(&tab[i].stamp, &mintime) && ((tab[i].sig && sigismember(&sigs, tab[i].sig)) || bitarray_peek(codes, tab[i].exitcode)) && ++matches >= n) { char fmtevent[4] ; char fmtseconds[UINT_FMT] ; char fmtn[UINT_FMT] ; fmtevent[uint_fmt(fmtevent, tab[i].sig ? tab[i].sig : tab[i].exitcode)] = 0 ; fmtseconds[uint_fmt(fmtseconds, seconds)] = 0 ; fmtn[uint_fmt(fmtn, n)] = 0 ; strerr_warni8x("PERMANENT FAILURE triggered after ", fmtn, " events involving ", tab[i].sig ? "signal " : "exit code ", fmtevent, " in the last ", fmtseconds, " seconds") ; return 125 ; } } } cont: xexec0(argv+4) ; } s6-2.13.1.0/src/supervision/s6-supervise.c000066400000000000000000000473421470151141200201660ustar00rootroot00000000000000/* ISC license. */ /* For SIGWINCH */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-supervise dir" #define CTL S6_SUPERVISE_CTLDIR "/control" #define LCK S6_SUPERVISE_CTLDIR "/lock" #define SLCK S6_SUPERVISE_CTLDIR "/service-lock" #define S6_PATH_MAX 512 typedef enum trans_e trans_t, *trans_t_ref ; enum trans_e { V_TIMEOUT, V_CHLD, V_TERM, V_HUP, V_QUIT, V_INT, V_a, V_b, V_q, V_h, V_k, V_t, V_i, V_1, V_2, V_p, V_c, V_y, V_r, V_P, V_C, V_K, V_o, V_d, V_u, V_D, V_U, V_x, V_O, V_Q } ; typedef enum state_e state_t, *state_t_ref ; enum state_e { DOWN, UP, FINISH, LASTUP, LASTFINISH } ; struct gflags_s { uint8_t cont : 1 ; uint8_t dying : 1 ; } gflags = { .cont = 1, .dying = 0 } ; typedef void action_t (void) ; typedef action_t *action_t_ref ; static tain deadline ; static tain nextstart = TAIN_ZERO ; static s6_svstatus_t status = S6_SVSTATUS_ZERO ; static int finish_wstat ; static state_t state = DOWN ; static int notifyfd = -1 ; static char const *servicename = 0 ; static rlim_t maxfd ; static inline void settimeout (int secs) { tain_addsec_g(&deadline, secs) ; } static inline void settimeout_infinite (void) { tain_add_g(&deadline, &tain_infinite_relative) ; } static inline void announce (void) { if (!s6_svstatus_write(".", &status)) strerr_warnwu1sys("write status file") ; } static int check_file (char const *file) { if (access(file, F_OK) == 0) return 1 ; if (errno != ENOENT) strerr_warnwu2sys("access ", file) ; return 0 ; } static int read_file (char const *file, char *buf, size_t n) { ssize_t r = openreadnclose_nb(file, buf, n) ; if (r == -1) { if (errno != ENOENT) strerr_warnwu2sys("open ", file) ; return 0 ; } buf[byte_chr(buf, r, '\n')] = 0 ; return 1 ; } static int read_uint (char const *file, unsigned int *fd) { char buf[UINT_FMT + 1] ; if (!read_file(file, buf, UINT_FMT)) return 0 ; if (!uint0_scan(buf, fd)) { strerr_warnw2x("invalid ", file) ; return 0 ; } return 1 ; } static inline int read_downsig (void) { int sig = SIGTERM ; char buf[16] ; if (read_file("down-signal", buf, 15) && !sig0_scan(buf, &sig)) strerr_warnw1x("invalid down-signal") ; return sig ; } static void set_down_and_ready (char const *s, unsigned int n) { status.pid = 0 ; status.pgid = 0 ; status.flagfinishing = 0 ; status.flagready = 1 ; state = DOWN ; if (tai_sec(tain_secp(&nextstart))) deadline = nextstart ; else tain_addsec_g(&deadline, 1) ; tain_wallclock_read(&status.readystamp) ; announce() ; ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, s, n) ; } /* The action array. */ static void nop (void) { } static void bail (void) { gflags.cont = 0 ; } static void killI (void) { if (status.pgid > 0) killpg(status.pgid, SIGINT) ; } static void sigint (void) { killI() ; bail() ; } static void closethem (void) { fd_close(0) ; fd_close(1) ; if (open_readb("/dev/null")) strerr_warnwu2sys("open /dev/null for ", "reading") ; else if (open_write("/dev/null") != 1 || ndelay_off(1) < 0) strerr_warnwu2sys("open /dev/null for ", "writing") ; } static void adddown (void) { if (!openwritenclose_unsafe("down", "", 0)) strerr_warnwu2sys("create ", "./down file") ; } static void deldown (void) { int e = errno ; if (unlink("down") == -1 && errno != ENOENT) strerr_warnwu2sys("unlink ", "./down file") ; errno = e ; } static void killa (void) { kill(status.pid, SIGALRM) ; } static void killb (void) { kill(status.pid, SIGABRT) ; } static void killh (void) { kill(status.pid, SIGHUP) ; } static void killq (void) { kill(status.pid, SIGQUIT) ; } static void killk (void) { kill(status.pid, SIGKILL) ; } static void killt (void) { kill(status.pid, SIGTERM) ; } static void killi (void) { kill(status.pid, SIGINT) ; } static void kill1 (void) { kill(status.pid, SIGUSR1) ; } static void kill2 (void) { kill(status.pid, SIGUSR2) ; } static void killp (void) { kill(status.pid, SIGSTOP) ; status.flagpaused = 1 ; announce() ; } static void killc (void) { kill(status.pid, SIGCONT) ; status.flagpaused = 0 ; announce() ; } static void killy (void) { kill(status.pid, SIGWINCH) ; } static void killr (void) { kill(status.pid, read_downsig()) ; } static void killP (void) { if (status.pgid > 0) { killpg(status.pgid, SIGSTOP) ; status.flagpaused = 1 ; announce() ; } } static void killC (void) { if (status.pgid > 0) { killpg(status.pgid, SIGCONT) ; status.flagpaused = 0 ; announce() ; } } static void killK (void) { if (status.pgid > 0) killpg(status.pgid, SIGKILL) ; } static void trystart (void) { cspawn_fileaction fa[2] = { [0] = { .type = CSPAWN_FA_CLOSE }, [1] = { .type = CSPAWN_FA_MOVE }, } ; char lkfmt[UINT_FMT] ; char const *cargv[8] = { S6_BINPREFIX "s6-setlock", "-d", lkfmt, "--", SLCK, "./run", servicename, 0 } ; size_t orig = 5 ; int notifyp[2] = { -1, -1 } ; unsigned int lk = 0, notif = 0 ; uint16_t cspawnflags = CSPAWN_FLAGS_SELFPIPE_FINISH | CSPAWN_FLAGS_SETSID ; if (read_uint("lock-fd", &lk)) { if (lk > maxfd) strerr_warnw2x("invalid ", "lock-fd") ; else { struct stat st ; int islocked ; int lfd = open_write(SLCK) ; if (lfd == -1) { settimeout(60) ; strerr_warnwu4sys("open ", SLCK, " for writing", " (waiting 60 seconds)") ; goto errn ; } if (fstat(lfd, &st) == -1) { settimeout(60) ; strerr_warnwu3sys("stat ", SLCK, " (waiting 60 seconds)") ; fd_close(lfd) ; return ; } if (st.st_size) { ftruncate(lfd, 0) ; strerr_warnw1x("a previous instance of the service wrote to the lock file!") ; } islocked = fd_islocked(lfd) ; if (islocked == -1) { settimeout(60) ; strerr_warnwu3sys("read lock state on ", SLCK, " (waiting 60 seconds)") ; fd_close(lfd) ; return ; } if (islocked) strerr_warnw1x("another instance of the service is already running, child will block") ; fd_close(lfd) ; lkfmt[uint_fmt(lkfmt, lk)] = 0 ; orig = 0 ; } } if (read_uint("notification-fd", ¬if)) { if (notif > maxfd) strerr_warnw2x("invalid ", "notification-fd") ; if (!orig && notif == lk) { settimeout_infinite() ; strerr_warnwu1x("start service: notification-fd and lock-fd are the same") ; return ; } if (pipe(notifyp) == -1) { settimeout(60) ; strerr_warnwu2sys("create notification pipe", " (waiting 60 seconds)") ; return ; } fa[0].x.fd = notifyp[0] ; fa[1].x.fd2[0] = notif ; fa[1].x.fd2[1] = notifyp[1] ; } if (check_file("flag-newpidns")) cspawnflags |= CSPAWN_FLAGS_NEWPIDNS ; status.pid = cspawn(cargv[orig], cargv + orig, (char const *const *)environ, cspawnflags, fa, notifyp[1] >= 0 ? 2 : 0) ; if (!status.pid) { settimeout(60) ; strerr_warnwu3sys("spawn ", cargv[orig], " (waiting 60 seconds)") ; goto errn ; } if (notifyp[1] >= 0) { fd_close(notifyp[1]) ; notifyfd = notifyp[0] ; } status.pgid = getpgid(status.pid) ; if (status.pgid == -1) strerr_warnwu1sys("getpgid() (process group control commands will have no effect)") ; settimeout_infinite() ; nextstart = tain_zero ; state = UP ; status.flagready = 0 ; tain_wallclock_read(&status.stamp) ; announce() ; ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "u", 1) ; return ; errn: if (notifyp[1] >= 0) { fd_close(notifyp[1]) ; fd_close(notifyp[0]) ; } } static void wantdown (void) { status.flagwantup = 0 ; announce() ; } static void wantup (void) { status.flagwantup = 1 ; announce() ; } static void wantDOWN (void) { adddown() ; wantdown() ; } static void wantUP (void) { deldown() ; wantup() ; } static void downtimeout (void) { if (status.flagwantup) trystart() ; else settimeout_infinite() ; } static void down_o (void) { wantdown() ; trystart() ; } static void down_u (void) { wantup() ; trystart() ; } static void down_U (void) { wantUP() ; trystart() ; } static int uplastup_z (void) { unsigned int n ; char fmt0[UINT_FMT] ; char fmt1[UINT_FMT] ; char fmt2[PID_FMT] ; char const *cargv[6] = { "finish", fmt0, fmt1, servicename, status.pgid > 0 ? fmt2 : "-1", 0 } ; status.flagpaused = 0 ; status.flagready = 0 ; gflags.dying = 0 ; tain_wallclock_read(&status.stamp) ; if (notifyfd >= 0) { fd_close(notifyfd) ; notifyfd = -1 ; } fmt0[uint_fmt(fmt0, WIFSIGNALED(status.wstat) ? 256 : WEXITSTATUS(status.wstat))] = 0 ; fmt1[uint_fmt(fmt1, WTERMSIG(status.wstat))] = 0 ; if (status.pgid > 0) fmt2[pid_fmt(fmt2, status.pgid)] = 0 ; if (!read_uint("max-death-tally", &n)) n = 100 ; if (n > S6_MAX_DEATH_TALLY) n = S6_MAX_DEATH_TALLY ; if (n) { s6_dtally_t tab[n+1] ; ssize_t m = s6_dtally_read(".", tab, n) ; if (m < 0) strerr_warnwu2sys("read ", S6_DTALLY_FILENAME) ; else { tab[m].stamp = status.stamp ; tab[m].sig = WIFSIGNALED(status.wstat) ? WTERMSIG(status.wstat) : 0 ; tab[m].exitcode = WIFSIGNALED(status.wstat) ? 128 + WTERMSIG(status.wstat) : WEXITSTATUS(status.wstat) ; if (!(m >= n ? s6_dtally_write(".", tab+1, n) : s6_dtally_write(".", tab, m+1))) strerr_warnwu2sys("write ", S6_DTALLY_FILENAME) ; } } status.pid = cspawn("./finish", cargv, (char const *const *)environ, CSPAWN_FLAGS_SELFPIPE_FINISH | CSPAWN_FLAGS_SETSID, 0, 0) ; if (!status.pid) { if (errno != ENOENT) strerr_warnwu2sys("spawn ", "./finish") ; set_down_and_ready("dD", 2) ; return 0 ; } { tain tto ; unsigned int timeout ; if (!read_uint("timeout-finish", &timeout)) timeout = 5000 ; if (timeout && tain_from_millisecs(&tto, timeout)) tain_add_g(&deadline, &tto) ; else settimeout_infinite() ; } status.pgid = getpgid(status.pid) ; status.flagfinishing = 1 ; announce() ; ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "d", 1) ; return 1 ; } static void up_z (void) { if (uplastup_z()) state = FINISH ; } static void lastup_z (void) { if (uplastup_z()) state = LASTFINISH ; else bail() ; } static void uptimeout (void) { if (gflags.dying) { killk() ; settimeout(5) ; } else { settimeout_infinite() ; strerr_warnw1x("can't happen: timeout while the service is up!") ; } } static void up_d (void) { tain tto ; unsigned int timeout ; status.flagwantup = 0 ; killr() ; killc() ; if (!read_uint("timeout-kill", &timeout)) timeout = 0 ; if (timeout && tain_from_millisecs(&tto, timeout)) { tain_add_g(&deadline, &tto) ; gflags.dying = 1 ; } else settimeout_infinite() ; } static void up_D (void) { adddown() ; up_d() ; } static void up_x (void) { state = LASTUP ; closethem() ; } static void up_term (void) { state = LASTUP ; up_d() ; } static void finishtimeout (void) { strerr_warnw1x("finish script lifetime reached maximum value - sending it a SIGKILL") ; killc() ; killk() ; settimeout(5) ; } static void finish_z (void) { if (WIFEXITED(finish_wstat) && WEXITSTATUS(finish_wstat) == 125) { status.flagwantup = 0 ; set_down_and_ready("OD", 2) ; } else set_down_and_ready("D", 1) ; } static void finish_x (void) { state = LASTFINISH ; closethem() ; } static void lastfinish_z (void) { finish_z() ; bail() ; } static action_t_ref const actions[5][30] = { { &downtimeout, &nop, &bail, &bail, &bail, &bail, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &down_o, &wantdown, &down_u, &wantDOWN, &down_U, &bail, &wantdown, &wantDOWN }, { &uptimeout, &up_z, &up_term, &up_x, &bail, &sigint, &killa, &killb, &killq, &killh, &killk, &killt, &killi, &kill1, &kill2, &killp, &killc, &killy, &killr, &killP, &killC, &killK, &wantdown, &up_d, &wantup, &up_D, &wantUP, &up_x, &wantdown, &wantDOWN }, { &finishtimeout, &finish_z, &finish_x, &finish_x, &bail, &sigint, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &wantdown, &wantdown, &wantup, &wantDOWN, &wantUP, &finish_x, &wantdown, &wantDOWN }, { &uptimeout, &lastup_z, &up_d, &closethem, &bail, &sigint, &killa, &killb, &killq, &killh, &killk, &killt, &killi, &kill1, &kill2, &killp, &killc, &killy, &killr, &killP, &killC, &killK, &wantdown, &up_d, &wantup, &up_D, &wantUP, &closethem, &wantdown, &wantDOWN }, { &finishtimeout, &lastfinish_z, &nop, &closethem, &bail, &sigint, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &wantdown, &wantdown, &wantup, &wantDOWN, &wantUP, &closethem, &wantdown, &wantDOWN } } ; /* The main loop. It just loops around the iopause(), calling snippets of code in "actions" when needed. */ static inline void handle_notifyfd (void) { char buf[512] ; ssize_t r = 1 ; while (r > 0) { r = sanitize_read(fd_read(notifyfd, buf, 512)) ; if (r > 0 && memchr(buf, '\n', r)) { tain_addsec_g(&nextstart, 1) ; tain_wallclock_read(&status.readystamp) ; status.flagready = 1 ; announce() ; ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "U", 1) ; r = -1 ; } if (r < 0) { fd_close(notifyfd) ; notifyfd = -1 ; } } } static inline void handle_signals (void) { for (;;) { switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "selfpipe_read") ; case 0 : return ; case SIGCHLD : if (!status.pid) wait_reap() ; else { int wstat ; int r = wait_pid_nohang(status.pid, &wstat) ; if (r < 0) if (errno != ECHILD) strerr_diefu1sys(111, "wait_pid_nohang") ; else break ; else if (!r) break ; if (status.flagfinishing) finish_wstat = wstat ; else status.wstat = wstat ; (*actions[state][V_CHLD])() ; } break ; case SIGTERM : (*actions[state][V_TERM])() ; break ; case SIGHUP : (*actions[state][V_HUP])() ; break ; case SIGQUIT : (*actions[state][V_QUIT])() ; break ; case SIGINT : (*actions[state][V_INT])() ; break ; default : strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ; } } } static inline void handle_control (int fd) { for (;;) { char c ; ssize_t r = sanitize_read(fd_read(fd, &c, 1)) ; if (r < 0) strerr_diefu1sys(111, "read " S6_SUPERVISE_CTLDIR "/control") ; else if (!r) break ; else { size_t pos = byte_chr("abqhkti12pcyrPCKoduDUxOQ", 24, c) ; if (pos < 24) (*actions[state][V_a + pos])() ; } } } static int trymkdir (char const *s) { char buf[S6_PATH_MAX] ; ssize_t r ; if (mkdir(s, 0700) >= 0) return 1 ; if (errno != EEXIST) strerr_diefu2sys(111, "mkdir ", s) ; r = readlink(s, buf, S6_PATH_MAX) ; if (r < 0) { struct stat st ; if (errno != EINVAL) { errno = EEXIST ; strerr_diefu2sys(111, "mkdir ", s) ; } if (stat(s, &st) < 0) strerr_diefu2sys(111, "stat ", s) ; if (!S_ISDIR(st.st_mode)) strerr_dief2x(100, s, " exists and is not a directory") ; return 0 ; } else if (r == S6_PATH_MAX) { errno = ENAMETOOLONG ; strerr_diefu2sys(111, "readlink ", s) ; } else { buf[r] = 0 ; if (mkdir(buf, 0700) < 0) strerr_diefu2sys(111, "mkdir ", buf) ; return 1 ; } } static inline int control_init (void) { mode_t m = umask(0) ; int fdctl, fdlck, r ; if (trymkdir(S6_SUPERVISE_EVENTDIR)) { if (chown(S6_SUPERVISE_EVENTDIR, -1, getegid()) < 0) strerr_diefu1sys(111, "chown " S6_SUPERVISE_EVENTDIR) ; if (chmod(S6_SUPERVISE_EVENTDIR, 03730) < 0) strerr_diefu1sys(111, "chmod " S6_SUPERVISE_EVENTDIR) ; } trymkdir(S6_SUPERVISE_CTLDIR) ; fdlck = open3(LCK, O_WRONLY | O_NONBLOCK | O_CREAT | O_CLOEXEC, 0644) ; if (fdlck < 0) strerr_diefu1sys(111, "open " LCK) ; r = fd_lock(fdlck, 1, 1) ; if (r < 0) strerr_diefu1sys(111, "lock " LCK) ; if (!r) strerr_dief1x(100, "another instance of s6-supervise is already running") ; /* fdlck leaks but it's coe */ if (mkfifo(CTL, 0600) < 0) { struct stat st ; if (errno != EEXIST) strerr_diefu1sys(111, "mkfifo " CTL) ; if (stat(CTL, &st) < 0) strerr_diefu1sys(111, "stat " CTL) ; if (!S_ISFIFO(st.st_mode)) strerr_dief1x(100, CTL " is not a FIFO") ; } fdctl = openc_read(CTL) ; if (fdctl < 0) strerr_diefu1sys(111, "open " CTL " for reading") ; r = openc_write(CTL) ; if (r < 0) strerr_diefu1sys(111, "open " CTL " for writing") ; /* r leaks but it's coe */ umask(m) ; return fdctl ; } int main (int argc, char const *const *argv) { iopause_fd x[3] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ; PROG = "s6-supervise" ; if (argc < 2) strerr_dieusage(100, USAGE) ; if (chdir(argv[1]) < 0) strerr_diefu2sys(111, "chdir to ", argv[1]) ; servicename = argv[1] ; { size_t proglen = strlen(PROG) ; size_t namelen = strlen(argv[1]) ; char progname[proglen + namelen + 2] ; memcpy(progname, PROG, proglen) ; progname[proglen] = ' ' ; memcpy(progname + proglen + 1, argv[1], namelen + 1) ; PROG = progname ; if (!fd_sanitize()) strerr_diefu1sys(111, "sanitize stdin and stdout") ; { struct rlimit rl ; if (getrlimit(RLIMIT_NOFILE, &rl) == -1) strerr_diefu1sys(111, "getrlimit") ; maxfd = rl.rlim_cur ; } x[1].fd = control_init() ; x[0].fd = selfpipe_init() ; if (x[0].fd == -1) strerr_diefu1sys(111, "init selfpipe") ; if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; { sigset_t set ; sigemptyset(&set) ; sigaddset(&set, SIGCHLD) ; sigaddset(&set, SIGTERM) ; sigaddset(&set, SIGHUP) ; sigaddset(&set, SIGQUIT) ; sigaddset(&set, SIGINT) ; if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ; } if (!ftrigw_clean(S6_SUPERVISE_EVENTDIR)) strerr_warnwu2sys("ftrigw_clean ", S6_SUPERVISE_EVENTDIR) ; { int fd = open_trunc(S6_DTALLY_FILENAME) ; if (fd < 0) strerr_diefu2sys(111, "truncate ", S6_DTALLY_FILENAME) ; fd_close(fd) ; } if (access("down", F_OK) == 0) status.flagwantup = 0 ; else if (errno != ENOENT) strerr_diefu1sys(111, "access ./down") ; tain_now_set_stopwatch_g() ; settimeout(0) ; tain_copynow(&status.stamp) ; status.readystamp = status.stamp ; announce() ; ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "s", 1) ; while (gflags.cont) { int r ; x[2].fd = notifyfd ; r = iopause_g(x, 2 + (notifyfd >= 0), &deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; else if (!r) (*actions[state][V_TIMEOUT])() ; else { if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) strerr_diefu1x(111, "iopause: trouble with pipes") ; if (notifyfd >= 0 && x[2].revents & IOPAUSE_READ) handle_notifyfd() ; if (x[0].revents & IOPAUSE_READ) handle_signals() ; else if (x[1].revents & IOPAUSE_READ) handle_control(x[1].fd) ; } } ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "x", 1) ; } return 0 ; } s6-2.13.1.0/src/supervision/s6-svc.c000066400000000000000000000076521470151141200167340ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-svc [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -s signal | -abqhkti12pcyrPCK ] [ -oduDUxOQ ] servicedir" #define dieusage() strerr_dieusage(100, USAGE) #define DATASIZE 63 int main (int argc, char const *const *argv) { static char const cmdsig[NSIG] = { [SIGALRM] = 'a', [SIGABRT] = 'b', [SIGQUIT] = 'q', [SIGHUP] = 'h', [SIGKILL] = 'k', [SIGTERM] = 't', [SIGINT] = 'i', [SIGUSR1] = '1', [SIGUSR2] = '2', [SIGSTOP] = 'p', [SIGCONT] = 'c', [SIGWINCH] = 'y' } ; size_t len ; unsigned int datalen = 1 ; unsigned int timeout = 0 ; char data[DATASIZE+1] = "-" ; char updown[3] = "-\0" ; PROG = "s6-svc" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "s:abqhkti12pcyrPCKoduDUxOQT:w:", &l) ; if (opt == -1) break ; switch (opt) { case 's' : { int sig ; if (!sig0_scan(l.arg, &sig)) strerr_dief2x(100, "invalid signal: ", l.arg) ; if (!cmdsig[sig]) strerr_dief2x(100, l.arg, " is not in the list of user-available signals") ; opt = cmdsig[sig] ; } case 'a' : case 'b' : case 'q' : case 'h' : case 'k' : case 't' : case 'i' : case '1' : case '2' : case 'p' : case 'c' : case 'y' : case 'r' : case 'P' : case 'C' : case 'K' : case 'o' : case 'd' : case 'u' : case 'D' : case 'U' : case 'x' : case 'O' : case 'Q' : { if (datalen >= DATASIZE) strerr_dief1x(100, "too many commands") ; data[datalen++] = opt ; break ; } case 'T' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ; case 'w' : { if (!memchr("dDuUrR", l.arg[0], 6)) dieusage() ; updown[1] = l.arg[0] ; break ; } default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; if (argc > 1) strerr_warnw1x("ignoring extra arguments") ; len = strlen(argv[0]) ; if (!len) strerr_dief1x(100, "invalid service path") ; if (updown[1] == 'U' || updown[1] == 'R') { char fn[len + 17] ; memcpy(fn, argv[0], len) ; memcpy(fn + len, "/notification-fd", 17) ; if (access(fn, F_OK) < 0) { if (errno != ENOENT) strerr_diefu2sys(111, "access ", fn) ; updown[1] = updown[1] == 'U' ? 'u' : 'r' ; strerr_warnw2x(fn, " not present - ignoring request for readiness notification") ; } } if (updown[1]) { char const *newargv[11] ; unsigned int m = 0 ; char fmt[UINT_FMT] ; newargv[m++] = datalen > 1 ? S6_BINPREFIX "s6-svlisten1" : S6_BINPREFIX "s6-svwait" ; newargv[m++] = updown ; if (timeout) { fmt[uint_fmt(fmt, timeout)] = 0 ; newargv[m++] = "-t" ; newargv[m++] = fmt ; } newargv[m++] = "--" ; newargv[m++] = argv[0] ; if (datalen > 1) { newargv[m++] = S6_BINPREFIX "s6-svc" ; newargv[m++] = data ; newargv[m++] = "--" ; newargv[m++] = argv[0] ; } newargv[m++] = 0 ; xexec(newargv) ; } else switch (s6_svc_writectl(argv[0], S6_SUPERVISE_CTLDIR, data + 1, datalen - 1)) { case -1 : strerr_diefu2sys(111, "control ", argv[0]) ; case -2 : strerr_dief3sys(100, "something is wrong with the ", argv[0], "/" S6_SUPERVISE_CTLDIR " directory") ; case 0 : strerr_diefu3x(100, "control ", argv[0], ": supervisor not listening") ; } return 0 ; } s6-2.13.1.0/src/supervision/s6-svdt-clear.c000066400000000000000000000006101470151141200201700ustar00rootroot00000000000000/* ISC license. */ #include #include #define USAGE "s6-svdt-clear servicedir" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { PROG = "s6-svdt-clear" ; if (argc < 2) dieusage() ; if (!s6_dtally_write(argv[1], 0, 0)) strerr_diefu2sys(111, "clear death tally for service ", argv[1]) ; return 0 ; } s6-2.13.1.0/src/supervision/s6-svdt.c000066400000000000000000000040421470151141200171070ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-svdt [ -S | -s ] [ -n maxentries ] servicedir" #define dieusage() strerr_dieusage(100, USAGE) #define die1() strerr_diefu1sys(111, "write to stdout") int main (int argc, char const *const *argv) { unsigned int n = S6_MAX_DEATH_TALLY ; int num = 0 ; PROG = "s6-svdt" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "Ssn:", &l) ; if (opt == -1) break ; switch (opt) { case 'S' : num = 0 ; break ; case 's' : num = 1 ; break ; case 'n' : if (!uint0_scan(l.arg, &n)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; if (n > S6_MAX_DEATH_TALLY) n = S6_MAX_DEATH_TALLY ; if (!n) return 0 ; { s6_dtally_t tab[n] ; ssize_t r = s6_dtally_read(argv[0], tab, n) ; if (r < 0) strerr_diefu2sys(111, "read death tally for service ", argv[0]) ; for (size_t i = 0 ; i < r ; i++) { char fmt[TIMESTAMP + 1] ; timestamp_fmt(fmt, &tab[i].stamp) ; fmt[TIMESTAMP] = ' ' ; if (buffer_put(buffer_1, fmt, TIMESTAMP + 1) < 0) die1() ; if (tab[i].sig) { if (buffer_puts(buffer_1, "signal ") < 0) die1() ; if (num) { char fmt[3] ; if (buffer_put(buffer_1, fmt, uint_fmt(fmt, tab[i].sig)) < 0) die1() ; } else { if (buffer_puts(buffer_1, "SIG") < 0) die1() ; if (buffer_puts(buffer_1, sig_name(tab[i].sig)) < 0) die1() ; } } else { char fmt[3] ; if (buffer_puts(buffer_1, "exitcode ") < 0) die1() ; if (buffer_put(buffer_1, fmt, uint_fmt(fmt, tab[i].exitcode)) < 0) die1() ; } if (buffer_put(buffer_1, "\n", 1) < 0) die1() ; } } if (!buffer_flush(buffer_1)) die1() ; return 0 ; } s6-2.13.1.0/src/supervision/s6-svlink.c000066400000000000000000000070121470151141200174350ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-svlink [ -d | -D ] [ -f ] [ -P ] [ -t timeout ] scandir servicedir [ name ]" #define dieusage() strerr_dieusage(100, USAGE) static inline void checkscandir (char const *s) { int r ; int fd ; size_t len = strlen(s) ; char fn[len + 6 + sizeof(S6_SVSCAN_CTLDIR)] ; memcpy(fn, s, len) ; memcpy(fn + len, "/" S6_SVSCAN_CTLDIR "/lock", 6 + sizeof(S6_SVSCAN_CTLDIR)) ; fd = open_read(fn) ; if (fd < 0) strerr_diefu2sys(111, "open ", fn) ; r = fd_islocked(fd) ; if (r < 0) strerr_diefu2sys(111, "check lock on ", fn) ; if (!r) strerr_dief2x(1, "s6-svscan not running on ", s) ; fd_close(fd) ; } static inline void checkservicedir (char const *s) { int r ; struct stat st ; size_t len = strlen(s) ; char fn[len + 9] ; memcpy(fn, s, len) ; memcpy(fn + len, "/run", 5) ; if (stat(fn, &st) == -1) strerr_diefu2sys(111, "stat ", fn) ; if (!(st.st_mode & S_IXUSR)) strerr_dief2x(100, fn, " is not executable") ; r = s6_svc_ok(s) ; if (r < 0) strerr_diefu2sys(111, "check supervision status of ", s) ; if (r) strerr_warnw2x("supervisor already running on ", s) ; memcpy(fn + len + 1, "log", 4) ; if (stat(fn, &st) == -1) { if (errno != ENOENT) strerr_diefu2sys(111, "stat ", fn) ; } else { if (!S_ISDIR(st.st_mode)) strerr_dief2x(100, fn, " is not a directory") ; memcpy(fn + len + 4, "/run", 5) ; if (stat(fn, &st) == -1) strerr_diefu2sys(111, "stat ", fn) ; if (!(st.st_mode & S_IXUSR)) strerr_dief2x(100, fn, " is not executable") ; fn[len + 4] = 0 ; r = s6_svc_ok(fn) ; if (r < 0) strerr_diefu2sys(111, "check supervision status of ", fn) ; if (r) strerr_warnw2x("supervisor already running on ", fn) ; } } int main (int argc, char const *const *argv) { tain tto = TAIN_INFINITE_RELATIVE ; uint32_t options = 0 ; char const *name ; PROG = "s6-svlink" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "dDfPt:", &l) ; if (opt == -1) break ; switch (opt) { case 'd' : options |= 12 ; break ; case 'D' : options |= 4 ; options &= ~8U ; break ; case 'f' : options |= 1 ; break ; case 'P' : options |= 2 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&tto, t) ; } if (argc < 2) dieusage() ; if (argv[2]) name = argv[2] ; else { stralloc sa = STRALLOC_ZERO ; if (!stralloc_cats(&sa, argv[1]) || !stralloc_0(&sa)) strerr_diefu1sys(111, "stralloc_cats") ; name = basename(sa.s) ; } if (!argv[0][0]) strerr_dief1x(100, "invalid scandir") ; if (!argv[1][0]) strerr_dief1x(100, "invalid servicedir") ; if (!name[0] || name[0] == '.' || name[0] == '/') strerr_dief1x(100, "invalid name") ; checkscandir(argv[0]) ; checkservicedir(argv[1]) ; tain_now_set_stopwatch_g() ; tain_add_g(&tto, &tto) ; if (s6_supervise_link_names_g(argv[0], argv + 1, &name, 1, options, &tto) == -1) strerr_diefu6sys(errno == ETIMEDOUT ? 99 : 111, "link servicedir ", argv[1], " into scandir ", argv[0], " with name ", name) ; return 0 ; } s6-2.13.1.0/src/supervision/s6-svlisten.c000066400000000000000000000055151470151141200200040ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include "s6-svlisten.h" #define USAGE "s6-svlisten [ -U | -u | -d | -D | -r | -R ] [ -a | -o ] [ -t timeout ] servicedir... \"\" prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const **argv, char const *const *envp) { tain deadline, tto ; int argc1 ; int or = 0 ; int wantup = 1, wantready = 0 ; PROG = "s6-svlisten" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int t = 0 ; for (;;) { int opt = subgetopt_r(argc, argv, "uUdDrRaot:", &l) ; if (opt == -1) break ; switch (opt) { case 'u' : wantup = 1 ; wantready = 0 ; break ; case 'U' : wantup = 1 ; wantready = 1 ; break ; case 'd' : wantup = 0 ; wantready = 0 ; break ; case 'D' : wantup = 0 ; wantready = 1 ; break ; case 'r' : wantup = 2 ; wantready = 0 ; break ; case 'R' : wantup = 2 ; wantready = 1 ; break ; case 'a' : or = 0 ; break ; case 'o' : or = 1 ; break ; case 't' : if (!uint0_scan(l.arg, &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 < 3) dieusage() ; argc1 = s6_el_semicolon(argv) ; if (argc == argc1 + 1) dieusage() ; if (argc1 >= argc) strerr_dief1x(100, "unterminated servicedir block") ; if (!argc1) xexec(argv + argc1 + 1) ; if (wantup == 2 && or) { or = 0 ; strerr_warnw3x("-o is unsupported when combined with -", wantready ? "R" : "r", "- using -a instead") ; } tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &tto) ; s6_svlisten_selfpipe_init() ; { s6_svlisten_t foo = S6_SVLISTEN_ZERO ; pid_t pid ; unsigned int e ; uint16_t ids[argc1] ; unsigned char upstate[bitarray_div8(argc1)] ; unsigned char readystate[bitarray_div8(argc1)] ; s6_svlisten_init(argc1, argv, &foo, ids, upstate, readystate, &deadline) ; pid = cspawn(argv[argc1 + 1], argv + argc1 + 1, envp, CSPAWN_FLAGS_SELFPIPE_FINISH, 0, 0) ; if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1 + 1]) ; if (wantup == 2) { wantup = 1 ; e = s6_svlisten_loop(&foo, 0, 1, or, &deadline, selfpipe_fd(), &s6_svlisten_signal_handler) ; if (e) strerr_dief1x(e, "some services reported permanent failure or their supervisor died") ; } e = s6_svlisten_loop(&foo, wantup, wantready, or, &deadline, selfpipe_fd(), &s6_svlisten_signal_handler) ; if (e) strerr_dief1x(e, "some services reported permanent failure or their supervisor died") ; } return 0 ; } s6-2.13.1.0/src/supervision/s6-svlisten.h000066400000000000000000000015231470151141200200040ustar00rootroot00000000000000/* ISC license. */ #ifndef S6_SVLISTEN_H #define S6_SVLISTEN_H #include #include #include typedef void action_func (void) ; typedef action_func *action_func_ref ; typedef struct s6_svlisten_s s6_svlisten_t, *s6_svlisten_t_ref ; struct s6_svlisten_s { ftrigr_t a ; unsigned int n ; uint16_t *ids ; unsigned char *upstate ; unsigned char *readystate ; } ; #define S6_SVLISTEN_ZERO { .a = FTRIGR_ZERO, .n = 0, .ids = 0, .upstate = 0, .readystate = 0 } extern void s6_svlisten_signal_handler (void) ; extern void s6_svlisten_selfpipe_init (void) ; extern void s6_svlisten_init (int, char const *const *, s6_svlisten_t *, uint16_t *, unsigned char *, unsigned char *, tain const *) ; extern unsigned int s6_svlisten_loop (s6_svlisten_t *, int, int, int, tain const *, int, action_func_ref) ; #endif s6-2.13.1.0/src/supervision/s6-svlisten1.c000066400000000000000000000043221470151141200200600ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include "s6-svlisten.h" #define USAGE "s6-svlisten1 [ -U | -u | -d | -D | -r | -R ] [ -t timeout ] servicedir prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv, char const *const *envp) { s6_svlisten_t foo = S6_SVLISTEN_ZERO ; tain deadline, tto ; pid_t pid ; int wantup = 1, wantready = 0, wantrestart = 0 ; uint16_t id ; unsigned char upstate, readystate ; PROG = "s6-svlisten1" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int t = 0 ; for (;;) { int opt = subgetopt_r(argc, argv, "uUdDrRt:", &l) ; if (opt == -1) break ; switch (opt) { case 'u' : wantup = 1 ; wantrestart = 0 ; wantready = 0 ; break ; case 'U' : wantup = 1 ; wantrestart = 0 ; wantready = 1 ; break ; case 'd' : wantup = 0 ; wantrestart = 0 ; wantready = 0 ; break ; case 'D' : wantup = 0 ; wantrestart = 0 ; wantready = 1 ; break ; case 'r' : wantup = 1 ; wantrestart = 1 ; wantready = 0 ; break ; case 'R' : wantup = 1 ; wantrestart = 1 ; wantready = 1 ; break ; case 't' : if (!uint0_scan(l.arg, &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 < 2) dieusage() ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &tto) ; s6_svlisten_selfpipe_init() ; s6_svlisten_init(1, argv, &foo, &id, &upstate, &readystate, &deadline) ; pid = cspawn(argv[1], argv + 1, envp, CSPAWN_FLAGS_SELFPIPE_FINISH, 0, 0) ; if (!pid) strerr_diefu2sys(111, "spawn ", argv[1]) ; if (wantrestart) if (s6_svlisten_loop(&foo, 0, 1, 1, &deadline, selfpipe_fd(), &s6_svlisten_signal_handler)) strerr_dief2x(1, argv[0], " failed permanently or its supervisor died") ; if (s6_svlisten_loop(&foo, wantup, wantready, 1, &deadline, selfpipe_fd(), &s6_svlisten_signal_handler)) strerr_dief2x(1, argv[0], " failed permanently or its supervisor died") ; return 0 ; } s6-2.13.1.0/src/supervision/s6-svok.c000066400000000000000000000005161470151141200171130ustar00rootroot00000000000000/* ISC license. */ #include #include #define USAGE "s6-svok servicedir" int main (int argc, char const *const *argv) { int r ; PROG = "s6-svok" ; if (argc < 2) strerr_dieusage(100, USAGE) ; r = s6_svc_ok(argv[1]) ; if (r < 0) strerr_diefu2sys(111, "check ", argv[1]) ; return !r ; } s6-2.13.1.0/src/supervision/s6-svperms.c000066400000000000000000000150271470151141200176330ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-svperms [ -v ] [ -u | -g group | -G group | -o | -O group ] [ -e | -E group ] servicedir..." #define dieusage() strerr_dieusage(100, USAGE) static gid_t scangid (char const *s) { if (s[0] == ':') { gid_t g ; if (!gid0_scan(s+1, &g)) dieusage() ; return g ; } else { struct group *gr ; errno = 0 ; gr = getgrnam(s) ; if (!gr) { if (errno) strerr_diefu1sys(111, "getgrnam") ; else strerr_diefu3x(100, "find entry for ", s, " in group database") ; } return gr->gr_gid ; } } static char *gidname (gid_t gid) { struct group *gr ; errno = 0 ; gr = getgrgid(gid) ; if (!gr) { static char fmt[GID_FMT] ; fmt[gid_fmt(fmt, gid)] = 0 ; if (errno) strerr_warnwu2sys("getgrgid ", fmt) ; return fmt ; } return gr->gr_name ; } static void out (char const *s) { if (buffer_puts(buffer_1, s) < 0) strerr_diefu1sys(111, "write to stdout") ; } static inline int printsupervise (char const *dir) { struct stat st ; size_t len = strlen(dir) ; char fn[len + sizeof(S6_SUPERVISE_CTLDIR) + 9] ; memcpy(fn, dir, len) ; memcpy(fn + len, "/" S6_SUPERVISE_CTLDIR, sizeof(S6_SUPERVISE_CTLDIR) + 1) ; if (stat(fn, &st) < 0) { strerr_warnwu2sys("stat ", fn) ; return 1 ; } if (!S_ISDIR(st.st_mode)) { strerr_warnw2x(fn, " is not a directory") ; return 1 ; } if (st.st_mode & 05066 || (st.st_mode & 0700) != 0700 || ((st.st_mode & 0001) && !(st.st_mode & 0010))) { char fmt[UINT_OFMT] ; fmt[uint_ofmt(fmt, st.st_mode & 07777)] = 0 ; strerr_warnw3x(fn, " has incorrect permissions: ", fmt) ; return 1 ; } out(dir) ; out(" status: ") ; if (st.st_mode & 0011) { if (st.st_mode & 0001) buffer_puts(buffer_1, "public") ; else { out("group ") ; out(gidname(st.st_gid)) ; } } else out("owner") ; out("\n") ; memcpy(fn + len + sizeof(S6_SUPERVISE_CTLDIR), "/control", 9) ; if (stat(fn, &st) < 0) { strerr_warnwu2sys("stat ", fn) ; return 1 ; } if (!S_ISFIFO(st.st_mode)) { strerr_warnw2x(fn, " is not a named pipe") ; return 1 ; } if (st.st_mode & 0157) { char fmt[UINT_OFMT] ; fmt[uint_ofmt(fmt, st.st_mode & 07777)] = 0 ; strerr_warnw3x(fn, " has incorrect permissions: ", fmt) ; return 1 ; } out(dir) ; out(" control: ") ; if (st.st_mode & 0020) { out("group ") ; out(gidname(st.st_gid)) ; } else out("owner") ; out("\n") ; return 0 ; } static inline int printevent (char const *dir) { struct stat st ; size_t len = strlen(dir) ; char fn[len + sizeof(S6_SUPERVISE_EVENTDIR) + 1] ; memcpy(fn, dir, len) ; memcpy(fn + len, "/" S6_SUPERVISE_EVENTDIR, sizeof(S6_SUPERVISE_EVENTDIR) + 1) ; if (stat(fn, &st) < 0) { strerr_warnwu2sys("stat ", fn) ; return 1 ; } if (!S_ISDIR(st.st_mode)) { strerr_warnw2x(fn, " is not a directory") ; return 1 ; } if ((st.st_mode & 07777) != 01733 && (st.st_mode & 07777) != 03730) { char fmt[UINT_OFMT] ; fmt[uint_ofmt(fmt, st.st_mode & 07777)] = 0 ; strerr_warnw3x(fn, " has incorrect permissions: ", fmt) ; return 1 ; } out(dir) ; out(" events: ") ; if ((st.st_mode & 07777) == 03730) { out("group ") ; out(gidname(st.st_gid)) ; } else out("public") ; out("\n") ; return 0 ; } static gid_t primarygid (char const *fn) { struct passwd *pw ; struct stat st ; if (stat(fn, &st) < 0) strerr_diefu2sys(111, "stat ", fn) ; errno = 0 ; pw = getpwuid(st.st_uid) ; if (!pw) { strerr_warnwu3sys("determine primary gid for the owner of ", fn, " (using root instead)") ; return 0 ; } else return pw->pw_gid ; } static inline void modsupervise (char const *dir, unsigned int what, gid_t gid) { size_t len = strlen(dir) ; gid_t cgid = 0 ; mode_t mode = 0700 ; char fn[len + sizeof(S6_SUPERVISE_CTLDIR) + 9] ; memcpy(fn, dir, len) ; memcpy(fn + len, "/" S6_SUPERVISE_CTLDIR, sizeof(S6_SUPERVISE_CTLDIR) + 1) ; switch (what & 3) { case 0 : cgid = primarygid(fn) ; mode = 0700 ; break ; case 1 : cgid = gid ; mode = 0710 ; break ; case 2 : cgid = primarygid(fn) ; mode = 0711 ; break ; } if (chown(fn, -1, cgid) < 0) strerr_diefu2sys(111, "chown ", fn) ; if (chmod(fn, mode) < 0) strerr_diefu2sys(111, "chmod ", fn) ; memcpy(fn + len + sizeof(S6_SUPERVISE_CTLDIR), "/control", 9) ; if (what & 4) mode = 0620 ; else { gid = primarygid(fn) ; mode = 0600 ; } if (chown(fn, -1, gid) < 0) strerr_diefu2sys(111, "chown ", fn) ; if (chmod(fn, mode) < 0) strerr_diefu2sys(111, "chmod ", fn) ; } static inline void modevent (char const *dir, gid_t gid) { size_t len = strlen(dir) ; mode_t mode ; char fn[len + sizeof(S6_SUPERVISE_EVENTDIR) + 1] ; memcpy(fn, dir, len) ; memcpy(fn + len, "/" S6_SUPERVISE_EVENTDIR, sizeof(S6_SUPERVISE_EVENTDIR) + 1) ; if (gid == (gid_t)-1) { gid = primarygid(fn) ; mode = 01733 ; } else mode = 03730 ; if (chown(fn, -1, gid) < 0) strerr_diefu2sys(111, "chown ", fn) ; if (chmod(fn, mode) < 0) strerr_diefu2sys(111, "chmod ", fn) ; } int main (int argc, char const *const *argv) { int e = 0 ; gid_t gid = -1 ; gid_t eventgid = -1 ; int rw = 0 ; unsigned int what = 0 ; PROG = "s6-svperms" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "vug:G:oO:eE:", &l) ; if (opt == -1) break ; switch (opt) { case 'v' : rw |= 1 ; break ; case 'u' : rw |= 2 ; what = 0 ; break ; case 'g' : rw |= 2 ; what = 1 ; gid = scangid(l.arg) ; break ; case 'G' : rw |= 2 ; what = 5 ; gid = scangid(l.arg) ; break ; case 'o' : rw |= 2 ; what = 2 ; break ; case 'O' : rw |= 2 ; what = 6 ; gid = scangid(l.arg) ; break ; case 'e' : rw |= 4 ; eventgid = -1 ; break ; case 'E' : rw |= 4 ; eventgid = scangid(l.arg) ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; if (!rw) rw = 1 ; for (; *argv ; argv++) { if (rw & 2) modsupervise(*argv, what, gid) ; if (rw & 4) modevent(*argv, eventgid) ; if (rw & 1) { e |= printsupervise(*argv) ; e |= printevent(*argv) ; } } if (rw & 1 && !buffer_flush(buffer_1)) strerr_diefu1sys(111, "write to stdout") ; return e ; } s6-2.13.1.0/src/supervision/s6-svscan.c000066400000000000000000000456201470151141200174330ustar00rootroot00000000000000/* 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 #include #include #include #include #include #define USAGE "s6-svscan [ -c services_max | -C services_max ] [ -L name_max ] [ -t timeout ] [ -d notif ] [ -X consoleholder ] [ dir ]" #define dieusage() strerr_dieusage(100, USAGE) #define CTL S6_SVSCAN_CTLDIR "/control" #define LCK S6_SVSCAN_CTLDIR "/lock" #define FINISH_PROG S6_SVSCAN_CTLDIR "/finish" #define CRASH_PROG S6_SVSCAN_CTLDIR "/crash" #define SIGNAL_PROG S6_SVSCAN_CTLDIR "/SIG" #define SIGNAL_PROG_LEN (sizeof(SIGNAL_PROG) - 1) #define SPECIAL_LOGGER_SERVICE "s6-svscan-log" typedef struct service_s service, *service_ref ; struct service_s { devino devino ; pid_t pid ; tain start ; int p ; uint32_t peer ; } ; struct flags_s { uint8_t cont : 1 ; uint8_t waitall : 1 ; } ; static unsigned int consoleholder = 0 ; static struct flags_s flags = { .cont = 1, .waitall = 0 } ; static uint32_t namemax = 251 ; static char *names ; #define NAME(i) (names + (i) * (namemax + 5)) static genset *services ; #define SERVICE(i) genset_p(service, services, (i)) static uint32_t max = 1000 ; static uint32_t special ; static avltreen *by_pid ; static avltreen *by_devino ; static char *active ; static tain scan_deadline = TAIN_EPOCH ; static tain start_deadline = TAIN_INFINITE ; static tain scantto = TAIN_INFINITE_RELATIVE ; /* Tree management */ static void *bydevino_dtok (uint32_t d, void *aux) { genset *g = aux ; return &genset_p(service, g, d)->devino ; } static int bydevino_cmp (void const *a, void const *b, void *aux) { (void)aux ; return devino_cmp(a, b) ; } static void *bypid_dtok (uint32_t d, void *aux) { genset *g = aux ; return &genset_p(service, g, d)->pid ; } static int bypid_cmp (void const *a, void const *b, void *aux) { (void)aux ; pid_t const *aa = a ; pid_t const *bb = b ; return *aa < *bb ? -1 : *aa > *bb ; } /* On-exit utility */ static void restore_console (void) { if (consoleholder) { fd_move(2, consoleholder) ; if (fd_copy(1, 2) < 0) strerr_warnwu1sys("restore stdout") ; } } static void panicnosp (char const *) gccattr_noreturn ; static void panicnosp (char const *errmsg) { char const *eargv[2] = { CRASH_PROG, 0 } ; strerr_warnwu1sys(errmsg) ; strerr_warnw2x("executing into ", eargv[0]) ; execv(eargv[0], (char *const *)eargv) ; strerr_dieexec(errno == ENOENT ? 127 : 126, eargv[0]) ; } static void panic (char const *) gccattr_noreturn ; static void panic (char const *errmsg) { int e = errno ; selfpipe_finish() ; restore_console() ; errno = e ; panicnosp(errmsg) ; } static int close_pipes_iter (void *data, void *aux) { service *sv = data ; if (sv->p >= 0) close(sv->p) ; (void)aux ; return 1 ; } static inline void close_pipes (void) { genset_iter(services, &close_pipes_iter, 0) ; if (special < max) { fd_close(1) ; if (open2("/dev/null", O_WRONLY) < 0) strerr_warnwu1sys("open /dev/null") ; if (fd_copy(2, 1) == -1) strerr_warnwu1sys("redirect stderr to /dev/null") ; } } static inline void waitthem (void) { while (avltreen_len(by_pid)) { int wstat ; pid_t pid = wait_nointr(&wstat) ; if (pid < 0) { strerr_warnwu1sys("wait for all s6-supervise processes") ; break ; } avltreen_delete(by_pid, &pid) ; } } /* Misc utility */ /* what: 1 -> kill all services 2 -> kill inactive services 4 -> send a SIGTERM to loggers instead of SIGHUP 8 -> reap 16 -> delay killing until the next scan */ static inline int is_logger (uint32_t i) { return !!strchr(NAME(i), '/') ; } /* Triggered actions: config */ static inline void chld (unsigned int *what) { *what |= 8 ; } static inline void alrm (unsigned int *what) { *what |= 16 ; tain_copynow(&scan_deadline) ; } static inline void abrt (void) { flags.cont = 0 ; flags.waitall = 0 ; } static void hup (unsigned int *what) { *what |= 18 ; tain_copynow(&scan_deadline) ; } static void term (unsigned int *what) { flags.cont = 0 ; flags.waitall = 1 ; *what |= 3 ; } static void quit (unsigned int *what) { flags.cont = 0 ; flags.waitall = 1 ; *what |= 7 ; } static void handle_signals (unsigned int *what) { for (;;) { int sig = selfpipe_read() ; switch (sig) { case -1 : panic("selfpipe_read") ; case 0 : return ; case SIGCHLD : chld(what) ; break ; case SIGALRM : alrm(what) ; break ; case SIGABRT : abrt() ; break ; default : { int usebuiltin = 1 ; char const *name = sig_name(sig) ; size_t len = strlen(name) ; char fn[SIGNAL_PROG_LEN + len + 1] ; char const *const newargv[2] = { fn, 0 } ; memcpy(fn, SIGNAL_PROG, SIGNAL_PROG_LEN) ; memcpy(fn + SIGNAL_PROG_LEN, name, len + 1) ; if (access(newargv[0], X_OK) == 0) /* avoids a spawn, don't care about the toctou */ { if (cspawn(newargv[0], newargv, (char const **)environ, CSPAWN_FLAGS_SELFPIPE_FINISH, 0, 0)) usebuiltin = 0 ; else if (errno != ENOENT) strerr_warnwu2sys("spawn ", newargv[0]) ; } if (usebuiltin) switch (sig) { case SIGHUP : hup(what) ; break ; case SIGINT : case SIGTERM : term(what) ; break ; case SIGQUIT : quit(what) ; break ; } } } } } static void handle_control (int fd, unsigned int *what) { for (;;) { char c ; ssize_t r = sanitize_read(fd_read(fd, &c, 1)) ; if (r < 0) panic("read control pipe") ; else if (!r) break ; switch (c) { case 'z' : chld(what) ; break ; case 'a' : alrm(what) ; break ; case 'b' : abrt() ; break ; case 'h' : hup(what) ; break ; case 'i' : case 't' : term(what) ; break ; case 'q' : quit(what) ; break ; case 'n' : *what |= 2 ; break ; case 'N' : *what |= 6 ; break ; default : { char s[2] = { c, 0 } ; strerr_warnw2x("received unknown control command: ", s) ; } } } } /* Triggered action: killer */ static int killthem_iter (void *data, void *aux) { service *sv = data ; unsigned int *what = aux ; uint32_t i = sv - SERVICE(0) ; if ((*what & 1 || !bitarray_peek(active, i)) && sv->pid) kill(sv->pid, *what & (2 << (i == special || is_logger(i))) ? SIGTERM : SIGHUP) ; return 1 ; } static inline void killthem (unsigned int *what) { if (*what & 16 || !(*what & 7)) return ; genset_iter(services, &killthem_iter, what) ; *what &= ~7 ; } /* Triggered action: reaper */ /* sv->p values: 0+ : this end of the pipe -1 : not a logged service -2 : inactive and peer dead, do not reactivate -3 : reactivation wanted, trigger rescan on death */ static void remove_service (service *sv) { if (sv->peer < max) { service *peer = SERVICE(sv->peer) ; if (peer->p >= 0) { close(peer->p) ; peer->p = -2 ; } peer->peer = max ; } if (sv->p == -3) tain_earliest1(&scan_deadline, &sv->start) ; else if (sv->p >= 0) close(sv->p) ; avltreen_delete(by_devino, &sv->devino) ; genset_delete(services, sv - SERVICE(0)) ; } static void reap (unsigned int *what) { if (!(*what & 8)) return ; *what &= ~8 ; for (;;) { uint32_t i ; int wstat ; pid_t pid = wait_nohang(&wstat) ; if (pid < 0) if (errno != ECHILD) panic("wait_nohang") ; else break ; else if (!pid) break ; else if (avltreen_search(by_pid, &pid, &i)) { service *sv = SERVICE(i) ; avltreen_delete(by_pid, &pid) ; sv->pid = 0 ; if (bitarray_peek(active, i)) tain_earliest1(&start_deadline, &sv->start) ; else remove_service(sv) ; } } } /* On-timeout action: scanner. (This can be triggered, but the trigger just sets the timeout to 0.) It's on-timeout because it can fail and get rescheduled for later. */ static int check (char const *name, uint32_t prod, char *act) { struct stat st ; devino di ; uint32_t i ; service *sv ; if (stat(name, &st) == -1) { if (prod < max && errno == ENOENT) { if (SERVICE(prod)->peer < max) strerr_warnw3x("logger for service ", NAME(prod), " has been moved") ; return max ; } strerr_warnwu2sys("stat ", name) ; return -4 ; } if (!S_ISDIR(st.st_mode)) return max ; di.dev = st.st_dev ; di.ino = st.st_ino ; if (avltreen_search(by_devino, &di, &i)) { sv = SERVICE(i) ; if (sv->peer < max) { if (prod < max && prod != sv->peer) { strerr_warnw3x("old service ", name, " still exists, waiting") ; return -10 ; } if (sv->p == -1) { sv->p = -2 ; return i ; } } } else { i = genset_new(services) ; if (i >= max) { strerr_warnwu3x("start supervisor for ", name, ": too many services") ; return -60 ; } sv = SERVICE(i) ; sv->devino = di ; sv->pid = 0 ; tain_copynow(&sv->start) ; tain_copynow(&start_deadline) ; /* XXX: may cause a superfluous start if logger fails, oh well */ if (prod >= max) { sv->peer = max ; sv->p = -1 ; if (special >= max && !strcmp(name, SPECIAL_LOGGER_SERVICE)) special = i ; } else { int p[2] ; if (pipecoe(p) == -1) { strerr_warnwu2sys("create pipe for ", name) ; genset_delete(services, i) ; return -3 ; } sv->peer = prod ; sv->p = p[0] ; SERVICE(prod)->peer = i ; SERVICE(prod)->p = p[1] ; } avltreen_insert(by_devino, i) ; } strcpy(NAME(i), name) ; bitarray_set(act, i) ; return i ; } static void set_scan_timeout (unsigned int n) { tain a ; tain_addsec_g(&a, n) ; tain_earliest1(&scan_deadline, &a) ; } static int remove_deadinactive_iter (void *data, void *aux) { service *sv = data ; uint32_t *n = aux ; if (!bitarray_peek(active, sv - SERVICE(0))) { if (!sv->pid) remove_service(sv) ; if (!--n) return 0 ; } return 1 ; } static void scan (unsigned int *what) { DIR *dir = opendir(".") ; char tmpactive[bitarray_div8(max)] ; tain_add_g(&scan_deadline, &scantto) ; memset(tmpactive, 0, bitarray_div8(max)) ; if (!dir) { strerr_warnwu1sys("opendir .") ; set_scan_timeout(5) ; return ; } for (;;) { int i ; size_t len ; direntry *d ; errno = 0 ; d = readdir(dir) ; if (!d) break ; if (d->d_name[0] == '.') continue ; len = strlen(d->d_name) ; if (len > namemax) { strerr_warnw2x("name too long - not spawning service: ", d->d_name) ; continue ; } i = check(d->d_name, max, tmpactive) ; if (i < 0) { dir_close(dir) ; set_scan_timeout(-i) ; return ; } if (i < max) { char logname[len + 5] ; memcpy(logname, d->d_name, len) ; memcpy(logname + len, "/log", 5) ; if (check(logname, i, tmpactive) < 0) { genset_delete(services, i) ; dir_close(dir) ; set_scan_timeout(-i) ; return ; } } } dir_close(dir) ; if (errno) { strerr_warnwu1sys("readdir .") ; set_scan_timeout(5) ; return ; } memcpy(active, tmpactive, bitarray_div8(max)) ; { uint32_t n = genset_n(services) - avltreen_len(by_devino) ; if (n) genset_iter(services, &remove_deadinactive_iter, &n) ; } *what &= ~16 ; } /* On-timeout action: starter. This cannot be user-triggered. It runs when a service needs to (re)start. */ static int start_iter (void *data, void *aux) { service *sv = data ; uint32_t i = sv - SERVICE(0) ; char const *const cargv[3] = { "s6-supervise", NAME(i), 0 } ; cspawn_fileaction fa[2] = { [0] = { .type = CSPAWN_FA_MOVE }, [1] = { .type = CSPAWN_FA_MOVE } } ; size_t j = 0 ; if (!bitarray_peek(active, i) || sv->pid || tain_future(&sv->start)) return 1 ; if (sv->peer < max) { fa[j].x.fd2[0] = !is_logger(i) ; fa[j].x.fd2[1] = sv->p ; j++ ; } if (consoleholder && i == special) { fa[j].x.fd2[0] = 2 ; fa[j].x.fd2[1] = consoleholder ; j++ ; } sv->pid = cspawn(S6_BINPREFIX "s6-supervise", cargv, (char const *const *)environ, CSPAWN_FLAGS_SELFPIPE_FINISH, fa, j) ; if (!sv->pid) { strerr_warnwu2sys("spawn s6-supervise for ", NAME(i)) ; tain_addsec_g(&start_deadline, 10) ; return 0 ; } avltreen_insert(by_pid, i) ; tain_addsec_g(&sv->start, 1) ; (void)aux ; return 1 ; } static inline void start (void) { start_deadline = tain_infinite ; genset_iter(services, &start_iter, 0) ; } /* Main. */ static inline int control_init (void) { mode_t m = umask(0) ; int fdctl, fdlck, r ; if (mkdir(S6_SVSCAN_CTLDIR, 0700) < 0) { struct stat st ; if (errno != EEXIST) strerr_diefu1sys(111, "mkdir " S6_SVSCAN_CTLDIR) ; if (stat(S6_SVSCAN_CTLDIR, &st) < 0) strerr_diefu1sys(111, "stat " S6_SVSCAN_CTLDIR) ; if (!S_ISDIR(st.st_mode)) strerr_dief1x(100, S6_SVSCAN_CTLDIR " exists and is not a directory") ; } fdlck = open3(LCK, O_WRONLY | O_NONBLOCK | O_CREAT | O_CLOEXEC, 0600) ; if (fdlck < 0) strerr_diefu1sys(111, "open " LCK) ; r = fd_lock(fdlck, 1, 1) ; if (r < 0) strerr_diefu1sys(111, "lock " LCK) ; if (!r) strerr_dief1x(100, "another instance of s6-svscan is already running on the same directory") ; /* fdlck leaks but it's coe */ if (mkfifo(CTL, 0600) < 0) { struct stat st ; if (errno != EEXIST) strerr_diefu1sys(111, "mkfifo " CTL) ; if (stat(CTL, &st) < 0) strerr_diefu1sys(111, "stat " CTL) ; if (!S_ISFIFO(st.st_mode)) strerr_dief1x(100, CTL " is not a FIFO") ; } fdctl = openc_read(CTL) ; if (fdctl < 0) strerr_diefu1sys(111, "open " CTL " for reading") ; r = openc_write(CTL) ; if (r < 0) strerr_diefu1sys(111, "open " CTL " for writing") ; /* r leaks but it's coe */ umask(m) ; return fdctl ; } int main (int argc, char const *const *argv) { iopause_fd x[2] = { { .fd = -1, .events = IOPAUSE_READ }, { .fd = -1, .events = IOPAUSE_READ } } ; PROG = "s6-svscan" ; { subgetopt l = SUBGETOPT_ZERO ; unsigned int notif = 0 ; unsigned int t = 0 ; for (;;) { int opt = subgetopt_r(argc, argv, "c:C:L:t:d:X:", &l) ; if (opt == -1) break ; switch (opt) { case 'c' : if (!uint320_scan(l.arg, &max)) dieusage() ; max <<= 1 ; break ; case 'C' : if (!uint320_scan(l.arg, &max)) dieusage() ; break ; case 'L' : if (!uint320_scan(l.arg, &namemax)) dieusage() ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; case 'd' : if (!uint0_scan(l.arg, ¬if)) dieusage() ; break ; case 'X' : if (!uint0_scan(l.arg, &consoleholder)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&scantto, t) ; if (max < 4) max = 4 ; if (max > 160000) max = 160000 ; special = max ; if (namemax < 11) namemax = 11 ; if (namemax > 1019) namemax = 1019 ; if (notif) { if (notif < 3) strerr_dief1x(100, "notification fd must be 3 or more") ; if (fcntl(notif, F_GETFD) == -1) strerr_dief1sys(100, "invalid notification fd") ; } if (consoleholder) { if (consoleholder < 3) strerr_dief1x(100, "console holder fd must be 3 or more") ; if (fcntl(consoleholder, F_GETFD) < 0) strerr_dief1sys(100, "invalid console holder fd") ; if (coe(consoleholder) == -1) strerr_diefu1sys(111, "coe console holder") ; } if (!fd_sanitize()) strerr_diefu1x(100, "sanitize standard fds") ; if (argc && (chdir(argv[0]) == -1)) strerr_diefu1sys(111, "chdir") ; x[1].fd = control_init() ; x[0].fd = selfpipe_init() ; if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; { sigset_t set ; sigemptyset(&set) ; sigaddset(&set, SIGCHLD) ; sigaddset(&set, SIGALRM) ; sigaddset(&set, SIGABRT) ; sigaddset(&set, SIGHUP) ; sigaddset(&set, SIGINT) ; sigaddset(&set, SIGTERM) ; sigaddset(&set, SIGQUIT) ; sigaddset(&set, SIGUSR1) ; sigaddset(&set, SIGUSR2) ; #ifdef SIGPWR sigaddset(&set, SIGPWR) ; #endif #ifdef SIGWINCH sigaddset(&set, SIGWINCH) ; #endif if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ; } if (notif) { write(notif, "\n", 1) ; close(notif) ; } } { unsigned int what = 0 ; service services_storage[max] ; uint32_t services_freelist[max] ; avlnode bydevino_storage[max] ; uint32_t bydevino_freelist[max] ; avlnode bypid_storage[max] ; uint32_t bypid_freelist[max] ; genset services_info ; avltreen bydevino_info ; avltreen bypid_info ; char name_storage[max * (namemax + 5)] ; char active_storage[bitarray_div8(max)] ; GENSET_init(&services_info, service, services_storage, services_freelist, max) ; avltreen_init(&bydevino_info, bydevino_storage, bydevino_freelist, max, &bydevino_dtok, &bydevino_cmp, &services_info) ; avltreen_init(&bypid_info, bypid_storage, bypid_freelist, max, &bypid_dtok, &bypid_cmp, &services_info) ; services = &services_info ; by_devino = &bydevino_info ; by_pid = &bypid_info ; names = name_storage ; active = active_storage ; tain_now_set_stopwatch_g() ; /* From now on, we must not die. Temporize on recoverable errors, and panic on serious ones. */ while (flags.cont) { int r ; tain deadline = scan_deadline ; tain_earliest1(&deadline, &start_deadline) ; r = iopause_g(x, 2, &deadline) ; if (r < 0) panic("iopause") ; else if (!r) { if (!tain_future(&scan_deadline)) scan(&what) ; if (!tain_future(&start_deadline)) start() ; } else { if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) { errno = EIO ; panic("check internal pipes") ; } if (x[0].revents & IOPAUSE_READ) handle_signals(&what) ; if (x[1].revents & IOPAUSE_READ) handle_control(x[1].fd, &what) ; } killthem(&what) ; reap(&what) ; } /* Finish phase. */ close_pipes() ; restore_console() ; if (flags.waitall) waitthem() ; selfpipe_finish() ; } { char const *eargv[2] = { FINISH_PROG, 0 } ; execv(eargv[0], (char **)eargv) ; if (errno != ENOENT) panicnosp("exec finish script " FINISH_PROG) ; } return 0 ; } s6-2.13.1.0/src/supervision/s6-svscanctl.c000066400000000000000000000024631470151141200201340ustar00rootroot00000000000000/* ISC license. */ #include #include #include #define USAGE "s6-svscanctl [ -zabhitqnN ] svscandir" #define dieusage() strerr_dieusage(100, USAGE) #define DATASIZE 64 int main (int argc, char const *const *argv) { char data[DATASIZE] ; unsigned int datalen = 0 ; PROG = "s6-svscanctl" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "phratszbnNiq0678", &l) ; if (opt == -1) break ; switch (opt) { case 'z' : case 'a' : case 'b' : case 'h' : case 'i' : case 't' : case 'q' : case 'n' : case 'N' : { if (datalen >= DATASIZE) strerr_dief1x(100, "too many commands") ; data[datalen++] = opt ; break ; } default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; switch (s6_svc_writectl(argv[0], S6_SVSCAN_CTLDIR, data, datalen)) { case -1 : strerr_diefu2sys(111, "control ", argv[0]) ; case -2 : strerr_dief3sys(100, "something is wrong with the ", argv[0], "/" S6_SVSCAN_CTLDIR " directory. errno reported") ; case 0 : strerr_diefu3x(100, "control ", argv[0], ": supervisor not listening") ; } return 0 ; } s6-2.13.1.0/src/supervision/s6-svstat.c000066400000000000000000000216321470151141200174570ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-svstat [ -uwNrpgest | -o up,wantedup,normallyup,ready,paused,pid,pgid,exitcode,signal,signum,updownsince,readysince,updownfor,readyfor ] [ -n ] servicedir" #define dieusage() strerr_dieusage(100, USAGE) #define MAXFIELDS 16 #define checkfields() if (n >= MAXFIELDS) strerr_dief1x(100, "too many option fields") static int normallyup ; typedef void pr_func (buffer *, s6_svstatus_t const *) ; typedef pr_func * pr_func_ref ; typedef struct funcmap_s funcmap_t ; struct funcmap_s { char const *s ; pr_func_ref f ; } ; static void pr_up (buffer *b, s6_svstatus_t const *st) { buffer_putsnoflush(b, st->pid && !st->flagfinishing ? "true" : "false") ; } static void pr_wantedup (buffer *b, s6_svstatus_t const *st) { buffer_putsnoflush(b, st->flagwantup ? "true" : "false") ; } static void pr_ready (buffer *b, s6_svstatus_t const *st) { buffer_putsnoflush(b, st->pid && st->flagready ? "true" : "false") ; } static void pr_paused (buffer *b, s6_svstatus_t const *st) { buffer_putsnoflush(b, st->flagpaused ? "true" : "false") ; } static void pr_pid (buffer *b, s6_svstatus_t const *st) { if (st->pid && !st->flagfinishing) { char fmt[PID_FMT] ; buffer_putnoflush(b, fmt, pid_fmt(fmt, st->pid)) ; } else buffer_putsnoflush(b, "-1") ; } static void pr_pgid (buffer *b, s6_svstatus_t const *st) { if (st->pgid > 0) { char fmt[PID_FMT] ; buffer_putnoflush(b, fmt, pid_fmt(fmt, st->pgid)) ; } else buffer_putsnoflush(b, "-1") ; } static void pr_tain (buffer *b, tain const *a) { char fmt[TIMESTAMP] ; buffer_putnoflush(b, fmt, timestamp_fmt(fmt, a)) ; } static void pr_stamp (buffer *b, s6_svstatus_t const *st) { pr_tain(b, &st->stamp) ; } static void pr_readystamp (buffer *b, s6_svstatus_t const *st) { pr_tain(b, &st->readystamp) ; } static void pr_seconds (buffer *b, tain const *a) { tain d ; char fmt[UINT64_FMT] ; tain_sub(&d, &STAMP, a) ; buffer_putnoflush(b, fmt, uint64_fmt(fmt, tai_sec(tain_secp(&d)))) ; } static void pr_upseconds (buffer *b, s6_svstatus_t const *st) { pr_seconds(b, &st->stamp) ; } static void pr_readyseconds (buffer *b, s6_svstatus_t const *st) { pr_seconds(b, &st->readystamp) ; } static void pr_exitcode (buffer *b, s6_svstatus_t const *st) { int e = st->pid && !st->flagfinishing ? -1 : WIFEXITED(st->wstat) ? WEXITSTATUS(st->wstat) : -1 ; char fmt[INT_FMT] ; buffer_putnoflush(b, fmt, int_fmt(fmt, e)) ; } static void pr_signum (buffer *b, s6_svstatus_t const *st) { int e = st->pid && !st->flagfinishing ? -1 : WIFSIGNALED(st->wstat) ? WTERMSIG(st->wstat) : -1 ; char fmt[INT_FMT] ; buffer_putnoflush(b, fmt, int_fmt(fmt, e)) ; } static void pr_signal (buffer *b, s6_svstatus_t const *st) { int e = st->pid && !st->flagfinishing ? -1 : WIFSIGNALED(st->wstat) ? WTERMSIG(st->wstat) : -1 ; if (e == -1) buffer_putsnoflush(b, "NA") ; else { buffer_putsnoflush(b, "SIG") ; buffer_putsnoflush(b, sig_name(e)) ; } } static void pr_normallyup (buffer *b, s6_svstatus_t const *st) { buffer_putsnoflush(b, normallyup ? "true" : "false") ; (void)st ; } static funcmap_t const fmtable[] = { { .s = "exitcode", .f = &pr_exitcode }, { .s = "normallyup", .f = &pr_normallyup }, { .s = "paused", .f = &pr_paused }, { .s = "pgid", .f = &pr_pgid }, { .s = "pid", .f = &pr_pid }, { .s = "ready", .f = &pr_ready }, { .s = "readyfor", .f = &pr_readyseconds }, { .s = "readysince", .f = &pr_readystamp }, { .s = "signal", .f = &pr_signal }, { .s = "signum", .f = &pr_signum }, { .s = "up", .f = &pr_up }, { .s = "updownfor", .f = &pr_upseconds }, { .s = "updownsince", .f = &pr_stamp }, { .s = "wantedup", .f = &pr_wantedup }, } ; static int funcmap_bcmp (void const *a, void const *b) { return strcmp((char const *)a, ((funcmap_t const *)b)->s) ; } #define BSEARCH(key, array) bsearch(key, (array), sizeof(array)/sizeof(funcmap_t), sizeof(funcmap_t), &funcmap_bcmp) static unsigned int parse_options (char const *arg, pr_func_ref *fields, unsigned int n) { while (*arg) { funcmap_t const *p ; size_t pos = str_chr(arg, ',') ; char blah[pos+1] ; memcpy(blah, arg, pos) ; blah[pos] = 0 ; p = BSEARCH(blah, fmtable) ; if (!p) strerr_dief2x(100, "invalid option field: ", blah) ; checkfields() ; fields[n++] = p->f ; arg += pos ; if (*arg) arg++ ; } return n ; } static void legacy (s6_svstatus_t *st, int flagnum) { s6_svstatus_t status = *st ; int isup = status.pid && !status.flagfinishing ; char fmt[UINT64_FMT] ; if (isup) { buffer_putnoflush(buffer_1small,"up (pid ", 8) ; buffer_putnoflush(buffer_1small, fmt, pid_fmt(fmt, status.pid)) ; buffer_putnoflush(buffer_1small, " pgid ", 6) ; buffer_putnoflush(buffer_1small, fmt, pid_fmt(fmt, status.pgid)) ; buffer_putnoflush(buffer_1small, ") ", 2) ; } else { buffer_putnoflush(buffer_1small, "down (", 6) ; if (WIFSIGNALED(status.wstat)) { buffer_putnoflush(buffer_1small, "signal ", 7) ; if (flagnum) buffer_putnoflush(buffer_1small, fmt, uint_fmt(fmt, WTERMSIG(status.wstat))) ; else { buffer_putnoflush(buffer_1small, "SIG", 3) ; buffer_putsnoflush(buffer_1small, sig_name(WTERMSIG(status.wstat))) ; } } else { buffer_putnoflush(buffer_1small, "exitcode ", 9) ; buffer_putnoflush(buffer_1small, fmt, uint_fmt(fmt, WEXITSTATUS(status.wstat))) ; } buffer_putnoflush(buffer_1small, ") ", 2) ; } tain_sub(&status.stamp, &STAMP, &status.stamp) ; buffer_putnoflush(buffer_1small, fmt, uint64_fmt(fmt, status.stamp.sec.x)) ; buffer_putnoflush(buffer_1small, " seconds", 8) ; if (isup && !normallyup) buffer_putnoflush(buffer_1small, ", normally down", 15) ; if (!isup && normallyup) buffer_putnoflush(buffer_1small, ", normally up", 13) ; if (isup && status.flagpaused) buffer_putnoflush(buffer_1small, ", paused", 8) ; if (!isup && status.flagwantup) buffer_putnoflush(buffer_1small, ", want up", 9) ; if (isup && !status.flagwantup) buffer_putnoflush(buffer_1small, ", want down", 11) ; if (status.flagready) { tain_sub(&status.readystamp, &STAMP, &status.readystamp) ; buffer_putnoflush(buffer_1small, ", ready ", 8) ; buffer_putnoflush(buffer_1small, fmt, uint64_fmt(fmt, status.readystamp.sec.x)) ; buffer_putnoflush(buffer_1small, " seconds", 8) ; } } int main (int argc, char const *const *argv) { s6_svstatus_t status ; int flagnum = 0 ; pr_func_ref fields[MAXFIELDS] ; unsigned int n = 0 ; PROG = "s6-svstat" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "no:uwNrpgest", &l) ; if (opt == -1) break ; switch (opt) { case 'n' : flagnum = 1 ; break ; case 'o' : n = parse_options(l.arg, fields, n) ; break ; case 'u' : checkfields() ; fields[n++] = &pr_up ; break ; case 'w' : checkfields() ; fields[n++] = &pr_wantedup ; break ; case 'N' : checkfields() ; fields[n++] = &pr_normallyup ; break ; case 'r' : checkfields() ; fields[n++] = &pr_ready ; break ; case 'p' : checkfields() ; fields[n++] = &pr_pid ; break ; case 'g' : checkfields() ; fields[n++] = &pr_pgid ; break ; case 'e' : checkfields() ; fields[n++] = &pr_exitcode ; break ; case 's' : checkfields() ; fields[n++] = &pr_signal ; break ; case 't' : checkfields() ; fields[n++] = &pr_upseconds ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (!argc) dieusage() ; fields[n] = 0 ; { int r = s6_svc_ok(argv[0]) ; if (r < 0) strerr_diefu2sys(111, "check ", argv[0]) ; if (!r) strerr_diefu3x(1, "read status for ", argv[0], ": s6-supervise not running") ; } if (!s6_svstatus_read(argv[0], &status)) strerr_diefu2sys(111, "read status for ", argv[0]) ; tain_wallclock_read_g() ; if (tain_future(&status.stamp)) tain_copynow(&status.stamp) ; { size_t dirlen = strlen(*argv) ; char fn[dirlen + 6] ; memcpy(fn, *argv, dirlen) ; memcpy(fn + dirlen, "/down", 6) ; if (access(fn, F_OK) < 0) if (errno != ENOENT) strerr_diefu2sys(111, "access ", fn) ; else normallyup = 1 ; else normallyup = 0 ; } if (!n) legacy(&status, flagnum) ; else { unsigned int i = 0 ; for (; fields[i] ; i++) { (*fields[i])(buffer_1small, &status) ; buffer_putsnoflush(buffer_1small, " ") ; } buffer_unput(buffer_1small, 1) ; } if (buffer_putflush(buffer_1small, "\n", 1) < 0) strerr_diefu1sys(111, "write to stdout") ; return 0 ; } s6-2.13.1.0/src/supervision/s6-svunlink.c000066400000000000000000000024001470151141200177740ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #define USAGE "s6-svunlink [ -X ] [ -t timeout ] scandir servicename" #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { tain tto = TAIN_INFINITE_RELATIVE ; uint32_t options = 1 ; PROG = "s6-svunlink" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "Xt:", &l) ; if (opt == -1) break ; switch (opt) { case 'X' : options &= ~1U ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&tto, t) ; } if (argc < 2) dieusage() ; if (!argv[1][0] || argv[1][0] == '.' || argv[1][0] == '/') strerr_dief1x(100, "invalid service name") ; tain_now_set_stopwatch_g() ; tain_add_g(&tto, &tto) ; if (s6_supervise_unlink_names_g(argv[0], argv + 1, 1, options, &tto) == -1) strerr_diefu4sys(111, "prepare unlinking of service ", argv[1], " in scandir ", argv[0]) ; return 0 ; } s6-2.13.1.0/src/supervision/s6-svwait.c000066400000000000000000000042651470151141200174530ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include "s6-svlisten.h" #define USAGE "s6-svwait [ -U | -u | -d | -D | -r | -R ] [ -a | -o ] [ -t timeout ] servicedir..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) { tain deadline ; int or = 0 ; int wantup = 1, wantready = 0 ; PROG = "s6-svwait" ; { unsigned int t = 0 ; subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, "UudDrRaot:", &l) ; if (opt == -1) break ; switch (opt) { case 'U' : wantup = 1 ; wantready = 1 ; break ; case 'u' : wantup = 1 ; wantready = 0 ; break ; case 'd' : wantup = 0 ; wantready = 0 ; break ; case 'D' : wantup = 0 ; wantready = 1 ; break ; case 'r' : wantup = 2 ; wantready = 0 ; break ; case 'R' : wantup = 2 ; wantready = 1 ; break ; case 'a' : or = 0 ; break ; case 'o' : or = 1 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ; } if (!argc) return 0 ; tain_now_set_stopwatch_g() ; tain_add_g(&deadline, &deadline) ; { s6_svlisten_t foo = S6_SVLISTEN_ZERO ; unsigned int e ; uint16_t ids[argc] ; unsigned char upstate[bitarray_div8(argc)] ; unsigned char readystate[bitarray_div8(argc)] ; if (!sig_ignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; s6_svlisten_init(argc, argv, &foo, ids, upstate, readystate, &deadline) ; if (wantup == 2) { wantup = 1 ; e = s6_svlisten_loop(&foo, 0, 0, 0, &deadline, -1, 0) ; if (e) strerr_dief1x(e, "some services reported permanent failure or their supervisor died") ; } e = s6_svlisten_loop(&foo, wantup, wantready, or, &deadline, -1, 0) ; if (e) strerr_dief1x(e, "some services reported permanent failure or their supervisor died") ; } return 0 ; } s6-2.13.1.0/src/supervision/s6_svlisten_loop.c000066400000000000000000000074041470151141200211160ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "s6-svlisten.h" void s6_svlisten_init (int argc, char const *const *argv, s6_svlisten_t *foo, uint16_t *ids, unsigned char *upstate, unsigned char *readystate, tain const *deadline) { gid_t gid = getegid() ; unsigned int i = 0 ; foo->n = (unsigned int)argc ; foo->ids = ids ; foo->upstate = upstate ; foo->readystate = readystate ; if (!ftrigr_startf_g(&foo->a, deadline)) strerr_diefu1sys(111, "ftrigr_startf") ; for (; i < foo->n ; i++) { s6_svstatus_t status = S6_SVSTATUS_ZERO ; size_t len = strlen(argv[i]) ; char s[len + 1 + sizeof(S6_SUPERVISE_EVENTDIR)] ; memcpy(s, argv[i], len) ; s[len] = '/' ; memcpy(s + len + 1, S6_SUPERVISE_EVENTDIR, sizeof(S6_SUPERVISE_EVENTDIR)) ; ftrigw_fifodir_make(s, gid, 0) ; foo->ids[i] = ftrigr_subscribe_g(&foo->a, s, "[DuUdOx]", FTRIGR_REPEAT, deadline) ; if (!foo->ids[i]) strerr_diefu2sys(111, "subscribe to events for ", argv[i]) ; if (!s6_svstatus_read(argv[i], &status)) strerr_diefu1sys(111, "s6_svstatus_read") ; bitarray_poke(foo->upstate, i, status.pid && !status.flagfinishing) ; bitarray_poke(foo->readystate, i, status.flagready) ; } } static inline int got (s6_svlisten_t const *foo, int wantup, int wantready, int or) { unsigned int m = bitarray_div8(foo->n) ; unsigned char t[m] ; memcpy(t, foo->upstate, m) ; if (!wantup) bitarray_not(t, 0, foo->n) ; if (wantready) bitarray_and(t, t, foo->readystate, foo->n) ; return (bitarray_first(t, foo->n, or) < foo->n) == or ; } unsigned int s6_svlisten_loop (s6_svlisten_t *foo, int wantup, int wantready, int or, tain const *deadline, int spfd, action_func_ref handler) { iopause_fd x[2] = { { .fd = ftrigr_fd(&foo->a), .events = IOPAUSE_READ }, { .fd = spfd, .events = IOPAUSE_READ, .revents = 0 } } ; unsigned int e = 0 ; stralloc sa = STRALLOC_ZERO ; if (got(foo, wantup, wantready, or)) return 0 ; for (;;) { int r = iopause_g(x, 1 + (spfd >= 0), deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; else if (!r) strerr_dief1x(99, "timed out") ; if (x[1].revents & IOPAUSE_READ) (*handler)() ; if (x[0].revents & IOPAUSE_READ) { unsigned int i = 0 ; if (ftrigr_update(&foo->a) < 0) strerr_diefu1sys(111, "ftrigr_update") ; for (; i < foo->n ; i++) { sa.len = 0 ; r = ftrigr_checksa(&foo->a, foo->ids[i], &sa) ; if (r < 0) strerr_diefu1sys(111, "ftrigr_check") ; else if (r) { size_t j = 0 ; for (; j < sa.len ; j++) { if (sa.s[j] == 'x') { if (bitarray_peek(foo->upstate, i) != wantup || bitarray_peek(foo->readystate, i) != wantready) e++ ; bitarray_poke(foo->upstate, i, wantup) ; bitarray_poke(foo->readystate, i, wantready) ; } else if (sa.s[j] == 'O') { if (wantup) { bitarray_poke(foo->upstate, i, wantup) ; bitarray_poke(foo->readystate, i, wantready) ; e++ ; } } else { unsigned int d = byte_chr("dDuU", 4, sa.s[j]) ; bitarray_poke(foo->upstate, i, d & 2) ; bitarray_poke(foo->readystate, i, d & 1) ; } if (got(foo, wantup, wantready, or)) goto gotit ; } } } } } gotit: stralloc_free(&sa) ; return e ; } s6-2.13.1.0/src/supervision/s6_svlisten_signal_handler.c000066400000000000000000000013051470151141200231110ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include "s6-svlisten.h" void s6_svlisten_selfpipe_init (void) { if (selfpipe_init() == -1) strerr_diefu1sys(111, "selfpipe_init") ; if (!selfpipe_trap(SIGCHLD)) strerr_diefu1sys(111, "selfpipe_trap") ; if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; } void s6_svlisten_signal_handler (void) { for (;;) switch (selfpipe_read()) { case -1 : strerr_diefu1sys(111, "selfpipe_read") ; case 0 : return ; case SIGCHLD : wait_reap() ; break ; default : strerr_dief1x(101, "unexpected data in selfpipe") ; } } s6-2.13.1.0/src/usertree/000077500000000000000000000000001470151141200147055ustar00rootroot00000000000000s6-2.13.1.0/src/usertree/deps-exe/000077500000000000000000000000001470151141200164175ustar00rootroot00000000000000s6-2.13.1.0/src/usertree/deps-exe/s6-usertree-maker000066400000000000000000000000341470151141200216200ustar00rootroot00000000000000libs6auto.a.xyzzy -lskarnet s6-2.13.1.0/src/usertree/s6-usertree-maker.c000066400000000000000000000166251470151141200203440ustar00rootroot00000000000000/* ISC license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USAGE "s6-usertree-maker [ -d userscandir ] [ -p path ] [ -E envdir [ -e var ... ] ] [ -r service/logger[/pipeline] ] [ -l loguser ] [ -t stamptype ] [ -n nfiles ] [ -s filesize ] [ -S maxsize ] [ -P prefix ] user logdir dir" #define dieusage() strerr_dieusage(100, USAGE) #define VARS_MAX 64 static stralloc sa = STRALLOC_ZERO ; typedef struct svinfo_s svinfo, *svinfo_ref ; struct svinfo_s { char const *user ; char const *sc ; char const *logger ; char const *path ; char const *userenvdir ; char const **vars ; size_t varlen ; } ; static int write_run (buffer *b, void *data) { svinfo *t = data ; if (!string_quote(&sa, t->user, strlen(t->user))) return 0 ; if (buffer_puts(b, "#!" EXECLINE_SHEBANGPREFIX "execlineb -P\n" EXECLINE_EXTBINPREFIX "fdmove -c 2 1\n" EXECLINE_EXTBINPREFIX "emptyenv -p\n" EXECLINE_EXTBINPREFIX "export USER ") < 0 || buffer_put(b, sa.s, sa.len) < 0 || buffer_puts(b, "\n" S6_EXTBINPREFIX "s6-envuidgid -i -- ") < 0 || buffer_put(b, sa.s, sa.len) < 0 || buffer_puts(b, "\n" S6_EXTBINPREFIX "s6-applyuidgid -U --\n" EXECLINE_EXTBINPREFIX "backtick -in HOME { " EXECLINE_EXTBINPREFIX "homeof ") < 0 || buffer_put(b, sa.s, sa.len) < 0 || buffer_put(b, " }\n", 3) < 0) goto err ; sa.len = 0 ; if (t->userenvdir) { if (!string_quote(&sa, t->userenvdir, strlen(t->userenvdir))) return 0 ; if (buffer_puts(b, S6_EXTBINPREFIX "s6-envdir -i -- ") < 0 || buffer_put(b, sa.s, sa.len) < 0 || buffer_put(b, "\n", 1) < 0) goto err ; sa.len = 0 ; if (t->varlen) { if (buffer_puts(b, EXECLINE_EXTBINPREFIX "multisubstitute\n{\n") < 0) return 0 ; for (size_t i = 0 ; i < t->varlen ; i++) { if (!string_quote(&sa, t->vars[i], strlen(t->vars[i]))) return 0 ; if (buffer_puts(b, " importas -D \"\" -- ") < 0 || buffer_put(b, sa.s, sa.len) < 0 || buffer_put(b, " ", 1) < 0 || buffer_put(b, sa.s, sa.len) < 0 || buffer_put(b, "\n", 1) < 0) goto err ; sa.len = 0 ; } if (buffer_put(b, "}\n", 2) < 0) return 0 ; } } if (buffer_puts(b, EXECLINE_EXTBINPREFIX "multisubstitute\n{\n" " importas -i USER USER\n" " importas -i HOME HOME\n" " importas -i UID UID\n" " importas -i GID GID\n" " importas -i GIDLIST GIDLIST\n}\n") < 0) return 0 ; if (t->userenvdir && t->varlen) { for (size_t i = 0 ; i < t->varlen ; i++) { if (!string_quote(&sa, t->vars[i], strlen(t->vars[i]))) return 0 ; if (buffer_puts(b, EXECLINE_EXTBINPREFIX "export ") < 0 || buffer_put(b, sa.s, sa.len) < 0 || buffer_put(b, " ${", 3) < 0 || buffer_put(b, sa.s, sa.len) < 0 || buffer_put(b, "}\n", 2) < 0) goto err ; sa.len = 0 ; } } if (!string_quote(&sa, t->path, strlen(t->path))) return 0 ; if (buffer_puts(b, EXECLINE_EXTBINPREFIX "export PATH ") < 0 || buffer_put(b, sa.s, sa.len) < 0) goto err ; sa.len = 0 ; if (!string_quote(&sa, t->sc, strlen(t->sc))) return 0 ; if (buffer_puts(b, "\n" S6_EXTBINPREFIX "s6-svscan -d3 -- ") < 0 || buffer_put(b, sa.s, sa.len) < 0) goto err ; sa.len = 0 ; if (!buffer_putflush(b, "\n", 1)) return 0 ; return 1 ; err: sa.len = 0 ; return 0 ; } int main (int argc, char *const *argv) { char const *vars[VARS_MAX] ; svinfo t = { .sc = "${HOME}/service", .logger = 0, .path = SKALIBS_DEFAULTPATH, .userenvdir = 0, .vars = vars, .varlen = 0 } ; char *rcinfo[3] = { 0, 0, 0 } ; char const *loguser = 0 ; unsigned int stamptype = 1 ; unsigned int nfiles = 10 ; uint64_t filesize = 1000000 ; uint64_t maxsize = 0 ; char const *prefix = 0 ; size_t dirlen ; PROG = "s6-usertree-maker" ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, (char const *const *)argv, "d:p:E:e:r:l:t:n:s:S:P:", &l) ; if (opt == -1) break ; switch (opt) { case 'd' : t.sc = l.arg ; break ; case 'p' : t.path = l.arg ; break ; case 'E' : t.userenvdir = l.arg ; break ; case 'e' : if (t.varlen >= VARS_MAX) strerr_dief1x(100, "too many -v variables") ; if (strchr(l.arg, '=')) strerr_dief2x(100, "invalid variable name: ", l.arg) ; t.vars[t.varlen++] = l.arg ; break ; case 'r' : rcinfo[0] = (char *)l.arg ; break ; case 'l' : loguser = l.arg ; break ; case 't' : if (!uint0_scan(l.arg, &stamptype)) dieusage() ; break ; case 'n' : if (!uint0_scan(l.arg, &nfiles)) dieusage() ; break ; case 's' : if (!uint640_scan(l.arg, &filesize)) dieusage() ; break ; case 'S' : if (!uint640_scan(l.arg, &maxsize)) dieusage() ; break ; case 'P' : prefix = l.arg ; break ; default : dieusage() ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 3) dieusage() ; if (argv[1][0] != '/') strerr_dief1x(100, "logdir must be absolute") ; if (t.sc[0] != '/' && !str_start(t.sc, "${HOME}/")) strerr_dief1x(100, "userscandir must be absolute or start with ${HOME}/") ; if (stamptype > 3) strerr_dief1x(100, "stamptype must be 0, 1, 2 or 3") ; if (rcinfo[0]) { if (strchr(rcinfo[0], '\n')) strerr_dief2x(100, "newlines", " are forbidden in s6-rc names") ; if (rcinfo[0][0] == '/') strerr_dief2x(100, "service", " name cannot be empty") ; rcinfo[1] = strchr(rcinfo[0], '/') ; if (!rcinfo[1]) strerr_dief1x(100, "argument to -r must be: service/logger or service/logger/pipeline") ; *rcinfo[1]++ = 0 ; if (!rcinfo[1][0]) strerr_dief1x(100, "argument to -r must be: service/logger or service/logger/pipeline") ; if (rcinfo[1][0] == '/') strerr_dief2x(100, "logger", " name cannot be empty") ; rcinfo[2] = strchr(rcinfo[1], '/') ; if (rcinfo[2]) { *rcinfo[2]++ = 0 ; if (!rcinfo[2][0]) strerr_dief2x(100, "pipeline", " name cannot be empty") ; if (strchr(rcinfo[2], '/')) strerr_dief2x(100, "slashes", " are forbidden in s6-rc names") ; } } t.user = argv[0] ; dirlen = strlen(argv[2]) ; if (rcinfo[0]) { size_t svclen = strlen(rcinfo[0]) ; size_t loglen = strlen(rcinfo[1]) ; char dir[dirlen + 2 + (svclen > loglen ? svclen : loglen)] ; memcpy(dir, argv[2], dirlen) ; dir[dirlen] = '/' ; if (mkdir(argv[2], 0755) < 0 && errno != EEXIST) strerr_diefu2sys(111, "mkdir ", argv[2]) ; t.logger = rcinfo[1] ; memcpy(dir + dirlen + 1, rcinfo[0], svclen + 1) ; s6_auto_write_service(dir, 3, &write_run, &t, rcinfo[1]) ; memcpy(dir + dirlen + 1, rcinfo[1], loglen + 1) ; s6_auto_write_logger_tmp(dir, loguser, argv[1], stamptype, nfiles, filesize, maxsize, prefix, rcinfo[0], rcinfo[2], &sa) ; } else { char dir[dirlen + 5] ; memcpy(dir, argv[2], dirlen) ; memcpy(dir + dirlen, "/log", 5) ; s6_auto_write_service(argv[2], 3, &write_run, &t, 0) ; s6_auto_write_logger_tmp(dir, loguser, argv[1], stamptype, nfiles, filesize, maxsize, prefix, 0, 0, &sa) ; } return 0 ; } s6-2.13.1.0/tools/000077500000000000000000000000001470151141200134205ustar00rootroot00000000000000s6-2.13.1.0/tools/gen-deps.sh000077500000000000000000000052641470151141200154700ustar00rootroot00000000000000#!/bin/sh -e . package/info echo '#' echo '# This file has been generated by tools/gen-deps.sh' echo '#' echo internal_libs= 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= libs= while read dep ; do if echo $dep | grep -q -e ^-l -e '^\${.*_LIB}' ; then libs="$libs $dep" else deps="$deps src/$dir/$dep" fi done < src/$dir/deps-lib/$file echo 'ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),)' echo "lib${file}.a.xyzzy:$deps" echo else echo "lib${file}.a.xyzzy:$(echo "$deps" | sed 's/\.o/.lo/g')" echo endif if grep -E "^LIB_DEFS [+:]=" package/targets.mak | grep -qF "$file" ; then echo "lib${file}.so.xyzzy: EXTRA_LIBS :=$libs" echo "lib${file}.so.xyzzy:$(echo "$deps" | sed 's/\.o/.lo/g')" else internal_libs="$internal_libs lib${file}.a.xyzzy" fi 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 -e ^-l -e '^\${.*_LIB}' ; then libs="$libs $dep" else deps="$deps $dep" fi done < src/$dir/deps-exe/$file echo "$file: EXTRA_LIBS :=$libs" echo "$file: src/$dir/$file.o$deps" done done echo "INTERNAL_LIBS :=$internal_libs" s6-2.13.1.0/tools/install.sh000077500000000000000000000020551470151141200154270ustar00rootroot00000000000000#!/bin/sh usage() { echo "usage: $0 [ -D ] [ -l ] [ -m mode ] [ -O owner:group ] src dst" 1>&2 exit 1 } mkdirp=false symlink=false mode=0755 og= while getopts Dlm:O: name ; do case "$name" in D) mkdirp=true ;; l) symlink=true ;; m) mode=$OPTARG ;; O) og=$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" if test -n "$og" ; then chown -- "$og" "$tmp" fi 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