diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2014-09-19 15:01:58 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2014-09-19 15:01:58 +0000 |
commit | 54b6467013bfbdb3ee606961c02fbff1271ca582 (patch) | |
tree | e3969743e61542979f12fca01017e67c4480eca8 | |
download | s6-linux-utils-54b6467013bfbdb3ee606961c02fbff1271ca582.tar.xz |
initial commit
73 files changed, 4749 insertions, 0 deletions
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.* @@ -0,0 +1,6 @@ +Main author: + Laurent Bercot <ska-skaware@skarnet.org> + +Thanks to: + Dan J. Bernstein <djb@cr.yp.to> + Jorge Almeida <jalmeida@math.ist.utl.pt> @@ -0,0 +1,13 @@ +Copyright (c) 2011-2014 Laurent Bercot <ska-skaware@skarnet.org> + +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. @@ -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/$(<F) $@ + +$(DESTDIR)$(sproot)/library.so/lib%.so: $(DESTDIR)$(dynlibdir)/lib%.so + exec $(INSTALL) -D -l ..$(exthome)/library.so/$(<F) $@ + +.PHONY: update global-links + +endif + +$(DESTDIR)$(dynlibdir)/lib%.so: lib%.so + $(INSTALL) -D -m 755 $< $@.$(version) && \ + $(INSTALL) -l $<.$(version) $@.$(version_m) && \ + $(INSTALL) -l $<.$(version_m) $@.$(version_M) && \ + $(INSTALL) -l $<.$(version_M) $@.$(version_l) && \ + exec $(INSTALL) -l $<.$(version_l) $@ + +$(DESTDIR)$(libexecdir)/% $(DESTDIR)$(bindir)/% $(DESTDIR)$(sbindir)/%: % package/modes + exec $(INSTALL) -D -m 600 $< $@ + grep -- ^$(@F) < package/modes | { read name mode owner && \ + if [ x$$owner != x ] ; then chown -- $$owner $@ ; fi && \ + chmod $$mode $@ ; } + +$(DESTDIR)$(libdir)/lib%.a: lib%.a + $(INSTALL) -D -m 644 $< $@ + +$(DESTDIR)$(includedir)/$(package)/%.h: src/include/$(package)/%.h + exec $(INSTALL) -D -m 644 $< $@ + +%.o: %.c + exec $(REALCC) $(CPPFLAGS_ALL) $(CFLAGS_ALL) -c -o $@ $< + +%.lo: %.c + exec $(REALCC) $(CPPFLAGS_ALL) $(CFLAGS_ALL) $(CFLAGS_SHARED) -c -o $@ $< + +$(ALL_BINS): + exec $(REALCC) -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL) $(LDFLAGS_NOSHARED) $^ $(LDLIBS_ALL) + +lib%.a: + exec $(AR) rc $@ $^ + exec $(RANLIB) $@ + +lib%.so: + exec $(REALCC) -o $@ $(CFLAGS_ALL) $(CFLAGS_SHARED) $(LDFLAGS_ALL) $(LDFLAGS_SHARED) -Wl,-soname,$@.$(version_l) $^ + +.PHONY: it all clean distclean tgz strip install install-dynlib install-bin install-sbin install-lib install-include + +.DELETE_ON_ERROR: @@ -0,0 +1,30 @@ +s6-linux-utils - tiny Linux-specific utilities +----------------------------------------------- + +s6-linux-utils is a set of tiny Linux-specific utilities, +often performing well-known tasks such as mount and swapon + but optimized for simplicity and small size. They were +designed for embedded systems and other constrained +environments, but they work on any Linux system. + + Of particular interest in this package are the following +programs: + - s6-devd, a minimal udevd implementation + - s6-ps, an independent ps implementation + + See http://skarnet.org/software/s6-linux-utils/ for details. + + +* Installation + ------------ + + See the INSTALL file. + + +* Contact information + ------------------- + + Laurent Bercot <ska-skaware at skarnet.org> + + Please use the <skaware at list.skarnet.org> 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 <<EOF +Usage: $0 [OPTION]... [VAR=VALUE]... [TARGET] + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +System types: + --target=TARGET configure to run on target TARGET [detected] + --host=TARGET same as --target + +Installation directories: + --prefix=PREFIX main installation prefix [/] + --exec-prefix=EPREFIX installation prefix for executable files [PREFIX] + +Fine tuning of the installation directories: + --dynlibdir=DIR shared library files [PREFIX/lib] + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR admin executables [EPREFIX/sbin] + --libexecdir=DIR package-scoped executables [EPREFIX/libexec] + --libdir=DIR static library files [PREFIX/lib] + --includedir=DIR include files for the C compiler [PREFIX/include] + +Dependencies: + --with-sysdeps=DIR use sysdeps in DIR [/usr/lib/skalibs/sysdeps] + --with-include=DIR add DIR to the list of searched directories for headers + --with-lib=DIR add DIR to the list of searched directories for static libraries + --with-dynlib=DIR add DIR to the list of searched directories for shared libraries + +Optional features: + --enable-shared build shared libraries [disabled] + --disable-static do not build static libraries [enabled] + --disable-allstatic do not prefer linking against static libraries [enabled] + --enable-static-libc make entirely static binaries [disabled] + --enable-slashpackage[=ROOT] assume /package installation at ROOT [disabled] + --enable-cross=PREFIX prefix toolchain executable names with PREFIX [none] + +EOF +exit 0 +} + + +# Helper functions + +# If your system does not have printf, you can comment this, but it is +# generally not a good idea to use echo. +# See http://www.etalabs.net/sh_tricks.html +echo () { + IFS=" " + printf %s\\n "$*" +} + +quote () { + tr '\n' ' ' <<EOF | grep '^[-[:alnum:]_=,./:]* $' >/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 <<EOF +/* ISC license. */ + +/* Generated by: $cmdline */ + +#ifndef ${package_macro_name}_CONFIG_H +#define ${package_macro_name}_CONFIG_H + +#define ${package_macro_name}_VERSION "$version" +EOF +if $slashpackage ; then + echo "#define ${package_macro_name}_BINPREFIX \"$binprefix\"" + echo "#define ${package_macro_name}_EXTBINPREFIX \"$extbinprefix\"" + echo "#define ${package_macro_name}_LIBEXECPREFIX \"$binprefix\"" +else + echo "#define ${package_macro_name}_BINPREFIX \"\"" + echo "#define ${package_macro_name}_EXTBINPREFIX \"\"" + echo "#define ${package_macro_name}_LIBEXECPREFIX \"$libexecdir\"" +fi +echo +echo "#endif" +exec 1>&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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils - skarnet's tiny Linux-specific utilities</title> + <meta name="Description" content="s6-linux-utils - skarnet's tiny Linux-specific utilities" /> + <meta name="Keywords" content="s6 unix administration root laurent bercot ska skarnet linux utilities tiny linux-specific" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> s6-linux-utils </h1> + +<h2> What is it ? </h2> + +<p> + s6-linux-utils is a set of minimalistic Linux-specific system utilities. +</p> + +<hr /> + +<h2> Installation </h2> + +<h3> Requirements </h3> + +<ul> + <li> A POSIX-compliant system with a standard C development environment </li> + <li> GNU make, version 3.81 or later </li> + <li> <a href="http://skarnet.org/software/skalibs/">skalibs</a> version +2.0.0.0 or later </li> +</ul> + +<h3> Licensing </h3> + +<p> + s6-linux-utils is free software. It is available under the +<a href="http://opensource.org/licenses/ISC">ISC license</a>. +</p> + +<h3> Download </h3> + +<ul> + <li> The current released version of s6-linux-utils is <a href="s6-linux-utils-2.0.0.0.tar.gz">2.0.0.0</a>. </li> + <li> Alternatively, you can checkout a copy of the s6-linux-utils git repository: +<pre> git clone git://git.skarnet.org/s6-linux-utils </pre> </li> +</ul> + +<h3> Compilation </h3> + +<ul> + <li> See the enclosed INSTALL file for installation details. </li> +</ul> + +<h3> Upgrade notes </h3> + +<ul> + <li> <a href="upgrade.html">This page</a> lists the differences to be aware of between +the previous versions of s6-linux-utils and the current one. </li> +</ul> + +<hr /> + +<h2> Reference </h2> + +<h3> Commands </h3> + +<p> + All these commands exit 111 if they encounter a temporary error, and +100 if they encounter a permanent error - such as a misuse. +</p> + +<ul> +<li><a href="s6-chroot.html">The <tt>s6-chroot</tt> program</a></li> +<li><a href="s6-devd.html">The <tt>s6-devd</tt> program</a></li> +<li><a href="s6-freeramdisk.html">The <tt>s6-freeramdisk</tt> program</a></li> +<li><a href="s6-halt.html">The <tt>s6-halt</tt> program</a></li> +<li><a href="s6-hiercopy.html">The <tt>s6-hiercopy</tt> program</a></li> +<li><a href="s6-hostname.html">The <tt>s6-hostname</tt> program</a></li> +<li><a href="s6-logwatch.html">The <tt>s6-logwatch</tt> program</a></li> +<li><a href="s6-mount.html">The <tt>s6-mount</tt> program</a></li> +<li><a href="s6-pivotchroot.html">The <tt>s6-pivotchroot</tt> program</a></li> +<li><a href="s6-poweroff.html">The <tt>s6-poweroff</tt> program</a></li> +<li><a href="s6-ps.html">The <tt>s6-ps</tt> program</a></li> +<li><a href="s6-reboot.html">The <tt>s6-reboot</tt> program</a></li> +<li><a href="s6-swapoff.html">The <tt>s6-swapoff</tt> program</a></li> +<li><a href="s6-swapon.html">The <tt>s6-swapon</tt> program</a></li> +<li><a href="s6-umount.html">The <tt>s6-umount</tt> program</a></li> +</ul> + +<h2> Related resources </h2> + +<ul> + <li> <tt>s6-linux-utils</tt> is discussed on the +<a href="http://skarnet.org/lists.html#skaware">skaware</a> mailing-list. </li> +</ul> + +<h2> Similar work </h2> + +<p> + There are several good projects aiming to provide a minimal userspace +environment for Linux, suitable for embedded systems. Among them, for +instance: +</p> + +<ul> + <li> <a href="http://busybox.net/">BusyBox</a> </li> + <li> <a href="http://landley.net/code/toybox/">toybox</a> </li> +</ul> + +<p> + 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 +<a href="http://skarnet.org/software/s6-portable-utils/">s6-portable-utils</a>, +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. +</p> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-chroot program</title> + <meta name="Description" content="s6-linux-utils: the s6-chroot program" /> + <meta name="Keywords" content="s6-linux-utils linux utilities chroot" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-chroot</tt> program </h1> + +<p> +<tt>s6-chroot</tt> executes a program in a chroot jail. +</p> + +<h2> Interface </h2> + +<pre> + s6-chroot <em>newroot</em> <em>prog...</em> +</pre> + +<ul> + <li> <tt>s6-chroot</tt> sets the root filesystem for the current +process to <em>newroot</em>, which must be an existing directory. </li> + <li> <tt>s6-chroot</tt> changes directory to this new root, and +executes <em>prog</em> +with its arguments. <em>prog</em> is searched under the new root. </li> + <li> s6-chroot's parent process is unaffected by the root change. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-devd program</title> + <meta name="Description" content="s6-linux-utils: the s6-devd program" /> + <meta name="Keywords" content="s6 linux administration root utilities devd mdev udev" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-devd</tt> program </h1> + +<p> +<tt>s6-devd</tt> listens to the netlink interface for udev events, and +launches a helper program for every event, similarly to what the hotplug +interface does. +</p> + +<h2> Interface </h2> + +<pre> + s6-devd [ -q | -v ] [ -b kbufsz ] [ -t l:t:k ] <em>prog...</em> +</pre> + +<ul> + <li> s6-devd binds to the netlink interface and listens for +hotplug events, as the <em>udev</em> program does. </li> + <li> For every event it receives, it spawns <em>prog...</em> with +the event variables added to the environment, just as if <em>prog...</em> +had been registered in <tt>/proc/sys/kernel/hotplug</tt>. </li> + <li> However, unlike the kernel, s6-devd spawns the <em>prog...</em> helpers +sequentially: it waits for an instance to finish before spawning another one. </li> + <li> 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. </li> +</ul> + +<h2> Options </h2> + +<ul> + <li> <tt>-q</tt> : be more quiet. </li> + <li> <tt>-v</tt> : be more verbose. </li> + <li> <tt>-b</tt> <em>kbufsz</em> : try and reserve a kernel buffer of +<em>kbufsz</em> bytes for the netlink queue. Too large a buffer wastes kernel memory; +too small a buffer risks losing events. The default is 65536. </li> + <li> <tt>-t</tt> <em>l:t:k</em> : If <em>l</em>, <em>t</em> or <em>k</em> is +specified, they specify timeouts; by default, they are infinite. +If <em>prog...</em> is still alive after <em>l</em> milliseconds, s6-devd sends +it a SIGTERM. Then, if <em>prog...</em> is still alive after <em>t</em> more +milliseconds, s6-devd sends it a SIGKILL. Then, if <em>prog...</em> is still +alive after <em>k</em> more milliseconds, s6-devd yells and exits 99. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> s6-devd is a daemon; it should be run under a proper supervision system such +as <a href="http://skarnet.org/software/s6/">s6</a>. (That is why it does not +fork and logs to stderr.) </li> + <li> The <em>prog...</em> helper, on the other hand, should be very short-lived, +even if you are not using the <tt>-t</tt> option to s6-devd. Since helpers are +spawned sequentially, slow helpers can make events queue up and fill the netlink +kernel buffer. </li> + <li> If you are using <a href="http://busybox.net/">busybox</a> and want a +minimal udev-style dynamic <tt>/dev</tt> +handling, <tt>/sbin/mdev</tt> is a suitable <em>prog...</em> helper. </li> + <li> 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, <tt>/proc/sys/kernel/hotplug</tt> should be empty. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-freeramdisk program</title> + <meta name="Description" content="s6-linux-utils: the s6-freeramdisk program" /> + <meta name="Keywords" content="s6 linux administration root utilities freeramdisk" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-freeramdisk</tt> program </h1> + +<p> +<tt>freeramdisk</tt> frees the memory occupied by a RAM disk. Call it +when your RAM disk is not in use anymore. +</p> + +<h2> Interface </h2> + +<pre> + s6-freeramdisk <em>ramdisk-device</em> +</pre> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-halt program</title> + <meta name="Description" content="s6-linux-utils: the s6-halt program" /> + <meta name="Keywords" content="s6 linux administration root utilities halt" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-halt</tt> program </h1> + +<p> +<tt>s6-halt</tt> syncs the filesystems and halts the machine +immediately, without switching the power off. +<br /> This is different from the sysvinit <tt>halt</tt> +command, which is an alias for <tt>shutdown -h</tt>. The +s6-linux-utils <tt>s6-halt</tt> command is more or less equivalent to +sysvinit's <tt>halt -f</tt>. +</p> + +<h2> Interface </h2> + +<pre> + s6-halt +</pre> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-hiercopy program</title> + <meta name="Description" content="s6-linux-utils: the s6-hiercopy program" /> + <meta name="Keywords" content="s6 linux administration root utilities hiercopy cp -a" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-hiercopy</tt> program </h1> + +<p> +<tt>s6-hiercopy</tt> copies a directory structure recursively. +</p> + +<h2> Interface </h2> + +<pre> + s6-hiercopy <em>source</em> <em>destination</em> +</pre> + +<ul> + <li> <tt>s6-hiercopy</tt> recursively copies <em>source</em> to +<em>destination</em>, which is created if it doesn't exist. +The permissions are preserved. The owner and group are preserved +if the user is the superuser. </li> + <li> It exits 0 on success and 111 on temporary failure. </li> +</ul> + +<h2> Notes </h2> + +<p> + Copying files and browsing through directories is one of Unix's +weakest points, and <tt>s6-hiercopy</tt> 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 <tt>cp</tt> utility, +portable and reliable and featureful and everything - while needing +approximately a hundred times less resources than GNU <tt>cp</tt> does, +of course. But I eventually dropped the idea: it's just impossible to +design, much less write, such a utility. +</p> + +<ul> + <li> 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 <tt>s6-hiercopy</tt> appears here instead of in +<a href="http://skarnet.org/software/s6-portable-utils/">s6-portable-utils</a>: +the platform where I needed that kind of tool is Linux. </li> + <li> 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, <tt>s6-hiercopy</tt> +is a walking race condition and should <strong>absolutely not</strong> +be considered instant when used in a multitasking environment. +But then, <tt>cp -a</tt> shouldn't either. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-hostname program</title> + <meta name="Description" content="s6-linux-utils: the s6-hostname program" /> + <meta name="Keywords" content="s6-linux-utils linux utilities hostname" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-hostname</tt> program </h1> + +<p> +<tt>s6-hostname</tt> gets or sets the machine hostname. +</p> + +<h2> Interface </h2> + +<pre> + s6-hostname [ <em>name</em> ] +</pre> + +<ul> + <li> When called without an argument, <tt>s6-hostname</tt> prints the +machine hostname to stdout. </li> + <li> When called with an argument, <tt>s6-hostname</tt> sets the machine +hostname to <em>name</em>. </li> + <li> Then it exits 0. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-logwatch program</title> + <meta name="Description" content="s6-linux-utils: the s6-logwatch program" /> + <meta name="Keywords" content="s6-linux-utils linux utilities log s6-log logwatch" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-logwatch</tt> program </h1> + +<p> +<tt>s6-logwatch</tt> watches the <tt>current</tt> file of a logdir, printing it +in real time. +</p> + +<h2> Interface </h2> + +<pre> + s6-logwatch [ -m <em>buflen</em> ] <em>logdir</em> +</pre> + +<ul> + <li> s6-logwatch prints <em>logdir</em><tt>/current</tt> and watches +the file. </li> + <li> <em>logdir</em> must be managed by a +<a href="http://skarnet.org/software/s6/s6-log.html">s6-log</a> instance. </li> + <li> When new logs are appended to the <tt>current</tt> file, s6-logwatch prints +them in real-time to stdout. </li> + <li> When a rotation happens, s6-logwatch notices, and keeps watching the +new <tt>current</tt> file. </li> + <li> s6-logwatch runs forever until killed. </li> +</ul> + +<h2> Options </h2> + +<ul> + <li> <tt>-m</tt> <em>buflen</em> : accumulate at most <em>buflen</em> +bytes into the stdout buffer before flushing it. By default, <em>buflen</em> is +4000. </li> +</ul> + +<h2> Bugs </h2> + +<ul> + <li> 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 <em>logdir</em> is in and will exit 101 with an error message. </li> + <li> 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. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> s6-logwatch is Linux-specific because it uses the +<a href="http://inotify.aiken.cz/">inotify</a> interface. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-mount program</title> + <meta name="Description" content="s6-linux-utils: the s6-mount program" /> + <meta name="Keywords" content="s6 linux administration root utilities mount" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-mount</tt> program </h1> + +<p> +<tt>s6-mount</tt> mounts filesystems. +</p> + +<h2> Interface </h2> + +<pre> + s6-mount -a + s6-mount [ -r | -w ] [ -t fstype ] [ -o option[,option...] ] <em>device</em> <em>mntpoint</em> +</pre> + +<ul> + <li> <tt>s6-mount -a</tt> mounts all partitions according to <tt>/etc/fstab</tt>. </li> + <li> If the <tt>-a</tt> option is not given, +<tt>s6-mount</tt> mounts <em>device</em> on <em>mntpoint</em>. </li> + <li> <tt>s6-mount</tt> does not touch <tt>/etc/mtab</tt>. </li> +</ul> + +<h2> Options </h2> + +<ul> + <li> <tt>-a</tt> : read <tt>/etc/fstab</tt> instead of other arguments </li> + <li> <tt>-t</tt> <em>fstype</em> : filesystem is of type <em>fstype</em>. Default: ext2. </li> + <li> <tt>-r</tt> : mount read-only </li> + <li> <tt>-w</tt> : mount read-write (default) </li> + <li> <tt>-o</tt> <em>option</em> : mount with option <em>option</em>. +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. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-pivotchroot program</title> + <meta name="Description" content="s6-linux-utils: the pivotchroot program" /> + <meta name="Keywords" content="s6 linux administration root utilities pivot_root chroot pivotchroot" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-pivotchroot</tt> program </h1> + +<p> +<tt>s6-pivotchroot</tt> performs a <em>pivot_root</em> system call, +then changes root to the new root filesystem and executes a program. +</p> + +<h2> Interface </h2> + +<pre> + s6-pivotchroot <em>newroot</em> <em>putold</em> <em>prog...</em> +</pre> + +<ul> + <li> <tt>s6-pivotchroot</tt> sets the root filesystem to <em>newroot</em>, +which must be a mounted filesystem entry point. The old root filesystem +will be available under <em>putold</em> (relative to the old root). </li> + <li> <tt>s6-pivotchroot</tt> changes directory to the new root, performs +a <em>chroot</em> system call to the new root, and executes <em>prog</em> +with its arguments. <em>prog</em> is searched under the new root. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-poweroff program</title> + <meta name="Description" content="s6-linux-utils: the s6-poweroff program" /> + <meta name="Keywords" content="s6 linux administration root poweroff system halt power off" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-poweroff</tt> program </h1> + +<p> +<tt>s6-poweroff</tt> syncs the filesystems and halts the machine +immediately, switching the power off if possible. +</p> + +<p> + This is different from the sysvinit <tt>poweroff</tt> +command, which performs <tt>shutdown</tt>. The +s6-linux-utils <tt>s6-poweroff</tt> command is more or less equivalent to +sysvinit's <tt>poweroff -f</tt>. +</p> + +<h2> Interface </h2> + +<pre> + s6-poweroff +</pre> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-ps program</title> + <meta name="Description" content="s6-linux-utils: the s6-ps program" /> + <meta name="Keywords" content="s6 linux administration root utilities ps proc procps process list" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-ps</tt> program </h1> + +<p> + <tt>s6-ps</tt> shows a list of processes on the system. +</p> + +<h2> Interface </h2> + +<pre> + s6-ps [ -H ] [ -w spacing ] [ -W chanfile ] [ -l | -o <em>field</em>,<em>field</em>,... ] +</pre> + +<ul> + <li> s6-ps assumes the <em>proc</em> filesystem is mounted on <tt>/proc</tt>. +It scans <tt>/proc</tt> for a list of all processes on the system. </li> + <li> It prints a header line, then information about the processes it has found, +one process per line. Then it exits 0. </li> + <li> If s6-ps is not allowed to read a field, it will print <tt>*</tt> (a star) instead +of the field's content. <tt>-</tt> (a dash) means the field is unapplicable here, +for instance the "tty" field for a process that has no controlling terminal. </li> +</ul> + +<h2> Options </h2> + +<ul> + <li> <tt>-H</tt> : tree. s6-ps will display he process list as a tree, +as <tt>ps f</tt> 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. </li> + <li> <tt>-w <em>spacing</em></tt> : leave at least <em>spacing</em> +space characters between two adjacent fields. Minimum is 1, default is 2, +maximum is 256. </li> + <li> <tt>-W <em>wchanfile</em></tt> : force <em>wchanfile</em> as the +file listing kernel addresses for WCHAN fields. By default, s6-ps tries to use +<tt>/proc/kallsyms</tt>, <tt>/boot/System.map-`uname -r`</tt>, and +<tt>/boot/System.map</tt>, in that order. </li> + <li> <tt>-l</tt> : long. Equivalent to +<tt>-o user,pid,cpcpu,pmem,vsize,rss,tty,s,start,cttime,args</tt>. </li> + <li> <tt>-o <em>fieldlist</em></tt> : list of fields to print. +<em>fieldlist</em> 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 +<tt>user,pid,tty,s,start,args</tt>. </li> +</ul> + +<h2> Fields </h2> + +<p> + The <tt>-o</tt> option makes it possible to customize s6-ps's output. Here are the +recognized <em>field</em> keywords and the corresponding field they display. +</p> + +<ul> + <li> <tt>pid</tt> : the process id number. </li> + <li> <tt>comm</tt> : the command name as known by the kernel. </li> + <li> <tt>s</tt> : the one-character state of the process, then <tt>s</tt> +if the process is a session leader or <tt>+</tt> if it is a foreground +process group leader, then <tt>N</tt> if +the process is niced or <tt><</tt> if it is anti-niced. Unlike ps, s6-ps +cannot tell whether a process has locked memory pages or not. </li> + <li> <tt>ppid</tt> : the parent process' pid. </li> + <li> <tt>pgrp</tt> : the process group number. </li> + <li> <tt>sess</tt> : the session leader's pid. </li> + <li> <tt>tty</tt> : the name of the process's controlling terminal.</li> + <li> <tt>tpgid</tt> : the pid of the foreground process group. </li> + <li> <tt>utime</tt> : the time the process spent in user mode. </li> + <li> <tt>stime</tt> : the time the process spent in kernel mode. </li> + <li> <tt>cutime</tt> : the time spent in user mode by the process and +all its dead children. </li> + <li> <tt>cstime</tt> : the time spent in kernel mode by the process and +all its dead children. </li> + <li> <tt>prio</tt> : the process' priority as computed by the kernel. </li> + <li> <tt>nice</tt> : the process' nice value. </li> + <li> <tt>thcount</tt> : the number of threads in the process. </li> + <li> <tt>start</tt> : the start time of the process. </li> + <li> <tt>vsize</tt> : the virtual memory size of the process. </li> + <li> <tt>rss</tt> : the resident set size of the process. </li> + <li> <tt>rsslimit</tt> : the maximum rss allowed for the process. </li> + <li> <tt>psr</tt> : the number of the CPU the process is running on. </li> + <li> <tt>rtprio</tt> : the real-time priority of the process. </li> + <li> <tt>policy</tt> : the real-time policy of the process - a symbolic name. </li> + <li> <tt>user</tt> : the user the process is running as. </li> + <li> <tt>group</tt> : the group the process is running as. </li> + <li> <tt>pmem</tt> : 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. </li> + <li> <tt>wchan</tt> : 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. </li> + <li> <tt>args</tt> : the command line of the process. </li> + <li> <tt>env</tt> : 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. </li> +</li> + <li> <tt>pcpu</tt> : 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. </li> + <li> <tt>ttime</tt> : total CPU time used by the process (user + kernel mode). </li> + <li> <tt>cttime</tt> : total CPU time used by the process and all its dead +children (user + kernel mode). </li> + <li> <tt>tstart</tt> : the start time of the process as a +<a href="http://cr.yp.to/libtai/tai64.html">TAI64N</a> value. </li> + <li> <tt>cpcpu</tt> : 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. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-reboot program</title> + <meta name="Description" content="s6-linux-utils: the s6-reboot program" /> + <meta name="Keywords" content="s6 linux administration root utilities reboot power shutdown" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-reboot</tt> program </h1> + +<p> +<tt>s6-reboot</tt> syncs the filesystems and reboots the machine +immediately. +</p> + +<p> This is different from the sysvinit <tt>reboot</tt> +command, which is an alias for <tt>shutdown -r</tt>. The +s6-linux-utils <tt>s6-reboot</tt> command is more or less equivalent to +sysvinit's <tt>reboot -f</tt>. +</p> + +<h2> Interface </h2> + +<pre> + s6-reboot +</pre> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-swapoff program</title> + <meta name="Description" content="s6-linux-utils: the s6-swapoff program" /> + <meta name="Keywords" content="s6 linux administration root utilities swap off partition" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-swapoff</tt> program </h1> + +<tt>s6-swapoff</tt> deactivates swap partitions. + +<h2> Interface </h2> + +<pre> + s6-swapoff [ -a | <em>device</em> ] +</pre> + +<ul> + <li> <tt>s6-swapoff</tt> deactivates the swap on <em>device</em>. </li> + <li> If the <tt>-a</tt> option is given instead of <em>device</em>, +<tt>s6-swapoff</tt> looks for swap partitions in +<tt>/proc/swaps</tt> and deactivates them all. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-swapon program</title> + <meta name="Description" content="s6-linux-utils: the s6-swapon program" /> + <meta name="Keywords" content="s6 linux administration root utilities swap on partition" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-swapon</tt> program </h1> + +<tt>s6-swapon</tt> activates swap partitions. + +<h2> Interface </h2> + +<pre> + s6-swapon [ -a | <em>device</em> ] +</pre> + +<ul> + <li> <tt>s6-swapon</tt> activates the swap on <em>device</em>. </li> + <li> If the <tt>-a</tt> option is given instead of <em>device</em>, +<tt>s6-swapon</tt> looks for partitions marked <em>swap</em> in +<tt>/etc/fstab</tt> and activates them all. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: the s6-umount program</title> + <meta name="Description" content="s6-linux-utils: the s6-umount program" /> + <meta name="Keywords" content="s6 linux administration root linux utilities umount unmount filesystem" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>s6-umount</tt> program </h1> + +<p> + <tt>s6-umount</tt> unmounts filesystems. +</p> + +<h2> Interface </h2> + +<pre> + s6-umount [ -a | <em>mntpoint</em> ] +</pre> + +<ul> + <li> <tt>s6-umount -a</tt> unmounts all partitions according to <tt>/proc/mounts</tt>. </li> + <li> If the <tt>-a</tt> option is not given, +<tt>s6-umount</tt> unmounts the filesystem mounted on <em>mntpoint</em>. </li> + <li> <tt>s6-umount</tt> does not touch <tt>/etc/mtab</tt>. </li> +</ul> + +</body> +</html> 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 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6-linux-utils: how to upgrade</title> + <meta name="Description" content="s6-linux-utils: how to upgrade" /> + <meta name="Keywords" content="s6-linux-utils installation upgrade" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6-linux-utils</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> How to upgrade s6-linux-utils </h1> + +<h2> to 2.0.0.0 </h2> + +<ul> + <li> The build system has completely changed. It is now a standard +<tt>./configure && make && sudo make install</tt> +build system. See the enclosed INSTALL file for details. </li> + <li> slashpackage is not activated by default. </li> + <li> shared libraries are not used by default. </li> + <li> skalibs dependency bumped to 2.0.0.0 </li> +</ul> + +</body> +</html> 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 <unistd.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#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 <sys/types.h> +#include <unistd.h> +#include <spawn.h> +#include <signal.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <errno.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/tai.h> +#include <skalibs/iopause.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/sig.h> +#include <skalibs/selfpipe.h> + +#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 <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/ioctl.h> +#include <skalibs/strerr2.h> + +#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 <unistd.h> +#include <sys/reboot.h> +#include <skalibs/strerr2.h> + +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 <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> +#include <skalibs/direntry.h> +#include <skalibs/skamisc.h> + +#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 <unistd.h> +#include <skalibs/bytestr.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> + +#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 <errno.h> +#include <unistd.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/inotify.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <skalibs/error.h> +#include <skalibs/buffer.h> +#include <skalibs/bufalloc.h> +#include <skalibs/sig.h> +#include <skalibs/siovec.h> +#include <skalibs/djbunix.h> +#include <skalibs/iopause.h> +#include <skalibs/ulong.h> + +#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 <sys/mount.h> +#include <mntent.h> +#include <stdio.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> +#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 <unistd.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#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 <unistd.h> +#include <sys/reboot.h> +#include <skalibs/strerr2.h> + +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 <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <dirent.h> + +#include <skalibs/uint.h> +#include <skalibs/uint64.h> +#include <skalibs/fmtscan.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/diuint.h> +#include <skalibs/tai.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/direntry.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> +#include <skalibs/unix-transactional.h> +#include <skalibs/avltreen.h> +#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 <sys/types.h> +#include <skalibs/uint32.h> +#include <skalibs/uint64.h> +#include <skalibs/stralloc.h> +#include <skalibs/tai.h> +#include <skalibs/avltreen.h> + + /* 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 <unistd.h> +#include <sys/reboot.h> +#include <skalibs/strerr2.h> + +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 <errno.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> + +extern int swapoff (char const *) ; + +#define USAGE "s6-swapoff device <or> 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 <stdio.h> +#include <mntent.h> +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> + +extern int swapon (const char *, unsigned int) ; + +#define USAGE "s6-swapon device <or> 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 <sys/mount.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> + +#define USAGE "s6-umount mountpoint <or> 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 <sys/types.h> +#include <grp.h> +#include <errno.h> +#include <skalibs/uint.h> +#include <skalibs/diuint.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/skamisc.h> +#include <skalibs/avltree.h> +#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 <errno.h> +#include <skalibs/avltreen.h> +#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 <unistd.h> +#include <time.h> +#include <sys/sysinfo.h> +#include <skalibs/uint32.h> +#include <skalibs/uint64.h> +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <skalibs/ulong.h> +#include <skalibs/fmtscan.h> +#include <skalibs/tai.h> +#include <skalibs/djbtime.h> +#include <skalibs/stralloc.h> +#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, " <defunct>", 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 <sys/types.h> +#include <pwd.h> +#include <errno.h> +#include <skalibs/uint.h> +#include <skalibs/diuint.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/skamisc.h> +#include <skalibs/avltree.h> +#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 <errno.h> +#include <skalibs/uint32.h> +#include <skalibs/uint64.h> +#include <skalibs/fmtscan.h> +#include <skalibs/stralloc.h> +#include <skalibs/tai.h> +#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 <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint.h> +#include <skalibs/uint32.h> +#include <skalibs/diuint32.h> +#include <skalibs/buffer.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> +#include <skalibs/avltree.h> +#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 <sys/utsname.h> +#include <skalibs/uint64.h> +#include <skalibs/bytestr.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/djbunix.h> +#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 |