From 54b6467013bfbdb3ee606961c02fbff1271ca582 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Fri, 19 Sep 2014 15:01:58 +0000 Subject: initial commit --- .gitignore | 5 + AUTHORS | 6 + COPYING | 13 + INSTALL | 130 ++++++++ Makefile | 121 ++++++++ README | 30 ++ README.macosx | 4 + README.solaris | 12 + configure | 385 +++++++++++++++++++++++ doc/index.html | 126 ++++++++ doc/s6-chroot.html | 40 +++ doc/s6-devd.html | 80 +++++ doc/s6-freeramdisk.html | 32 ++ doc/s6-halt.html | 36 +++ doc/s6-hiercopy.html | 66 ++++ doc/s6-hostname.html | 39 +++ doc/s6-logwatch.html | 72 +++++ doc/s6-mount.html | 52 ++++ doc/s6-pivotchroot.html | 41 +++ doc/s6-poweroff.html | 39 +++ doc/s6-ps.html | 128 ++++++++ doc/s6-reboot.html | 38 +++ doc/s6-swapoff.html | 36 +++ doc/s6-swapon.html | 36 +++ doc/s6-umount.html | 38 +++ doc/upgrade.html | 32 ++ package/deps-build | 1 + package/deps.mak | 42 +++ package/info | 4 + package/modes | 15 + package/targets.mak | 22 ++ patch-for-solaris | 17 ++ src/minutils/deps-exe/s6-chroot | 1 + src/minutils/deps-exe/s6-devd | 1 + src/minutils/deps-exe/s6-freeramdisk | 1 + src/minutils/deps-exe/s6-halt | 1 + src/minutils/deps-exe/s6-hiercopy | 1 + src/minutils/deps-exe/s6-hostname | 1 + src/minutils/deps-exe/s6-logwatch | 1 + src/minutils/deps-exe/s6-mount | 1 + src/minutils/deps-exe/s6-pivotchroot | 1 + src/minutils/deps-exe/s6-poweroff | 1 + src/minutils/deps-exe/s6-ps | 8 + src/minutils/deps-exe/s6-reboot | 1 + src/minutils/deps-exe/s6-swapoff | 1 + src/minutils/deps-exe/s6-swapon | 1 + src/minutils/deps-exe/s6-umount | 1 + src/minutils/mount-constants.h | 81 +++++ src/minutils/s6-chroot.c | 21 ++ src/minutils/s6-devd.c | 288 ++++++++++++++++++ src/minutils/s6-freeramdisk.c | 21 ++ src/minutils/s6-halt.c | 13 + src/minutils/s6-hiercopy.c | 156 ++++++++++ src/minutils/s6-hostname.c | 37 +++ src/minutils/s6-logwatch.c | 156 ++++++++++ src/minutils/s6-mount.c | 126 ++++++++ src/minutils/s6-pivotchroot.c | 24 ++ src/minutils/s6-poweroff.c | 13 + src/minutils/s6-ps.c | 385 +++++++++++++++++++++++ src/minutils/s6-ps.h | 153 ++++++++++ src/minutils/s6-reboot.c | 13 + src/minutils/s6-swapoff.c | 53 ++++ src/minutils/s6-swapon.c | 37 +++ src/minutils/s6-umount.c | 65 ++++ src/minutils/s6ps_grcache.c | 66 ++++ src/minutils/s6ps_otree.c | 98 ++++++ src/minutils/s6ps_pfield.c | 570 +++++++++++++++++++++++++++++++++++ src/minutils/s6ps_pwcache.c | 66 ++++ src/minutils/s6ps_statparse.c | 155 ++++++++++ src/minutils/s6ps_ttycache.c | 153 ++++++++++ src/minutils/s6ps_wchan.c | 96 ++++++ tools/gen-deps.sh | 79 +++++ tools/install.sh | 64 ++++ 73 files changed, 4749 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 README create mode 100644 README.macosx create mode 100644 README.solaris create mode 100755 configure create mode 100644 doc/index.html create mode 100644 doc/s6-chroot.html create mode 100644 doc/s6-devd.html create mode 100644 doc/s6-freeramdisk.html create mode 100644 doc/s6-halt.html create mode 100644 doc/s6-hiercopy.html create mode 100644 doc/s6-hostname.html create mode 100644 doc/s6-logwatch.html create mode 100644 doc/s6-mount.html create mode 100644 doc/s6-pivotchroot.html create mode 100644 doc/s6-poweroff.html create mode 100644 doc/s6-ps.html create mode 100644 doc/s6-reboot.html create mode 100644 doc/s6-swapoff.html create mode 100644 doc/s6-swapon.html create mode 100644 doc/s6-umount.html create mode 100644 doc/upgrade.html create mode 100644 package/deps-build create mode 100644 package/deps.mak create mode 100644 package/info create mode 100644 package/modes create mode 100644 package/targets.mak create mode 100755 patch-for-solaris create mode 100644 src/minutils/deps-exe/s6-chroot create mode 100644 src/minutils/deps-exe/s6-devd create mode 100644 src/minutils/deps-exe/s6-freeramdisk create mode 100644 src/minutils/deps-exe/s6-halt create mode 100644 src/minutils/deps-exe/s6-hiercopy create mode 100644 src/minutils/deps-exe/s6-hostname create mode 100644 src/minutils/deps-exe/s6-logwatch create mode 100644 src/minutils/deps-exe/s6-mount create mode 100644 src/minutils/deps-exe/s6-pivotchroot create mode 100644 src/minutils/deps-exe/s6-poweroff create mode 100644 src/minutils/deps-exe/s6-ps create mode 100644 src/minutils/deps-exe/s6-reboot create mode 100644 src/minutils/deps-exe/s6-swapoff create mode 100644 src/minutils/deps-exe/s6-swapon create mode 100644 src/minutils/deps-exe/s6-umount create mode 100644 src/minutils/mount-constants.h create mode 100644 src/minutils/s6-chroot.c create mode 100644 src/minutils/s6-devd.c create mode 100644 src/minutils/s6-freeramdisk.c create mode 100644 src/minutils/s6-halt.c create mode 100644 src/minutils/s6-hiercopy.c create mode 100644 src/minutils/s6-hostname.c create mode 100644 src/minutils/s6-logwatch.c create mode 100644 src/minutils/s6-mount.c create mode 100644 src/minutils/s6-pivotchroot.c create mode 100644 src/minutils/s6-poweroff.c create mode 100644 src/minutils/s6-ps.c create mode 100644 src/minutils/s6-ps.h create mode 100644 src/minutils/s6-reboot.c create mode 100644 src/minutils/s6-swapoff.c create mode 100644 src/minutils/s6-swapon.c create mode 100644 src/minutils/s6-umount.c create mode 100644 src/minutils/s6ps_grcache.c create mode 100644 src/minutils/s6ps_otree.c create mode 100644 src/minutils/s6ps_pfield.c create mode 100644 src/minutils/s6ps_pwcache.c create mode 100644 src/minutils/s6ps_statparse.c create mode 100644 src/minutils/s6ps_ttycache.c create mode 100644 src/minutils/s6ps_wchan.c create mode 100755 tools/gen-deps.sh create mode 100755 tools/install.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c6415e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.a +*.lo +*.so +*.so.* diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..4efb17d --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Main author: + Laurent Bercot + +Thanks to: + Dan J. Bernstein + Jorge Almeida diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..63309ba --- /dev/null +++ b/COPYING @@ -0,0 +1,13 @@ +Copyright (c) 2011-2014 Laurent Bercot + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..d78becc --- /dev/null +++ b/INSTALL @@ -0,0 +1,130 @@ +Build Instructions +------------------ + +* Requirements + ------------ + + - A POSIX-compliant C development environment + - GNU make version 3.81 or later + - skalibs version 2.0.0.0 or later: http://skarnet.org/software/skalibs/ + + This software is Linux-specific. It will run on a Linux kernel, +version 2.6.32 or later. + + +* Standard usage + -------------- + + ./configure && make && sudo make install + + will work for most users. + It will install the binaries in /bin. + + You can strip the binaries and libraries of their extra symbols via +"make strip" before the "make install" phase. It will shave a few bytes +off them. + + +* Customization + ------------- + + You can customize paths via flags given to configure. + See ./configure --help for a list of all available configure options. + + +* Environment variables + --------------------- + + Controlling a build process via environment variables is a big and +dangerous hammer. You should try and pass flags to configure instead; +nevertheless, the standard environment variables are recognized. + + The value of the CROSS_COMPILE environment variable will prefix the +building tools' names. The --enable-cross option is preferred, see +"Cross-compilation" below. + + If the CC environment variable is set, its value will override compiler +detection by configure. + + The values of CFLAGS, CPPFLAGS and LDFLAGS will be appended to flags +auto-detected by configure. To entirely override the flags set by +configure, use make -e. + + The value of LDLIBS will be appended by make to command lines that link +an executable, even without the -e option. + + The Makefile supports the DESTDIR convention for staging. + + +* Shared libraries + ---------------- + + Software from skarnet.org is small enough that shared libraries are +generally not worth using. Static linking is simpler and incurs less +runtime overhead and less points of failure: so by default, shared +libraries are not built and binaries are linked against the static +versions of the skarnet.org libraries. Nevertheless, you can: + * build shared libraries: --enable-shared + * link binaries against shared libraries: --disable-allstatic + + +* Static binaries + --------------- + + By default, binaries are linked against static versions of all the +libraries they depend on, except for the libc. You can enforce +linking against the static libc with --enable-static-libc. + + Be aware that the GNU libc behaves badly with static linking and +produces huge executables, which is why it is not the default. +Other libcs are better suited to static linking, for instance +musl: http://musl-libc.org/ + + +* Cross-compilation + ----------------- + + skarnet.org packages centralize all the difficulty of +cross-compilation in one place: skalibs. Once you have +cross-compiled skalibs, the rest is easy. + + Use the --enable-cross=PREFIX option to configure, or simply +--enable-cross if your default toolchain is a cross-compiling +toolchain. And make sure to use the correct version of skalibs +for your target, and the correct sysdeps directory, making use +of the --with-include, --with-lib, --with-dynlib and --with-sysdeps +options as necessary. + + +* The slashpackage convention + --------------------------- + + The slashpackage convention (http://cr.yp.to/slashpackage.html) +is a package installation scheme that provides a few guarantees +over other conventions such as the FHS, for instance fixed +absolute pathnames. skarnet.org packages support it: use the +--enable-slashpackage option to configure, or +--enable-slashpackage=DIR for a prefixed DIR/package tree. +This option will activate slashpackage support during the build +and set slashpackage-compatible installation directories. +Other options setting individual installation directories will be +ignored. + + When using slashpackage, two additional Makefile targets are +available after "make install": + - "make update" changes the default version of the software to the +freshly installed one. (This is useful when you have several installed +versions of the same software, which slashpackage supports.) + - "make -L global-links" adds links from /command and /library.so to the +default version of the binaries and shared libraries. The "-L" option to +make is necessary because targets are symbolic links, and the default make +behaviour is to check the pointed file's timestamp and not the symlink's +timestamp. + + +* Out-of-tree builds + ------------------ + + skarnet.org packages do not support out-of-tree builds. They +are small, so it does not cost much to duplicate the entire +source tree if parallel builds are needed. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7d8901c --- /dev/null +++ b/Makefile @@ -0,0 +1,121 @@ +# +# This Makefile requires GNU make. +# +# Do not make changes here. +# Use the included .mak files. +# + +it: all + +CC = $(error Please use ./configure first) + +include package/targets.mak +include package/deps.mak +-include config.mak + +version_m := $(basename $(version)) +version_M := $(basename $(version_m)) +version_l := $(basename $(version_M)) +CPPFLAGS_ALL := -iquote src/include-local -Isrc/include $(CPPFLAGS) +CFLAGS_ALL := $(CFLAGS) -pipe -Wall +CFLAGS_SHARED := -fPIC +LDFLAGS_ALL := $(LDFLAGS) +LDFLAGS_SHARED := -shared +LDLIBS_ALL := $(LDLIBS) +REALCC = $(CROSS_COMPILE)$(CC) +AR := $(CROSS_COMPILE)ar +RANLIB := $(CROSS_COMPILE)ranlib +STRIP := $(CROSS_COMPILE)strip +INSTALL := ./tools/install.sh + +ALL_BINS := $(LIBEXEC_TARGETS) $(BIN_TARGETS) $(SBIN_TARGETS) +ALL_LIBS := $(SHARED_LIBS) $(STATIC_LIBS) +ALL_INCLUDES := $(wildcard src/include/$(package)/*.h) + +all: $(ALL_LIBS) $(ALL_BINS) $(ALL_INCLUDES) + +clean: + @exec rm -f $(ALL_LIBS) $(ALL_BINS) $(wildcard src/*/*.o src/*/*.lo) + +distclean: clean + @exec rm -f config.mak src/include/${package}/config.h + +tgz: distclean + @. package/info && \ + rm -rf /tmp/$$package-$$version && \ + cp -a . /tmp/$$package-$$version && \ + cd /tmp && \ + tar -zpcv --owner=0 --group=0 --numeric-owner --exclude=.git* -f /tmp/$$package-$$version.tar.gz $$package-$$version && \ + exec rm -rf /tmp/$$package-$$version + +strip: $(ALL_LIBS) $(ALL_BINS) +ifneq ($(strip $(ALL_LIBS)),) + exec ${STRIP} -x -R .note -R .comment -R .note.GNU-stack $(ALL_LIBS) +endif +ifneq ($(strip $(ALL_BINS)),) + exec ${STRIP} -R .note -R .comment -R .note.GNU-stack $(ALL_BINS) +endif + +install: install-dynlib install-libexec install-bin install-sbin install-lib install-include +install-dynlib: $(SHARED_LIBS:lib%.so=$(DESTDIR)$(dynlibdir)/lib%.so) +install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%) +install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%) +install-sbin: $(SBIN_TARGETS:%=$(DESTDIR)$(sbindir)/%) +install-lib: $(STATIC_LIBS:lib%.a=$(DESTDIR)$(libdir)/lib%.a) +install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h) + +ifneq ($(exthome),) + +update: + exec $(INSTALL) -l $(notdir $(home)) $(DESTDIR)$(exthome) + +global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so=$(DESTDIR)$(sproot)/library.so/lib%.so) $(BIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) $(SBIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) + +$(DESTDIR)$(sproot)/command/%: $(DESTDIR)$(home)/command/% + exec $(INSTALL) -D -l ..$(exthome)/command/$( + + Please use the mailing-list for +questions about s6-linux-utils. diff --git a/README.macosx b/README.macosx new file mode 100644 index 0000000..d71a096 --- /dev/null +++ b/README.macosx @@ -0,0 +1,4 @@ + + This package will compile and run on Darwin (MacOS X), but the building of +shared libraries is not supported. + Make sure you use the --disable-shared option to configure. diff --git a/README.solaris b/README.solaris new file mode 100644 index 0000000..91a5b26 --- /dev/null +++ b/README.solaris @@ -0,0 +1,12 @@ + + This package assumes the existence of a POSIX shell in /bin/sh. + On Solaris, /bin/sh is not POSIX. Most versions of Solaris provide +a POSIX shell in /usr/xpg4/bin/sh. + + To compile this package on Solaris, you will need to run + + ./patch-for-solaris + + before you run ./configure. This script will change the #! invocation +of the configure script and various tools so that a POSIX shell is used +for the compilation process. diff --git a/configure b/configure new file mode 100755 index 0000000..deabe48 --- /dev/null +++ b/configure @@ -0,0 +1,385 @@ +#!/bin/sh + +usage () { +cat </dev/null 2>&1 && { echo "$1" ; return 0 ; } +$1 +EOF + echo "$1" | sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/" -e "s#^'\([-[:alnum:]_,./:]*\)=\(.*\)\$#\1='\2#" -e "s|\*/|* /|g" +} + +fail () { + echo "$*" + exit 1 +} + +fnmatch () { + eval "case \"\$2\" in $1) return 0 ;; *) return 1 ;; esac" +} + +cmdexists () { + type "$1" >/dev/null 2>&1 +} + +trycc () { + test -z "$CC_AUTO" && cmdexists "$1" && CC_AUTO=$1 +} + +stripdir () { + while eval "fnmatch '*/' \"\${$1}\"" ; do + eval "$1=\${$1%/}" + done +} + +tryflag () { + echo "checking whether compiler accepts $2 ..." + echo "typedef int x;" > "$tmpc" + if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO "$2" -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then + echo " ... yes" + eval "$1=\"\${$1} \$2\"" + eval "$1=\${$1# }" + return 0 + else + echo " ... no" + return 1 + fi +} + +tryldflag () { + echo "checking whether linker accepts $2 ..." + echo "typedef int x;" > "$tmpc" + if $CC_AUTO $CFLAGS_AUTO $LDFLAGS_AUTO -nostdlib "$2" -o /dev/null "$tmpc" >/dev/null 2>&1 ; then + echo " ... yes" + eval "$1=\"\${$1} \$2\"" + eval "$1=\${$1# }" + return 0 + else + echo " ... no" + return 1 + fi +} + + +# Actual script + +. package/info + +CC_AUTO="$CC" +CFLAGS_AUTO="$CFLAGS" +CPPFLAGS_AUTO="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 $CPPFLAGS" +LDFLAGS_AUTO="$LDFLAGS" +LDFLAGS_NOSHARED= +prefix= +exec_prefix='$prefix' +dynlibdir='$prefix/lib' +libexecdir='$exec_prefix/libexec' +bindir='$exec_prefix/bin' +sbindir='$exec_prefix/sbin' +libdir='$prefix/usr/lib/'$package +includedir='$prefix/usr/include' +sysdeps='$prefix/usr/lib/skalibs/sysdeps' +manualsysdeps=false +shared=false +static=true +slashpackage=false +sproot= +home= +exthome= +allstatic=true +evenmorestatic=false +addincpath='' +addlibspath='' +addlibdpath='' +vpaths='' +vpathd='' +cross="$CROSS_COMPILE" + +for arg ; do + case "$arg" in + --help) usage ;; + --prefix=*) prefix=${arg#*=} ;; + --exec-prefix=*) exec_prefix=${arg#*=} ;; + --dynlibdir=*) dynlibdir=${arg#*=} ;; + --libexecdir=*) libexecdir=${arg#*=} ;; + --bindir=*) bindir=${arg#*=} ;; + --sbindir=*) sbindir=${arg#*=} ;; + --libdir=*) libdir=${arg#*=} ;; + --includedir=*) includedir=${arg#*=} ;; + --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 ;; + --enable-static-libc|--enable-static-libc=yes) evenmorestatic=true ;; + --disable-static-libc|--enable-static-libc=no) evenmorestatic=false ;; + --enable-slashpackage=*) sproot=${arg#*=} ; slashpackage=true ; ;; + --enable-slashpackage) sproot= ; slashpackage=true ;; + --disable-slashpackage) sproot= ; slashpackage=false ;; + --enable-cross=*) cross=${arg#*=} ;; + --enable-cross) cross= ;; + --disable-cross) cross= ;; + --enable-*|--disable-*|--with-*|--without-*|--*dir=*|--build=*) ;; + --host=*|--target=*) target=${arg#*=} ;; + -* ) echo "$0: unknown option $arg" ;; + *=*) ;; + *) target=$arg ;; + esac +done + +for i in prefix exec_prefix dynlibdir libexecdir bindir sbindir libdir includedir linkdynlibdir linkbindir linksbindir sysdeps sproot skalibs ; do + eval tmp=\${$i} + eval $i=$tmp + stripdir $i +done + +# Get usable temp filenames +i=0 +set -C +while : ; do + i=$(($i+1)) + tmpc="./tmp-configure-$$-$PPID-$i.c" + tmpe="./tmp-configure-$$-$PPID-$i.tmp" + 2>|/dev/null > "$tmpc" && break + 2>|/dev/null > "$tmpe" && break + test "$i" -gt 50 && fail "$0: cannot create temporary files" +done +set +C +trap 'rm -f "$tmpc" "$tmpe"' EXIT ABRT INT QUIT TERM HUP + +# Set slashpackage values +if $slashpackage ; then + home=${sproot}/package/${category}/${package}-${version} + exthome=${sproot}/package/${category}/${package} + if $manualsysdeps ; then + : + else + sysdeps=${sproot}/package/prog/skalibs/sysdeps + fi + binprefix=${home}/command + extbinprefix=${exthome}/command + dynlibdir=${home}/library.so + libexecdir=$binprefix + bindir=$binprefix + sbindir=$binprefix + libdir=${home}/library + includedir=${home}/include + while read dep ; do + addincpath="$addincpath -I${sproot}${dep}/include" + vpaths="$vpaths ${sproot}${dep}/library" + addlibspath="$addlibspath -L${sproot}${dep}/library" + if $allstatic ; then : ; else + vpathd="$vpathd ${sproot}${dep}/library.so" + addlibdpath="$addlibdpath -L${sproot}${dep}/library.so" + fi + done < package/deps-build +fi + +# Find a C compiler to use +echo "checking for C compiler..." +trycc ${cross}gcc +trycc ${cross}c99 +trycc ${cross}cc +test -n "$CC_AUTO" || { echo "$0: cannot find a C compiler" ; exit 1 ; } +echo " ... $CC_AUTO" +echo "checking whether C compiler works... " +echo "typedef int x;" > "$tmpc" +if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO -c -o /dev/null "$tmpc" 2>"$tmpe" ; then + echo " ... yes" +else + echo " ... no. Compiler output follows:" + cat < "$tmpe" + exit 1 +fi + +echo "checking target system type..." +test -n "$target" || target=$($CC_AUTO -dumpmachine 2>/dev/null) || target=unknown +echo " ... $target" +if test ! -d $sysdeps || test ! -f $sysdeps/target ; then + echo "$0: error: $sysdeps is not a valid sysdeps directory" + exit 1 +fi +if [ "x$target" != "x$(cat $sysdeps/target)" ] ; then + echo "$0: error: target $target does not match the contents of $sysdeps/target" + exit 1 +fi + +rt_lib=$(cat $sysdeps/rt.lib) +socket_lib=$(cat $sysdeps/socket.lib) +sysclock_lib=$(cat $sysdeps/sysclock.lib) +tainnow_lib=$(cat $sysdeps/tainnow.lib) +util_lib=$(cat $sysdeps/util.lib) + +tryflag CFLAGS_AUTO -std=c99 +tryflag CFLAGS_AUTO -fomit-frame-pointer +tryflag CFLAGS_AUTO -fno-exceptions +tryflag CFLAGS_AUTO -fno-unwind-tables +tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables +tryflag CFLAGS_AUTO -Wa,--noexecstack +tryflag CFLAGS_AUTO -fno-stack-protector +tryflag CPPFLAGS_AUTO -Werror=implicit-function-declaration +tryflag CPPFLAGS_AUTO -Werror=implicit-int +tryflag CPPFLAGS_AUTO -Werror=pointer-sign +tryflag CPPFLAGS_AUTO -Werror=pointer-arith +tryflag CPPFLAGS_AUTO -Wno-parentheses +tryflag CPPFLAGS_AUTO -Wno-uninitialized +tryflag CPPFLAGS_AUTO -Wno-missing-braces +tryflag CPPFLAGS_AUTO -Wno-unused-value +tryflag CPPFLAGS_AUTO -Wno-unused-but-set-variable +tryflag CPPFLAGS_AUTO -Wno-unknown-pragmas +tryflag CPPFLAGS_AUTO -Wno-pointer-to-int-cast + +if $evenmorestatic ; then + LDFLAGS_NOSHARED=-static +fi + +if $shared ; then + tryldflag LDFLAGS_AUTO -Wl,--hash-style=both +fi + +if test -z "$vpaths" ; then + while read dep ; do + base=$(basename $dep) ; + vpaths="$vpaths /usr/lib/$base" + addlibspath="$addlibspath -L/usr/lib/$base" + done < package/deps-build +fi + +CPPFLAGS_AUTO="$CPPFLAGS_AUTO $addincpath" +LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibspath" +$allstatic || LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibdpath" + +echo "creating config.mak..." +cmdline=$(quote "$0") +for i ; do cmdline="$cmdline $(quote "$i")" ; done +exec 3>&1 1>config.mak +cat << EOF +# This file was generated by: +# $cmdline +# Any changes made here will be lost if configure is re-run. + +target := $target +package := $package +prefix := $prefix +exec_prefix := $exec_prefix +dynlibdir := $dynlibdir +libexecdir := $libexecdir +bindir := $bindir +sbindir := $sbindir +libdir := $libdir +includedir := $includedir +sysdeps := $sysdeps +slashpackage := $slashpackage +sp_root := $sproot +version := $version +home := $home +exthome := $exthome +RT_LIB := ${rt_lib} +SOCKET_LIB := ${socket_lib} +SYSCLOCK_LIB := ${sysclock_lib} +TAINNOW_LIB := ${tainnow_lib} +UTIL_LIB := ${util_lib} + +CC := $CC_AUTO +CFLAGS := $CFLAGS_AUTO +CPPFLAGS := $CPPFLAGS_AUTO +LDFLAGS := $LDFLAGS_AUTO +LDFLAGS_NOSHARED := $LDFLAGS_NOSHARED +CROSS_COMPILE := $cross + +vpath lib%a$vpaths +EOF +if $allstatic ; then + echo ".LIBPATTERNS := lib%.a" + vpathd= +fi + echo "vpath lib%.so$vpathd" +echo +$static || echo "STATIC_LIBS :=" +$shared || echo "SHARED_LIBS :=" +exec 1>&3 3>&- +echo " ... done." + +echo "creating src/include/${package}/config.h..." +mkdir -p -m 0755 src/include/${package} +exec 3>&1 1> src/include/${package}/config.h +cat <&3 3>&- +echo " ... done." diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..1d289cc --- /dev/null +++ b/doc/index.html @@ -0,0 +1,126 @@ + + + + + s6-linux-utils - skarnet's tiny Linux-specific utilities + + + + + + +

+Software
+skarnet.org +

+ +

s6-linux-utils

+ +

What is it ?

+ +

+ s6-linux-utils is a set of minimalistic Linux-specific system utilities. +

+ +
+ +

Installation

+ +

Requirements

+ +
    +
  • A POSIX-compliant system with a standard C development environment
  • +
  • GNU make, version 3.81 or later
  • +
  • skalibs version +2.0.0.0 or later
  • +
+ +

Licensing

+ +

+ s6-linux-utils is free software. It is available under the +ISC license. +

+ +

Download

+ +
    +
  • The current released version of s6-linux-utils is 2.0.0.0.
  • +
  • Alternatively, you can checkout a copy of the s6-linux-utils git repository: +
     git clone git://git.skarnet.org/s6-linux-utils 
  • +
+ +

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-linux-utils and the current one.
  • +
+ +
+ +

Reference

+ +

Commands

+ +

+ All these commands exit 111 if they encounter a temporary error, and +100 if they encounter a permanent error - such as a misuse. +

+ + + +

Related resources

+ +
    +
  • s6-linux-utils is discussed on the +skaware mailing-list.
  • +
+ +

Similar work

+ +

+ There are several good projects aiming to provide a minimal userspace +environment for Linux, suitable for embedded systems. Among them, for +instance: +

+ + + +

+ Most of the time, these projects aim to implement standard commands in a +lightweight way, and +they do it well enough. So, although some standard reimplentation already +exists in s6-linux-utils and its sibling package +s6-portable-utils, +it is an explicit non-goal of those packages to duplicate the work of those +projects, and no more rewriting of standard commands will occur. +

+ + + diff --git a/doc/s6-chroot.html b/doc/s6-chroot.html new file mode 100644 index 0000000..a15d0fa --- /dev/null +++ b/doc/s6-chroot.html @@ -0,0 +1,40 @@ + + + + + s6-linux-utils: the s6-chroot program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-chroot program

+ +

+s6-chroot executes a program in a chroot jail. +

+ +

Interface

+ +
+     s6-chroot newroot prog...
+
+ +
    +
  • s6-chroot sets the root filesystem for the current +process to newroot, which must be an existing directory.
  • +
  • s6-chroot changes directory to this new root, and +executes prog +with its arguments. prog is searched under the new root.
  • +
  • s6-chroot's parent process is unaffected by the root change.
  • +
+ + + diff --git a/doc/s6-devd.html b/doc/s6-devd.html new file mode 100644 index 0000000..5ae6160 --- /dev/null +++ b/doc/s6-devd.html @@ -0,0 +1,80 @@ + + + + + s6-linux-utils: the s6-devd program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-devd program

+ +

+s6-devd listens to the netlink interface for udev events, and +launches a helper program for every event, similarly to what the hotplug +interface does. +

+ +

Interface

+ +
+     s6-devd [ -q | -v ] [ -b kbufsz ] [ -t l:t:k ] prog...
+
+ +
    +
  • s6-devd binds to the netlink interface and listens for +hotplug events, as the udev program does.
  • +
  • For every event it receives, it spawns prog... with +the event variables added to the environment, just as if prog... +had been registered in /proc/sys/kernel/hotplug.
  • +
  • However, unlike the kernel, s6-devd spawns the prog... helpers +sequentially: it waits for an instance to finish before spawning another one. 
  • +
  • s6-devd is a long-lived program; it exits 0 when it receives a +SIGTERM. If a helper program is alive at that time, s6-devd waits for it to die +before exiting.
  • +
+ +

Options

+ +
    +
  • -q : be more quiet.
  • +
  • -v : be more verbose.
  • +
  • -b kbufsz : try and reserve a kernel buffer of +kbufsz bytes for the netlink queue. Too large a buffer wastes kernel memory; +too small a buffer risks losing events. The default is 65536.
  • +
  • -t l:t:k : If l, t or k is +specified, they specify timeouts; by default, they are infinite. +If prog... is still alive after l milliseconds, s6-devd sends +it a SIGTERM. Then, if prog... is still alive after t more +milliseconds, s6-devd sends it a SIGKILL. Then, if prog... is still +alive after k more milliseconds, s6-devd yells and exits 99.
  • +
+ +

Notes

+ +
    +
  • s6-devd is a daemon; it should be run under a proper supervision system such +as s6. (That is why it does not +fork and logs to stderr.)
  • +
  • The prog... helper, on the other hand, should be very short-lived, +even if you are not using the -t option to s6-devd. Since helpers are +spawned sequentially, slow helpers can make events queue up and fill the netlink +kernel buffer.
  • +
  •  If you are using busybox and want a +minimal udev-style dynamic /dev +handling, /sbin/mdev is a suitable prog... helper.
  • +
  • The point of s6-devd is that it runs the helpers sequentially, so it solves +the race condition that appears when helpers are run via the hotplug interface. +When s6-devd is used, /proc/sys/kernel/hotplug should be empty.
  • +
+ + + diff --git a/doc/s6-freeramdisk.html b/doc/s6-freeramdisk.html new file mode 100644 index 0000000..859571a --- /dev/null +++ b/doc/s6-freeramdisk.html @@ -0,0 +1,32 @@ + + + + + s6-linux-utils: the s6-freeramdisk program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-freeramdisk program

+ +

+freeramdisk frees the memory occupied by a RAM disk. Call it +when your RAM disk is not in use anymore. +

+ +

Interface

+ +
+     s6-freeramdisk ramdisk-device
+
+ + + diff --git a/doc/s6-halt.html b/doc/s6-halt.html new file mode 100644 index 0000000..2c11c5a --- /dev/null +++ b/doc/s6-halt.html @@ -0,0 +1,36 @@ + + + + + s6-linux-utils: the s6-halt program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-halt program

+ +

+s6-halt syncs the filesystems and halts the machine +immediately, without switching the power off. +
This is different from the sysvinit halt +command, which is an alias for shutdown -h. The +s6-linux-utils s6-halt command is more or less equivalent to +sysvinit's halt -f. +

+ +

Interface

+ +
+     s6-halt
+
+ + + diff --git a/doc/s6-hiercopy.html b/doc/s6-hiercopy.html new file mode 100644 index 0000000..e8dd2ed --- /dev/null +++ b/doc/s6-hiercopy.html @@ -0,0 +1,66 @@ + + + + + s6-linux-utils: the s6-hiercopy program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-hiercopy program

+ +

+s6-hiercopy copies a directory structure recursively. +

+ +

Interface

+ +
+     s6-hiercopy source destination
+
+ +
    +
  • s6-hiercopy recursively copies source to +destination, which is created if it doesn't exist. +The permissions are preserved. The owner and group are preserved +if the user is the superuser.
  • +
  • It exits 0 on success and 111 on temporary failure.
  • +
+ +

Notes

+ +

+ Copying files and browsing through directories is one of Unix's +weakest points, and s6-hiercopy is not meant to work around +the problem; it's only a quick hack that I needed to boot my embedded +platform. I originally planned to write the ultimate cp utility, +portable and reliable and featureful and everything - while needing +approximately a hundred times less resources than GNU cp does, +of course. But I eventually dropped the idea: it's just impossible to +design, much less write, such a utility. +

+ +
    +
  • You can't make it portable because there's no universal standard. +There is no portable way of creating device special files, for instance. +So s6-hiercopy appears here instead of in +s6-portable-utils: +the platform where I needed that kind of tool is Linux.
  • +
  • You can't make it reliable because Unix's set of filesystem +management primitives is just too weak. It lacks a lot of atomic +operations, and filesystem transactions. As a result, s6-hiercopy +is a walking race condition and should absolutely not +be considered instant when used in a multitasking environment. +But then, cp -a shouldn't either.
  • +
+ + + diff --git a/doc/s6-hostname.html b/doc/s6-hostname.html new file mode 100644 index 0000000..8ff0081 --- /dev/null +++ b/doc/s6-hostname.html @@ -0,0 +1,39 @@ + + + + + s6-linux-utils: the s6-hostname program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-hostname program

+ +

+s6-hostname gets or sets the machine hostname. +

+ +

Interface

+ +
+     s6-hostname [ name ]
+
+ +
    +
  • When called without an argument, s6-hostname prints the +machine hostname to stdout.
  • +
  • When called with an argument, s6-hostname sets the machine +hostname to name.
  • +
  • Then it exits 0.
  • +
+ + + diff --git a/doc/s6-logwatch.html b/doc/s6-logwatch.html new file mode 100644 index 0000000..5075d83 --- /dev/null +++ b/doc/s6-logwatch.html @@ -0,0 +1,72 @@ + + + + + s6-linux-utils: the s6-logwatch program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-logwatch program

+ +

+s6-logwatch watches the current file of a logdir, printing it +in real time. +

+ +

Interface

+ +
+     s6-logwatch [ -m buflen ] logdir
+
+ +
    +
  • s6-logwatch prints logdir/current and watches +the file.
  • +
  • logdir must be managed by a +s6-log instance.
  • +
  • When new logs are appended to the current file, s6-logwatch prints +them in real-time to stdout.
  • +
  • When a rotation happens, s6-logwatch notices, and keeps watching the +new current file.
  • +
  • s6-logwatch runs forever until killed. 
  • +
+ +

Options

+ +
    +
  • -m buflen : accumulate at most buflen +bytes into the stdout buffer before flushing it. By default, buflen is +4000.
  • +
+ +

Bugs

+ +
    +
  • s6-logwatch is not entirely reliable because there is an unavoidable +race condition when a rotation occurs; it's a hack for humans to keep reading +logs across rotations, not a tool to be used in safe programming. When the +race condition is triggered, s6-logwatch will be unable to understand what +state logdir is in and will exit 101 with an error message.
  • +
  • Specific support in the logger program would be needed to avoid this +race condition; it would significantly bloat the logger program, so it has +not been deemed useful.
  • +
+ +

Notes

+ +
    +
  • s6-logwatch is Linux-specific because it uses the +inotify interface.
  • +
+ + + diff --git a/doc/s6-mount.html b/doc/s6-mount.html new file mode 100644 index 0000000..b12c505 --- /dev/null +++ b/doc/s6-mount.html @@ -0,0 +1,52 @@ + + + + + s6-linux-utils: the s6-mount program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-mount program

+ +

+s6-mount mounts filesystems. +

+ +

Interface

+ +
+     s6-mount -a
+     s6-mount [ -r | -w ] [ -t fstype ] [ -o option[,option...] ] device mntpoint
+
+ +
    +
  • s6-mount -a mounts all partitions according to /etc/fstab.
  • +
  • If the -a option is not given, +s6-mount mounts device on mntpoint.
  • +
  • s6-mount does not touch /etc/mtab.
  • +
+ +

Options

+ +
    +
  • -a : read /etc/fstab instead of other arguments
  • +
  • -t fstype : filesystem is of type fstype. Default: ext2.
  • +
  • -r : mount read-only
  • +
  • -w : mount read-write (default)
  • +
  • -o option : mount with option option. +Currently recognized options: defaults, ro, rw, remount, sync, async, +nodev, dev, noexec, exec, nosuid, suid, noatime, atime, nodiratime, diratime, +bind, nobind, move, nomove. Unrecognized options are given directly to the kernel.
  • +
+ + + diff --git a/doc/s6-pivotchroot.html b/doc/s6-pivotchroot.html new file mode 100644 index 0000000..42ba383 --- /dev/null +++ b/doc/s6-pivotchroot.html @@ -0,0 +1,41 @@ + + + + + s6-linux-utils: the s6-pivotchroot program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-pivotchroot program

+ +

+s6-pivotchroot performs a pivot_root system call, +then changes root to the new root filesystem and executes a program. +

+ +

Interface

+ +
+     s6-pivotchroot newroot putold prog...
+
+ +
    +
  • s6-pivotchroot sets the root filesystem to newroot, +which must be a mounted filesystem entry point. The old root filesystem +will be available under putold (relative to the old root).
  • +
  • s6-pivotchroot changes directory to the new root, performs +a chroot system call to the new root, and executes prog +with its arguments. prog is searched under the new root.
  • +
+ + + diff --git a/doc/s6-poweroff.html b/doc/s6-poweroff.html new file mode 100644 index 0000000..3685fff --- /dev/null +++ b/doc/s6-poweroff.html @@ -0,0 +1,39 @@ + + + + + s6-linux-utils: the s6-poweroff program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-poweroff program

+ +

+s6-poweroff syncs the filesystems and halts the machine +immediately, switching the power off if possible. +

+ +

+ This is different from the sysvinit poweroff +command, which performs shutdown. The +s6-linux-utils s6-poweroff command is more or less equivalent to +sysvinit's poweroff -f. +

+ +

Interface

+ +
+     s6-poweroff
+
+ + + diff --git a/doc/s6-ps.html b/doc/s6-ps.html new file mode 100644 index 0000000..43ed0d2 --- /dev/null +++ b/doc/s6-ps.html @@ -0,0 +1,128 @@ + + + + + s6-linux-utils: the s6-ps program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-ps program

+ +

+ s6-ps shows a list of processes on the system. +

+ +

Interface

+ +
+     s6-ps [ -H ] [ -w spacing ] [ -W chanfile ] [ -l | -o field,field,... ]
+
+ +
    +
  • s6-ps assumes the proc filesystem is mounted on /proc. +It scans /proc for a list of all processes on the system.
  • +
  • It prints a header line, then information about the processes it has found, +one process per line. Then it exits 0.
  • +
  • If s6-ps is not allowed to read a field, it will print * (a star) instead +of the field's content. - (a dash) means the field is unapplicable here, +for instance the "tty" field for a process that has no controlling terminal.
  • +
+ +

Options

+ +
    +
  • -H : tree. s6-ps will display he process list as a tree, +as ps f does. It will print kernel processes first, then user processes. +By default, the processes are simply ordered by increasing pid number, without +care for the process hierarchy.
  • +
  • -w spacing : leave at least spacing +space characters between two adjacent fields. Minimum is 1, default is 2, +maximum is 256.
  • +
  • -W wchanfile : force wchanfile as the +file listing kernel addresses for WCHAN fields. By default, s6-ps tries to use +/proc/kallsyms, /boot/System.map-`uname -r`, and +/boot/System.map, in that order.
  • +
  • -l : long. Equivalent to +-o user,pid,cpcpu,pmem,vsize,rss,tty,s,start,cttime,args.
  • +
  • -o fieldlist : list of fields to print. +fieldlist must be a comma-separated list of fields, without spaces. +Fields cannot be duplicated. They will be printed in the given order. +The valid field names are listed below. The default field list is +user,pid,tty,s,start,args.
  • +
+ +

Fields

+ +

+ The -o option makes it possible to customize s6-ps's output. Here are the +recognized field keywords and the corresponding field they display. +

+ +
    +
  • pid : the process id number.
  • +
  • comm : the command name as known by the kernel.
  • +
  • s : the one-character state of the process, then s +if the process is a session leader or + if it is a foreground +process group leader, then N if +the process is niced or < if it is anti-niced. Unlike ps, s6-ps +cannot tell whether a process has locked memory pages or not.
  • +
  • ppid : the parent process' pid.
  • +
  • pgrp : the process group number.
  • +
  • sess : the session leader's pid.
  • +
  • tty : the name of the process's controlling terminal.
  • +
  • tpgid : the pid of the foreground process group.
  • +
  • utime : the time the process spent in user mode.
  • +
  • stime : the time the process spent in kernel mode.
  • +
  • cutime : the time spent in user mode by the process and +all its dead children.
  • +
  • cstime : the time spent in kernel mode by the process and +all its dead children.
  • +
  • prio : the process' priority as computed by the kernel.
  • +
  • nice : the process' nice value.
  • +
  • thcount : the number of threads in the process.
  • +
  • start : the start time of the process.
  • +
  • vsize : the virtual memory size of the process.
  • +
  • rss : the resident set size of the process.
  • +
  • rsslimit : the maximum rss allowed for the process.
  • +
  • psr : the number of the CPU the process is running on.
  • +
  • rtprio : the real-time priority of the process.
  • +
  • policy : the real-time policy of the process - a symbolic name.
  • +
  • user : the user the process is running as.
  • +
  • group : the group the process is running as.
  • +
  • pmem : the percentage of the available virtual memory the process +is using. Be aware that it is a very rough estimate: determining exactly how much memory +a process is using is a complex task.
  • +
  • wchan : the name or address of the kernel function where the +proces is sleeping. It is actually very rare that a kernel is configured to properly +export that value, because it incurs a small performance cost.
  • +
  • args : the command line of the process.
  • +
  • env : the process environment. s6-ps can normally only print +the environment for processes running as the same user, or if it is run as root. +Be aware that the environment is usually quite large, and will create very long +lines, likely to mess up the display on a standard terminal.
  • + +
  • pcpu : the percentage of CPU used by the process (total cpu time +divided by total running time). It is a mean value and does not reflect current CPU +usage.
  • +
  • ttime : total CPU time used by the process (user + kernel mode).
  • +
  • cttime : total CPU time used by the process and all its dead +children (user + kernel mode).
  • +
  • tstart : the start time of the process as a +TAI64N value.
  • +
  • cpcpu : the percentage of CPU used by the process and its dead +children (total cpu time +divided by total running time). It is a mean value and does not reflect current CPU +usage.
  • +
+ + + diff --git a/doc/s6-reboot.html b/doc/s6-reboot.html new file mode 100644 index 0000000..9d2c594 --- /dev/null +++ b/doc/s6-reboot.html @@ -0,0 +1,38 @@ + + + + + s6-linux-utils: the s6-reboot program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-reboot program

+ +

+s6-reboot syncs the filesystems and reboots the machine +immediately. +

+ +

This is different from the sysvinit reboot +command, which is an alias for shutdown -r. The +s6-linux-utils s6-reboot command is more or less equivalent to +sysvinit's reboot -f. +

+ +

Interface

+ +
+     s6-reboot
+
+ + + diff --git a/doc/s6-swapoff.html b/doc/s6-swapoff.html new file mode 100644 index 0000000..00307da --- /dev/null +++ b/doc/s6-swapoff.html @@ -0,0 +1,36 @@ + + + + + s6-linux-utils: the s6-swapoff program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-swapoff program

+ +s6-swapoff deactivates swap partitions. + +

Interface

+ +
+     s6-swapoff [ -a | device ]
+
+ +
    +
  • s6-swapoff deactivates the swap on device.
  • +
  • If the -a option is given instead of device, +s6-swapoff looks for swap partitions in +/proc/swaps and deactivates them all.
  • +
+ + + diff --git a/doc/s6-swapon.html b/doc/s6-swapon.html new file mode 100644 index 0000000..3298478 --- /dev/null +++ b/doc/s6-swapon.html @@ -0,0 +1,36 @@ + + + + + s6-linux-utils: the s6-swapon program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-swapon program

+ +s6-swapon activates swap partitions. + +

Interface

+ +
+     s6-swapon [ -a | device ]
+
+ +
    +
  • s6-swapon activates the swap on device.
  • +
  • If the -a option is given instead of device, +s6-swapon looks for partitions marked swap in +/etc/fstab and activates them all.
  • +
+ + + diff --git a/doc/s6-umount.html b/doc/s6-umount.html new file mode 100644 index 0000000..3efe82c --- /dev/null +++ b/doc/s6-umount.html @@ -0,0 +1,38 @@ + + + + + s6-linux-utils: the s6-umount program + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

The s6-umount program

+ +

+ s6-umount unmounts filesystems. +

+ +

Interface

+ +
+     s6-umount [ -a | mntpoint ]
+
+ +
    +
  • s6-umount -a unmounts all partitions according to /proc/mounts.
  • +
  • If the -a option is not given, +s6-umount unmounts the filesystem mounted on mntpoint.
  • +
  • s6-umount does not touch /etc/mtab.
  • +
+ + + diff --git a/doc/upgrade.html b/doc/upgrade.html new file mode 100644 index 0000000..5ab660e --- /dev/null +++ b/doc/upgrade.html @@ -0,0 +1,32 @@ + + + + + s6-linux-utils: how to upgrade + + + + + + +

+s6-linux-utils
+Software
+skarnet.org +

+ +

How to upgrade s6-linux-utils

+ +

to 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
  • +
+ + + diff --git a/package/deps-build b/package/deps-build new file mode 100644 index 0000000..05d5af4 --- /dev/null +++ b/package/deps-build @@ -0,0 +1 @@ +/package/prog/skalibs diff --git a/package/deps.mak b/package/deps.mak new file mode 100644 index 0000000..7946756 --- /dev/null +++ b/package/deps.mak @@ -0,0 +1,42 @@ +# +# This file has been generated by tools/gen-deps.sh +# + +src/minutils/s6-chroot.o src/minutils/s6-chroot.lo: src/minutils/s6-chroot.c +src/minutils/s6-devd.o src/minutils/s6-devd.lo: src/minutils/s6-devd.c +src/minutils/s6-freeramdisk.o src/minutils/s6-freeramdisk.lo: src/minutils/s6-freeramdisk.c +src/minutils/s6-halt.o src/minutils/s6-halt.lo: src/minutils/s6-halt.c +src/minutils/s6-hiercopy.o src/minutils/s6-hiercopy.lo: src/minutils/s6-hiercopy.c +src/minutils/s6-hostname.o src/minutils/s6-hostname.lo: src/minutils/s6-hostname.c +src/minutils/s6-logwatch.o src/minutils/s6-logwatch.lo: src/minutils/s6-logwatch.c +src/minutils/s6-mount.o src/minutils/s6-mount.lo: src/minutils/s6-mount.c src/minutils/mount-constants.h +src/minutils/s6-pivotchroot.o src/minutils/s6-pivotchroot.lo: src/minutils/s6-pivotchroot.c +src/minutils/s6-poweroff.o src/minutils/s6-poweroff.lo: src/minutils/s6-poweroff.c +src/minutils/s6-ps.o src/minutils/s6-ps.lo: src/minutils/s6-ps.c src/minutils/s6-ps.h +src/minutils/s6-reboot.o src/minutils/s6-reboot.lo: src/minutils/s6-reboot.c +src/minutils/s6-swapoff.o src/minutils/s6-swapoff.lo: src/minutils/s6-swapoff.c +src/minutils/s6-swapon.o src/minutils/s6-swapon.lo: src/minutils/s6-swapon.c +src/minutils/s6-umount.o src/minutils/s6-umount.lo: src/minutils/s6-umount.c +src/minutils/s6ps_grcache.o src/minutils/s6ps_grcache.lo: src/minutils/s6ps_grcache.c +src/minutils/s6ps_otree.o src/minutils/s6ps_otree.lo: src/minutils/s6ps_otree.c src/minutils/s6-ps.h +src/minutils/s6ps_pfield.o src/minutils/s6ps_pfield.lo: src/minutils/s6ps_pfield.c src/minutils/s6-ps.h +src/minutils/s6ps_pwcache.o src/minutils/s6ps_pwcache.lo: src/minutils/s6ps_pwcache.c src/minutils/s6-ps.h +src/minutils/s6ps_statparse.o src/minutils/s6ps_statparse.lo: src/minutils/s6ps_statparse.c src/minutils/s6-ps.h +src/minutils/s6ps_ttycache.o src/minutils/s6ps_ttycache.lo: src/minutils/s6ps_ttycache.c src/minutils/s6-ps.h +src/minutils/s6ps_wchan.o src/minutils/s6ps_wchan.lo: src/minutils/s6ps_wchan.c src/minutils/s6-ps.h + +s6-chroot: src/minutils/s6-chroot.o -lskarnet +s6-devd: src/minutils/s6-devd.o -lskarnet +s6-freeramdisk: src/minutils/s6-freeramdisk.o -lskarnet +s6-halt: src/minutils/s6-halt.o -lskarnet +s6-hiercopy: src/minutils/s6-hiercopy.o -lskarnet +s6-hostname: src/minutils/s6-hostname.o -lskarnet +s6-logwatch: src/minutils/s6-logwatch.o -lskarnet +s6-mount: src/minutils/s6-mount.o -lskarnet +s6-pivotchroot: src/minutils/s6-pivotchroot.o -lskarnet +s6-poweroff: src/minutils/s6-poweroff.o -lskarnet +s6-ps: src/minutils/s6-ps.o src/minutils/s6ps_statparse.o src/minutils/s6ps_otree.o src/minutils/s6ps_pfield.o src/minutils/s6ps_pwcache.o src/minutils/s6ps_grcache.o src/minutils/s6ps_ttycache.o src/minutils/s6ps_wchan.o -lskarnet +s6-reboot: src/minutils/s6-reboot.o -lskarnet +s6-swapoff: src/minutils/s6-swapoff.o -lskarnet +s6-swapon: src/minutils/s6-swapon.o -lskarnet +s6-umount: src/minutils/s6-umount.o -lskarnet diff --git a/package/info b/package/info new file mode 100644 index 0000000..2fcdf48 --- /dev/null +++ b/package/info @@ -0,0 +1,4 @@ +package=s6-linux-utils +version=2.0.0.0 +category=admin +package_macro_name=S6_LINUX_UTILS diff --git a/package/modes b/package/modes new file mode 100644 index 0000000..fddaa83 --- /dev/null +++ b/package/modes @@ -0,0 +1,15 @@ +s6-chroot 0700 +s6-devd 0700 +s6-freeramdisk 0700 +s6-halt 0700 +s6-hiercopy 0755 +s6-hostname 0755 +s6-logwatch 0755 +s6-mount 0700 +s6-pivotchroot 0700 +s6-poweroff 0700 +s6-ps 0755 +s6-reboot 0700 +s6-swapoff 0700 +s6-swapon 0700 +s6-umount 0700 diff --git a/package/targets.mak b/package/targets.mak new file mode 100644 index 0000000..49d5077 --- /dev/null +++ b/package/targets.mak @@ -0,0 +1,22 @@ +BIN_TARGETS = \ +s6-chroot \ +s6-devd \ +s6-freeramdisk \ +s6-halt \ +s6-hiercopy \ +s6-hostname \ +s6-logwatch \ +s6-mount \ +s6-pivotchroot \ +s6-poweroff \ +s6-ps \ +s6-reboot \ +s6-swapoff \ +s6-swapon \ +s6-umount \ + +SBIN_TARGETS = +LIBEXEC_TARGETS = + +SHARED_LIBS = +STATIC_LIBS = diff --git a/patch-for-solaris b/patch-for-solaris new file mode 100755 index 0000000..02f2e3c --- /dev/null +++ b/patch-for-solaris @@ -0,0 +1,17 @@ +#!/usr/xpg4/bin/sh + +patchit () { + echo '#!/usr/xpg4/bin/sh' > $1.tmp + tail -n +2 $1 >> $1.tmp + mv -f $1.tmp $1 + chmod 755 $1 +} + +patchit ./configure +patchit ./tools/install.sh +patchit ./tools/gen-deps.sh + +echo 'SHELL := /usr/xpg4/bin/sh' > Makefile.tmp +echo >> Makefile.tmp +cat Makefile >> Makefile.tmp +mv -f Makefile.tmp Makefile diff --git a/src/minutils/deps-exe/s6-chroot b/src/minutils/deps-exe/s6-chroot new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-chroot @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-devd b/src/minutils/deps-exe/s6-devd new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-devd @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-freeramdisk b/src/minutils/deps-exe/s6-freeramdisk new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-freeramdisk @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-halt b/src/minutils/deps-exe/s6-halt new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-halt @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-hiercopy b/src/minutils/deps-exe/s6-hiercopy new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-hiercopy @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-hostname b/src/minutils/deps-exe/s6-hostname new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-hostname @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-logwatch b/src/minutils/deps-exe/s6-logwatch new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-logwatch @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-mount b/src/minutils/deps-exe/s6-mount new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-mount @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-pivotchroot b/src/minutils/deps-exe/s6-pivotchroot new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-pivotchroot @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-poweroff b/src/minutils/deps-exe/s6-poweroff new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-poweroff @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-ps b/src/minutils/deps-exe/s6-ps new file mode 100644 index 0000000..f03a58b --- /dev/null +++ b/src/minutils/deps-exe/s6-ps @@ -0,0 +1,8 @@ +s6ps_statparse.o +s6ps_otree.o +s6ps_pfield.o +s6ps_pwcache.o +s6ps_grcache.o +s6ps_ttycache.o +s6ps_wchan.o +-lskarnet diff --git a/src/minutils/deps-exe/s6-reboot b/src/minutils/deps-exe/s6-reboot new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-reboot @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-swapoff b/src/minutils/deps-exe/s6-swapoff new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-swapoff @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-swapon b/src/minutils/deps-exe/s6-swapon new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-swapon @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/deps-exe/s6-umount b/src/minutils/deps-exe/s6-umount new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/minutils/deps-exe/s6-umount @@ -0,0 +1 @@ +-lskarnet diff --git a/src/minutils/mount-constants.h b/src/minutils/mount-constants.h new file mode 100644 index 0000000..1e6f3c0 --- /dev/null +++ b/src/minutils/mount-constants.h @@ -0,0 +1,81 @@ +/* ISC license. */ + +#ifndef MOUNT_CONSTANTS_H +#define MOUNT_CONSTANTS_H + +/* taken from util-linux-ng */ + +#ifndef MS_RDONLY +#define MS_RDONLY 1 /* Mount read-only */ +#endif +#ifndef MS_NOSUID +#define MS_NOSUID 2 /* Ignore suid and sgid bits */ +#endif +#ifndef MS_NODEV +#define MS_NODEV 4 /* Disallow access to device special files */ +#endif +#ifndef MS_NOEXEC +#define MS_NOEXEC 8 /* Disallow program execution */ +#endif +#ifndef MS_SYNCHRONOUS +#define MS_SYNCHRONOUS 16 /* Writes are synced at once */ +#endif +#ifndef MS_REMOUNT +#define MS_REMOUNT 32 /* Alter flags of a mounted FS */ +#endif +#ifndef MS_MANDLOCK +#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */ +#endif +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 /* Directory modifications are synchronous */ +#endif +#ifndef MS_NOATIME +#define MS_NOATIME 0x400 /* 1024: Do not update access times. */ +#endif +#ifndef MS_NODIRATIME +#define MS_NODIRATIME 0x800 /* 2048: Don't update directory access times */ +#endif +#ifndef MS_BIND +#define MS_BIND 0x1000 /* 4096: Mount existing tree also elsewhere */ +#endif +#ifndef MS_MOVE +#define MS_MOVE 0x2000 /* 8192: Atomically move tree */ +#endif +#ifndef MS_REC +#define MS_REC 0x4000 /* 16384: Recursive loopback */ +#endif +#ifndef MS_VERBOSE +#define MS_VERBOSE 0x8000 /* 32768 */ +#endif +#ifndef MS_RELATIME +#define MS_RELATIME 0x200000 /* 200000: Update access times relative to mtime/ctime */ +#endif +#ifndef MS_UNBINDABLE +#define MS_UNBINDABLE (1<<17) /* 131072 unbindable */ +#endif +#ifndef MS_PRIVATE +#define MS_PRIVATE (1<<18) /* 262144 Private */ +#endif +#ifndef MS_SLAVE +#define MS_SLAVE (1<<19) /* 524288 Slave */ +#endif +#ifndef MS_SHARED +#define MS_SHARED (1<<20) /* 1048576 Shared */ +#endif +#ifndef MS_I_VERSION +#define MS_I_VERSION (1<<23) /* update inode I_version field */ +#endif +#ifndef MS_STRICTATIME +#define MS_STRICTATIME (1<<24) /* strict atime semantics */ +#endif +/* + * Magic mount flag number. Had to be or-ed to the flag values. + */ +#ifndef MS_MGC_VAL +#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */ +#endif +#ifndef MS_MGC_MSK +#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */ +#endif + +#endif diff --git a/src/minutils/s6-chroot.c b/src/minutils/s6-chroot.c new file mode 100644 index 0000000..654ee8f --- /dev/null +++ b/src/minutils/s6-chroot.c @@ -0,0 +1,21 @@ +/* ISC license. */ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include +#include +#include + +#define USAGE "s6-chroot dir prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + PROG = "s6-chroot" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (chdir(argv[1]) == -1) strerr_diefu2sys(111, "chdir to ", argv[1]) ; + if (chroot(".") == -1) strerr_diefu2sys(111, "chroot in ", argv[1]) ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/minutils/s6-devd.c b/src/minutils/s6-devd.c new file mode 100644 index 0000000..2e5c9ce --- /dev/null +++ b/src/minutils/s6-devd.c @@ -0,0 +1,288 @@ +/* ISC license. */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-devd [ -q | -v ] [ -b kbufsz ] [ -t maxlife:maxterm:maxkill ] helperprogram..." +#define dieusage() strerr_dieusage(100, USAGE) + +static unsigned int cont = 1, state = 0, verbosity = 1 ; +static pid_t pid ; +static tain_t lifetto = TAIN_INFINITE_RELATIVE, + termtto = TAIN_INFINITE_RELATIVE, + killtto = TAIN_INFINITE_RELATIVE, + deadline ; + +static inline int fd_recvmsg (int fd, struct msghdr *hdr) +{ + int r ; + do r = recvmsg(fd, hdr, 0) ; + while ((r == -1) && (errno == EINTR)) ; + return r ; +} + +static inline int netlink_init (unsigned int kbufsz) +{ + struct sockaddr_nl nl = { .nl_family = AF_NETLINK, .nl_pad = 0, .nl_groups = 1, .nl_pid = 0 } ; + int fd = socket_internal(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT, DJBUNIX_FLAG_NB|DJBUNIX_FLAG_COE) ; + if (fd < 0) return -1 ; + if (bind(fd, (struct sockaddr *)&nl, sizeof(struct sockaddr_nl)) < 0) goto err ; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &kbufsz, sizeof(unsigned int)) < 0) goto err ; + return fd ; + err: + { + register int e = errno ; + fd_close(fd) ; + errno = e ; + } + return -1 ; +} + +static inline void on_death (void) +{ + pid = 0 ; + state = 0 ; + tain_add_g(&deadline, &tain_infinite_relative) ; + if (cont == 2) cont = 0 ; +} + +static inline void on_event (char const *const *argv, char const *const *envp, char const *s, unsigned int len) +{ + unsigned int envlen = env_len(envp) ; + unsigned int n = envlen + 1 + byte_count(s, len, '\0') ; + int e ; + char const *v[n] ; + if (!env_merge(v, n, envp, envlen, s, len)) + strerr_diefu1sys(111, "env_merge") ; + e = posix_spawnp(&pid, argv[0], 0, 0, (char *const *)argv, (char * const *)v) ; + if (e) { errno = e ; strerr_diefu2sys(111, "spawn ", argv[0]) ; } + state = 1 ; + tain_add_g(&deadline, &lifetto) ; +} + +static inline void handle_timeout (void) +{ + switch (state) + { + case 0 : + tain_add_g(&deadline, &tain_infinite_relative) ; + break ; + case 1 : + kill(pid, SIGTERM) ; + tain_add_g(&deadline, &termtto) ; + state++ ; + break ; + case 2 : + kill(pid, SIGKILL) ; + tain_add_g(&deadline, &killtto) ; + state++ ; + break ; + case 3 : + strerr_dief1x(99, "child resisted SIGKILL - check your kernel logs.") ; + default : + strerr_dief1x(101, "internal error: inconsistent state. Please submit a bug-report.") ; + } +} + +static inline void handle_signals (void) +{ + for (;;) + { + char c = selfpipe_read() ; + switch (c) + { + case -1 : strerr_diefu1sys(111, "selfpipe_read") ; + case 0 : return ; + case SIGTERM : + cont = pid ? 2 : 0 ; + break ; + case SIGCHLD : + if (!pid) wait_reap() ; + else + { + int wstat ; + int r = wait_pid_nohang(pid, &wstat) ; + if (r < 0) + if (errno != ECHILD) strerr_diefu1sys(111, "wait_pid_nohang") ; + else break ; + else if (!r) break ; + on_death() ; + } + break ; + default : + strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ; + } + } +} + +static inline void handle_netlink (int fd, char const *const *argv, char const *const *envp) +{ + char buf[4096] ; + int r ; + { + struct sockaddr_nl nl; + struct iovec iov = { .iov_base = &buf, .iov_len = sizeof(buf) } ; + char ctlmsg[CMSG_SPACE(sizeof(struct ucred))] ; + struct msghdr msg = { + .msg_name = &nl, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = ctlmsg, + .msg_controllen = sizeof(ctlmsg), + .msg_flags = 0 + } ; + r = sanitize_read(fd_recvmsg(fd, &msg)) ; + if (r < 0) + { + if (errno == EPIPE) + { + if (verbosity >= 2) strerr_warnw1x("received EOF on netlink") ; + cont = 0 ; + return ; + } + else strerr_diefu1sys(111, "receive netlink message") ; + } + if (!r) return ; + if (r < 32 || r > 4096) + { + if (verbosity >= 2) + strerr_warnw2x("received and ignored netlink message ", "with invalid length") ; + return ; + } + if (nl.nl_pid) + { + if (verbosity >= 3) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, nl.nl_pid)] = 0 ; + strerr_warnw3x("received and ignored netlink message ", "from userspace process ", fmt) ; + } + return ; + } + } + { + unsigned int start = str_len(buf) + 1 ; + if (start < 5 || start > (unsigned int)r) + { + if (verbosity >= 2) + strerr_warnw3x("received and ignored netlink message ", "with invalid header", " length") ; + return ; + } + if (str_strn(buf, start, "@/", 2) >= start) + { + if (verbosity >= 2) + strerr_warnw2x("received and ignored netlink message ", "with invalid header") ; + return ; + } + on_event(argv, envp, buf + start, r - start) ; + } +} + +static inline int make_ttos (char const *s) +{ + unsigned int tlife = 0, tterm = 0, tkill = 0, pos = 0 ; + pos += uint_scan(s + pos, &tlife) ; + if (s[pos] && s[pos++] != ':') return 0 ; + if (!tlife) return 1 ; + tain_from_millisecs(&lifetto, tlife) ; + pos += uint_scan(s + pos, &tterm) ; + if (s[pos] && s[pos++] != ':') return 0 ; + if (!tterm) return 1 ; + tain_from_millisecs(&termtto, tterm) ; + tain_add(&termtto, &termtto, &lifetto) ; + pos += uint_scan(s + pos, &tkill) ; + if (s[pos]) return 0 ; + if (!tkill) return 1 ; + tain_from_millisecs(&killtto, tkill) ; + tain_add(&killtto, &killtto, &termtto) ; + return 1 ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ; + PROG = "s6-devd" ; + { + unsigned int kbufsz = 65536 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "qvb:t:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'q' : if (verbosity) verbosity-- ; break ; + case 'v' : verbosity++ ; break ; + case 'b' : if (!uint0_scan(l.arg, &kbufsz)) dieusage() ; break ; + case 't' : if (!make_ttos(l.arg)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (!argc) strerr_dieusage(100, USAGE) ; + { + int fd = open_readb("/dev/null") ; + if (fd < 0) strerr_diefu2sys(111, "open /dev/null for ", "reading") ; + if (fd_move(0, fd) < 0) strerr_diefu2sys(111, "redirect std", "in") ; + fd = open_write("/dev/null") ; + if (fd < 0) strerr_diefu2sys(111, "open /dev/null for ", "writing") ; + if (ndelay_off(fd) < 0) strerr_diefu1sys(111, "ndelay_off /dev/null") ; + if (fd_move(1, fd) < 0) strerr_diefu2sys(111, "redirect std", "out") ; + } + x[1].fd = netlink_init(kbufsz) ; + if (x[1].fd < 0) strerr_diefu1sys(111, "init netlink") ; + } + + x[0].fd = selfpipe_init() ; + if (x[0].fd == -1) strerr_diefu1sys(111, "init selfpipe") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGCHLD) ; + if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ; + } + + tain_now_g() ; + tain_add_g(&deadline, &tain_infinite_relative) ; + if (verbosity >= 2) strerr_warni1x("starting") ; + + while (cont) + { + register int r = iopause_g(x, 1 + !pid, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + else if (!r) handle_timeout() ; + else + { + if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) + strerr_diefu1x(111, "iopause: trouble with pipes") ; + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + else if (!pid && (x[1].revents & IOPAUSE_READ)) + handle_netlink(x[1].fd, argv, envp) ; + } + } + if (verbosity >= 2) strerr_warni1x("exiting") ; + return 0 ; +} diff --git a/src/minutils/s6-freeramdisk.c b/src/minutils/s6-freeramdisk.c new file mode 100644 index 0000000..3b24656 --- /dev/null +++ b/src/minutils/s6-freeramdisk.c @@ -0,0 +1,21 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-freeramdisk ramdisk_device" + +int main (int argc, char const *const *argv) +{ + int fd ; + PROG = "s6-freeramdisk" ; + if (argc < 2) strerr_dieusage(100, USAGE) ; + fd = open(argv[1], O_RDWR) ; + if (fd < 0) strerr_diefu3sys(111, "open ", argv[1], " in read-write mode") ; + if (ioctl(fd, BLKFLSBUF) < 0) strerr_diefu2sys(111, "ioctl ", argv[1]) ; + return 0 ; +} diff --git a/src/minutils/s6-halt.c b/src/minutils/s6-halt.c new file mode 100644 index 0000000..9613017 --- /dev/null +++ b/src/minutils/s6-halt.c @@ -0,0 +1,13 @@ +/* ISC license. */ + +#include +#include +#include + +int main () +{ + PROG = "s6-halt" ; + sync() ; + reboot(RB_HALT_SYSTEM) ; + strerr_diefu1sys(111, "reboot()") ; +} diff --git a/src/minutils/s6-hiercopy.c b/src/minutils/s6-hiercopy.c new file mode 100644 index 0000000..f8b7666 --- /dev/null +++ b/src/minutils/s6-hiercopy.c @@ -0,0 +1,156 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-hiercopy src dst" + +static void hiercopy (char const *, char const *) ; + +static int filecopy (char const *src, char const *dst, mode_t mode) +{ + int d ; + int s = open_readb(src) ; + if (s < 0) return 0 ; + d = open3(dst, O_WRONLY | O_CREAT | O_TRUNC, mode) ; + if (d < 0) + { + fd_close(s) ; + return 0 ; + } + if (fd_cat(s, d) < 0) goto err ; + fd_close(s) ; + fd_close(d) ; + return 1 ; + +err: + { + register int e = errno ; + fd_close(s) ; + fd_close(d) ; + errno = e ; + } + return 0 ; +} + +static int dircopy (char const *src, char const *dst, mode_t mode) +{ + unsigned int tmpbase = satmp.len ; + unsigned int maxlen = 0 ; + { + DIR *dir = opendir(src) ; + if (!dir) return 0 ; + for (;;) + { + direntry *d ; + register unsigned int n ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') + if (((d->d_name[1] == '.') && !d->d_name[2]) || !d->d_name[1]) + continue ; + n = str_len(d->d_name) ; + if (n > maxlen) maxlen = n ; + if (!stralloc_catb(&satmp, d->d_name, n+1)) break ; + } + if (errno) + { + int e = errno ; + dir_close(dir) ; + errno = e ; + goto err ; + } + dir_close(dir) ; + } + + if (mkdir(dst, S_IRWXU) == -1) + { + struct stat st ; + if (errno != EEXIST) goto err ; + if (stat(dst, &st) < 0) goto err ; + if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR ; goto err ; } + } + + { + unsigned int srclen = str_len(src) ; + unsigned int dstlen = str_len(dst) ; + unsigned int i = tmpbase ; + char srcbuf[srclen + maxlen + 2] ; + char dstbuf[dstlen + maxlen + 2] ; + byte_copy(srcbuf, srclen, src) ; + byte_copy(dstbuf, dstlen, dst) ; + srcbuf[srclen] = '/' ; + dstbuf[dstlen] = '/' ; + while (i < satmp.len) + { + register unsigned int n = str_len(satmp.s + i) + 1 ; + byte_copy(srcbuf + srclen + 1, n, satmp.s + i) ; + byte_copy(dstbuf + dstlen + 1, n, satmp.s + i) ; + i += n ; + hiercopy(srcbuf, dstbuf) ; + } + } + if (chmod(dst, mode) == -1) goto err ; + satmp.len = tmpbase ; + return 1 ; +err: + satmp.len = tmpbase ; + return 0 ; +} + +static void hiercopy (char const *src, char const *dst) +{ + struct stat st ; + if (lstat(src, &st) == -1) strerr_diefu2sys(111, "stat ", src) ; + if (S_ISREG(st.st_mode)) + { + if (!filecopy(src, dst, st.st_mode)) + strerr_diefu4sys(111, "copy regular file ", src, " to ", dst) ; + } + else if (S_ISDIR(st.st_mode)) + { + if (!dircopy(src, dst, st.st_mode)) + strerr_diefu4sys(111, "recursively copy directory ", src, " to ", dst) ; + } + else if (S_ISFIFO(st.st_mode)) + { + if (mkfifo(dst, st.st_mode) < 0) + strerr_diefu2sys(111, "mkfifo ", dst) ; + } + else if (S_ISLNK(st.st_mode)) + { + unsigned int tmpbase = satmp.len ; + if ((sareadlink(&satmp, src) < 0) || !stralloc_0(&satmp)) + strerr_diefu2sys(111, "readlink ", src) ; + if (symlink(satmp.s + tmpbase, dst) < 0) + strerr_diefu4sys(111, "symlink ", satmp.s + tmpbase, " to ", dst) ; + satmp.len = tmpbase ; + } + else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISSOCK(st.st_mode)) + { + if (mknod(dst, st.st_mode, st.st_rdev) < 0) + strerr_diefu2sys(111, "mknod ", dst) ; + } + else strerr_dief2x(111, "unrecognized file type for ", src) ; + lchown(dst, st.st_uid, st.st_gid) ; + if (!S_ISLNK(st.st_mode)) chmod(dst, st.st_mode) ; +} + +int main (int argc, char const *const *argv) +{ + PROG = "s6-hiercopy" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + umask(0) ; + hiercopy(argv[1], argv[2]) ; + return 0 ; +} diff --git a/src/minutils/s6-hostname.c b/src/minutils/s6-hostname.c new file mode 100644 index 0000000..deb525b --- /dev/null +++ b/src/minutils/s6-hostname.c @@ -0,0 +1,37 @@ +/* ISC license. */ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-hostname [ hostname ]" + +static int getit (void) +{ + stralloc sa = STRALLOC_ZERO ; + if (sagethostname(&sa) < 0) strerr_diefu1sys(111, "get hostname") ; + sa.s[sa.len++] = '\n' ; + if (allwrite(1, sa.s, sa.len) < sa.len) + strerr_diefu1sys(111, "write to stdout") ; + return 0 ; +} + +static int setit (char const *h) +{ + if (sethostname(h, str_len(h)) < 0) + strerr_diefu1sys(111, "set hostname") ; + return 0 ; +} + +int main (int argc, char const *const *argv) +{ + PROG = "s6-hostname" ; + return (argc < 2) ? getit() : setit(argv[1]) ; +} diff --git a/src/minutils/s6-logwatch.c b/src/minutils/s6-logwatch.c new file mode 100644 index 0000000..a2c493d --- /dev/null +++ b/src/minutils/s6-logwatch.c @@ -0,0 +1,156 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-logwatch [ -m maxbuffer ] logdir" +#define dieusage() strerr_dieusage(100, USAGE) + +#define N 4096 +#define IESIZE 100 + +typedef enum bstate_e bstate_t, *bstate_t_ref ; +enum bstate_e +{ + B_TAILING = 0, + B_WAITING = 1 +} ; + +static void X (void) +{ + strerr_diefu1x(101, "follow file state changes (race condition triggered). Sorry.") ; +} + +static unsigned long nbcat (int fdcurrent) +{ + char buf[N+1] ; + buffer b = BUFFER_INIT(&buffer_read, fdcurrent, buf, N+1) ; + siovec_t v[2] ; + unsigned long bytes = 0 ; + for (;;) + { + int r = sanitize_read(buffer_fill(&b)) ; + if (!r) break ; + if (r < 0) + { + if (errno == EPIPE) break ; + else strerr_diefu1sys(111, "buffer_fill") ; + } + buffer_rpeek(&b, v) ; + if (!bufalloc_putv(bufalloc_1, v, 2)) + strerr_diefu1sys(111, "bufalloc_putv") ; + buffer_rseek(&b, r) ; + bytes += r ; + } + return bytes ; +} + + +int main (int argc, char const *const *argv) +{ + char const *dir = "." ; + unsigned long maxlen = 4000 ; + PROG = "s6-logwatch" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "m:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'm' : if (!ulong0_scan(l.arg, &maxlen)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + if (argc) dir = *argv ; + if (chdir(dir) < 0) strerr_diefu2sys(111, "chdir to ", dir) ; + { + iopause_fd x[1] = { { -1, IOPAUSE_READ, 0 } } ; + int fdcurrent = -1 ; + unsigned long pos = 0 ; + int w ; + bstate_t state = B_TAILING ; + x[0].fd = inotify_init() ; + if (x[0].fd < 0) strerr_diefu1sys(111, "inotify_init") ; + if (ndelay_on(x[0].fd) < 0) strerr_diefu1sys(111, "ndelay_on inotify fd") ; + w = inotify_add_watch(x[0].fd, ".", IN_CREATE | IN_MODIFY | IN_CLOSE_WRITE) ; + if (w < 0) strerr_diefu1sys(111, "inotify_add_watch") ; + if (sig_ignore(SIGPIPE) == -1) strerr_diefu1sys(111, "sig_ignore(SIGPIPE)") ; + fdcurrent = open_readb("current") ; + if (fdcurrent < 0) + if (errno != ENOENT) strerr_diefu1sys(111, "open_readb current") ; + else state = B_WAITING ; + else pos = nbcat(fdcurrent) ; + + for (;;) + { + int r ; + if (!bufalloc_flush(bufalloc_1)) strerr_diefu1sys(111, "write to stdout") ; + r = iopause(x, 1, 0, 0) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + if (x[0].revents & IOPAUSE_READ) + { + char iebuf[IESIZE] ; + while (bufalloc_len(bufalloc_1) < maxlen) + { + unsigned int i = 0 ; + r = sanitize_read(fd_read(x[0].fd, iebuf, IESIZE)) ; + if (r < 0) strerr_diefu1sys(111, "read from inotify fd") ; + if (!r) break ; + while (i < (unsigned int)r) + { + struct inotify_event *ie = (struct inotify_event *)(iebuf + i) ; + if ((ie->wd != w) || !ie->len || str_diff(ie->name, "current")) goto cont ; + if (ie->mask & IN_MODIFY) + { + if (state) X() ; + pos += nbcat(fdcurrent) ; + } + else if (ie->mask & IN_CLOSE_WRITE) + { + if (state) X() ; + fd_close(fdcurrent) ; + fdcurrent = -1 ; + pos = 0 ; + state = B_WAITING ; + } + else if (ie->mask & IN_CREATE) + { + if (!state) X() ; + fdcurrent = open_readb("current") ; + if (fdcurrent < 0) + { + if (errno != ENOENT) strerr_diefu1sys(111, "open_readb current") ; + else goto cont ; + } + pos = nbcat(fdcurrent) ; + state = B_TAILING ; + } + cont: + i += sizeof(struct inotify_event) + ie->len ; + } + } + } + } + } + return 0 ; +} diff --git a/src/minutils/s6-mount.c b/src/minutils/s6-mount.c new file mode 100644 index 0000000..6970c05 --- /dev/null +++ b/src/minutils/s6-mount.c @@ -0,0 +1,126 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mount-constants.h" + +#define USAGE "s6-mount -a [ -z fstab ] | s6-mount [ -t type ] [ -o option[,option...] ]... device mountpoint" +#define BUFSIZE 4096 + +#define SWITCH(opt) do +#define HCTIWS(opt) while(0) ; +#define CASE(s) if (!str_diffn(opt, (s), str_len(s))) + +static void scanopt (stralloc *data, unsigned long *flags, char const *opt) +{ + for (;;) + { + register unsigned int n = str_chr(opt, ',') ; + + SWITCH(opt) + { + CASE("defaults") { *flags = MS_MGC_VAL ; break ; } + CASE("ro") { *flags |= MS_RDONLY ; break ; } + CASE("rw") { *flags &= ~MS_RDONLY ; break ; } + CASE("remount") { *flags |= MS_REMOUNT ; break ; } + CASE("sync") { *flags |= MS_SYNCHRONOUS ; break ; } + CASE("async") { *flags &= ~MS_SYNCHRONOUS ; break ; } + CASE("nodev") { *flags |= MS_NODEV ; break ; } + CASE("dev") { *flags &= ~MS_NODEV ; break ; } + CASE("noexec") { *flags |= MS_NOEXEC ; break ; } + CASE("exec") { *flags &= ~MS_NOEXEC ; break ; } + CASE("nosuid") { *flags |= MS_NOSUID ; break ; } + CASE("suid") { *flags &= ~MS_NOSUID ; break ; } + CASE("noatime") { *flags |= MS_NOATIME ; break ; } + CASE("atime") { *flags &= ~MS_NOATIME ; break ; } + CASE("nodiratime") { *flags |= MS_NODIRATIME ; break ; } + CASE("diratime") { *flags &= ~MS_NODIRATIME ; break ; } + CASE("bind") { *flags |= MS_BIND ; break ; } + CASE("nobind") { *flags &= ~MS_BIND ; break ; } + CASE("move") { *flags |= MS_MOVE ; break ; } + CASE("nomove") { *flags &= ~MS_MOVE ; break ; } + + if ((data->s && data->len && !stralloc_catb(data, ",", 1)) || !stralloc_catb(data, opt, n)) + strerr_diefu1sys(111, "build data string") ; + } + HCTIWS(opt) + + opt += n ; + if (!*opt) break ; + if (*opt != ',') strerr_dief1x(100, "unrecognized option") ; + opt++ ; + } +} + +static int mountall (char const *fstab) +{ + struct mntent *d ; + int e = 0 ; + FILE *yuck = setmntent(fstab, "r") ; + if (!yuck) strerr_diefu2sys(111, "open ", fstab) ; + while ((d = getmntent(yuck))) + { + unsigned long flags = MS_MGC_VAL ; + stralloc data = STRALLOC_ZERO ; + scanopt(&data, &flags, d->mnt_opts) ; + if (!stralloc_0(&data)) + strerr_diefu1sys(111, "build data string") ; +#ifdef DEBUG + strerr_warni4x("mounting ", d->mnt_fsname, " on ", d->mnt_dir) ; +#endif + if (mount(d->mnt_fsname, d->mnt_dir, d->mnt_type, flags, data.s) == -1) + { + e++ ; + strerr_warnwu4sys("mount ", d->mnt_fsname, " on ", d->mnt_dir) ; + } + stralloc_free(&data) ; + } + endmntent(yuck) ; + return e ; +} + +int main (int argc, char const *const *argv) +{ + stralloc data = STRALLOC_ZERO ; + unsigned long flags = MS_MGC_VAL ; + char const *fstype = "none" ; + char const *fstab = "/etc/fstab" ; + PROG = "s6-mount" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "z:arwt:o:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'z' : fstab = l.arg ; break ; + case 'a' : return mountall(fstab) ; + case 't' : fstype = l.arg ; break ; + case 'w' : scanopt(&data, &flags, "rw") ; break ; + case 'r' : scanopt(&data, &flags, "ro") ; break ; + case 'o' : scanopt(&data, &flags, l.arg) ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) + { + int fd = open_readb("/proc/mounts") ; + if (fd < 0) strerr_diefu2sys(111, "read ", "/proc/mounts") ; + if (fd_cat(fd, 1) < 0) strerr_diefu2sys(111, "fd_cat ", "/proc/mounts") ; + fd_close(fd) ; + } + else if (argc == 1) strerr_dieusage(100, USAGE) ; + else if (!stralloc_0(&data)) strerr_diefu1sys(111, "build data string") ; + else if (mount(argv[0], argv[1], fstype, flags, data.s) == -1) + strerr_diefu4sys(111, "mount ", argv[0], " on ", argv[1]) ; + return 0 ; +} diff --git a/src/minutils/s6-pivotchroot.c b/src/minutils/s6-pivotchroot.c new file mode 100644 index 0000000..ee4db73 --- /dev/null +++ b/src/minutils/s6-pivotchroot.c @@ -0,0 +1,24 @@ +/* ISC license. */ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include +#include +#include + +#define USAGE "s6-pivotchroot old-place-for-new-root new-place-for-old-root prog..." + +extern int pivot_root (char const *, char const *) ; + +int main (int argc, char const *const *argv, char const *const *envp) +{ + PROG = "s6-pivotchroot" ; + if (argc < 4) strerr_dieusage(100, USAGE) ; + if (chdir(argv[1]) < 0) strerr_diefu2sys(111, "chdir to ", argv[1]) ; + if (pivot_root(".", argv[2]) < 0) strerr_diefu1sys(111, "pivot_root") ; + if (chroot(".") < 0) strerr_diefu1sys(111, "chroot") ; + pathexec_run(argv[3], argv+3, envp) ; + strerr_dieexec(111, argv[3]) ; +} diff --git a/src/minutils/s6-poweroff.c b/src/minutils/s6-poweroff.c new file mode 100644 index 0000000..b3576b3 --- /dev/null +++ b/src/minutils/s6-poweroff.c @@ -0,0 +1,13 @@ +/* ISC license. */ + +#include +#include +#include + +int main () +{ + PROG = "s6-poweroff" ; + sync() ; + reboot(RB_POWER_OFF) ; + strerr_diefu1sys(111, "reboot()") ; +} diff --git a/src/minutils/s6-ps.c b/src/minutils/s6-ps.c new file mode 100644 index 0000000..5a22068 --- /dev/null +++ b/src/minutils/s6-ps.c @@ -0,0 +1,385 @@ +/* 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 "s6-ps.h" + +#define USAGE "s6-ps [ -H ] [ -w spacing ] [ -W wchanfile ] [ -l | -o field,field... ]" + +#define RIGHTFORMATTED ( \ + (1 << PFIELD_PID) | \ + (1 << PFIELD_PPID) | \ + (1 << PFIELD_PGRP) | \ + (1 << PFIELD_SESSION) | \ + (1 << PFIELD_TPGID) | \ + (1 << PFIELD_PRIO) | \ + (1 << PFIELD_NICE) | \ + (1 << PFIELD_THREADS) | \ + (1 << PFIELD_VSIZE) | \ + (1 << PFIELD_RSS) | \ + (1 << PFIELD_RSSLIM) | \ + (1 << PFIELD_CPUNO) | \ + (1 << PFIELD_RTPRIO) | \ + (1 << PFIELD_PMEM) | \ + (1 << PFIELD_PCPU) | \ + ((uint64)1 << PFIELD_CPCPU)) + +void *left_dtok (unsigned int d, void *x) +{ + return (void *)&genalloc_s(diuint, (genalloc *)x)[d].left ; +} + +int uint_cmp (void const *a, void const *b, void *x) +{ + register unsigned int aa = *(unsigned int *)a ; + register unsigned int bb = *(unsigned int *)b ; + (void)x ; + return (aa < bb) ? -1 : (aa > bb) ; +} + +static void *pid_dtok (unsigned int d, void *x) +{ + return &((pscan_t *)x)[d].pid ; +} + +static int fillo_notree (unsigned int i, unsigned int h, void *x) +{ + static unsigned int j = 0 ; + unsigned int *list = x ; + list[j++] = i ; + (void)h ; + return 1 ; +} + +static inline unsigned int fieldscan (char const *s, pfield_t *list, uint64 *fbf) +{ + uint64 bits = 0 ; + unsigned int n = 0 ; + int cont = 1 ; + for (; cont ; n++) + { + unsigned int len = str_chr(s, ',') ; + register pfield_t i = 0 ; + if (!len) strerr_dief3x(100, "invalid", " (empty)", " field for -o option") ; + if (!s[len]) cont = 0 ; + { + char tmp[len+1] ; + byte_copy(tmp, len, s) ; + tmp[len] = 0 ; + for (; i < PFIELD_PHAIL ; i++) if (!str_diff(tmp, s6ps_opttable[i])) break ; + if (i >= PFIELD_PHAIL) + strerr_dief4x(100, "invalid", " field for -o option", ": ", tmp) ; + if (bits & (1 << i)) + strerr_dief4x(100, "duplicate", " field for -o option", ": ", tmp) ; + } + s += len + 1 ; + list[n] = i ; + bits |= (1 << i) ; + } + *fbf = bits ; + return n ; +} + +static int slurpit (unsigned int dirfd, stralloc *data, char const *buf, char const *what, unsigned int *len) +{ + unsigned int start = data->len ; + int fd = open_readat(dirfd, what) ; + if (fd < 0) return 0 ; + if (!slurp(data, fd)) strerr_diefu4sys(111, "slurp ", buf, "/", what) ; + fd_close(fd) ; + *len = data->len - start ; + return 1 ; +} + +int main (int argc, char const *const *argv) +{ + genalloc pscans = GENALLOC_ZERO ; /* array of pscan_t */ + pfield_t fieldlist[PFIELD_PHAIL] = { PFIELD_USER, PFIELD_PID, PFIELD_TTY, PFIELD_STATE, PFIELD_START, PFIELD_ARGS } ; + uint64 fbf = (1 << PFIELD_USER) | (1 << PFIELD_PID) | (1 << PFIELD_TTY) | (1 << PFIELD_STATE) | (1 << PFIELD_START) | (1 << PFIELD_ARGS) ; + unsigned int mypos = 0 ; + unsigned int nfields = 6 ; + pscan_t *p ; + unsigned int n ; + unsigned int spacing = 2 ; + int flagtree = 0 ; + char const *wchanfile = 0 ; + int needstat ; + PROG = "s6-ps" ; + + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "Hlw:W:o:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'H' : flagtree = 1 ; break ; + case 'l' : + { + nfields = 11 ; + fbf = (1 << PFIELD_USER) | (1 << PFIELD_PID) | ((uint64)1 << PFIELD_CPCPU) | (1 << PFIELD_PMEM) | (1 << PFIELD_VSIZE) | (1 << PFIELD_RSS) | (1 << PFIELD_TTY) | (1 << PFIELD_STATE) | (1 << PFIELD_START) | (1 << PFIELD_CTTIME) | (1 << PFIELD_ARGS) ; + fieldlist[0] = PFIELD_USER ; + fieldlist[1] = PFIELD_PID ; + fieldlist[2] = PFIELD_CPCPU ; + fieldlist[3] = PFIELD_PMEM ; + fieldlist[4] = PFIELD_VSIZE ; + fieldlist[5] = PFIELD_RSS ; + fieldlist[6] = PFIELD_TTY ; + fieldlist[7] = PFIELD_STATE ; + fieldlist[8] = PFIELD_START ; + fieldlist[9] = PFIELD_CTTIME ; + fieldlist[10] = PFIELD_ARGS ; + break ; + } + case 'w' : + { + if (!uint0_scan(l.arg, &spacing)) strerr_dieusage(100, USAGE) ; + break ; + } + case 'W' : wchanfile = l.arg ; break ; + case 'o' : + { + nfields = fieldscan(l.arg, fieldlist, &fbf) ; + break ; + } + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + if (!spacing) spacing = 1 ; + if (spacing > 256) spacing = 256 ; + + needstat = flagtree || !!(fbf & ( + (1 << PFIELD_PID) | + (1 << PFIELD_COMM) | + (1 << PFIELD_STATE) | + (1 << PFIELD_PPID) | + (1 << PFIELD_PGRP) | + (1 << PFIELD_SESSION) | + (1 << PFIELD_TTY) | + (1 << PFIELD_TPGID) | + (1 << PFIELD_UTIME) | + (1 << PFIELD_STIME) | + (1 << PFIELD_CUTIME) | + (1 << PFIELD_CSTIME) | + (1 << PFIELD_PRIO) | + (1 << PFIELD_NICE) | + (1 << PFIELD_THREADS) | + (1 << PFIELD_START) | + (1 << PFIELD_VSIZE) | + (1 << PFIELD_RSS) | + (1 << PFIELD_RSSLIM) | + (1 << PFIELD_CPUNO) | + (1 << PFIELD_RTPRIO) | + (1 << PFIELD_RTPOLICY) | + (1 << PFIELD_PMEM) | + (1 << PFIELD_WCHAN) | + (1 << PFIELD_PCPU) | + (1 << PFIELD_TTIME) | + (1 << PFIELD_CTTIME) | + ((uint64)1 << PFIELD_TSTART) | + ((uint64)1 << PFIELD_CPCPU))) ; + + + /* Scan /proc */ + + { + int needstatdir = !!(fbf & ((1 << PFIELD_USER) | (1 << PFIELD_GROUP))) ; + unsigned int mypid = getpid() ; + DIR *dir = opendir("/proc") ; + direntry *d ; + char buf[25] = "/proc/" ; + + if (!dir) strerr_diefu1sys(111, "open /proc") ; + for (;;) + { + pscan_t pscan = PSCAN_ZERO ; + int dirfd ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (!uint0_scan(d->d_name, &pscan.pid)) continue ; + strcpy(buf+6, d->d_name) ; + dirfd = open_read(buf) ; + if (dirfd < 0) continue ; + + if (needstatdir) + { + struct stat st ; + if (fstat(dirfd, &st) < 0) goto errindir ; + pscan.uid = st.st_uid ; + pscan.gid = st.st_gid ; + } + if (needstat) + { + if (!slurpit(dirfd, &pscan.data, buf, "stat", &pscan.statlen)) + goto errindir ; + if (!slurpit(dirfd, &pscan.data, buf, "comm", &pscan.commlen)) + goto errindir ; + if (pscan.commlen) { pscan.commlen-- ; pscan.data.len-- ; } + } + if (fbf & (1 << PFIELD_ARGS)) + { + if (!slurpit(dirfd, &pscan.data, buf, "cmdline", &pscan.cmdlen)) goto errindir ; + while (!pscan.data.s[pscan.data.len-1]) + { + pscan.cmdlen-- ; + pscan.data.len-- ; + } + } + if (fbf & (1 << PFIELD_ENV)) slurpit(dirfd, &pscan.data, buf, "environ", &pscan.envlen) ; + fd_close(dirfd) ; + if (!genalloc_append(pscan_t, &pscans, &pscan)) + strerr_diefu1sys(111, "genalloc_append") ; + if (pscan.pid == mypid) mypos = genalloc_len(pscan_t, &pscans) ; + continue ; + errindir: + fd_close(dirfd) ; + stralloc_free(&pscan.data) ; + } + if (errno) strerr_diefu1sys(111, "readdir /proc") ; + dir_close(dir) ; + } + + /* Add a process 0 as a root and sentinel */ + { + pscan_t pscan = { .pid = 0, .ppid = 0 } ; + if (!genalloc_append(pscan_t, &pscans, &pscan)) strerr_diefu1sys(111, "genalloc_append") ; + } + + p = genalloc_s(pscan_t, &pscans) ; + n = genalloc_len(pscan_t, &pscans) - 1 ; + + { + unsigned int orderedlist[n+1] ; /* 1st element will be 0, ignored */ + register unsigned int i = 0 ; + + /* Order the processes for display */ + + { + AVLTREEB_TYPE(n+1) pidtree ; + avltreeb_init(&pidtree, n+1, &pid_dtok, &uint_cmp, p) ; + for (i = 0 ; i < n ; i++) + { + if (needstat && !s6ps_statparse(p+i)) + strerr_diefu1sys(111, "parse process stats") ; + if (!avltreeb_insert(&pidtree, i)) + strerr_diefu1sys(111, "avltreeb_insert") ; + } + if (!avltreeb_insert(&pidtree, n)) + strerr_diefu1sys(111, "avltreeb_insert") ; + + if (flagtree) s6ps_otree(p, n+1, &pidtree.info, orderedlist) ; + else avltreeb_iter(&pidtree, &fillo_notree, orderedlist) ; + } + + + /* Format, compute length, output */ + + if (fbf & ((1 << PFIELD_START) | ((uint64)1 << PFIELD_TSTART) | (1 << PFIELD_PCPU) | ((uint64)1 << PFIELD_CPCPU))) + { + tain_now_g() ; + s6ps_compute_boottime(p, mypos) ; + } + if (fbf & (1 << PFIELD_USER) && !s6ps_pwcache_init()) + strerr_diefu1sys(111, "init user name cache") ; + if (fbf & (1 << PFIELD_GROUP) && !s6ps_grcache_init()) + strerr_diefu1sys(111, "init group name cache") ; + if (fbf & (1 << PFIELD_TTY) && !s6ps_ttycache_init()) + strerr_diefu1sys(111, "init tty name cache") ; + if (fbf & (1 << PFIELD_WCHAN) && !s6ps_wchan_init(wchanfile)) + { + if (wchanfile) strerr_warnwu2sys("init wchan file ", wchanfile) ; + else strerr_warnwu1sys("init wchan") ; + } + + { + unsigned int fmtpos[n][nfields] ; + unsigned int fmtlen[n][nfields] ; + unsigned int maxlen[nfields] ; + unsigned int maxspaces = 0 ; + for (i = 0 ; i < nfields ; i++) maxlen[i] = str_len(s6ps_fieldheaders[fieldlist[i]]) ; + for (i = 0 ; i < n ; i++) + { + register unsigned int j = 0 ; + for (; j < nfields ; j++) + { + if (!(*s6ps_pfield_fmt[fieldlist[j]])(p+i, &fmtpos[i][j], &fmtlen[i][j])) + strerr_diefu1sys(111, "format fields") ; + if (fmtlen[i][j] > maxlen[j]) maxlen[j] = fmtlen[i][j] ; + } + } + for (i = 0 ; i < nfields ; i++) + if (maxlen[i] > maxspaces) maxspaces = maxlen[i] ; + maxspaces += spacing ; + if (fbf & (1 << PFIELD_USER)) s6ps_pwcache_finish() ; + if (fbf & (1 << PFIELD_GROUP)) s6ps_grcache_finish() ; + if (fbf & (1 << PFIELD_TTY)) s6ps_ttycache_finish() ; + if (fbf & (1 << PFIELD_WCHAN)) s6ps_wchan_finish() ; + stralloc_free(&satmp) ; + { + char spaces[maxspaces] ; + for (i = 0 ; i < maxspaces ; i++) spaces[i] = ' ' ; + for (i = 0 ; i < nfields ; i++) + { + register unsigned int rightformatted = !!(((uint64)1 << fieldlist[i]) & RIGHTFORMATTED) ; + register unsigned int len = str_len(s6ps_fieldheaders[fieldlist[i]]) ; + if (rightformatted && (buffer_put(buffer_1, spaces, maxlen[i] - len) < (int)(maxlen[i] - len))) + goto nowrite ; + if (buffer_put(buffer_1, s6ps_fieldheaders[fieldlist[i]], len) < (int)len) + goto nowrite ; + if ((i < nfields-1) && (buffer_put(buffer_1, spaces, !rightformatted * (maxlen[i] - len) + spacing) < (int)(!rightformatted * (maxlen[i] - len) + spacing))) + goto nowrite ; + } + if (buffer_put(buffer_1, "\n", 1) < 1) goto nowrite ; + for (i = 0 ; i < n ; i++) + { + register unsigned int oi = orderedlist[i+1] ; + register unsigned int j = 0 ; + for (; j < nfields ; j++) + { + register unsigned int rightformatted = !!(((uint64)1 << fieldlist[j]) & RIGHTFORMATTED) ; + if (rightformatted && (buffer_put(buffer_1, spaces, maxlen[j] - fmtlen[oi][j]) < (int)(maxlen[j] - fmtlen[oi][j]))) + goto nowrite ; + if (buffer_put(buffer_1, p[oi].data.s + fmtpos[oi][j], fmtlen[oi][j]) < (int)fmtlen[oi][j]) + goto nowrite ; + if ((j < nfields-1) && (buffer_put(buffer_1, spaces, !rightformatted * (maxlen[j] - fmtlen[oi][j]) + spacing) < (int)(!rightformatted * (maxlen[j] - fmtlen[oi][j]) + spacing))) + goto nowrite ; + } + if (buffer_put(buffer_1, "\n", 1) < 1) goto nowrite ; + } + } + } + } + buffer_flush(buffer_1) ; + return 0 ; + + nowrite: + strerr_diefu1sys(111, "write to stdout") ; +} diff --git a/src/minutils/s6-ps.h b/src/minutils/s6-ps.h new file mode 100644 index 0000000..3e7d84a --- /dev/null +++ b/src/minutils/s6-ps.h @@ -0,0 +1,153 @@ +/* ISC license. */ + +#ifndef _S6PS_H_ +#define _S6PS_H_ + +#include +#include +#include +#include +#include +#include + + /* pfield: the output fields */ + +typedef enum pfield_e pfield_t, *pfield_t_ref ; +enum pfield_e +{ + PFIELD_PID, + PFIELD_COMM, + PFIELD_STATE, + PFIELD_PPID, + PFIELD_PGRP, + PFIELD_SESSION, + PFIELD_TTY, + PFIELD_TPGID, + PFIELD_UTIME, + PFIELD_STIME, + PFIELD_CUTIME, + PFIELD_CSTIME, + PFIELD_PRIO, + PFIELD_NICE, + PFIELD_THREADS, + PFIELD_START, + PFIELD_VSIZE, + PFIELD_RSS, + PFIELD_RSSLIM, + PFIELD_CPUNO, + PFIELD_RTPRIO, + PFIELD_RTPOLICY, + PFIELD_USER, + PFIELD_GROUP, + PFIELD_PMEM, + PFIELD_WCHAN, + PFIELD_ARGS, + PFIELD_ENV, + PFIELD_PCPU, + PFIELD_TTIME, + PFIELD_CTTIME, + PFIELD_TSTART, + PFIELD_CPCPU, + PFIELD_PHAIL +} ; + +extern char const *const *s6ps_opttable ; +extern char const *const *s6ps_fieldheaders ; + + /* pscan: the main structure */ + +typedef struct pscan_s pscan_t, *pscan_t_ref ; +struct pscan_s +{ + stralloc data ; + unsigned int pid ; + signed int height ; + unsigned int statlen ; + unsigned int commlen ; + unsigned int cmdlen ; + unsigned int envlen ; + uid_t uid ; + gid_t gid ; + uint32 ppid ; + unsigned int state ; + uint32 pgrp ; + uint32 session ; + uint32 ttynr ; + int tpgid ; + uint64 utime ; + uint64 stime ; + uint64 cutime ; + uint64 cstime ; + int prio ; + int nice ; + uint32 threads ; + uint64 start ; + uint64 vsize ; + uint64 rss ; + uint64 rsslim ; + uint64 wchan ; + uint32 cpuno ; + uint32 rtprio ; + uint32 policy ; +} ; + +#define PSCAN_ZERO \ +{ \ + .data = STRALLOC_ZERO, \ + .pid = 0, \ + .height = 0, \ + .statlen = 0, \ + .commlen = 0, \ + .cmdlen = 0, \ + .envlen = 0, \ + .uid = 0, \ + .gid = 0, \ + .ppid = 0, \ + .state = 0, \ + .pgrp = 0, \ + .session = 0, \ + .ttynr = 0, \ + .tpgid = -1, \ + .utime = 0, \ + .stime = 0, \ + .cutime = 0, \ + .cstime = 0, \ + .prio = 0, \ + .nice = 0, \ + .threads = 0, \ + .start = 0, \ + .vsize = 0, \ + .rss = 0, \ + .rsslim = 0, \ + .wchan = 0, \ + .cpuno = 0, \ + .rtprio = 0, \ + .policy = 0 \ +} + +extern int s6ps_statparse (pscan_t *) ; +extern void s6ps_otree (pscan_t *, unsigned int, avltreen *, unsigned int *) ; + +extern int s6ps_compute_boottime (pscan_t *, unsigned int) ; + +typedef int pfieldfmt_func_t (pscan_t *, unsigned int *, unsigned int *) ; +typedef pfieldfmt_func_t *pfieldfmt_func_t_ref ; + +extern pfieldfmt_func_t_ref *s6ps_pfield_fmt ; + +extern void *left_dtok (unsigned int, void *) ; +extern int uint_cmp (void const *, void const *, void *) ; +extern int s6ps_pwcache_init (void) ; +extern void s6ps_pwcache_finish (void) ; +extern int s6ps_pwcache_lookup (stralloc *, unsigned int) ; +extern int s6ps_grcache_init (void) ; +extern void s6ps_grcache_finish (void) ; +extern int s6ps_grcache_lookup (stralloc *, unsigned int) ; +extern int s6ps_ttycache_init (void) ; +extern void s6ps_ttycache_finish (void) ; +extern int s6ps_ttycache_lookup (stralloc *, uint32) ; +extern int s6ps_wchan_init (char const *) ; +extern void s6ps_wchan_finish (void) ; +extern int s6ps_wchan_lookup (stralloc *, uint64) ; + +#endif diff --git a/src/minutils/s6-reboot.c b/src/minutils/s6-reboot.c new file mode 100644 index 0000000..f006b35 --- /dev/null +++ b/src/minutils/s6-reboot.c @@ -0,0 +1,13 @@ +/* ISC license. */ + +#include +#include +#include + +int main () +{ + PROG = "s6-reboot" ; + sync() ; + reboot(RB_AUTOBOOT) ; + strerr_diefu1sys(111, "reboot()") ; +} diff --git a/src/minutils/s6-swapoff.c b/src/minutils/s6-swapoff.c new file mode 100644 index 0000000..1067040 --- /dev/null +++ b/src/minutils/s6-swapoff.c @@ -0,0 +1,53 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +extern int swapoff (char const *) ; + +#define USAGE "s6-swapoff device s6-swapoff -a" + +#define BUFSIZE 4096 + +static int swapoffall ( ) +{ + char buf[BUFSIZE+1] ; + buffer b ; + stralloc sa = STRALLOC_ZERO ; + int e = 0 ; + int r ; + int fd = open_readb("/proc/swaps") ; + if (fd < 0) strerr_diefu1sys(111, "open_readb /proc/swaps") ; + buffer_init(&b, &buffer_read, fd, buf, BUFSIZE+1) ; + if (skagetln(&b, &sa, '\n') < 0) strerr_diefu1sys(111, "skagetln") ; + for (;;) + { + unsigned int n ; + sa.len = 0 ; + r = skagetln(&b, &sa, '\n') ; + if (r < 0) strerr_diefu1sys(111, "skagetln") ; + if (!r) break ; + n = byte_chr(sa.s, sa.len, ' ') ; + if (n >= sa.len) strerr_dief1x(111, "invalid line in /proc/swaps") ; + sa.s[n] = 0 ; + if (swapoff(sa.s) < 0) { e++ ; strerr_warnwu2sys("swapoff ", sa.s) ; } + } + fd_close(fd) ; + stralloc_free(&sa) ; + return e ; +} + +int main (int argc, char const *const *argv) +{ + PROG = "s6-swapoff" ; + if (argc < 2) strerr_dieusage(100, USAGE) ; + if ((argv[1][0] == '-') && (argv[1][1] == 'a') && !argv[1][2]) + return swapoffall() ; + if (swapoff(argv[1]) == -1) strerr_diefu2sys(111, "swapoff ", argv[1]) ; + return 0 ; +} diff --git a/src/minutils/s6-swapon.c b/src/minutils/s6-swapon.c new file mode 100644 index 0000000..6edd743 --- /dev/null +++ b/src/minutils/s6-swapon.c @@ -0,0 +1,37 @@ +/* ISC license. */ + +#include +#include +#include +#include + +extern int swapon (const char *, unsigned int) ; + +#define USAGE "s6-swapon device s6-swapon -a" + +static int swaponall () +{ + struct mntent *d ; + int e = 0 ; + FILE *yuck = setmntent("/etc/fstab", "r") ; + if (!yuck) strerr_diefu1sys(111, "setmntent /etc/fstab") ; + while ((d = getmntent(yuck))) + if (!str_diff(d->mnt_type, "swap") && (swapon(d->mnt_fsname, 0) == -1)) + { + e++ ; + strerr_warnwu2sys("swapon ", d->mnt_fsname) ; + } + endmntent(yuck) ; + return e ; +} + +int main (int argc, char const *const *argv) +{ + PROG = "s6-swapon" ; + if (argc < 2) strerr_dieusage(100, USAGE) ; + if ((argv[1][0] == '-') && (argv[1][1] == 'a') && !argv[1][2]) + return swaponall() ; + if (swapon(argv[1], 0) == -1) + strerr_diefu2sys(111, "swapon ", argv[1]) ; + return 0 ; +} diff --git a/src/minutils/s6-umount.c b/src/minutils/s6-umount.c new file mode 100644 index 0000000..966b455 --- /dev/null +++ b/src/minutils/s6-umount.c @@ -0,0 +1,65 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-umount mountpoint s6-umount -a" + +#define BUFSIZE 4096 +#define MAXLINES 512 + +static int umountall ( ) +{ + stralloc mountpoints[MAXLINES] ; + char buf[BUFSIZE+1] ; + buffer b ; + stralloc sa = STRALLOC_ZERO ; + unsigned int line = 0 ; + int e = 0 ; + int r ; + int fd = open_readb("/proc/mounts") ; + if (fd < 0) strerr_diefu1sys(111, "open /proc/mounts") ; + byte_zero(mountpoints, sizeof(mountpoints)) ; + buffer_init(&b, &buffer_read, fd, buf, BUFSIZE+1) ; + for (;;) + { + unsigned int n, p ; + if (line >= MAXLINES) strerr_dief1x(111, "/proc/mounts too big") ; + sa.len = 0 ; + r = skagetln(&b, &sa, '\n') ; + if (r <= 0) break ; + p = byte_chr(sa.s, sa.len, ' ') ; + if (p >= sa.len) strerr_dief1x(111, "bad /proc/mounts format") ; + p++ ; + n = byte_chr(sa.s + p, sa.len - p, ' ') ; + if (n == sa.len - p) strerr_dief1x(111, "bad /proc/mounts format") ; + if (!stralloc_catb(&mountpoints[line], sa.s + p, n) || !stralloc_0(&mountpoints[line])) + strerr_diefu1sys(111, "store mount point") ; + line++ ; + } + fd_close(fd) ; + stralloc_free(&sa) ; + if (r < 0) strerr_diefu1sys(111, "read /proc/mounts") ; + while (line--) + if (umount(mountpoints[line].s) == -1) + { + e++ ; + strerr_warnwu2sys("umount ", mountpoints[line].s) ; + } + return e ; +} + +int main (int argc, char const *const *argv) +{ + PROG = "s6-umount" ; + if (argc < 2) strerr_dieusage(100, USAGE) ; + if ((argv[1][0] == '-') && (argv[1][1] == 'a') && !argv[1][2]) + return umountall() ; + if (umount(argv[1]) == -1) strerr_diefu2sys(111, "umount ", argv[1]) ; + return 0 ; +} diff --git a/src/minutils/s6ps_grcache.c b/src/minutils/s6ps_grcache.c new file mode 100644 index 0000000..1fe9380 --- /dev/null +++ b/src/minutils/s6ps_grcache.c @@ -0,0 +1,66 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s6-ps.h" + +static avltree grcache_tree = AVLTREE_ZERO ; +static genalloc grcache_index = GENALLOC_ZERO ; + +int s6ps_grcache_init (void) +{ + avltree_init(&grcache_tree, 5, 3, 8, &left_dtok, &uint_cmp, &grcache_index) ; + return 1 ; +} + +void s6ps_grcache_finish (void) +{ + avltree_free(&grcache_tree) ; + genalloc_free(diuint, &grcache_index) ; +} + +int s6ps_grcache_lookup (stralloc *sa, unsigned int gid) +{ + int wasnull = !satmp.s ; + diuint d = { .left = gid, .right = satmp.len } ; + unsigned int i ; + if (!avltree_search(&grcache_tree, &d.left, &i)) + { + struct group *gr ; + unsigned int n = genalloc_len(diuint, &grcache_index) ; + errno = 0 ; + gr = getgrgid(gid) ; + if (!gr) + { + if (errno) return 0 ; + if (!stralloc_readyplus(&satmp, UINT_FMT + 2)) return 0 ; + stralloc_catb(&satmp, "(", 1) ; + satmp.len += uint_fmt(satmp.s + satmp.len, gid) ; + stralloc_catb(&satmp, ")", 2) ; + } + else if (!stralloc_cats(&satmp, gr->gr_name) || !stralloc_0(&satmp)) return 0 ; + if (!genalloc_append(diuint, &grcache_index, &d)) goto err ; + if (!avltree_insert(&grcache_tree, n)) + { + genalloc_setlen(diuint, &grcache_index, n) ; + goto err ; + } + i = n ; + } + return stralloc_cats(sa, satmp.s + genalloc_s(diuint, &grcache_index)[i].right) ; + err: + { + register int e = errno ; + if (wasnull) stralloc_free(&satmp) ; + else satmp.len = d.right ; + errno = e ; + } + return 0 ; +} diff --git a/src/minutils/s6ps_otree.c b/src/minutils/s6ps_otree.c new file mode 100644 index 0000000..5e96409 --- /dev/null +++ b/src/minutils/s6ps_otree.c @@ -0,0 +1,98 @@ +/* ISC license. */ + +#include +#include +#include "s6-ps.h" + +typedef struct ptreeiter_s ptreeiter_t, *ptreeiter_t_ref ; +struct ptreeiter_s +{ + unsigned int *childlist ; + unsigned int const *childindex ; + unsigned int const *ppindex ; + unsigned int *cpos ; +} ; + +typedef struct pstuff_s pstuff_t, *pstuff_t_ref ; +struct pstuff_s +{ + unsigned int *orderedlist ; + pscan_t *p ; + unsigned int const *childlist ; + unsigned int const *childindex ; + unsigned int const *nchild ; +} ; + +static int fillchildlist (unsigned int i, unsigned int h, void *x) +{ + register ptreeiter_t *pt = x ; + register unsigned int j = pt->ppindex[i] ; + pt->childlist[pt->childindex[j] + pt->cpos[j]++] = i ; + (void)h ; + return 1 ; +} + +static void fillo_tree_rec (pstuff_t *blah, unsigned int root, signed int h) +{ + static unsigned int j = 0 ; + register unsigned int i = !blah->p[root].pid ; + if (blah->p[root].pid == 1) h = -1 ; + blah->p[root].height = (h > 0) ? h : 0 ; + blah->orderedlist[j++] = root ; + for (; i < blah->nchild[root] ; i++) + fillo_tree_rec(blah, blah->childlist[blah->childindex[root] + i], h+1) ; +} + + /* + Fills up orderedlist with the right indices to print a process tree. + O(n log n) time, O(n) space, all in the stack. + */ + +void s6ps_otree (pscan_t *p, unsigned int n, avltreen *pidtree, unsigned int *orderedlist) +{ + unsigned int childlist[n] ; + unsigned int childindex[n] ; + unsigned int nchild[n] ; + register unsigned int i = 0 ; + for (; i < n ; i++) nchild[i] = 0 ; + + /* Compute the ppid tree */ + for (i = 0 ; i < n ; i++) + { + unsigned int k ; + if (!avltreen_search(pidtree, &p[i].ppid, &k)) k = n-1 ; + orderedlist[i] = k ; /* using orderedlist as ppindex */ + nchild[k]++ ; + } + { + unsigned int j = 0 ; + for (i = 0 ; i < n ; i++) + { + childindex[i] = j ; + j += nchild[i] ; + } + } + + /* Fill the childlist by increasing pids so it is sorted */ + { + unsigned int cpos[n] ; + ptreeiter_t blah = { .childlist = childlist, .childindex = childindex, .ppindex = orderedlist, .cpos = cpos } ; + for (i = 0 ; i < n ; i++) cpos[i] = 0 ; + avltreen_iter(pidtree, &fillchildlist, &blah) ; + } + + /* If we have init, make it the last in the orphan list */ + if (p[childlist[childindex[n-1]+1]].pid == 1) + { + unsigned int pos1 = childlist[childindex[n-1] + 1] ; + for (i = 2 ; i < nchild[n-1] ; i++) + childlist[childindex[n-1]+i-1] = childlist[childindex[n-1]+i] ; + childlist[childindex[n-1]+nchild[n-1]-1] = pos1 ; + } + + /* Finally, fill orderedlist by walking the childindex tree. */ + { + pstuff_t blah = { .orderedlist = orderedlist, .p = p, .childlist = childlist, .childindex = childindex, .nchild = nchild } ; + fillo_tree_rec(&blah, n-1, -1) ; + } +} diff --git a/src/minutils/s6ps_pfield.c b/src/minutils/s6ps_pfield.c new file mode 100644 index 0000000..3a960a4 --- /dev/null +++ b/src/minutils/s6ps_pfield.c @@ -0,0 +1,570 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s6-ps.h" + +static char const *const fieldheaders[PFIELD_PHAIL] = +{ + "PID", + "COMM", + "STAT", + "PPID", + "PGRP", + "SESSION", + "TTY", + "TPGID", + "UTIME", + "STIME", + "CUTIME", + "CSTIME", + "PRIO", + "NICE", + "THREADS", + "START", + "VSZ", + "RSS", + "RSSLIM", + "CPU", + "RTPRIO", + "RTPOLICY", + "USER", + "GROUP", + "%MEM", + "WCHAN", + "COMMAND", + "ENVIRONMENT", + "%CPU", + "TTIME", + "CTTIME", + "TSTART", + "C%CPU" +} ; + +char const *const *s6ps_fieldheaders = fieldheaders ; + +static char const *const opttable[PFIELD_PHAIL] = +{ + "pid", + "comm", + "s", + "ppid", + "pgrp", + "sess", + "tty", + "tpgid", + "utime", + "stime", + "cutime", + "cstime", + "prio", + "nice", + "thcount", + "start", + "vsize", + "rss", + "rsslimit", + "psr", + "rtprio", + "policy", + "user", + "group", + "pmem", + "wchan", + "args", + "env", + "pcpu", + "ttime", + "cttime", + "tstart", + "cpcpu" +} ; + +char const *const *s6ps_opttable = opttable ; + +static tain_t boottime = TAIN_EPOCH ; + +static int fmt_32 (pscan_t *p, unsigned int *pos, unsigned int *len, uint32 u) +{ + if (!stralloc_readyplus(&p->data, UINT32_FMT)) return 0 ; + *pos = p->data.len ; + *len = uint32_fmt(p->data.s + *pos, u) ; + p->data.len += *len ; + return 1 ; +} + +static int fmt_64 (pscan_t *p, unsigned int *pos, unsigned int *len, uint64 u) +{ + if (!stralloc_readyplus(&p->data, UINT64_FMT)) return 0 ; + *pos = p->data.len ; + *len = uint64_fmt(p->data.s + *pos, u) ; + p->data.len += *len ; + return 1 ; +} + +static int fmt_i (pscan_t *p, unsigned int *pos, unsigned int *len, int d) +{ + if (!stralloc_readyplus(&p->data, UINT32_FMT+1)) return 0 ; + *pos = p->data.len ; + *len = int_fmt(p->data.s + *pos, d) ; + p->data.len += *len ; + return 1 ; +} + +static int fmt_pid (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_32(p, pos, len, p->pid) ; +} + +static int fmt_comm (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + *pos = p->statlen ; + *len = p->commlen ; + return 1 ; +} + +static int fmt_s (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + if (!stralloc_readyplus(&p->data, 4)) return 0 ; + *pos = p->data.len ; + p->data.s[p->data.len++] = p->data.s[p->state] ; + if (p->pid == p->session) p->data.s[p->data.len++] = 's' ; + if (p->threads > 1) p->data.s[p->data.len++] = 'l' ; + if ((p->tpgid > 0) && ((unsigned int)p->tpgid == p->pgrp)) + p->data.s[p->data.len++] = '+' ; + if (p->nice) p->data.s[p->data.len++] = (p->nice < 0) ? '<' : 'N' ; + + *len = p->data.len - *pos ; + return 1 ; +} + +static int fmt_ppid (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_32(p, pos, len, p->ppid) ; +} + +static int fmt_pgrp (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_32(p, pos, len, p->pgrp) ; +} + +static int fmt_session (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_32(p, pos, len, p->session) ; +} + +static int fmt_ttynr(pscan_t *p, unsigned int *pos, unsigned int *len) +{ + if (p->ttynr) + { + unsigned int tmppos = p->data.len ; + if (!s6ps_ttycache_lookup(&p->data, p->ttynr)) return 0 ; + *pos = tmppos ; + *len = p->data.len - tmppos ; + } + else + { + if (!stralloc_catb(&p->data, "-", 1)) return 0 ; + *pos = p->data.len - 1 ; + *len = 1 ; + } + return 1 ; +} + +static int fmt_tpgid (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_i(p, pos, len, p->tpgid) ; +} + +static unsigned int gethz (void) +{ + static unsigned int hz = 0 ; + if (!hz) + { + long jiffies = sysconf(_SC_CLK_TCK) ; + if (jiffies < 1) + { + char fmt[ULONG_FMT + 1] ; + fmt[long_fmt(fmt, jiffies)] = 0 ; + strerr_warnw3x("invalid _SC_CLK_TCK value (", fmt, "), using 100") ; + hz = 100 ; + } + else hz = (unsigned int)jiffies ; + } + return hz ; +} + +int s6ps_compute_boottime (pscan_t *p, unsigned int mypos) +{ + if (!mypos--) + { + strerr_warnwu1x("compute boot time - using epoch") ; + return 0 ; + } + else + { + unsigned int hz = gethz() ; + tain_t offset = { .sec = { .x = p[mypos].start / hz }, .nano = (p[mypos].start % hz) * (1000000000 / hz) } ; + tain_sub(&boottime, &STAMP, &offset) ; + return 1 ; + } +} + +static int fmt_jiffies (pscan_t *p, unsigned int *pos, unsigned int *len, uint64 j) +{ + unsigned int hz = gethz() ; + uint32 hrs, mins, secs, hfrac ; + if (!stralloc_readyplus(&p->data, UINT64_FMT + 13)) return 0 ; + hfrac = (j % hz) * 100 / hz ; + *pos = p->data.len ; + j /= hz ; + secs = j % 60 ; j /= 60 ; + mins = j % 60 ; j /= 60 ; + hrs = j % 24 ; j /= 24 ; + if (j) + { + p->data.len += uint64_fmt(p->data.s + p->data.len, j) ; + p->data.s[p->data.len++] = 'd' ; + } + if (j || hrs) + { + uint320_fmt(p->data.s + p->data.len, hrs, 2) ; + p->data.len += 2 ; + p->data.s[p->data.len++] = 'h' ; + } + if (j || hrs || mins) + { + uint320_fmt(p->data.s + p->data.len, mins, 2) ; + p->data.len += 2 ; + p->data.s[p->data.len++] = 'm' ; + } + uint320_fmt(p->data.s + p->data.len, secs, 2) ; + p->data.len += 2 ; + if (!j && !hrs && !mins) + { + p->data.s[p->data.len++] = '.' ; + uint320_fmt(p->data.s + p->data.len, hfrac, 2) ; + p->data.len += 2 ; + } + p->data.s[p->data.len++] = 's' ; + *len = p->data.len - *pos ; + return 1 ; +} + +static int fmt_utime (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_jiffies(p, pos, len, p->utime) ; +} + +static int fmt_stime (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_jiffies(p, pos, len, p->stime) ; +} + +static int fmt_cutime (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_jiffies(p, pos, len, p->utime + p->cutime) ; +} + +static int fmt_cstime (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_jiffies(p, pos, len, p->stime + p->cstime) ; +} + +static int fmt_prio (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_i(p, pos, len, p->prio) ; +} + +static int fmt_nice (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_i(p, pos, len, p->nice) ; +} + +static int fmt_threads (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_32(p, pos, len, p->threads) ; +} + +static int fmt_timedate (pscan_t *p, unsigned int *pos, unsigned int *len, struct tm const *tm) +{ + static struct tm nowtm = { .tm_year = 0 } ; + unsigned int tmplen ; + char *tmpstrf = "%F" ; + if (!nowtm.tm_year && !localtm_from_tai(&nowtm, tain_secp(&STAMP), 1)) return 0 ; + if (!stralloc_readyplus(&p->data, 20)) return 0 ; + if (tm->tm_year == nowtm.tm_year && tm->tm_yday == nowtm.tm_yday) + tmpstrf = "%T" ; + else if (tm->tm_year == nowtm.tm_year || (tm->tm_year+1 == nowtm.tm_year && (nowtm.tm_mon + 12 - tm->tm_mon) % 12 < 9)) + tmpstrf = "%b%d %R" ; + tmplen = strftime(p->data.s + p->data.len, 20, tmpstrf, tm) ; + if (!tmplen) return 0 ; + *len = tmplen ; + *pos = p->data.len ; + p->data.len += tmplen ; + return 1 ; +} + +static int fmt_start (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + struct tm starttm ; + unsigned int hz = gethz() ; + tain_t blah = { .sec = { .x = p->start / hz }, .nano = (p->start % hz) * (1000000000 / hz) } ; + tain_add(&blah, &boottime, &blah) ; + if (!localtm_from_tai(&starttm, tain_secp(&blah), 1)) return 0 ; + return fmt_timedate(p, pos, len, &starttm) ; +} + +static unsigned int getpgsz (void) +{ + static unsigned int pgsz = 0 ; + if (!pgsz) + { + long sz = sysconf(_SC_PAGESIZE) ; + if (sz < 1) + { + char fmt[ULONG_FMT + 1] ; + fmt[long_fmt(fmt, sz)] = 0 ; + strerr_warnw3x("invalid _SC_PAGESIZE value (", fmt, "), using 4096") ; + pgsz = 4096 ; + } + else pgsz = sz ; + } + return pgsz ; +} + +static int fmt_vsize (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_64(p, pos, len, p->vsize / 1024) ; +} + +static int fmt_rss (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_64(p, pos, len, p->rss * (getpgsz() / 1024)) ; +} + +static int fmt_rsslim (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_64(p, pos, len, p->rsslim / 1024) ; +} + +static int fmt_cpuno (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_32(p, pos, len, p->cpuno) ; +} + +static int fmt_rtprio (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_32(p, pos, len, p->rtprio) ; +} + +static int fmt_policy (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + static char const *const policies[8] = { "NORMAL", "FIFO", "RR", "BATCH", "ISO", "IDLE", "UNKNOWN", "UNKNOWN" } ; + unsigned int tmppos = p->data.len ; + if (!stralloc_cats(&p->data, policies[p->policy & 7])) return 0 ; + *pos = tmppos ; + *len = p->data.len - tmppos ; + return 1 ; +} + +static int fmt_user (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + unsigned int tmppos = p->data.len ; + if (!s6ps_pwcache_lookup(&p->data, p->uid)) return 0 ; + *pos = tmppos ; + *len = p->data.len - tmppos ; + return 1 ; +} + +static int fmt_group (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + unsigned int tmppos = p->data.len ; + if (!s6ps_grcache_lookup(&p->data, p->gid)) return 0 ; + *pos = tmppos ; + *len = p->data.len - tmppos ; + return 1 ; +} + +static struct sysinfo si = { .totalram = 0, .loads = { 0, 0, 0 } } ; + +static uint64 gettotalmem (void) +{ + uint64 totalmem = 0 ; + if (!si.totalram && (sysinfo(&si) < 0)) return 0 ; + totalmem = si.totalram ; + totalmem *= si.mem_unit ; + return totalmem ; +} + +static int percent (stralloc *sa, unsigned int n, unsigned int *pos, unsigned int *len) +{ + if (!stralloc_readyplus(sa, UINT64_FMT+1)) return 0 ; + *pos = sa->len ; + sa->len += uint64_fmt(sa->s + sa->len, n / 100) ; + sa->s[sa->len++] = '.' ; + uint320_fmt(sa->s + sa->len, (uint32)(n % 100), 2) ; + sa->len += 2 ; + *len = sa->len - *pos ; + return 1 ; +} + +static int fmt_pmem (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + uint64 l = gettotalmem() ; + return l ? percent(&p->data, p->rss * getpgsz() * 10000 / l, pos, len) : 0 ; +} + +static int fmt_wchan (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + unsigned int tmppos = p->data.len ; + if (!s6ps_wchan_lookup(&p->data, p->wchan)) return 0 ; + *len = p->data.len - tmppos ; + *pos = tmppos ; + return 1 ; +} + +static int fmt_args (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + if (!stralloc_readyplus(&p->data, (p->height << 2) + (p->cmdlen ? p->cmdlen : p->commlen + (p->data.s[p->state] == 'Z' ? 11 : 3)))) + return 0 ; + *pos = p->data.len ; + if (p->height) + { + register unsigned int i = 0 ; + for (; i < 4 * (unsigned int)p->height - 3 ; i++) + p->data.s[p->data.len + i] = ' ' ; + byte_copy(p->data.s + p->data.len + 4 * p->height - 3, 3, "\\_ ") ; + p->data.len += p->height << 2 ; + } + if (p->cmdlen) + { + register char const *r = p->data.s + p->statlen + p->commlen ; + register char *w = p->data.s + p->data.len ; + register unsigned int i = p->cmdlen ; + while (i--) + { + register char c = *r++ ; + *w++ = c ? c : ' ' ; + } + p->data.len += p->cmdlen ; + } + else if (p->data.s[p->state] == 'Z') + { + stralloc_catb(&p->data, p->data.s + uint32_fmt(0, p->pid) + 2, p->commlen) ; + stralloc_catb(&p->data, " ", 10) ; + } + else + stralloc_catb(&p->data, p->data.s + uint32_fmt(0, p->pid) + 1, p->commlen+2) ; + *len = p->data.len - *pos ; + return 1 ; +} + +static int fmt_env (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + register unsigned int i = 0 ; + if (!p->envlen) + { + if (!stralloc_catb(&p->data, "*", 1)) return 0 ; + *pos = p->data.len - 1 ; + *len = 1 ; + return 1 ; + } + *pos = p->statlen + p->commlen + p->cmdlen ; + *len = p->envlen ; + for (; i < *len ; i++) + if (!p->data.s[*pos + i]) p->data.s[*pos + i] = ' ' ; + return 1 ; +} + +static uint64 gettotalj (uint64 j) +{ + tain_t totaltime ; + register unsigned int hz = gethz() ; + tain_sub(&totaltime, &STAMP, &boottime) ; + j = totaltime.sec.x * hz + totaltime.nano / (1000000000 / hz) - j ; + if (!j) j = 1 ; + return j ; +} + +static int fmt_pcpu (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return percent(&p->data, 10000 * (p->utime + p->stime) / gettotalj(p->start), pos, len) ; +} + + +static int fmt_ttime (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_jiffies(p, pos, len, p->utime + p->stime) ; +} + +static int fmt_cttime (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return fmt_jiffies(p, pos, len, p->utime + p->stime + p->cutime + p->cstime) ; +} + +static int fmt_tstart (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + unsigned int hz = gethz() ; + tain_t blah = { .sec = { .x = p->start / hz }, .nano = (p->start % hz) * (1000000000 / hz) } ; + if (!stralloc_readyplus(&p->data, TIMESTAMP)) return 0 ; + tain_add(&blah, &boottime, &blah) ; + *pos = p->data.len ; + *len = timestamp_fmt(p->data.s + p->data.len, &blah) ; + p->data.len += *len ; + return 1 ; +} + +static int fmt_cpcpu (pscan_t *p, unsigned int *pos, unsigned int *len) +{ + return percent(&p->data, 10000 * (p->utime + p->stime + p->cutime + p->cstime) / gettotalj(p->start), pos, len) ; +} + +static pfieldfmt_func_t_ref pfieldfmt_table[PFIELD_PHAIL] = +{ + &fmt_pid, + &fmt_comm, + &fmt_s, + &fmt_ppid, + &fmt_pgrp, + &fmt_session, + &fmt_ttynr, + &fmt_tpgid, + &fmt_utime, + &fmt_stime, + &fmt_cutime, + &fmt_cstime, + &fmt_prio, + &fmt_nice, + &fmt_threads, + &fmt_start, + &fmt_vsize, + &fmt_rss, + &fmt_rsslim, + &fmt_cpuno, + &fmt_rtprio, + &fmt_policy, + &fmt_user, + &fmt_group, + &fmt_pmem, + &fmt_wchan, + &fmt_args, + &fmt_env, + &fmt_pcpu, + &fmt_ttime, + &fmt_cttime, + &fmt_tstart, + &fmt_cpcpu +} ; + +pfieldfmt_func_t_ref *s6ps_pfield_fmt = pfieldfmt_table ; diff --git a/src/minutils/s6ps_pwcache.c b/src/minutils/s6ps_pwcache.c new file mode 100644 index 0000000..4c78460 --- /dev/null +++ b/src/minutils/s6ps_pwcache.c @@ -0,0 +1,66 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s6-ps.h" + +static avltree pwcache_tree = AVLTREE_ZERO ; +static genalloc pwcache_index = GENALLOC_ZERO ; + +int s6ps_pwcache_init (void) +{ + avltree_init(&pwcache_tree, 5, 3, 8, &left_dtok, &uint_cmp, &pwcache_index) ; + return 1 ; +} + +void s6ps_pwcache_finish (void) +{ + avltree_free(&pwcache_tree) ; + genalloc_free(diuint, &pwcache_index) ; +} + +int s6ps_pwcache_lookup (stralloc *sa, unsigned int uid) +{ + int wasnull = !satmp.s ; + diuint d = { .left = uid, .right = satmp.len } ; + unsigned int i ; + if (!avltree_search(&pwcache_tree, &d.left, &i)) + { + struct passwd *pw ; + unsigned int n = genalloc_len(diuint, &pwcache_index) ; + errno = 0 ; + pw = getpwuid(uid) ; + if (!pw) + { + if (errno) return 0 ; + if (!stralloc_readyplus(&satmp, UINT_FMT + 2)) return 0 ; + stralloc_catb(&satmp, "(", 1) ; + satmp.len += uint_fmt(satmp.s + satmp.len, uid) ; + stralloc_catb(&satmp, ")", 2) ; + } + else if (!stralloc_cats(&satmp, pw->pw_name) || !stralloc_0(&satmp)) return 0 ; + if (!genalloc_append(diuint, &pwcache_index, &d)) goto err ; + if (!avltree_insert(&pwcache_tree, n)) + { + genalloc_setlen(diuint, &pwcache_index, n) ; + goto err ; + } + i = n ; + } + return stralloc_cats(sa, satmp.s + genalloc_s(diuint, &pwcache_index)[i].right) ; + err: + { + register int e = errno ; + if (wasnull) stralloc_free(&satmp) ; + else satmp.len = d.right ; + errno = e ; + } + return 0 ; +} diff --git a/src/minutils/s6ps_statparse.c b/src/minutils/s6ps_statparse.c new file mode 100644 index 0000000..b5976a1 --- /dev/null +++ b/src/minutils/s6ps_statparse.c @@ -0,0 +1,155 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include "s6-ps.h" + + /* + going to great lengths to avoid scanf(), but all this code + is still smaller than scanf (no floating point parsing etc.) + */ + +#define STATVARS 41 + +typedef unsigned int scanfunc_t (char const *, void *) ; +typedef scanfunc_t *scanfunc_t_ref ; + +static unsigned int f32 (char const *s, void *u32) +{ + uint32 *u = u32 ; + return uint32_scan(s, u) ; +} + +static unsigned int f64 (char const *s, void *u64) +{ + uint64 *u = u64 ; + return uint64_scan(s, u) ; +} + +static unsigned int fint (char const *s, void *i) +{ + int *d = i ; + return int_scan(s, d) ; +} + +static scanfunc_t_ref scanfuncs[STATVARS] = +{ + &f32, /* ppid */ + &f32, /* pgrp */ + &f32, /* session */ + &f32, /* tty_nr */ + &fint, /* tpgid */ + &f32, /* flags */ + &f32, /* minflt */ + &f32, /* cminflt */ + &f32, /* majflt */ + &f32, /* cmajflt */ + &f64, /* utime */ + &f64, /* stime */ + &f64, /* cutime */ + &f64, /* cstime */ + &fint, /* priority */ + &fint, /* nice */ + &f32, /* num_threads */ + &f32, /* itrealvalue */ + &f64, /* starttime */ + &f64, /* vsize */ + &f64, /* rss */ + &f64, /* rsslim */ + &f64, /* startcode */ + &f64, /* endcode */ + &f64, /* startstack */ + &f64, /* kstkesp */ + &f64, /* kstkeip */ + &f32, /* signal */ + &f32, /* blocked */ + &f32, /* sigignore */ + &f32, /* sigcatch */ + &f64, /* wchan */ + &f32, /* nswap */ + &f32, /* cnswap */ + &f32, /* exit_signal */ + &f32, /* processor */ + &f32, /* rt_priority */ + &f32, /* policy */ + &f64, /* delayacct_blkio_ticks */ + &f32, /* guest_time */ + &f32 /* cguest_time */ +} ; + +int s6ps_statparse (pscan_t *p) +{ + uint64 dummy64 ; + uint32 dummy32 ; + unsigned int pos = 0 ; + void *scanresults[STATVARS] = + { + &p->ppid, + &p->pgrp, + &p->session, + &p->ttynr, + &p->tpgid, + &dummy32, + &dummy32, + &dummy32, + &dummy32, + &dummy32, + &p->utime, + &p->stime, + &p->cutime, + &p->cstime, + &p->prio, + &p->nice, + &p->threads, + &dummy32, + &p->start, + &p->vsize, + &p->rss, + &p->rsslim, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy32, + &dummy32, + &dummy32, + &dummy32, + &p->wchan, + &dummy32, + &dummy32, + &dummy32, + &p->cpuno, + &p->rtprio, + &p->policy, + &dummy64, + &dummy32, + &dummy32 + } ; + register unsigned int i = 0 ; + + if (!p->statlen) return 0 ; + pos = uint32_scan(p->data.s, &dummy32) ; + if (!pos) return 0 ; + if (dummy32 != p->pid) return 0 ; + if (pos + 5 + p->commlen > p->statlen) return 0 ; + if (p->data.s[pos++] != ' ') return 0 ; + if (p->data.s[pos++] != '(') return 0 ; + pos += p->commlen ; + if (p->data.s[pos++] != ')') return 0 ; + if (p->data.s[pos++] != ' ') return 0 ; + p->state = pos++ ; + for (; i < STATVARS ; i++) + { + unsigned int w ; + if (pos + 1 > p->statlen) return 0 ; + if (p->data.s[pos++] != ' ') return 0 ; + w = (*scanfuncs[i])(p->data.s + pos, scanresults[i]) ; + if (!w) return 0 ; pos += w ; + } + return 1 ; +} diff --git a/src/minutils/s6ps_ttycache.c b/src/minutils/s6ps_ttycache.c new file mode 100644 index 0000000..c50c3ea --- /dev/null +++ b/src/minutils/s6ps_ttycache.c @@ -0,0 +1,153 @@ +/* ISC license. */ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s6-ps.h" + +static avltree ttycache_tree = AVLTREE_ZERO ; +static genalloc ttycache_index = GENALLOC_ZERO ; + +static void *left32_dtok (unsigned int d, void *x) +{ + return (void *)&genalloc_s(diuint32, (genalloc *)x)[d].left ; +} + +static int uint32_cmp (void const *a, void const *b, void *x) +{ + register uint32 aa = *(uint32 *)a ; + register uint32 bb = *(uint32 *)b ; + (void)x ; + return (aa < bb) ? -1 : (aa > bb) ; +} + +int s6ps_ttycache_init (void) +{ + avltree_init(&ttycache_tree, 5, 3, 8, &left32_dtok, &uint32_cmp, &ttycache_index) ; + return 1 ; +} + +void s6ps_ttycache_finish (void) +{ + avltree_free(&ttycache_tree) ; + genalloc_free(diuint, &ttycache_index) ; +} + +static int check (char const *s, uint32 ttynr) +{ + struct stat st ; + if (stat(s, &st) < 0) return 0 ; + return S_ISCHR(st.st_mode) && (st.st_rdev == ttynr) ; +} + + + /* No blind scanning of all /dev or /sys/devices, kthx */ + +static int ttyguess (stralloc *sa, uint32 ttynr) +{ + unsigned int maj = major(ttynr), min = minor(ttynr) ; + + /* Try /dev/tty? and /dev/pts/? */ + if (maj == 4 && min < 64) + { + char tmp[11] = "/dev/tty" ; + tmp[uint_fmt(tmp+8, min)] = 0 ; + if (check(tmp, ttynr)) return stralloc_cats(sa, tmp+5) && stralloc_0(sa) ; + } + else if (maj >= 136 && maj < 144) + { + char tmp[9 + UINT_FMT] = "/dev/pts/" ; + register unsigned int n = ((maj - 136) << 20) | min ; + tmp[9 + uint_fmt(tmp+9, n)] = 0 ; + if (check(tmp, ttynr)) return stralloc_cats(sa, tmp+5) && stralloc_0(sa) ; + } + + /* Use /sys/dev/char/maj:min if it exists */ + { + int fd ; + char path[23 + 2 * UINT_FMT] = "/sys/dev/char/" ; + register unsigned int pos = 14 ; + pos += uint_fmt(path + pos, maj) ; + path[pos++] = ':' ; + pos += uint_fmt(path + pos, min) ; + byte_copy(path + pos, 8, "/uevent") ; + fd = open_read(path) ; + if (fd >= 0) + { + char buf[4097] ; + buffer b = BUFFER_INIT(&buffer_read, fd, buf, 4097) ; + unsigned int start = satmp.len ; + register int r ; + for (;;) + { + satmp.len = start ; + r = skagetln(&b, &satmp, '\n') ; + if (r <= 0) break ; + if ((satmp.len - start) > 8 && !byte_diff(satmp.s + start, 8, "DEVNAME=")) break ; + } + fd_close(fd) ; + if (r > 0) + { + satmp.s[satmp.len - 1] = 0 ; + satmp.len = start ; + byte_copy(satmp.s + start + 3, 5, "/dev/") ; + if (check(satmp.s + start + 3, ttynr)) + return stralloc_cats(sa, satmp.s + start + 8) && stralloc_0(sa) ; + } + } + } + + /* Fallback: print explicit maj:min */ + { + char tmp[3 + 2 * UINT_FMT] = "(" ; + register unsigned int pos = 1 ; + pos += uint_fmt(tmp + pos, maj) ; + tmp[pos++] = ':' ; + pos += uint_fmt(tmp + pos, min) ; + tmp[pos++] = ')' ; + tmp[pos++] = 0 ; + return stralloc_catb(sa, tmp, pos) ; + } +} + +int s6ps_ttycache_lookup (stralloc *sa, uint32 ttynr) +{ + int wasnull = !satmp.s ; + diuint32 d = { .left = ttynr, .right = satmp.len } ; + unsigned int i ; + if (!avltree_search(&ttycache_tree, &d.left, &i)) + { + unsigned int n = genalloc_len(diuint32, &ttycache_index) ; + if (!ttyguess(&satmp, ttynr)) return 0 ; + if (!genalloc_append(diuint32, &ttycache_index, &d)) goto err ; + if (!avltree_insert(&ttycache_tree, n)) + { + genalloc_setlen(diuint32, &ttycache_index, n) ; + goto err ; + } + i = n ; + } + return stralloc_cats(sa, satmp.s + genalloc_s(diuint32, &ttycache_index)[i].right) ; + err: + { + register int e = errno ; + if (wasnull) stralloc_free(&satmp) ; + else satmp.len = d.right ; + errno = e ; + } + return 0 ; +} diff --git a/src/minutils/s6ps_wchan.c b/src/minutils/s6ps_wchan.c new file mode 100644 index 0000000..e702ba9 --- /dev/null +++ b/src/minutils/s6ps_wchan.c @@ -0,0 +1,96 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include "s6-ps.h" + +static stralloc sysmap = STRALLOC_ZERO ; +static genalloc ind = GENALLOC_ZERO ; + +int s6ps_wchan_init (char const *file) +{ + if (file) + { + if (!openslurpclose(&sysmap, file)) return 0 ; + } + else + { + char *files[3] = { "/proc/kallsyms", 0, "/boot/System.map" } ; + struct utsname uts ; + unsigned int n ; + if (uname(&uts) < 0) return 0 ; + n = str_len(uts.release) ; + { + char buf[18 + n] ; + register unsigned int i = 0 ; + byte_copy(buf, 16, "/boot/System.map") ; + buf[16] = '-' ; + byte_copy(buf + 17, n + 1, uts.release) ; + files[1] = buf ; + for (; i < 3 ; i++) + if (openslurpclose(&sysmap, files[i])) break ; + if (i >= 3) return 0 ; + } + } + { + unsigned int i = 0 ; + if (!genalloc_append(unsigned int, &ind, &i)) goto err2 ; + for (i = 1 ; i <= sysmap.len ; i++) + if (sysmap.s[i-1] == '\n') + if (!genalloc_append(unsigned int, &ind, &i)) goto err ; + } + return 1 ; + err: + genalloc_free(unsigned int, &ind) ; + err2: + stralloc_free(&sysmap) ; + return 0 ; +} + +void s6ps_wchan_finish (void) +{ + genalloc_free(unsigned int, &ind) ; + stralloc_free(&sysmap) ; +} + +static inline unsigned int lookup (uint64 addr, unsigned int *i) +{ + unsigned int low = 0, mid, high = genalloc_len(unsigned int, &ind), len ; + for (;;) + { + uint64 cur ; + mid = (low + high) >> 1 ; + len = uint64_xscan(sysmap.s + genalloc_s(unsigned int, &ind)[mid], &cur) ; + if (!len) return 0 ; + if (cur == addr) break ; + if (mid == low) return 0 ; + if (addr < cur) high = mid ; else low = mid ; + } + *i = mid ; + return len ; +} + +int s6ps_wchan_lookup (stralloc *sa, uint64 addr) +{ + if (addr == (sizeof(void *) == 8 ? 0xffffffffffffffffULL : 0xffffffffUL)) + return stralloc_catb(sa, "*", 1) ; + if (!addr) return stralloc_catb(sa, "-", 1) ; + if (sysmap.len) + { + unsigned int i ; + unsigned int len = lookup(addr, &i) ; + register unsigned int pos ; + if (!len) return stralloc_catb(sa, "?", 1) ; + pos = genalloc_s(unsigned int, &ind)[i] + len + 3 ; + return stralloc_catb(sa, sysmap.s + pos, genalloc_s(unsigned int, &ind)[i+1] - 1 - pos) ; + } + if (!stralloc_readyplus(sa, UINT64_FMT + 3)) return 0 ; + stralloc_catb(sa, "(0x", 3) ; + sa->len += uint64_fmt(sa->s + sa->len, addr) ; + stralloc_catb(sa, ")", 1) ; + return 1 ; +} diff --git a/tools/gen-deps.sh b/tools/gen-deps.sh new file mode 100755 index 0000000..af31259 --- /dev/null +++ b/tools/gen-deps.sh @@ -0,0 +1,79 @@ +#!/bin/sh -e + +. package/info + +echo '#' +echo '# This file has been generated by tools/gen-deps.sh' +echo '#' +echo + +for dir in src/include/${package} src/* ; do + for file in $(ls -1 $dir | grep -- \\.h$) ; do + { + grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ; + grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2 + } | sort -u | { + deps= + while read dep ; do + if echo $dep | grep -q "^${package}/" ; then + deps="$deps src/include/$dep" + elif test -f "${dir}/$dep" ; then + deps="$deps ${dir}/$dep" + else + deps="$deps src/include-local/$dep" + fi + done + if test -n "$deps" ; then + echo "${dir}/${file}:${deps}" + fi + } + done +done + +for dir in src/* ; do + for file in $(ls -1 $dir | grep -- \\.c$) ; do + { + grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ; + grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2 + } | sort -u | { + deps=" ${dir}/$file" + while read dep ; do + if echo $dep | grep -q "^${package}/" ; then + deps="$deps src/include/$dep" + elif test -f "${dir}/$dep" ; then + deps="$deps ${dir}/$dep" + else + deps="$deps src/include-local/$dep" + fi + done + o=$(echo $file | sed s/\\.c$/.o/) + lo=$(echo $file | sed s/\\.c$/.lo/) + echo "${dir}/${o} ${dir}/${lo}:${deps}" + } + done +done +echo + +for dir in $(ls -1 src | grep -v ^include) ; do + for file in $(ls -1 src/$dir/deps-lib) ; do + deps= + while read dep ; do + deps="$deps src/$dir/$dep" + done < src/$dir/deps-lib/$file + echo "lib$file.a: $deps" + if test -x "src/$dir/deps-lib/$file" ; then + echo "lib${file}.so: $(echo "$deps" | sed 's/\.o/.lo/g')" + fi + done + + for file in $(ls -1 src/$dir/deps-exe) ; do + deps= + while read dep ; do + if echo $dep | grep -q -- \\.o$ ; then + dep="src/$dir/$dep" + fi + deps="$deps $dep" + done < src/$dir/deps-exe/$file + echo "$file: src/$dir/$file.o$deps" + done +done diff --git a/tools/install.sh b/tools/install.sh new file mode 100755 index 0000000..89f9428 --- /dev/null +++ b/tools/install.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +usage() { + echo "usage: $0 [-D] [-l] [-m mode] src dst" 1>&2 + exit 1 +} + +mkdirp=false +symlink=false +mode=0755 + +while getopts Dlm: name ; do + case "$name" in + D) mkdirp=true ;; + l) symlink=true ;; + m) mode=$OPTARG ;; + ?) usage ;; + esac +done +shift $(($OPTIND - 1)) + +test "$#" -eq 2 || usage +src=$1 +dst=$2 +tmp="$dst.tmp.$$" + +case "$dst" in + */) echo "$0: $dst ends in /" 1>&2 ; exit 1 ;; +esac + +set -C +set -e + +if $mkdirp ; then + umask 022 + case "$2" in + */*) mkdir -p "${dst%/*}" ;; + esac +fi + +trap 'rm -f "$tmp"' EXIT INT QUIT TERM HUP + +umask 077 + +if $symlink ; then + ln -s "$src" "$tmp" +else + cat < "$1" > "$tmp" + chmod "$mode" "$tmp" +fi + +mv -f "$tmp" "$dst" +if test -d "$dst" ; then + rm -f "$dst/$(basename $tmp)" + if $symlink ; then + mkdir "$tmp" + ln -s "$src" "$tmp/$(basename $dst)" + mv -f "$tmp/$(basename $dst)" "${dst%/*}" + rmdir "$tmp" + else + echo "$0: $dst is a directory" 1>&2 + exit 1 + fi +fi -- cgit v1.2.3