]> git.mdlowis.com Git - proto/albase.git/commitdiff
Added ubase files from suckless
authorMike Lowis <mike.lowis@gentex.com>
Tue, 3 May 2016 02:53:25 +0000 (22:53 -0400)
committerMike Lowis <mike.lowis@gentex.com>
Tue, 3 May 2016 02:53:25 +0000 (22:53 -0400)
133 files changed:
Makefile
source/ubase/LICENSE [new file with mode: 0644]
source/ubase/Makefile [new file with mode: 0644]
source/ubase/README [new file with mode: 0644]
source/ubase/Rules.mk [new file with mode: 0644]
source/ubase/TODO [new file with mode: 0644]
source/ubase/arg.h [new file with mode: 0644]
source/ubase/chvt.1 [new file with mode: 0644]
source/ubase/chvt.c [new file with mode: 0644]
source/ubase/clear.1 [new file with mode: 0644]
source/ubase/clear.c [new file with mode: 0644]
source/ubase/config.def.h [new file with mode: 0644]
source/ubase/config.h [new file with mode: 0644]
source/ubase/config.mk [new file with mode: 0644]
source/ubase/ctrlaltdel.8 [new file with mode: 0644]
source/ubase/ctrlaltdel.c [new file with mode: 0644]
source/ubase/dd.1 [new file with mode: 0644]
source/ubase/dd.c [new file with mode: 0644]
source/ubase/df.1 [new file with mode: 0644]
source/ubase/df.c [new file with mode: 0644]
source/ubase/dmesg.1 [new file with mode: 0644]
source/ubase/dmesg.c [new file with mode: 0644]
source/ubase/eject.1 [new file with mode: 0644]
source/ubase/eject.c [new file with mode: 0644]
source/ubase/fallocate.1 [new file with mode: 0644]
source/ubase/fallocate.c [new file with mode: 0644]
source/ubase/free.1 [new file with mode: 0644]
source/ubase/free.c [new file with mode: 0644]
source/ubase/freeramdisk.8 [new file with mode: 0644]
source/ubase/freeramdisk.c [new file with mode: 0644]
source/ubase/fsfreeze.8 [new file with mode: 0644]
source/ubase/fsfreeze.c [new file with mode: 0644]
source/ubase/getty.8 [new file with mode: 0644]
source/ubase/getty.c [new file with mode: 0644]
source/ubase/halt.8 [new file with mode: 0644]
source/ubase/halt.c [new file with mode: 0644]
source/ubase/hwclock.8 [new file with mode: 0644]
source/ubase/hwclock.c [new file with mode: 0644]
source/ubase/id.1 [new file with mode: 0644]
source/ubase/id.c [new file with mode: 0644]
source/ubase/insmod.8 [new file with mode: 0644]
source/ubase/insmod.c [new file with mode: 0644]
source/ubase/killall5.8 [new file with mode: 0644]
source/ubase/killall5.c [new file with mode: 0644]
source/ubase/last.c [new file with mode: 0644]
source/ubase/lastlog.8 [new file with mode: 0644]
source/ubase/lastlog.c [new file with mode: 0644]
source/ubase/libutil/agetcwd.c [new file with mode: 0644]
source/ubase/libutil/agetline.c [new file with mode: 0644]
source/ubase/libutil/apathmax.c [new file with mode: 0644]
source/ubase/libutil/concat.c [new file with mode: 0644]
source/ubase/libutil/ealloc.c [new file with mode: 0644]
source/ubase/libutil/eprintf.c [new file with mode: 0644]
source/ubase/libutil/estrtol.c [new file with mode: 0644]
source/ubase/libutil/estrtoul.c [new file with mode: 0644]
source/ubase/libutil/explicit_bzero.c [new file with mode: 0644]
source/ubase/libutil/passwd.c [new file with mode: 0644]
source/ubase/libutil/proc.c [new file with mode: 0644]
source/ubase/libutil/putword.c [new file with mode: 0644]
source/ubase/libutil/recurse.c [new file with mode: 0644]
source/ubase/libutil/strlcat.c [new file with mode: 0644]
source/ubase/libutil/strlcpy.c [new file with mode: 0644]
source/ubase/libutil/strtonum.c [new file with mode: 0644]
source/ubase/libutil/tty.c [new file with mode: 0644]
source/ubase/login.1 [new file with mode: 0644]
source/ubase/login.c [new file with mode: 0644]
source/ubase/lsmod.8 [new file with mode: 0644]
source/ubase/lsmod.c [new file with mode: 0644]
source/ubase/lsusb.8 [new file with mode: 0644]
source/ubase/lsusb.c [new file with mode: 0644]
source/ubase/mesg.1 [new file with mode: 0644]
source/ubase/mesg.c [new file with mode: 0644]
source/ubase/mknod.1 [new file with mode: 0644]
source/ubase/mknod.c [new file with mode: 0644]
source/ubase/mkswap.8 [new file with mode: 0644]
source/ubase/mkswap.c [new file with mode: 0644]
source/ubase/mount.8 [new file with mode: 0644]
source/ubase/mount.c [new file with mode: 0644]
source/ubase/mountpoint.1 [new file with mode: 0644]
source/ubase/mountpoint.c [new file with mode: 0644]
source/ubase/nologin.8 [new file with mode: 0644]
source/ubase/nologin.c [new file with mode: 0644]
source/ubase/pagesize.1 [new file with mode: 0644]
source/ubase/pagesize.c [new file with mode: 0644]
source/ubase/passwd.1 [new file with mode: 0644]
source/ubase/passwd.c [new file with mode: 0644]
source/ubase/passwd.h [new file with mode: 0644]
source/ubase/pidof.1 [new file with mode: 0644]
source/ubase/pidof.c [new file with mode: 0644]
source/ubase/pivot_root.8 [new file with mode: 0644]
source/ubase/pivot_root.c [new file with mode: 0644]
source/ubase/proc.h [new file with mode: 0644]
source/ubase/ps.1 [new file with mode: 0644]
source/ubase/ps.c [new file with mode: 0644]
source/ubase/queue.h [new file with mode: 0644]
source/ubase/readahead.8 [new file with mode: 0644]
source/ubase/readahead.c [new file with mode: 0644]
source/ubase/reboot.h [new file with mode: 0644]
source/ubase/respawn.1 [new file with mode: 0644]
source/ubase/respawn.c [new file with mode: 0644]
source/ubase/rmmod.8 [new file with mode: 0644]
source/ubase/rmmod.c [new file with mode: 0644]
source/ubase/rtc.h [new file with mode: 0644]
source/ubase/stat.1 [new file with mode: 0644]
source/ubase/stat.c [new file with mode: 0644]
source/ubase/su.1 [new file with mode: 0644]
source/ubase/su.c [new file with mode: 0644]
source/ubase/swaplabel.8 [new file with mode: 0644]
source/ubase/swaplabel.c [new file with mode: 0644]
source/ubase/swapoff.8 [new file with mode: 0644]
source/ubase/swapoff.c [new file with mode: 0644]
source/ubase/swapon.8 [new file with mode: 0644]
source/ubase/swapon.c [new file with mode: 0644]
source/ubase/switch_root.8 [new file with mode: 0644]
source/ubase/switch_root.c [new file with mode: 0644]
source/ubase/sysctl.8 [new file with mode: 0644]
source/ubase/sysctl.c [new file with mode: 0644]
source/ubase/text.h [new file with mode: 0644]
source/ubase/truncate.1 [new file with mode: 0644]
source/ubase/truncate.c [new file with mode: 0644]
source/ubase/umount.8 [new file with mode: 0644]
source/ubase/umount.c [new file with mode: 0644]
source/ubase/unshare.1 [new file with mode: 0644]
source/ubase/unshare.c [new file with mode: 0644]
source/ubase/uptime.1 [new file with mode: 0644]
source/ubase/uptime.c [new file with mode: 0644]
source/ubase/util.h [new file with mode: 0644]
source/ubase/vtallow.1 [new file with mode: 0644]
source/ubase/vtallow.c [new file with mode: 0644]
source/ubase/watch.1 [new file with mode: 0644]
source/ubase/watch.c [new file with mode: 0644]
source/ubase/who.1 [new file with mode: 0644]
source/ubase/who.c [new file with mode: 0644]

index 82c7062053678e786ae4dd9867c4d63179e42d43..9c6904ce33ec42d7eb8f299477c36af9155d71e1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,22 +3,28 @@
 #------------------------------------------------------------------------------
 # tools
 CC = cc
+LD = $(CC)
+AR = ar
 
 # flags
 LIBS     =
 INCS     = -Iinclude
 DEFS     = -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L
 CPPFLAGS = $(INCS) $(DEFS)
-CFLAGS   = -O2
+CFLAGS   = -O2 --std=gnu99
 LDFLAGS  = $(LIBS)
-BUILD    = $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^
+ARFLAGS  = rcs
+
+# commands
+BUILD   = $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^
+ARCHIVE = $(AR) $(ARFLAGS) $@ $^
 
 # dirs
 BUILDDIR = build
 BINDIR   = $(BUILDDIR)/bin
 OBJDIR   = $(BUILDDIR)/obj
 
-# targets
+# collections
 BINS   =
 ECLEAN =
 DIRS   = $(BUILDDIR) $(BINDIR) $(OBJDIR)
@@ -28,6 +34,8 @@ DIRS   = $(BUILDDIR) $(BINDIR) $(OBJDIR)
 #------------------------------------------------------------------------------
 include source/Rules.mk
 include source/sh/Rules.mk
+include source/ubase/Rules.mk
+#include source/sbase/Rules.mk
 
 .PHONY: all $(BINS)
 
diff --git a/source/ubase/LICENSE b/source/ubase/LICENSE
new file mode 100644 (file)
index 0000000..76cf9ea
--- /dev/null
@@ -0,0 +1,34 @@
+MIT/X Consortium License
+
+© 2013-2016 Dimitris Papapastamos <sin@2f30.org>
+© 2014-2016 Laslo Hunhold <dev@frign.de>
+© 2014-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+Authors/contributors include:
+
+© 2013 oblique <psyberbits@gmail.com>
+© 2013 s-p-k <mr.dwts@gmail.com>
+© 2013 Jakob Kramer <jakob.kramer@gmx.de>
+© 2013 David Galos <galosd83@students.rowan.edu>
+© 2014 Carlos J. Torres <vlaadbrain@gmail.com>
+© 2014 Roberto E. Vargas Caballero <k0ga@shike2.com>
+© 2014 Jan Tatje <jan@jnt.io>
+© 2015 Risto Salminen <ripejcp@gmail.com>
diff --git a/source/ubase/Makefile b/source/ubase/Makefile
new file mode 100644 (file)
index 0000000..59616a4
--- /dev/null
@@ -0,0 +1,225 @@
+include config.mk
+
+.SUFFIXES:
+.SUFFIXES: .o .c
+
+HDR = \
+       arg.h        \
+       config.h     \
+       passwd.h     \
+       proc.h       \
+       queue.h      \
+       reboot.h     \
+       rtc.h        \
+       text.h       \
+       util.h
+
+LIBUTIL = libutil.a
+LIBUTILSRC = \
+       libutil/agetcwd.c        \
+       libutil/agetline.c       \
+       libutil/apathmax.c       \
+       libutil/concat.c         \
+       libutil/ealloc.c         \
+       libutil/eprintf.c        \
+       libutil/estrtol.c        \
+       libutil/estrtoul.c       \
+       libutil/explicit_bzero.c \
+       libutil/passwd.c         \
+       libutil/proc.c           \
+       libutil/putword.c        \
+       libutil/recurse.c        \
+       libutil/strlcat.c        \
+       libutil/strlcpy.c        \
+       libutil/strtonum.c       \
+       libutil/tty.c
+
+LIB = $(LIBUTIL)
+
+BIN = \
+       chvt              \
+       clear             \
+       ctrlaltdel        \
+       dd                \
+       df                \
+       dmesg             \
+       eject             \
+       fallocate         \
+       free              \
+       freeramdisk       \
+       fsfreeze          \
+       getty             \
+       halt              \
+       hwclock           \
+       id                \
+       insmod            \
+       killall5          \
+       last              \
+       lastlog           \
+       login             \
+       lsmod             \
+       lsusb             \
+       mesg              \
+       mknod             \
+       mkswap            \
+       mount             \
+       mountpoint        \
+       nologin           \
+       pagesize          \
+       passwd            \
+       pidof             \
+       pivot_root        \
+       ps                \
+       readahead         \
+       respawn           \
+       rmmod             \
+       stat              \
+       su                \
+       swaplabel         \
+       swapoff           \
+       swapon            \
+       switch_root       \
+       sysctl            \
+       truncate          \
+       umount            \
+       unshare           \
+       uptime            \
+       vtallow           \
+       watch             \
+       who
+
+MAN1 = \
+       chvt.1              \
+       clear.1             \
+       dd.1                \
+       df.1                \
+       dmesg.1             \
+       eject.1             \
+       fallocate.1         \
+       free.1              \
+       id.1                \
+       login.1             \
+       mesg.1              \
+       mknod.1             \
+       mountpoint.1        \
+       pagesize.1          \
+       passwd.1            \
+       pidof.1             \
+       ps.1                \
+       respawn.1           \
+       stat.1              \
+       su.1                \
+       truncate.1          \
+       unshare.1           \
+       uptime.1            \
+       vtallow.1           \
+       watch.1             \
+       who.1
+
+MAN8 = \
+       ctrlaltdel.8        \
+       freeramdisk.8       \
+       fsfreeze.8          \
+       getty.8             \
+       halt.8              \
+       hwclock.8           \
+       insmod.8            \
+       killall5.8          \
+       lastlog.8           \
+       lsmod.8             \
+       lsusb.8             \
+       mkswap.8            \
+       mount.8             \
+       nologin.8           \
+       pivot_root.8        \
+       readahead.8         \
+       rmmod.8             \
+       swaplabel.8         \
+       swapoff.8           \
+       swapon.8            \
+       switch_root.8       \
+       sysctl.8            \
+       umount.8
+
+LIBUTILOBJ = $(LIBUTILSRC:.c=.o)
+OBJ = $(BIN:=.o) $(LIBUTILOBJ)
+SRC = $(BIN:=.c)
+
+all: $(BIN)
+
+$(BIN): $(LIB)
+
+$(OBJ): $(HDR) config.mk
+
+config.h:
+       cp config.def.h $@
+
+.o:
+       $(CC) $(LDFLAGS) -o $@ $< $(LIB) $(LDLIBS)
+
+.c.o:
+       $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
+
+$(LIBUTIL): $(LIBUTILOBJ)
+       $(AR) rc $@ $?
+       $(RANLIB) $@
+
+install: all
+       mkdir -p $(DESTDIR)$(PREFIX)/bin
+       cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin
+       cd $(DESTDIR)$(PREFIX)/bin && chmod 755 $(BIN)
+       mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+       for m in $(MAN1); do sed "s/^\.Os ubase/.Os ubase $(VERSION)/g" < "$$m" > $(DESTDIR)$(MANPREFIX)/man1/"$$m"; done
+       mkdir -p $(DESTDIR)$(MANPREFIX)/man8
+       for m in $(MAN8); do sed "s/^\.Os ubase/.Os ubase $(VERSION)/g" < "$$m" > $(DESTDIR)$(MANPREFIX)/man8/"$$m"; done
+       cd $(DESTDIR)$(MANPREFIX)/man1 && chmod 644 $(MAN1)
+       cd $(DESTDIR)$(MANPREFIX)/man8 && chmod 644 $(MAN8)
+
+uninstall:
+       cd $(DESTDIR)$(PREFIX)/bin && rm -f $(BIN)
+       cd $(DESTDIR)$(MANPREFIX)/man1 && rm -f $(MAN1)
+       cd $(DESTDIR)$(MANPREFIX)/man8 && rm -f $(MAN8)
+
+dist: clean
+       mkdir -p ubase-$(VERSION)
+       cp -r LICENSE Makefile README TODO config.mk $(SRC) $(MAN1) $(MAN8) libutil $(HDR) config.def.h ubase-$(VERSION)
+       tar -cf ubase-$(VERSION).tar ubase-$(VERSION)
+       gzip ubase-$(VERSION).tar
+       rm -rf ubase-$(VERSION)
+
+ubase-box: $(LIB) $(SRC)
+       mkdir -p build
+       cp $(HDR) build
+       cp config.h build
+       for f in $(SRC); do sed "s/^main(/`basename $$f .c`_&/" < $$f > build/$$f; done
+       echo '#include <libgen.h>'  > build/$@.c
+       echo '#include <stdio.h>'  >> build/$@.c
+       echo '#include <stdlib.h>' >> build/$@.c
+       echo '#include <string.h>' >> build/$@.c
+       echo '#include "util.h"'   >> build/$@.c
+       for f in $(SRC); do echo "int `basename $$f .c`_main(int, char **);" >> build/$@.c; done
+       echo 'int main(int argc, char *argv[]) { char *s = basename(argv[0]); if(!strcmp(s,"ubase-box")) { argc--; argv++; s = basename(argv[0]); } if(0) ;' >> build/$@.c
+       for f in $(SRC); do echo "else if(!strcmp(s, \"`basename $$f .c`\")) return `basename $$f .c`_main(argc, argv);" >> build/$@.c; done
+       echo 'else {' >> build/$@.c
+       for f in $(SRC); do echo "printf(\"`basename $$f .c`\"); putchar(' ');" >> build/$@.c; done
+       echo "putchar(0xa); }; return 0; }" >> build/$@.c
+       $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ build/*.c $(LIB) $(LDLIBS)
+       rm -r build
+
+ubase-box-install: ubase-box
+       mkdir -p $(DESTDIR)$(PREFIX)/bin
+       cp -f ubase-box $(DESTDIR)$(PREFIX)/bin
+       chmod 755 $(DESTDIR)$(PREFIX)/bin/ubase-box
+       for f in $(BIN); do ln -sf ubase-box $(DESTDIR)$(PREFIX)/bin/"$$f"; done
+       mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+       for m in $(MAN1); do sed "s/^\.Os ubase/.Os ubase $(VERSION)/g" < "$$m" > $(DESTDIR)$(MANPREFIX)/man1/"$$m"; done
+       mkdir -p $(DESTDIR)$(MANPREFIX)/man8
+       for m in $(MAN8); do sed "s/^\.Os ubase/.Os ubase $(VERSION)/g" < "$$m" > $(DESTDIR)$(MANPREFIX)/man8/"$$m"; done
+       cd $(DESTDIR)$(MANPREFIX)/man1 && chmod 644 $(MAN1)
+       cd $(DESTDIR)$(MANPREFIX)/man8 && chmod 644 $(MAN8)
+
+clean:
+       rm -f $(BIN) $(OBJ) $(LIB) ubase-box ubase-$(VERSION).tar.gz
+
+.PHONY:
+       all install uninstall dist ubase-box ubase-box-install clean
diff --git a/source/ubase/README b/source/ubase/README
new file mode 100644 (file)
index 0000000..9b5c31b
--- /dev/null
@@ -0,0 +1,29 @@
+ubase - suckless linux base utils
+=================================
+
+ubase is  a collection of  tools similar  in spirit to  util-linux but
+much simpler.
+
+The complement  of ubase  is sbase[1] which  mostly follows  POSIX and
+provides all the portable tools.  Together they are intended to form a
+base system similar to busybox but much smaller and suckless.
+
+Building
+--------
+
+To  build ubase,  simply  type  make.  You  may  have  to fiddle  with
+config.mk and config.h depending on your system.
+
+You  can  also  build  ubase-box,  which  generates  a  single  binary
+containing  all  the  required  tools.    You  can  then  symlink  the
+individual tools to ubase-box or run: make ubase-box-install.
+
+To run the tools for ubase-box directly use: ubase-box cmd [args]
+
+Ideally you will  want to statically link ubase.   We highly recommend
+using musl-libc[2].
+
+ubase is known to compile with gcc, clang and tcc.
+
+[1] http://git.suckless.org/sbase/
+[2] http://www.musl-libc.org/
diff --git a/source/ubase/Rules.mk b/source/ubase/Rules.mk
new file mode 100644 (file)
index 0000000..fce5369
--- /dev/null
@@ -0,0 +1,305 @@
+BINS   += $(UBASE_BINS)
+DIRS   += $(UBASE_OBJDIR)
+ECLEAN += $(UBASE_LIBUTIL) \
+          $(UBASE_LIBUTIL_OBJS) \
+          $(addprefix $(BINDIR)/,$(UBASE_BINS))
+
+UBASE_SUBDIR  = source/ubase
+UBASE_OBJDIR  = $(OBJDIR)/ubase
+UBASE_LIBUTIL = $(UBASE_OBJDIR)/libutil.a
+UBASE_DEFS    = -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
+UBASE_COMPILE = $(CC) $(UBASE_DEFS) $(CFLAGS) $(CPPFLAGS) -I$(UBASE_SUBDIR) -o $@ -c $<
+UBASE_BUILD   = $(CC) $(UBASE_DEFS) $(CFLAGS) $(CPPFLAGS) -I$(UBASE_SUBDIR) -o $@ $^
+UBASE_ARCHIVE = $(AR) $(ARFLAGS) $@ $^
+
+UBASE_LIBUTIL_OBJS =                 \
+    $(UBASE_OBJDIR)/agetcwd.o        \
+    $(UBASE_OBJDIR)/agetline.o       \
+    $(UBASE_OBJDIR)/apathmax.o       \
+    $(UBASE_OBJDIR)/concat.o         \
+    $(UBASE_OBJDIR)/ealloc.o         \
+    $(UBASE_OBJDIR)/eprintf.o        \
+    $(UBASE_OBJDIR)/estrtol.o        \
+    $(UBASE_OBJDIR)/estrtoul.o       \
+    $(UBASE_OBJDIR)/explicit_bzero.o \
+    $(UBASE_OBJDIR)/passwd.o         \
+    $(UBASE_OBJDIR)/proc.o           \
+    $(UBASE_OBJDIR)/putword.o        \
+    $(UBASE_OBJDIR)/recurse.o        \
+    $(UBASE_OBJDIR)/strlcat.o        \
+    $(UBASE_OBJDIR)/strlcpy.o        \
+    $(UBASE_OBJDIR)/strtonum.o       \
+    $(UBASE_OBJDIR)/tty.o
+
+UBASE_BINS =    \
+    chvt        \
+    clear       \
+    ctrlaltdel  \
+    dd          \
+    df          \
+    dmesg       \
+    eject       \
+    fallocate   \
+    free        \
+    freeramdisk \
+    fsfreeze    \
+    getty       \
+    halt        \
+    hwclock     \
+    id          \
+    insmod      \
+    killall5    \
+    last        \
+    lastlog     \
+    login       \
+    lsmod       \
+    lsusb       \
+    mesg        \
+    mknod       \
+    mkswap      \
+    mount       \
+    mountpoint  \
+    nologin     \
+    pagesize    \
+    passwd      \
+    pidof       \
+    pivot_root  \
+    ps          \
+    readahead   \
+    respawn     \
+    rmmod       \
+    stat        \
+    su          \
+    swaplabel   \
+    swapoff     \
+    swapon      \
+    switch_root \
+    sysctl      \
+    truncate    \
+    umount      \
+    unshare     \
+    uptime      \
+    vtallow     \
+    watch       \
+    who
+
+$(UBASE_LIBUTIL): $(UBASE_LIBUTIL_OBJS)
+       $(UBASE_ARCHIVE)
+$(UBASE_OBJDIR)/agetcwd.o: $(UBASE_SUBDIR)/libutil/agetcwd.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/agetline.o: $(UBASE_SUBDIR)/libutil/agetline.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/apathmax.o: $(UBASE_SUBDIR)/libutil/apathmax.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/concat.o: $(UBASE_SUBDIR)/libutil/concat.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/ealloc.o: $(UBASE_SUBDIR)/libutil/ealloc.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/eprintf.o: $(UBASE_SUBDIR)/libutil/eprintf.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/estrtol.o: $(UBASE_SUBDIR)/libutil/estrtol.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/estrtoul.o: $(UBASE_SUBDIR)/libutil/estrtoul.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/explicit_bzero.o: $(UBASE_SUBDIR)/libutil/explicit_bzero.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/passwd.o:  $(UBASE_SUBDIR)/libutil/passwd.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/proc.o: $(UBASE_SUBDIR)/libutil/proc.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/putword.o: $(UBASE_SUBDIR)/libutil/putword.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/recurse.o: $(UBASE_SUBDIR)/libutil/recurse.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/strlcat.o: $(UBASE_SUBDIR)/libutil/strlcat.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/strlcpy.o: $(UBASE_SUBDIR)/libutil/strlcpy.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/strtonum.o: $(UBASE_SUBDIR)/libutil/strtonum.c
+       $(UBASE_COMPILE)
+$(UBASE_OBJDIR)/tty.o: $(UBASE_SUBDIR)/libutil/tty.c
+       $(UBASE_COMPILE)
+
+chvt: $(BINDIR)/chvt
+$(BINDIR)/chvt: $(UBASE_SUBDIR)/chvt.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+clear: $(BINDIR)/clear
+$(BINDIR)/clear: $(UBASE_SUBDIR)/clear.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+ctrlaltdel: $(BINDIR)/ctrlaltdel
+$(BINDIR)/ctrlaltdel: $(UBASE_SUBDIR)/ctrlaltdel.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+dd: $(BINDIR)/dd
+$(BINDIR)/dd: $(UBASE_SUBDIR)/dd.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+df: $(BINDIR)/df
+$(BINDIR)/df: $(UBASE_SUBDIR)/df.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+eject: $(BINDIR)/eject
+$(BINDIR)/eject: $(UBASE_SUBDIR)/eject.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+fallocate: $(BINDIR)/fallocate
+$(BINDIR)/fallocate: $(UBASE_SUBDIR)/fallocate.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+free: $(BINDIR)/free
+$(BINDIR)/free: $(UBASE_SUBDIR)/free.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+freeramdisk: $(BINDIR)/freeramdisk
+$(BINDIR)/freeramdisk: $(UBASE_SUBDIR)/freeramdisk.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+fsfreeze: $(BINDIR)/fsfreeze
+$(BINDIR)/fsfreeze: $(UBASE_SUBDIR)/fsfreeze.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+halt: $(BINDIR)/halt
+$(BINDIR)/halt: $(UBASE_SUBDIR)/halt.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+hwclock: $(BINDIR)/hwclock
+$(BINDIR)/hwclock: $(UBASE_SUBDIR)/hwclock.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+id: $(BINDIR)/id
+$(BINDIR)/id: $(UBASE_SUBDIR)/id.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+insmod: $(BINDIR)/insmod
+$(BINDIR)/insmod: $(UBASE_SUBDIR)/insmod.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+killall5: $(BINDIR)/killall5
+$(BINDIR)/killall5: $(UBASE_SUBDIR)/killall5.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+last: $(BINDIR)/last
+$(BINDIR)/last: $(UBASE_SUBDIR)/last.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+lastlog: $(BINDIR)/lastlog
+$(BINDIR)/lastlog: $(UBASE_SUBDIR)/lastlog.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+lsmod: $(BINDIR)/lsmod
+$(BINDIR)/lsmod: $(UBASE_SUBDIR)/lsmod.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+lsusb: $(BINDIR)/lsusb
+$(BINDIR)/lsusb: $(UBASE_SUBDIR)/lsusb.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+mesg: $(BINDIR)/mesg
+$(BINDIR)/mesg: $(UBASE_SUBDIR)/mesg.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+mknod: $(BINDIR)/mknod
+$(BINDIR)/mknod: $(UBASE_SUBDIR)/mknod.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+mkswap: $(BINDIR)/mkswap
+$(BINDIR)/mkswap: $(UBASE_SUBDIR)/mkswap.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+mountpoint: $(BINDIR)/mountpoint
+$(BINDIR)/mountpoint: $(UBASE_SUBDIR)/mountpoint.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+nologin: $(BINDIR)/nologin
+$(BINDIR)/nologin: $(UBASE_SUBDIR)/nologin.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+pagesize: $(BINDIR)/pagesize
+$(BINDIR)/pagesize: $(UBASE_SUBDIR)/pagesize.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+passwd: $(BINDIR)/passwd
+$(BINDIR)/passwd: $(UBASE_SUBDIR)/passwd.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD) -lcrypt
+
+pidof: $(BINDIR)/pidof
+$(BINDIR)/pidof: $(UBASE_SUBDIR)/pidof.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+pivot_root: $(BINDIR)/pivot_root
+$(BINDIR)/pivot_root: $(UBASE_SUBDIR)/pivot_root.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+ps: $(BINDIR)/ps
+$(BINDIR)/ps: $(UBASE_SUBDIR)/ps.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+readahead: $(BINDIR)/readahead
+$(BINDIR)/readahead: $(UBASE_SUBDIR)/readahead.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+respawn: $(BINDIR)/respawn
+$(BINDIR)/respawn: $(UBASE_SUBDIR)/respawn.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+rmmod: $(BINDIR)/rmmod
+$(BINDIR)/rmmod: $(UBASE_SUBDIR)/rmmod.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+stat: $(BINDIR)/stat
+$(BINDIR)/stat: $(UBASE_SUBDIR)/stat.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+su: $(BINDIR)/su
+$(BINDIR)/su: $(UBASE_SUBDIR)/su.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD) -lcrypt
+
+swaplabel: $(BINDIR)/swaplabel
+$(BINDIR)/swaplabel: $(UBASE_SUBDIR)/swaplabel.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+swapoff: $(BINDIR)/swapoff
+$(BINDIR)/swapoff: $(UBASE_SUBDIR)/swapoff.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+swapon: $(BINDIR)/swapon
+$(BINDIR)/swapon: $(UBASE_SUBDIR)/swapon.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+switch_root: $(BINDIR)/switch_root
+$(BINDIR)/switch_root: $(UBASE_SUBDIR)/switch_root.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+sysctl: $(BINDIR)/sysctl
+$(BINDIR)/sysctl: $(UBASE_SUBDIR)/sysctl.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+truncate: $(BINDIR)/truncate
+$(BINDIR)/truncate: $(UBASE_SUBDIR)/truncate.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+unmount: $(BINDIR)/unmount
+$(BINDIR)/umount: $(UBASE_SUBDIR)/umount.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+unshare: $(BINDIR)/unshare
+$(BINDIR)/unshare: $(UBASE_SUBDIR)/unshare.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+uptime: $(BINDIR)/uptime
+$(BINDIR)/uptime: $(UBASE_SUBDIR)/uptime.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+vtallow: $(BINDIR)/vtallow
+$(BINDIR)/vtallow: $(UBASE_SUBDIR)/vtallow.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+watch: $(BINDIR)/watch
+$(BINDIR)/watch: $(UBASE_SUBDIR)/watch.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
+
+who: $(BINDIR)/who
+$(BINDIR)/who: $(UBASE_SUBDIR)/who.c $(UBASE_LIBUTIL)
+       $(UBASE_BUILD)
diff --git a/source/ubase/TODO b/source/ubase/TODO
new file mode 100644 (file)
index 0000000..3e89e68
--- /dev/null
@@ -0,0 +1,37 @@
+Tools
+=====
+
+acpi
+addgroup
+adduser
+blkid
+capchroot
+fakeroot
+fuser
+getcap
+ifconfig
+ionice
+less or pg
+losetup
+lsattr
+lspci
+mkswap [-L]
+partprobe
+pmap
+ps (support for more options)
+pwdx
+rfkill
+rmgroup
+rmuser
+setcap
+tabs
+taskset
+top
+tput
+vmstat
+
+Misc
+====
+
+Beautify passwd(1).
+last(1) manpage.
diff --git a/source/ubase/arg.h b/source/ubase/arg.h
new file mode 100644 (file)
index 0000000..aeab52a
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN       for (argv0 = *argv, argv++, argc--;\
+                                       argv[0] && argv[0][0] == '-'\
+                                       && argv[0][1];\
+                                       argc--, argv++) {\
+                               char argc_;\
+                               char **argv_;\
+                               int brk_;\
+                               if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+                                       argv++;\
+                                       argc--;\
+                                       break;\
+                               }\
+                               for (brk_ = 0, argv[0]++, argv_ = argv;\
+                                               argv[0][0] && !brk_;\
+                                               argv[0]++) {\
+                                       if (argv_ != argv)\
+                                               break;\
+                                       argc_ = argv[0][0];\
+                                       switch (argc_)
+
+/* Handles obsolete -NUM syntax */
+#define ARGNUM                         case '0':\
+                                       case '1':\
+                                       case '2':\
+                                       case '3':\
+                                       case '4':\
+                                       case '5':\
+                                       case '6':\
+                                       case '7':\
+                                       case '8':\
+                                       case '9'
+
+#define ARGEND                 }\
+                       }
+
+#define ARGC()         argc_
+
+#define ARGNUMF(base)  (brk_ = 1, estrtol(argv[0], (base)))
+
+#define EARGF(x)       ((argv[0][1] == '\0' && argv[1] == NULL)?\
+                               ((x), abort(), (char *)0) :\
+                               (brk_ = 1, (argv[0][1] != '\0')?\
+                                       (&argv[0][1]) :\
+                                       (argc--, argv++, argv[0])))
+
+#define ARGF()         ((argv[0][1] == '\0' && argv[1] == NULL)?\
+                               (char *)0 :\
+                               (brk_ = 1, (argv[0][1] != '\0')?\
+                                       (&argv[0][1]) :\
+                                       (argc--, argv++, argv[0])))
+
+#endif
diff --git a/source/ubase/chvt.1 b/source/ubase/chvt.1
new file mode 100644 (file)
index 0000000..50f2005
--- /dev/null
@@ -0,0 +1,15 @@
+.Dd September 7, 2015
+.Dt CHVT 1
+.Os ubase
+.Sh NAME
+.Nm chvt
+.Nd change foreground virtual terminal
+.Sh SYNOPSIS
+.Nm
+.Ar num
+.Sh DESCRIPTION
+.Nm
+brings
+.Pf /dev/tty Ar num
+to the foreground. This has the same effect as
+.Pf Ctrl-Alt-F Ar num .
diff --git a/source/ubase/chvt.c b/source/ubase/chvt.c
new file mode 100644 (file)
index 0000000..edd2d9b
--- /dev/null
@@ -0,0 +1,67 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define KDGKBTYPE      0x4B33  /* get keyboard type */
+
+#define VT_ACTIVATE    0x5606  /* make vt active */
+#define VT_WAITACTIVE  0x5607  /* wait for vt active */
+
+static char *vt[] = {
+       "/proc/self/fd/0",
+       "/dev/console",
+       "/dev/tty",
+       "/dev/tty0",
+};
+
+static void
+usage(void)
+{
+       eprintf("usage: %s num\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       unsigned int n, i;
+       int fd;
+       char c;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc != 1)
+               usage();
+
+       n = estrtonum(argv[0], 0, UINT_MAX);
+       for (i = 0; i < LEN(vt); i++) {
+               if ((fd = open(vt[i], O_RDONLY)) < 0)
+                       continue;
+               c = 0;
+               if (ioctl(fd, KDGKBTYPE, &c) == 0)
+                       goto found;
+               if (close(fd) < 0)
+                       eprintf("close %s:", vt[i]);
+       }
+       eprintf("no console found\n");
+
+found:
+       if (ioctl(fd, VT_ACTIVATE, n) == -1)
+               eprintf("VT_ACTIVATE %u:", n);
+       if (ioctl(fd, VT_WAITACTIVE, n) == -1)
+               eprintf("VT_WAITACTIVE %u:", n);
+       if (close(fd) < 0)
+               eprintf("close %s:", vt[i]);
+
+       return 0;
+}
diff --git a/source/ubase/clear.1 b/source/ubase/clear.1
new file mode 100644 (file)
index 0000000..d3c6870
--- /dev/null
@@ -0,0 +1,11 @@
+.Dd February 2, 2015
+.Dt CLEAR 1
+.Os ubase
+.Sh NAME
+.Nm clear
+.Nd clear the screen
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+clears the screen.
diff --git a/source/ubase/clear.c b/source/ubase/clear.c
new file mode 100644 (file)
index 0000000..c6481f0
--- /dev/null
@@ -0,0 +1,23 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       argv0 = argv[0], argc--, argv++;
+
+       if (argc)
+               usage();
+
+       printf("\x1b[2J\x1b[H");
+
+       return 0;
+}
diff --git a/source/ubase/config.def.h b/source/ubase/config.def.h
new file mode 100644 (file)
index 0000000..577833e
--- /dev/null
@@ -0,0 +1,11 @@
+/* See LICENSE file for copyright and license details. */
+
+#define ENV_SUPATH     "/bin"
+#define ENV_PATH       "/bin"
+#define PW_CIPHER      "$6$"   /* SHA-512 */
+#undef UTMP_PATH
+#define UTMP_PATH      "/var/run/utmp"
+#undef BTMP_PATH
+#define BTMP_PATH      "/var/log/btmp"
+#undef WTMP_PATH
+#define WTMP_PATH      "/var/log/wtmp"
diff --git a/source/ubase/config.h b/source/ubase/config.h
new file mode 100644 (file)
index 0000000..577833e
--- /dev/null
@@ -0,0 +1,11 @@
+/* See LICENSE file for copyright and license details. */
+
+#define ENV_SUPATH     "/bin"
+#define ENV_PATH       "/bin"
+#define PW_CIPHER      "$6$"   /* SHA-512 */
+#undef UTMP_PATH
+#define UTMP_PATH      "/var/run/utmp"
+#undef BTMP_PATH
+#define BTMP_PATH      "/var/log/btmp"
+#undef WTMP_PATH
+#define WTMP_PATH      "/var/log/wtmp"
diff --git a/source/ubase/config.mk b/source/ubase/config.mk
new file mode 100644 (file)
index 0000000..245a590
--- /dev/null
@@ -0,0 +1,15 @@
+# ubase version
+VERSION = 0.1
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = $(PREFIX)/share/man
+
+CC = cc
+AR = ar
+RANLIB = ranlib
+
+CPPFLAGS = -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
+CFLAGS   = -std=c99 -Wall -Wextra
+LDLIBS   = -lcrypt
+LDFLAGS  = -s
diff --git a/source/ubase/ctrlaltdel.8 b/source/ubase/ctrlaltdel.8
new file mode 100644 (file)
index 0000000..a7eb566
--- /dev/null
@@ -0,0 +1,31 @@
+.Dd September 7, 2015
+.Dt CTRLALTDEL 8
+.Os ubase
+.Sh NAME
+.Nm ctrlaltdel
+.Nd toggle Ctrl-Alt-Del behaviour
+.Sh SYNOPSIS
+.Nm
+.Fl h | s
+.Sh DESCRIPTION
+.Nm
+toggles the function of Ctrl-Alt-Del based on the two choices given in
+.Pa linux/kernel/sys.c :
+.Bl -tag -width Ds
+.It hard reset
+reboot the computer immediately without calling
+.Xr sync 2 .
+.It soft reset
+send SIGINT to
+.Xr init 8 .
+.El
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl h
+Set to hard reset.
+.It Fl s
+Set to soft reset.
+.El
+.Sh SEE ALSO
+.Xr sync 2 ,
+.Xr init 8
diff --git a/source/ubase/ctrlaltdel.c b/source/ubase/ctrlaltdel.c
new file mode 100644 (file)
index 0000000..29a23c5
--- /dev/null
@@ -0,0 +1,42 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/syscall.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "reboot.h"
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s -h | -s\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int hflag = 0, sflag = 0, cmd;
+
+       ARGBEGIN {
+       case 'h':
+               hflag = 1;
+               break;
+       case 's':
+               sflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc || !(hflag ^ sflag))
+               usage();
+
+       cmd = hflag ? LINUX_REBOOT_CMD_CAD_ON : LINUX_REBOOT_CMD_CAD_OFF;
+
+       if (syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+                   cmd, NULL) < 0)
+               eprintf("reboot:");
+
+       return 0;
+}
diff --git a/source/ubase/dd.1 b/source/ubase/dd.1
new file mode 100644 (file)
index 0000000..477e99f
--- /dev/null
@@ -0,0 +1,66 @@
+.Dd February 2, 2015
+.Dt DD 1
+.Os ubase
+.Sh NAME
+.Nm dd
+.Nd convert and copy a file
+.Sh SYNOPSIS
+.Nm
+.Op Ar operand...
+.Sh DESCRIPTION
+.Nm
+copies the standard input to the standard output. By default input data is
+read and written in 64kB blocks. When finished,
+.Nm
+displays the number of records read and written as well as the total number
+of bytes copied.
+.Nm
+syncs the filesystem once it is done copying. If you want to disable that use
+the
+.Ar nosync
+option.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Ar bs Ns Op Ar =N
+If
+.Ar bs
+is not specified, the default blocksize is 64kB. If
+.Ar bs
+is specified
+without setting it to a specific value then an optimal value between the
+source and target filesystem will be selected. If this process fails it will
+fallback to the system's pagesize. Adjust
+.Ar N
+to set the block size of the transfers in bytes.
+.It Ar count=N
+Copy only
+.Ar N
+input blocks.
+.It Ar direct
+Use direct I/O for data.
+.It Ar if=file
+Read input from
+.Ar file
+instead of the standard input.
+.It Ar nosync
+Do not sync the filesystem once we are done copying.
+.It Ar quiet
+Enable quiet output.
+.It Ar of=file
+Write output to
+.Ar file
+instead of the standard output. If an initial portion of the output
+.Ar file
+is skipped using the seek operand, the output file is truncated at that
+point.
+.It Ar seek=N
+Seek
+.Ar N
+blocks from the beginning of the output before copying.
+.It Ar skip=N
+Skip
+.Ar N
+blocks from the beginning of the input before copying.
+.It Ar conv=notrunc
+Do not truncate the output file.
+.El
diff --git a/source/ubase/dd.c b/source/ubase/dd.c
new file mode 100644 (file)
index 0000000..cc05d40
--- /dev/null
@@ -0,0 +1,298 @@
+/* (C) 2011-2012 Sebastian Krahmer all rights reserved.
+ *
+ * Optimized dd, to speed up backups etc.
+ *
+ * Permission has been granted to release this code under MIT/X.
+ * The original code is at https://github.com/stealth/odd.  This
+ * version of the code has been modified by sin@2f30.org.
+ */
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "util.h"
+
+struct dd_config {
+       const char *in, *out;
+       uint64_t skip, seek, count, b_in, b_out, rec_in, rec_out;
+       off_t fsize;
+       blksize_t bs;
+       char quiet, nosync, notrunc, direct;
+       time_t t_start, t_end;
+};
+
+static int sigint = 0;
+
+static int
+prepare_copy(struct dd_config *ddc, int *ifd, int *ofd)
+{
+       struct stat st;
+       int fli = O_RDONLY|O_LARGEFILE|O_NOCTTY, flo = O_WRONLY|O_LARGEFILE|O_CREAT|O_NOATIME|O_NOCTTY;
+       long pagesize;
+
+       if (ddc->direct) {
+               fli |= O_DIRECT;
+               flo |= O_DIRECT;
+       }
+
+       if (!ddc->in) *ifd = 0;
+       else if ((*ifd = open(ddc->in, fli)) < 0)
+               return -1;
+
+       if (fstat(*ifd, &st) < 0)
+               return -1;
+
+       ddc->fsize = st.st_size;
+
+       /* If "bsize" is not given, use optimum of both FS' */
+       if (!ddc->bs) {
+               struct statfs fst;
+               memset(&fst, 0, sizeof(fst));
+               pagesize = sysconf(_SC_PAGESIZE);
+               if (pagesize <= 0)
+                       pagesize = 0x1000;
+               if (statfs(ddc->out, &fst) < 0 || fst.f_bsize == 0)
+                       fst.f_bsize = pagesize;
+               if ((unsigned long)fst.f_bsize > (unsigned long)st.st_blksize)
+                       ddc->bs = fst.f_bsize;
+               else
+                       ddc->bs = st.st_blksize;
+               if (ddc->bs == 0)
+                       ddc->bs = pagesize;
+       }
+
+       /* check if device or regular file */
+       if (!S_ISREG(st.st_mode)) {
+               if (S_ISBLK(st.st_mode)) {
+                       if (ioctl(*ifd, BLKGETSIZE64, &ddc->fsize) < 0) {
+                               close(*ifd);
+                               return -1;
+                       }
+               } else {
+                       ddc->fsize = (off_t)-1;
+               }
+       }
+
+       /* skip and seek are in block items */
+       ddc->skip *= ddc->bs;
+       ddc->seek *= ddc->bs;
+
+       /* skip more bytes than are inside source file? */
+       if (ddc->fsize != (off_t)-1 && ddc->skip >= (uint64_t)ddc->fsize) {
+               errno = EINVAL;
+               close(*ifd);
+               return -1;
+       }
+
+       if (!ddc->seek && !ddc->notrunc)
+               flo |= O_TRUNC;
+
+       if (!ddc->out) *ofd = 1;
+       else if ((*ofd = open(ddc->out, flo, st.st_mode)) < 0) {
+               close(*ifd);
+               return -1;
+       }
+
+       if (ddc->seek && !ddc->notrunc) {
+               if (fstat(*ofd, &st) < 0)
+                       return -1;
+               if (!S_ISREG(st.st_mode))
+                       ;
+               else if (ftruncate(*ofd, ddc->seek) < 0)
+                       return -1;
+       }
+
+       if (lseek(*ifd, ddc->skip, SEEK_CUR) < 0) {
+               char buffer[ddc->bs];
+               for (uint64_t i = 0; i < ddc->skip; i += ddc->bs) {
+                       if (read(*ifd, &buffer, ddc->bs) < 0) {
+                               errno = EINVAL;
+                               close(*ifd);
+                               return -1;
+                       }
+               }
+       }
+       lseek(*ofd, ddc->seek, SEEK_CUR);
+       posix_fadvise(*ifd, ddc->skip, 0, POSIX_FADV_SEQUENTIAL);
+       posix_fadvise(*ofd, 0, 0, POSIX_FADV_DONTNEED);
+
+       /* count is in block items too */
+       ddc->count *= ddc->bs;
+
+       /* If no count is given, its the filesize minus skip offset */
+       if (ddc->count == (uint64_t) -1)
+               ddc->count = ddc->fsize - ddc->skip;
+
+       return 0;
+}
+
+static int
+copy_splice(struct dd_config *ddc)
+{
+       int ifd, ofd, p[2] = {-1, -1};
+       ssize_t r = 0;
+       size_t n = 0;
+       fd_set rfd, wfd;
+
+       if (prepare_copy(ddc, &ifd, &ofd) < 0)
+               return -1;
+       if (pipe(p) < 0) {
+               close(ifd); close(ofd);
+               close(p[0]); close(p[1]);
+               return -1;
+       }
+#ifdef F_SETPIPE_SZ
+       for (n = 29; n >= 20; --n) {
+               if (fcntl(p[0], F_SETPIPE_SZ, 1<<n) != -1)
+                       break;
+       }
+       errno = 0;
+#endif
+       n = ddc->bs;
+       for (;ddc->b_out != ddc->count && !sigint;) {
+               FD_ZERO(&rfd);
+               FD_ZERO(&wfd);
+               FD_SET(ifd, &rfd);
+               FD_SET(ofd, &wfd);
+               r = select(ifd > ofd ? ifd + 1 : ofd + 1, &rfd, &wfd, NULL, NULL);
+               if (r < 0)
+                       break;
+               if (FD_ISSET(ifd, &rfd) == 1 && FD_ISSET(ofd, &wfd) == 1) {
+                       if (n > ddc->count - ddc->b_out)
+                               n = ddc->count - ddc->b_out;
+                       r = splice(ifd, NULL, p[1], NULL, n, SPLICE_F_MORE);
+                       if (r <= 0)
+                               break;
+                       ++ddc->rec_in;
+                       r = splice(p[0], NULL, ofd, NULL, r, SPLICE_F_MORE);
+                       if (r <= 0)
+                               break;
+                       ddc->b_out += r;
+                       ++ddc->rec_out;
+               }
+       }
+       close(ifd);
+       close(ofd);
+       close(p[0]);
+       close(p[1]);
+       if (r < 0)
+               return -1;
+       return 0;
+}
+
+static int
+copy(struct dd_config *ddc)
+{
+       int r = 0;
+
+       ddc->t_start = time(NULL);
+
+       r = copy_splice(ddc);
+       ddc->t_end = time(NULL);
+
+       /* avoid div by zero */
+       if (ddc->t_start == ddc->t_end)
+               ++ddc->t_end;
+       return r;
+}
+
+static void
+print_stat(const struct dd_config *ddc)
+{
+       if (ddc->quiet)
+               return;
+
+       fprintf(stderr, "%"PRIu64" records in\n", ddc->rec_in);
+       fprintf(stderr, "%"PRIu64" records out\n", ddc->rec_out);
+       fprintf(stderr, "%"PRIu64" bytes (%"PRIu64" MB) copied", ddc->b_out,
+               ddc->b_out/(1<<20));
+       fprintf(stderr, ", %lu s, %f MB/s\n",
+               (unsigned long)ddc->t_end - ddc->t_start,
+               ((double)(ddc->b_out/(1<<20)))/(ddc->t_end - ddc->t_start));
+}
+
+static void
+sig_int(int unused)
+{
+       (void) unused;
+       fprintf(stderr, "SIGINT! Aborting ...\n");
+       sigint = 1;
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-h] [if=infile] [of=outfile] [bs[=N]] [seek=N] "
+               "[skip=N] [count=N] [direct] [quiet] [nosync]"
+               "[conv=notrunc]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int i = 0;
+       char buf[1024];
+       struct dd_config config;
+
+       argv0 = argv[0];
+       memset(&config, 0, sizeof(config));
+       config.bs = 1<<16;
+       config.in = NULL;
+       config.out = NULL;
+       config.count = (uint64_t) -1;
+
+       /* emulate 'dd' argument parsing */
+       for (i = 1; i < argc; ++i) {
+               memset(buf, 0, sizeof(buf));
+               if (strncmp(argv[i], "if=", 3) == 0)
+                       config.in = argv[i]+3;
+               else if (strncmp(argv[i], "of=", 3) == 0)
+                       config.out = argv[i]+3;
+               else if (sscanf(argv[i], "skip=%1023s", buf) == 1)
+                       config.skip = estrtoul(buf, 0);
+               else if (sscanf(argv[i], "seek=%1023s", buf) == 1)
+                       config.seek = estrtoul(buf, 0);
+               else if (sscanf(argv[i], "count=%1023s", buf) == 1)
+                       config.count = estrtoul(buf, 0);
+               else if (strcmp(argv[i], "direct") == 0)
+                       config.direct = 1;
+               else if (sscanf(argv[i], "bs=%1023s", buf) == 1)
+                       config.bs = estrtoul(buf, 0);
+               else if (strcmp(argv[i], "bs") == 0)
+                       config.bs = 0;
+               else if (strcmp(argv[i], "quiet") == 0)
+                       config.quiet = 1;
+               else if (strcmp(argv[i], "nosync") == 0)
+                       config.nosync = 1;
+               else if (strcmp(argv[i], "conv=notrunc") == 0)
+                       config.notrunc = 1;
+               else if (strcmp(argv[i], "-h") == 0)
+                       usage();
+       }
+
+       signal(SIGPIPE, SIG_IGN);
+       signal(SIGINT, sig_int);
+
+       if (copy(&config) < 0)
+               weprintf("copy:");
+       print_stat(&config);
+
+       if (config.nosync == 0)
+               sync();
+       return errno;
+}
diff --git a/source/ubase/df.1 b/source/ubase/df.1
new file mode 100644 (file)
index 0000000..290a328
--- /dev/null
@@ -0,0 +1,27 @@
+.Dd February 2, 2015
+.Dt DF 1
+.Os ubase
+.Sh NAME
+.Nm df
+.Nd show file system usage
+.Sh SYNOPSIS
+.Nm
+.Op Fl ahis
+.Sh DESCRIPTION
+.Nm
+displays the amount of disk space available on a file system.
+If no arguments are given,
+.Nm
+shows all the file systems using 512-byte blocks.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl a
+Show all file systems including dummy ones. This is the default
+option.
+.It Fl h
+Not implemented.
+.It Fl i
+Not implemented.
+.It Fl s
+Not implemented.
+.El
diff --git a/source/ubase/df.c b/source/ubase/df.c
new file mode 100644 (file)
index 0000000..4d595d6
--- /dev/null
@@ -0,0 +1,139 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/statvfs.h>
+
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+static long blksize = 512;
+static int aflag = 0;
+static int hflag = 0;
+static int kflag = 0;
+
+#define CALC_POWER(n, power, base, i) do { \
+       while (n > power) {                \
+               power = power * base;      \
+               i++;                       \
+       }                                  \
+} while(0)
+
+static void
+print_human(
+       const char         *fsname,
+       unsigned long long total,
+       unsigned long long used,
+       unsigned long long avail,
+       int                capacity,
+       const char         *dir)
+{
+       long base = 1024;
+       unsigned long long power_total = base;
+       unsigned long long power_used = base;
+       unsigned long long power_avail = base;
+       char postfixes[] = {'B', 'K', 'M', 'G', 'T', 'P', 'E'};
+       int i = 0, j = 0, k = 0;
+
+       total = total * blksize;
+       used = used * blksize;
+       avail = avail * blksize;
+
+       CALC_POWER(total, power_total, base, i);
+       CALC_POWER(used, power_used, base, j);
+       CALC_POWER(avail, power_avail, base, k);
+
+       total = i ? total / (power_total / base) : total;
+       used = j ? used / (power_used / base) : used;
+       avail = k ? avail / (power_avail / base) : avail;
+       printf("%-12s %9llu%c %9llu%c %9llu%c %7d%%  %s\n",
+              fsname, total, postfixes[i], used, postfixes[j],
+              avail, postfixes[k], capacity, dir);
+}
+
+static int
+mnt_show(const char *fsname, const char *dir)
+{
+       struct statvfs s;
+       unsigned long long total, used, avail;
+       int capacity = 0;
+       int bs;
+
+       if (statvfs(dir, &s) < 0)
+               return -1;
+
+       bs = s.f_frsize / blksize;
+       total = s.f_blocks * bs;
+       avail = s.f_bfree * bs;
+       used = total - avail;
+
+       if (used + avail) {
+               capacity = (used * 100) / (used + avail);
+               if (used * 100 != capacity * (used + avail))
+                       capacity++;
+       }
+
+       if (hflag)
+               print_human(fsname, total, used, avail, capacity, dir);
+       else
+               printf("%-12s %9llu %9llu %9llu %7d%%  %s\n",
+                      fsname, total, used, avail, capacity, dir);
+
+       return 0;
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-a]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct mntent *me = NULL;
+       FILE *fp;
+       int ret = 0;
+
+       ARGBEGIN {
+       case 'a':
+               aflag = 1;
+               break;
+       case 'h':
+               hflag = 1;
+               kflag = 0;
+               break;
+       case 'k':
+               kflag = 1;
+               hflag = 0;
+               blksize = 1024;
+               break;
+       case 's':
+       case 'i':
+               eprintf("not implemented\n");
+       default:
+               usage();
+       } ARGEND;
+
+       if (hflag)
+               printf("Filesystem         Size       Used      "
+                      "Avail Capacity   Mounted on\n");
+       else
+               printf("Filesystem  %ld-blocks      Used     "
+                      "Avail Capacity  Mounted on\n", blksize);
+
+       fp = setmntent("/proc/mounts", "r");
+       if (!fp)
+               eprintf("setmntent %s:", "/proc/mounts");
+       while ((me = getmntent(fp)) != NULL) {
+               if (aflag == 0)
+                       if (strcmp(me->mnt_type, "rootfs") == 0)
+                               continue;
+               if (mnt_show(me->mnt_fsname, me->mnt_dir) < 0)
+                       ret = 1;
+       }
+       endmntent(fp);
+
+       return ret;
+}
diff --git a/source/ubase/dmesg.1 b/source/ubase/dmesg.1
new file mode 100644 (file)
index 0000000..c66785c
--- /dev/null
@@ -0,0 +1,28 @@
+.Dd February 2, 2015
+.Dt DMESG 1
+.Os ubase
+.Sh NAME
+.Nm dmesg
+.Nd print or control the kernel ring buffer
+.Sh SYNOPSIS
+.Nm
+.Op Fl Ccr
+.Op Fl n Ar level
+.Sh DESCRIPTION
+.Nm
+examines or controls the kernel ring buffer. By default it reads all the
+messages from the kernel ring buffer and prints them to stdout.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl C
+Clear the ring buffer.
+.It Fl c
+Clear the ring buffer after printing its contents.
+.It Fl n Ar level
+Set the console
+.Ar level .
+The log levels are defined in the file
+.Pa include/linux/kern_levels.h .
+.It Fl r
+Print the raw message buffer.
+.El
diff --git a/source/ubase/dmesg.c b/source/ubase/dmesg.c
new file mode 100644 (file)
index 0000000..fff1461
--- /dev/null
@@ -0,0 +1,81 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/klog.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+enum {
+       SYSLOG_ACTION_READ_ALL = 3,
+       SYSLOG_ACTION_CLEAR = 5,
+       SYSLOG_ACTION_CONSOLE_LEVEL = 8,
+       SYSLOG_ACTION_SIZE_BUFFER = 10
+};
+
+static void
+dmesg_show(const void *buf, size_t n)
+{
+       const char *p = buf;
+       ssize_t r;
+
+       r = write(1, p, n);
+       if (r < 0)
+               eprintf("write:");
+       if (r > 0 && p[r - 1] != '\n')
+               putchar('\n');
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-Ccr] [-n level]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int n;
+       char *buf;
+       int cflag = 0;
+       long level;
+
+       ARGBEGIN {
+       case 'C':
+               if (klogctl(SYSLOG_ACTION_CLEAR, NULL, 0) < 0)
+                       eprintf("klogctl:");
+               return 0;
+       case 'c':
+               cflag = 1;
+               break;
+       case 'r':
+               break;
+       case 'n':
+               level = estrtol(EARGF(usage()), 10);
+               if (klogctl(SYSLOG_ACTION_CONSOLE_LEVEL, NULL, level) < 0)
+                       eprintf("klogctl:");
+               return 0;
+       default:
+               usage();
+       } ARGEND;
+
+       n = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
+       if (n < 0)
+               eprintf("klogctl:");
+
+       buf = emalloc(n);
+
+       n = klogctl(SYSLOG_ACTION_READ_ALL, buf, n);
+       if (n < 0)
+               eprintf("klogctl:");
+
+       dmesg_show(buf, n);
+
+       if (cflag && klogctl(SYSLOG_ACTION_CLEAR, NULL, 0) < 0)
+               eprintf("klogctl:");
+
+       free(buf);
+       return 0;
+}
\ No newline at end of file
diff --git a/source/ubase/eject.1 b/source/ubase/eject.1
new file mode 100644 (file)
index 0000000..480db5f
--- /dev/null
@@ -0,0 +1,25 @@
+.Dd September 9, 2015
+.Dt EJECT 1
+.Os ubase
+.Sh NAME
+.Nm eject
+.Nd control device trays
+.Sh SYNOPSIS
+.Nm
+.Op Fl t
+.Op Ar device ...
+.Sh DESCRIPTION
+.Nm
+opens the tray of each
+.Ar device .
+If no
+.Ar device
+is given
+.Nm
+opens the tray of
+.Pa /dev/sr0 .
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl t
+Close instead of open the tray.
+.El
diff --git a/source/ubase/eject.c b/source/ubase/eject.c
new file mode 100644 (file)
index 0000000..1de9179
--- /dev/null
@@ -0,0 +1,68 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "util.h"
+
+enum {
+       OPEN_TRAY = 0x5309,
+       CLOSE_TRAY = 0x5319,
+};
+
+static int tflag = 0;
+static int ret = 0;
+
+static void
+eject(const char *devname)
+{
+       int fd, out;
+
+       if ((fd = open(devname, O_RDONLY | O_NONBLOCK)) < 0) {
+               weprintf("open %s:", devname);
+               ret = 1;
+       } else if (tflag && ioctl(fd, CLOSE_TRAY, &out) < 0) {
+               weprintf("ioctl %s:", devname);
+               ret = 1;
+       } else if (!tflag && ioctl(fd, OPEN_TRAY, &out) < 0) {
+               weprintf("ioctl %s:", devname);
+               ret = 1;
+       }
+
+       if (fd >= 0 && close(fd) < 0) {
+               weprintf("close %s:", devname);
+               ret = 1;
+       }
+}
+
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-t] [device ...]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       ARGBEGIN {
+       case 't':
+               tflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (!argc) {
+               eject("/dev/sr0");
+       } else {
+               for (; *argv; argc--, argv++)
+                       eject(*argv);
+       }
+
+       return ret;
+}
diff --git a/source/ubase/fallocate.1 b/source/ubase/fallocate.1
new file mode 100644 (file)
index 0000000..d0fb01b
--- /dev/null
@@ -0,0 +1,33 @@
+.Dd September 12, 2015
+.Dt FALLOCATE 1
+.Os ubase
+.Sh NAME
+.Nm fallocate
+.Nd preallocate files
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar num
+.Fl l Ar num
+.Ar file ...
+.Sh DESCRIPTION
+.Nm
+if necessary creates and preallocates each
+.Ar file
+without truncation.
+.sp
+Given the filesystem supports
+.Xr fallocate 2 ,
+it is a very fast method of preallocation.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl l Ar num
+Preallocate
+.Ar num
+bytes.
+.It Fl o Ar num
+Offset allocation by
+.Ar num
+bytes.
+.El
+.Sh SEE ALSO
+.Xr fallocate 2
diff --git a/source/ubase/fallocate.c b/source/ubase/fallocate.c
new file mode 100644 (file)
index 0000000..47eb470
--- /dev/null
@@ -0,0 +1,55 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-o num] -l num file ...\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int fd, ret = 0;
+       off_t size = 0, offset = 0;
+
+       ARGBEGIN {
+       case 'l':
+               size = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
+               break;
+       case 'o':
+               offset = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (!argc || !size)
+               usage();
+
+       for (; *argv; argc--, argv++) {
+               if ((fd = open(*argv, O_RDWR | O_CREAT, 0644)) < 0) {
+                       weprintf("open %s:", *argv);
+                       ret = 1;
+               } else if (posix_fallocate(fd, offset, size) < 0) {
+                       weprintf("posix_fallocate %s:", *argv);
+                       ret = 1;
+               }
+
+               if (fd >= 0 && close(fd) < 0) {
+                       weprintf("close %s:", *argv);
+                       ret = 1;
+               }
+       }
+
+       return ret;
+}
diff --git a/source/ubase/free.1 b/source/ubase/free.1
new file mode 100644 (file)
index 0000000..70403e6
--- /dev/null
@@ -0,0 +1,24 @@
+.Dd February 2, 2015
+.Dt FREE 1
+.Os ubase
+.Sh NAME
+.Nm free
+.Nd display amount of free and used memory in the system
+.Sh SYNOPSIS
+.Nm
+.Op Fl bgkm
+.Sh DESCRIPTION
+.Nm
+displays the total amount of free and used physical and swap memory in the
+system, as well as the buffers used by the kernel.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl b
+Display the amount of memory in bytes. This is the default.
+.It Fl g
+Display the amount of memory in gigabytes.
+.It Fl k
+Display the amount of memory in kilobytes.
+.It Fl m
+Display the amount of memory in megabytes.
+.El
\ No newline at end of file
diff --git a/source/ubase/free.c b/source/ubase/free.c
new file mode 100644 (file)
index 0000000..2537fe5
--- /dev/null
@@ -0,0 +1,72 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/sysinfo.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+static unsigned int mem_unit = 1;
+static unsigned int unit_shift;
+
+static unsigned long long
+scale(unsigned long long v)
+{
+       return (v * mem_unit) >> unit_shift;
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-bkmg]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct sysinfo info;
+
+       if (sysinfo(&info) < 0)
+               eprintf("sysinfo:");
+       mem_unit = info.mem_unit ? info.mem_unit : 1;
+
+       ARGBEGIN {
+       case 'b':
+               unit_shift = 0;
+               break;
+       case 'k':
+               unit_shift = 10;
+               break;
+       case 'm':
+               unit_shift = 20;
+               break;
+       case 'g':
+               unit_shift = 30;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       printf("     %13s%13s%13s%13s%13s\n",
+              "total",
+              "used",
+              "free",
+              "shared", "buffers");
+       printf("Mem: ");
+       printf("%13llu%13llu%13llu%13llu%13llu\n",
+              scale(info.totalram),
+              scale(info.totalram - info.freeram),
+              scale(info.freeram),
+              scale(info.sharedram),
+              scale(info.bufferram));
+       printf("-/+ buffers/cache:");
+       printf("%13llu%13llu\n",
+              scale(info.totalram - info.freeram - info.bufferram),
+              scale(info.freeram + info.bufferram));
+       printf("Swap:");
+       printf("%13llu%13llu%13llu\n",
+              scale(info.totalswap),
+              scale(info.totalswap - info.freeswap),
+              scale(info.freeswap));
+       return 0;
+}
diff --git a/source/ubase/freeramdisk.8 b/source/ubase/freeramdisk.8
new file mode 100644 (file)
index 0000000..1becdde
--- /dev/null
@@ -0,0 +1,13 @@
+.Dd February 2, 2015
+.Dt FREERAMDISK 8
+.Os ubase
+.Sh NAME
+.Nm freeramdisk
+.Nd free memory used by the loadlin ramdisk
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+frees the memory that is used by the ramdisk. It uses the
+.Pa /dev/ram
+device node.
\ No newline at end of file
diff --git a/source/ubase/freeramdisk.c b/source/ubase/freeramdisk.c
new file mode 100644 (file)
index 0000000..c9943ba
--- /dev/null
@@ -0,0 +1,39 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *dev = "/dev/ram";
+       int fd;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc != 0)
+               usage();
+
+       if ((fd = open(dev, O_RDWR)) < 0)
+               eprintf("open: %s:", dev);
+       if (ioctl(fd, BLKFLSBUF, dev) < 0)
+               eprintf("BLKFLSBUF %s:", dev);
+       close(fd);
+       return 0;
+}
diff --git a/source/ubase/fsfreeze.8 b/source/ubase/fsfreeze.8
new file mode 100644 (file)
index 0000000..4b48982
--- /dev/null
@@ -0,0 +1,33 @@
+.Dd March 26, 2016
+.Dt FSFREEZE 8
+.Os ubase
+.Sh NAME
+.Nm fsfreeze
+.Nd suspend access to a filesystem
+.Sh SYNOPSIS
+.Nm
+.Po Fl f | Fl u Pc
+.Ar mountpoint
+.Sh DESCRIPTION
+.Nm
+suspends and resumes access to a filesystem.
+.Nm
+is intended to be used with hardware RAID devices that support the creation
+of snapshots.
+The
+.Ar mountpoint
+argument is the pathname of the directory where the filesystem is mounted.
+The filesystem must be mounted to be frozen.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl f
+Freeze the filesystem mounted at
+.Ar mountpoint .
+.It Fl u
+Unfreeze the filesystem mounted at
+.Ar mountpoint .
+.El
+.Sh SEE ALSO
+.Xr mount 8
+.Sh BUGS
+Only works for ext3/4, reiserfs, jfs and xfs.
diff --git a/source/ubase/fsfreeze.c b/source/ubase/fsfreeze.c
new file mode 100644 (file)
index 0000000..1ce77ff
--- /dev/null
@@ -0,0 +1,54 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define FIFREEZE       _IOWR('X', 119, int)    /* Freeze */
+#define FITHAW         _IOWR('X', 120, int)    /* Thaw */
+
+static void
+usage(void)
+{
+       eprintf("usage: %s (-f | -u) mountpoint\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int fflag = 0;
+       int uflag = 0;
+       long p = 1;
+       int fd;
+
+       ARGBEGIN {
+       case 'f':
+               fflag = 1;
+               break;
+       case 'u':
+               uflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc != 1)
+               usage();
+
+       if ((fflag ^ uflag) == 0)
+               usage();
+
+       fd = open(argv[0], O_RDONLY);
+       if (fd < 0)
+               eprintf("open: %s:", argv[0]);
+       if (ioctl(fd, fflag == 1 ? FIFREEZE : FITHAW, &p) < 0)
+               eprintf("%s %s:", fflag == 1 ? "FIFREEZE" : "FITHAW", argv[0]);
+       close(fd);
+       return 0;
+}
diff --git a/source/ubase/getty.8 b/source/ubase/getty.8
new file mode 100644 (file)
index 0000000..f2df064
--- /dev/null
@@ -0,0 +1,22 @@
+.Dd February 2, 2015
+.Dt GETTY 8
+.Os ubase
+.Sh NAME
+.Nm getty
+.Nd suckless linux getty
+.Sh SYNOPSIS
+.Nm
+.Op Ar tty Op Ar term Op Ar cmd Op Ar args...
+.Sh DESCRIPTION
+.Nm
+opens a tty device, prompts for a login name and by default
+invokes the /bin/login program. You can start another program instead of
+/bin/login via
+.Ar cmd
+with
+.Ar args .
+The hostname is printed in the login name prompt as well. The
+.Ar tty
+should be specified using an absolute path.
+.Sh SEE ALSO
+.Xr login 1
\ No newline at end of file
diff --git a/source/ubase/getty.c b/source/ubase/getty.c
new file mode 100644 (file)
index 0000000..cef5c4e
--- /dev/null
@@ -0,0 +1,140 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "config.h"
+#include "util.h"
+
+static char *tty = "/dev/tty1";
+static char *defaultterm = "linux";
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [tty] [term] [cmd] [args...]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char term[128], logname[LOGIN_NAME_MAX], c;
+       char hostname[HOST_NAME_MAX + 1];
+       struct utmp usr;
+       struct sigaction sa;
+       FILE *fp;
+       int fd;
+       unsigned int i = 0;
+       ssize_t n;
+       long pos;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       strlcpy(term, defaultterm, sizeof(term));
+       if (argc > 0) {
+               tty = argv[0];
+               if (argc > 1)
+                       strlcpy(term, argv[1], sizeof(term));
+       }
+
+       sa.sa_handler = SIG_IGN;
+       sa.sa_flags = 0;
+       sigemptyset(&sa.sa_mask);
+       sigaction(SIGHUP, &sa, NULL);
+
+       setenv("TERM", term, 1);
+
+       setsid();
+
+       fd = open(tty, O_RDWR);
+       if (fd < 0)
+               eprintf("open %s:", tty);
+       if (isatty(fd) == 0)
+               eprintf("%s is not a tty\n", tty);
+
+       /* steal the controlling terminal if necessary */
+       if (ioctl(fd, TIOCSCTTY, (void *)1) != 0)
+               weprintf("TIOCSCTTY: could not set controlling tty\n");
+       vhangup();
+       close(fd);
+
+       fd = open(tty, O_RDWR);
+       if (fd < 0)
+               eprintf("open %s:", tty);
+       dup2(fd, 0);
+       dup2(fd, 1);
+       dup2(fd, 2);
+       if (fchown(fd, 0, 0) < 0)
+               weprintf("fchown %s:", tty);
+       if (fchmod(fd, 0600) < 0)
+               weprintf("fchmod %s:", tty);
+       if (fd > 2)
+               close(fd);
+
+       sa.sa_handler = SIG_DFL;
+       sa.sa_flags = 0;
+       sigemptyset(&sa.sa_mask);
+       sigaction(SIGHUP, &sa, NULL);
+
+       /* Clear all utmp entries for this tty */
+       fp = fopen(UTMP_PATH, "r+");
+       if (fp) {
+               do {
+                       pos = ftell(fp);
+                       if (fread(&usr, sizeof(usr), 1, fp) != 1)
+                               break;
+                       if (usr.ut_line[0] == '\0')
+                               continue;
+                       if (strcmp(usr.ut_line, tty) != 0)
+                               continue;
+                       memset(&usr, 0, sizeof(usr));
+                       fseek(fp, pos, SEEK_SET);
+                       if (fwrite(&usr, sizeof(usr), 1, fp) != 1)
+                               break;
+               } while (1);
+               if (ferror(fp))
+                       weprintf("%s: I/O error:", UTMP_PATH);
+               fclose(fp);
+       }
+
+       if (argc > 2)
+               return execvp(argv[2], argv + 2);
+
+       if (gethostname(hostname, sizeof(hostname)) == 0)
+               printf("%s ", hostname);
+       printf("login: ");
+       fflush(stdout);
+
+       /* Flush pending input */
+       ioctl(0, TCFLSH, (void *)0);
+       memset(logname, 0, sizeof(logname));
+       while (1) {
+               n = read(0, &c, 1);
+               if (n < 0)
+                       eprintf("read:");
+               if (n == 0)
+                       return 1;
+               if (i >= sizeof(logname) - 1)
+                       eprintf("login name too long\n");
+               if (c == '\n' || c == '\r')
+                       break;
+               logname[i++] = c;
+       }
+       if (logname[0] == '-')
+               eprintf("login name cannot start with '-'\n");
+       if (logname[0] == '\0')
+               return 1;
+       return execlp("/bin/login", "login", "-p", logname, NULL);
+}
diff --git a/source/ubase/halt.8 b/source/ubase/halt.8
new file mode 100644 (file)
index 0000000..9face36
--- /dev/null
@@ -0,0 +1,21 @@
+.Dd February 2, 2015
+.Dt HALT 8
+.Os ubase
+.Sh NAME
+.Nm halt
+.Nd power-off or reboot the machine
+.Sh SYNOPSIS
+.Nm
+.Op Fl pr
+.Sh DESCRIPTION
+.Nm
+can be used to power-off or reboot the machine.
+This is a low-level tool and should not be used directly or data-loss
+can happen if the filesystems are not properly unmounted first.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl p
+Power-off the machine.
+.It Fl r
+Reboot the machine.
+.El
diff --git a/source/ubase/halt.c b/source/ubase/halt.c
new file mode 100644 (file)
index 0000000..6bab298
--- /dev/null
@@ -0,0 +1,51 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/syscall.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "reboot.h"
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-pr]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int pflag = 0, rflag = 0;
+       int cmd = LINUX_REBOOT_CMD_HALT;
+
+       ARGBEGIN {
+       case 'p':
+               pflag = 1;
+               break;
+       case 'r':
+               rflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc > 0)
+               usage();
+
+       sync();
+
+       if (pflag && rflag)
+               usage();
+
+       if (pflag)
+               cmd = LINUX_REBOOT_CMD_POWER_OFF;
+       if (rflag)
+               cmd = LINUX_REBOOT_CMD_RESTART;
+
+       if (syscall(__NR_reboot, LINUX_REBOOT_MAGIC1,
+                   LINUX_REBOOT_MAGIC2, cmd, NULL) < 0)
+               eprintf("reboot:");
+       return 0;
+}
diff --git a/source/ubase/hwclock.8 b/source/ubase/hwclock.8
new file mode 100644 (file)
index 0000000..483c3a4
--- /dev/null
@@ -0,0 +1,31 @@
+.Dd February 2, 2015
+.Dt HWCLOCK 8
+.Os ubase
+.Sh NAME
+.Nm hwclock
+.Nd query or set the hardware clock
+.Sh SYNOPSIS
+.Nm
+.Op Fl r | Fl s | Fl w
+.Op Fl u
+.Op Ar dev
+.Sh DESCRIPTION
+.Nm
+is a tool for accessing the hardware clock. You can display the current time,
+set the hardware clock from the System Time, or set the System Time from the
+hardware clock. It currently only works with UTC. You can use
+.Ar dev
+to specify the RTC device node absolute path. By default
+it will use
+.Pa /dev/rtc .
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl r
+Read the hardware clock and print the time on stdout.
+.It Fl s
+Set the system time from the hardware clock.
+.It Fl u
+Use UTC. This is the default option.
+.It Fl w
+Set the hardware clock to the system time.
+.El
diff --git a/source/ubase/hwclock.c b/source/ubase/hwclock.c
new file mode 100644 (file)
index 0000000..d63a72a
--- /dev/null
@@ -0,0 +1,159 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "rtc.h"
+#include "util.h"
+
+static void
+readrtctm(struct tm *tm, int fd)
+{
+       struct rtc_time rt;
+
+       memset(&rt, 0, sizeof(rt));
+       if (ioctl(fd, RTC_RD_TIME, &rt) < 0)
+               eprintf("RTC_RD_TIME:");
+       tm->tm_sec = rt.tm_sec;
+       tm->tm_min = rt.tm_min;
+       tm->tm_hour = rt.tm_hour;
+       tm->tm_mday = rt.tm_mday;
+       tm->tm_mon = rt.tm_mon;
+       tm->tm_year = rt.tm_year;
+       tm->tm_wday = rt.tm_wday;
+       tm->tm_yday = rt.tm_yday;
+       tm->tm_isdst = rt.tm_isdst;
+}
+
+static void
+writertctm(struct tm *tm, int fd)
+{
+       struct rtc_time rt;
+
+       rt.tm_sec = tm->tm_sec;
+       rt.tm_min = tm->tm_min;
+       rt.tm_hour = tm->tm_hour;
+       rt.tm_mday = tm->tm_mday;
+       rt.tm_mon = tm->tm_mon;
+       rt.tm_year = tm->tm_year;
+       rt.tm_wday = tm->tm_wday;
+       rt.tm_yday = tm->tm_yday;
+       rt.tm_isdst = tm->tm_isdst;
+       if (ioctl(fd, RTC_SET_TIME, &rt) < 0)
+               eprintf("RTC_SET_TIME:");
+}
+
+static void
+show(char *dev)
+{
+       struct tm tm;
+       time_t t;
+       int fd;
+
+       fd = open(dev, O_RDONLY);
+       if (fd < 0)
+               eprintf("open %s:", dev);
+       readrtctm(&tm, fd);
+       t = mktime(&tm);
+       printf("%s", asctime(localtime(&t)));
+       close(fd);
+}
+
+static void
+hctosys(char *dev)
+{
+       struct timeval tv;
+       struct tm tm;
+       int r;
+       int fd;
+
+       fd = open(dev, O_RDONLY);
+       if (fd < 0)
+               eprintf("open %s:", dev);
+       readrtctm(&tm, fd);
+       tv.tv_sec = mktime(&tm);
+       tv.tv_usec = 0;
+       r = settimeofday(&tv, NULL);
+       if (r < 0)
+               eprintf("settimeofday:");
+       close(fd);
+}
+
+static void
+systohc(char *dev)
+{
+       struct timeval tv;
+       struct tm *tm;
+       time_t t;
+       int fd;
+
+       fd = open(dev, O_WRONLY);
+       if (fd < 0)
+               eprintf("open %s:", dev);
+       gettimeofday(&tv, NULL);
+       t = tv.tv_sec;
+       tm = gmtime(&t);
+       weprintf("warning: assuming UTC for systohc\n");
+       writertctm(tm, fd);
+       close(fd);
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-rsw] [-u] [dev]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *dev = "/dev/rtc";
+       int rflag = 0;
+       int sflag = 0;
+       int wflag = 0;
+
+       ARGBEGIN {
+       case 'r':
+               rflag = 1;
+               break;
+       case 's':
+               sflag = 1;
+               break;
+       case 'w':
+               wflag = 1;
+               break;
+       case 'u':
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc > 1)
+               usage();
+       else if (argc == 1)
+               dev = argv[0];
+
+       if ((rflag ^ sflag ^ wflag) == 0)
+               eprintf("missing or incompatible function\n");
+
+       /* Only UTC support at the moment */
+       setenv("TZ", "UTC0", 1);
+       tzset();
+
+       if (rflag == 1)
+               show(dev);
+       else if (sflag == 1)
+               hctosys(dev);
+       else if (wflag == 1)
+               systohc(dev);
+
+       return 0;
+}
\ No newline at end of file
diff --git a/source/ubase/id.1 b/source/ubase/id.1
new file mode 100644 (file)
index 0000000..5f575f5
--- /dev/null
@@ -0,0 +1,29 @@
+.Dd April 24, 2015
+.Dt ID 1
+.Os ubase
+.Sh NAME
+.Nm id
+.Nd print real and effective user and group IDs
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Op Fl g | u | G
+.Op Ar user | uid
+.Sh DESCRIPTION
+.Nm
+prints user and group information of the calling process to standard output.
+If a login name or uid is specified, the user and group information of that
+user is displayed.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl n
+Print names instead of ID numbers, for -g, -u, and -G.
+.It Fl g
+Print only the effective group ID.
+.It Fl u
+Print only the effective user ID.
+.It Fl G
+Display group information as whitespace separated numbers, in no particular order.
+.El
+.Sh SEE ALSO
+.Xr who 1
diff --git a/source/ubase/id.c b/source/ubase/id.c
new file mode 100644 (file)
index 0000000..655750e
--- /dev/null
@@ -0,0 +1,176 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void groupid(struct passwd *pw);
+static void user(struct passwd *pw);
+static void userid(uid_t id);
+static void usernam(const char *nam);
+
+static int gflag = 0;
+static int uflag = 0;
+static int Gflag = 0;
+static int nflag = 0;
+
+static void
+groupid(struct passwd *pw)
+{
+       gid_t gid, groups[NGROUPS_MAX];
+       struct group *gr;
+       int ngroups;
+       int i;
+
+       ngroups = NGROUPS_MAX;
+       getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);
+       for (i = 0; i < ngroups; i++) {
+               gid = groups[i];
+               if (nflag) {
+                       if (!(gr = getgrgid(gid)))
+                               eprintf("getgrgid:");
+                       printf("%s", gr->gr_name);
+               } else
+                       printf("%u", gid);
+
+               if (i < ngroups - 1)
+                       putchar(' ');
+       }
+       putchar('\n');
+}
+
+static void
+user(struct passwd *pw)
+{
+       struct group *gr;
+       gid_t gid, groups[NGROUPS_MAX];
+       int ngroups;
+       int i;
+
+       if (uflag) {
+               if (nflag)
+                       printf("%s\n", pw->pw_name);
+               else
+                       printf("%u\n", pw->pw_uid);
+               return;
+       } else if (gflag) {
+               if (nflag) {
+                       if (!(gr = getgrgid(pw->pw_gid)))
+                               eprintf("getgrgid:");
+                       printf("%s\n", gr->gr_name);
+               } else
+                       printf("%u\n", pw->pw_gid);
+               return;
+       }
+
+       printf("uid=%u(%s)", pw->pw_uid, pw->pw_name);
+       printf(" gid=%u", pw->pw_gid);
+       if (!(gr = getgrgid(pw->pw_gid)))
+               eprintf("getgrgid:");
+       printf("(%s)", gr->gr_name);
+
+       ngroups = NGROUPS_MAX;
+       getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);
+       for (i = 0; i < ngroups; i++) {
+               gid = groups[i];
+               printf("%s%u", !i ? " groups=" : ",", gid);
+               if (!(gr = getgrgid(gid)))
+                       eprintf("getgrgid:");
+               printf("(%s)", gr->gr_name);
+       }
+       putchar('\n');
+}
+
+static void
+usernam(const char *nam)
+{
+       struct passwd *pw;
+
+       errno = 0;
+       pw = getpwnam(nam);
+       if (!pw) {
+               if (errno)
+                       eprintf("getpwnam %s:", nam);
+               else
+                       eprintf("getpwnam %s: no such user\n", nam);
+       }
+       if (Gflag)
+               groupid(pw);
+       else
+               user(pw);
+}
+
+static void
+userid(uid_t id)
+{
+       struct passwd *pw;
+
+       errno = 0;
+       pw = getpwuid(id);
+       if (!pw) {
+               if (errno)
+                       eprintf("getpwuid %d:", id);
+               else
+                       eprintf("getpwuid %d: no such user\n", id);
+       }
+       if (Gflag)
+               groupid(pw);
+       else
+               user(pw);
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-n] [-g | -u | -G] [user | uid]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       ARGBEGIN {
+       case 'g':
+               gflag = 1;
+               break;
+       case 'u':
+               uflag = 1;
+               break;
+       case 'G':
+               Gflag = 1;
+               break;
+       case 'n':
+               nflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       /* ensure that only one of -g, -u, or -G was specified */
+       if (gflag + uflag + Gflag > 1)
+               usage();
+
+       switch (argc) {
+       case 0:
+               userid(getuid());
+               break;
+       case 1:
+               /* user names can't begin [0-9] */
+               if (isdigit(argv[0][0]))
+                       userid(estrtol(argv[0], 0));
+               else
+                       usernam(argv[0]);
+               break;
+       default:
+               usage();
+       }
+
+       return 0;
+}
diff --git a/source/ubase/insmod.8 b/source/ubase/insmod.8
new file mode 100644 (file)
index 0000000..2afe150
--- /dev/null
@@ -0,0 +1,18 @@
+.Dd February 2, 2015
+.Dt INSMOD 8
+.Os ubase
+.Sh NAME
+.Nm insmod
+.Nd insert a module into the Linux kernel
+.Sh SYNOPSIS
+.Nm
+.Ar filename
+.Op Ar args...
+.Sh DESCRIPTION
+.Nm
+inserts the module specified by
+.Ar filename
+into the kernel. It does not handle module dependencies.
+.Sh SEE ALSO
+.Xr lsmod 8 ,
+.Xr rmmod 8
\ No newline at end of file
diff --git a/source/ubase/insmod.c b/source/ubase/insmod.c
new file mode 100644 (file)
index 0000000..33c6ab5
--- /dev/null
@@ -0,0 +1,69 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s filename [args...]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *buf = NULL, *opts = NULL;
+       size_t blen, plen = 0;
+       int i, fd;
+       ssize_t n;
+       struct stat sb;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               usage();
+
+       fd = open(argv[0], O_RDONLY);
+       if (fd < 0)
+               eprintf("open %s:", argv[0]);
+       if (fstat(fd, &sb) < 0)
+               eprintf("stat %s:", argv[0]);
+       blen = sb.st_size;
+       buf = emalloc(blen);
+
+       n = read(fd, buf, blen);
+       if (n < 0 || (size_t)n != blen)
+               eprintf("read:");
+
+       argc--;
+       argv++;
+
+       for (i = 0; i < argc; i++)
+               plen += strlen(argv[i]);
+       if (plen > 0) {
+               plen += argc;
+               opts = ecalloc(1, plen);
+               for (i = 0; i < argc; i++) {
+                       strcat(opts, argv[i]);
+                       if (i + 1 < argc)
+                               strcat(opts, " ");
+               }
+       }
+
+       if (syscall(__NR_init_module, buf, blen, !opts ? "" : opts) < 0)
+               eprintf("init_module:");
+
+       free(opts);
+       free(buf);
+       return 0;
+}
diff --git a/source/ubase/killall5.8 b/source/ubase/killall5.8
new file mode 100644 (file)
index 0000000..1ef5c50
--- /dev/null
@@ -0,0 +1,31 @@
+.Dd February 2, 2015
+.Dt KILLALL5 8
+.Os ubase
+.Sh NAME
+.Nm killall5
+.Nd send a signal to all processes
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar pid1,pid2,...,pidN
+.Op Fl s Ar signal
+.Sh DESCRIPTION
+.Nm
+is an implementation of the SystemV
+.Xr killall 8
+command. It sends a signal to all processes except kernel threads and the
+processes in its own session. It is primarily used by the system's init
+scripts.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl o
+Tell
+.Nm
+to omit processes with that process id.
+.It Fl s Ar signal
+Send
+.Ar signal
+instead of the default SIGTERM.
+.El
+.Sh SEE ALSO
+.Xr halt 8 ,
+.Xr reboot 8
diff --git a/source/ubase/killall5.c b/source/ubase/killall5.c
new file mode 100644 (file)
index 0000000..14790b9
--- /dev/null
@@ -0,0 +1,109 @@
+/* See LICENSE file for copyright and license details. */
+#include <dirent.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "proc.h"
+#include "queue.h"
+#include "util.h"
+
+struct {
+       const char *name;
+       int sig;
+} sigs[] = {
+#define SIG(n) { #n, SIG##n }
+       SIG(ABRT), SIG(ALRM), SIG(BUS),  SIG(CHLD), SIG(CONT), SIG(FPE),  SIG(HUP),
+       SIG(ILL),  SIG(INT),  SIG(KILL), SIG(PIPE), SIG(QUIT), SIG(SEGV), SIG(STOP),
+       SIG(TERM), SIG(TSTP), SIG(TTIN), SIG(TTOU), SIG(USR1), SIG(USR2), SIG(URG),
+#undef SIG
+};
+
+struct pidentry {
+       pid_t pid;
+       SLIST_ENTRY(pidentry) entry;
+};
+
+static SLIST_HEAD(, pidentry) omitpid_head;
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-o pid1,pid2,..,pidN] [-s signal]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct pidentry *pe;
+       struct dirent *entry;
+       DIR *dp;
+       char *p, *arg = NULL;
+       char *end, *v;
+       int oflag = 0;
+       int sig = SIGTERM;
+       pid_t pid;
+       size_t i;
+
+       ARGBEGIN {
+       case 's':
+               v = EARGF(usage());
+               sig = strtol(v, &end, 0);
+               if (*end == '\0')
+                       break;
+               for (i = 0; i < LEN(sigs); i++) {
+                       if (strcasecmp(v, sigs[i].name) == 0) {
+                               sig = sigs[i].sig;
+                               break;
+                       }
+               }
+               if (i == LEN(sigs))
+                       eprintf("%s: unknown signal\n", v);
+               break;
+       case 'o':
+               oflag = 1;
+               arg = EARGF(usage());
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       SLIST_INIT(&omitpid_head);
+
+       for (p = strtok(arg, ","); p; p = strtok(NULL, ",")) {
+               pe = emalloc(sizeof(*pe));
+               pe->pid = estrtol(p, 10);
+               SLIST_INSERT_HEAD(&omitpid_head, pe, entry);
+       }
+
+       if (sig != SIGSTOP && sig != SIGCONT)
+               kill(-1, SIGSTOP);
+
+       if (!(dp = opendir("/proc")))
+               eprintf("opendir /proc:");
+       while ((entry = readdir(dp))) {
+               if (pidfile(entry->d_name) == 0)
+                       continue;
+               pid = estrtol(entry->d_name, 10);
+               if (pid == 1 || pid == getpid() ||
+                   getsid(pid) == getsid(0) || getsid(pid) == 0)
+                       continue;
+               if (oflag == 1) {
+                       SLIST_FOREACH(pe, &omitpid_head, entry)
+                               if (pe->pid == pid)
+                                       break;
+                       if (pe)
+                               continue;
+               }
+               kill(pid, sig);
+       }
+       closedir(dp);
+
+       if (sig != SIGSTOP && sig != SIGCONT)
+               kill(-1, SIGCONT);
+
+       return 0;
+}
diff --git a/source/ubase/last.c b/source/ubase/last.c
new file mode 100644 (file)
index 0000000..95b25fa
--- /dev/null
@@ -0,0 +1,64 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <libgen.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <utmp.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [user]\n", argv0);
+}
+
+int
+main(int argc, char **argv)
+{
+       FILE *fp;
+       struct utmp ut;
+       char *user, *file, *prog;
+       time_t t;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       switch (argc) {
+       case 0:
+               user = NULL;
+               break;
+       case 1:
+               user = argv[0];
+               break;
+       default:
+               usage();
+       }
+
+       prog = basename(argv0);
+       file = (!strcmp(prog, "last")) ? WTMP_PATH : BTMP_PATH;
+       if ((fp = fopen(file, "r")) == NULL)
+               eprintf("fopen %s:", file);
+
+       while (fread(&ut, sizeof(ut), 1, fp) == 1) {
+               if (ut.ut_type != USER_PROCESS ||
+                   (user && strcmp(user, ut.ut_name))) {
+                       continue;
+               }
+
+               t = ut.ut_time;
+               printf("%-8.8s %-8.8s %-16.16s %s",
+                      ut.ut_user, ut.ut_line, ut.ut_host, ctime(&t));
+       }
+       if (fclose(fp))
+               eprintf("fclose %s:", file);
+       return 0;
+}
diff --git a/source/ubase/lastlog.8 b/source/ubase/lastlog.8
new file mode 100644 (file)
index 0000000..ced08e6
--- /dev/null
@@ -0,0 +1,18 @@
+.Dd February 2, 2015
+.Dt LASTLOG 8
+.Os ubase
+.Sh NAME
+.Nm lastlog
+.Nd show last login of users
+.Sh SYNOPSIS
+.Nm
+.Op Ar user...
+.Sh DESCRIPTION
+.Nm
+shows the time, tty and host (if it was a remote connection) of the last
+login of the users. If one or more
+.Ar user
+names are passed as a parameter then information about the last login of these
+users are shown, otherwise the users in
+.Pa /etc/passwd
+will be used and shown in order of appearance.
diff --git a/source/ubase/lastlog.c b/source/ubase/lastlog.c
new file mode 100644 (file)
index 0000000..941a579
--- /dev/null
@@ -0,0 +1,78 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <utmp.h>
+
+#include "text.h"
+#include "util.h"
+
+#define PASSWD   "/etc/passwd"
+
+static FILE *last;
+
+static void
+lastlog(char *user)
+{
+       struct passwd *pwd;
+       struct lastlog ll;
+       time_t lltime;
+
+       errno = 0;
+       if ((pwd = getpwnam(user)) == NULL) {
+               if (errno)
+                       weprintf("getpwnam %s:", user);
+               else
+                       weprintf("unknown user: %s\n", user);
+               return;
+       }
+
+       fseek(last, pwd->pw_uid * sizeof(struct lastlog), 0);
+       fread(&ll, sizeof(struct lastlog), 1, last);
+
+       if (ferror(last))
+               eprintf("%s: read error:", _PATH_LASTLOG);
+
+       /* on glibc `ll_time' can be an int32_t with compat32
+        * avoid compiler warning when calling ctime() */
+       lltime = ll.ll_time;
+       printf("%-8.8s %-8.8s %-16.16s %s",
+              user, ll.ll_line, ll.ll_host, ctime(&lltime));
+}
+
+int
+main(int argc, char **argv)
+{
+       FILE *fp;
+       char *line = NULL, *p;
+       size_t sz = 0;
+
+       if ((last = fopen(_PATH_LASTLOG, "r")) == NULL)
+               eprintf("fopen %s:", _PATH_LASTLOG);
+
+       if (argc > 1) {
+               while (*++argv)
+                       lastlog(*argv);
+       } else {
+               if ((fp = fopen(PASSWD, "r")) == NULL)
+                       eprintf("fopen %s:", PASSWD);
+               while (agetline(&line, &sz, fp) != -1) {
+                       if ((p = strchr(line, ':')) == NULL)
+                               eprintf("invalid passwd entry\n");
+                       *p = '\0';
+                       lastlog(line);
+               }
+               if (fclose(fp))
+                       eprintf("fclose %s:", PASSWD);
+               free(line);
+       }
+
+       if (fclose(last))
+               eprintf("fclose %s:", _PATH_LASTLOG);
+
+       return 0;
+}
diff --git a/source/ubase/libutil/agetcwd.c b/source/ubase/libutil/agetcwd.c
new file mode 100644 (file)
index 0000000..4ebdb89
--- /dev/null
@@ -0,0 +1,18 @@
+/* See LICENSE file for copyright and license details. */
+#include <unistd.h>
+
+#include "../util.h"
+
+char *
+agetcwd(void)
+{
+       char *buf;
+       long size;
+
+       apathmax(&buf, &size);
+       if (!getcwd(buf, size))
+               eprintf("getcwd:");
+
+       return buf;
+}
+
diff --git a/source/ubase/libutil/agetline.c b/source/ubase/libutil/agetline.c
new file mode 100644 (file)
index 0000000..0953dac
--- /dev/null
@@ -0,0 +1,13 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../text.h"
+#include "../util.h"
+
+ssize_t
+agetline(char **p, size_t *size, FILE *fp)
+{
+       return getline(p, size, fp);
+}
diff --git a/source/ubase/libutil/apathmax.c b/source/ubase/libutil/apathmax.c
new file mode 100644 (file)
index 0000000..c570329
--- /dev/null
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../util.h"
+
+void
+apathmax(char **p, long *size)
+{
+       errno = 0;
+
+       if ((*size = pathconf("/", _PC_PATH_MAX)) == -1) {
+               if (errno == 0) {
+                       *size = BUFSIZ;
+               } else {
+                       eprintf("pathconf:");
+               }
+       }
+       *p = emalloc(*size);
+}
diff --git a/source/ubase/libutil/concat.c b/source/ubase/libutil/concat.c
new file mode 100644 (file)
index 0000000..ef1e5b9
--- /dev/null
@@ -0,0 +1,21 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+
+#include "../text.h"
+#include "../util.h"
+
+void
+concat(FILE *fp1, const char *s1, FILE *fp2, const char *s2)
+{
+       char buf[BUFSIZ];
+       size_t n;
+
+       while ((n = fread(buf, 1, sizeof(buf), fp1)) > 0) {
+               if (fwrite(buf, 1, n, fp2) != n)
+                       eprintf("%s: write error:", s2);
+               if (feof(fp1))
+                       break;
+       }
+       if (ferror(fp1))
+               eprintf("%s: read error:", s1);
+}
diff --git a/source/ubase/libutil/ealloc.c b/source/ubase/libutil/ealloc.c
new file mode 100644 (file)
index 0000000..05bdd62
--- /dev/null
@@ -0,0 +1,47 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdlib.h>
+#include <string.h>
+
+#include "../util.h"
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+       void *p;
+
+       p = calloc(nmemb, size);
+       if (!p)
+               eprintf("calloc: out of memory\n");
+       return p;
+}
+
+void *
+emalloc(size_t size)
+{
+       void *p;
+
+       p = malloc(size);
+       if (!p)
+               eprintf("malloc: out of memory\n");
+       return p;
+}
+
+void *
+erealloc(void *p, size_t size)
+{
+       p = realloc(p, size);
+       if (!p)
+               eprintf("realloc: out of memory\n");
+       return p;
+}
+
+char *
+estrdup(const char *s)
+{
+       char *p;
+
+       p = strdup(s);
+       if (!p)
+               eprintf("strdup: out of memory\n");
+       return p;
+}
diff --git a/source/ubase/libutil/eprintf.c b/source/ubase/libutil/eprintf.c
new file mode 100644 (file)
index 0000000..4d8f726
--- /dev/null
@@ -0,0 +1,65 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../util.h"
+
+char *argv0;
+
+static void venprintf(int, const char *, va_list);
+
+void
+eprintf(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       venprintf(1, fmt, ap);
+       va_end(ap);
+}
+
+void
+enprintf(int status, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       venprintf(status, fmt, ap);
+       va_end(ap);
+}
+
+void
+venprintf(int status, const char *fmt, va_list ap)
+{
+       if (strncmp(fmt, "usage", strlen("usage")))
+               fprintf(stderr, "%s: ", argv0);
+
+       vfprintf(stderr, fmt, ap);
+
+       if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+               fputc(' ', stderr);
+               perror(NULL);
+       }
+
+       exit(status);
+}
+
+void
+weprintf(const char *fmt, ...)
+{
+       va_list ap;
+
+       if (strncmp(fmt, "usage", strlen("usage")))
+               fprintf(stderr, "%s: ", argv0);
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+
+       if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+               fputc(' ', stderr);
+               perror(NULL);
+       }
+}
diff --git a/source/ubase/libutil/estrtol.c b/source/ubase/libutil/estrtol.c
new file mode 100644 (file)
index 0000000..74c7fb4
--- /dev/null
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../util.h"
+
+long
+estrtol(const char *s, int base)
+{
+       char *end;
+       long n;
+
+       errno = 0;
+       n = strtol(s, &end, base);
+       if (*end != '\0') {
+               if (base == 0)
+                       eprintf("%s: not an integer\n", s);
+               else
+                       eprintf("%s: not a base %d integer\n", s, base);
+       }
+       if (errno != 0)
+               eprintf("%s:", s);
+
+       return n;
+}
+
diff --git a/source/ubase/libutil/estrtoul.c b/source/ubase/libutil/estrtoul.c
new file mode 100644 (file)
index 0000000..b8907be
--- /dev/null
@@ -0,0 +1,26 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../util.h"
+
+unsigned long
+estrtoul(const char *s, int base)
+{
+       char *end;
+       unsigned long n;
+
+       errno = 0;
+       n = strtoul(s, &end, base);
+       if (*end != '\0') {
+               if (base == 0)
+                       eprintf("%s: not an integer\n", s);
+               else
+                       eprintf("%s: not a base %d integer\n", s, base);
+       }
+       if (errno != 0)
+               eprintf("%s:", s);
+
+       return n;
+}
diff --git a/source/ubase/libutil/explicit_bzero.c b/source/ubase/libutil/explicit_bzero.c
new file mode 100644 (file)
index 0000000..0217bad
--- /dev/null
@@ -0,0 +1,12 @@
+/* See LICENSE file for copyright and license details. */
+#include <string.h>
+
+#include "../util.h"
+
+static void *(*volatile explicit_memset)(void *, int, size_t) = memset;
+
+void
+explicit_bzero(void *b, size_t len)
+{
+       (*explicit_memset)(b, 0, len);
+}
diff --git a/source/ubase/libutil/passwd.c b/source/ubase/libutil/passwd.c
new file mode 100644 (file)
index 0000000..0798225
--- /dev/null
@@ -0,0 +1,77 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../passwd.h"
+#include "../text.h"
+#include "../util.h"
+
+/* Returns -1 on error, 0 for incorrect password
+ * and 1 if all went OK */
+int
+pw_check(const struct passwd *pw, const char *pass)
+{
+       char *cryptpass, *p;
+       struct spwd *spw;
+
+       p = pw->pw_passwd;
+       if (p[0] == '!' || p[0] == '*') {
+               weprintf("denied\n");
+               return -1;
+       }
+
+       if (pw->pw_passwd[0] == '\0') {
+               if (pass[0] == '\0')
+                       return 1;
+               weprintf("incorrect password\n");
+               return 0;
+       }
+
+       if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0') {
+               errno = 0;
+               spw = getspnam(pw->pw_name);
+               if (!spw) {
+                       if (errno)
+                               weprintf("getspnam: %s:", pw->pw_name);
+                       else
+                               weprintf("who are you?\n");
+                       return -1;
+               }
+               p = spw->sp_pwdp;
+               if (p[0] == '!' || p[0] == '*') {
+                       weprintf("denied\n");
+                       return -1;
+               }
+       }
+
+       cryptpass = crypt(pass, p);
+       if (!cryptpass) {
+               weprintf("crypt:");
+               return -1;
+       }
+       if (strcmp(cryptpass, p) != 0) {
+               weprintf("incorrect password\n");
+               return 0;
+       }
+       return 1;
+}
+
+int
+pw_init(void)
+{
+       struct rlimit rlim;
+
+       rlim.rlim_cur = 0;
+       rlim.rlim_max = 0;
+       if (setrlimit(RLIMIT_CORE, &rlim) < 0)
+               eprintf("setrlimit:");
+       return 0;
+}
diff --git a/source/ubase/libutil/proc.c b/source/ubase/libutil/proc.c
new file mode 100644 (file)
index 0000000..9c4b503
--- /dev/null
@@ -0,0 +1,117 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../proc.h"
+#include "../util.h"
+
+int
+parsecmdline(pid_t pid, char *buf, size_t siz)
+{
+       int fd;
+       char path[PATH_MAX];
+       ssize_t n, i;
+
+       snprintf(path, sizeof(path), "/proc/%ld/cmdline", (long)pid);
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return -1;
+       n = read(fd, buf, siz > 0 ? siz - 1 : 0);
+       if (n < 0) {
+               weprintf("read %s:", path);
+               close(fd);
+               return -1;
+       }
+       if (!n) {
+               close(fd);
+               return -1;
+       }
+       buf[n] = '\0';
+       for (i = 0; i < n; i++)
+               if (buf[i] == '\0')
+                       buf[i] = ' ';
+       close(fd);
+       return 0;
+}
+
+int
+parsestat(pid_t pid, struct procstat *ps)
+{
+       char path[PATH_MAX];
+       FILE *fp;
+       size_t len;
+
+       snprintf(path, sizeof(path), "/proc/%d/stat", pid);
+       if (!(fp = fopen(path, "r")))
+               return -1;
+       fscanf(fp, "%d %s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu",
+              &ps->pid, ps->comm,
+              &ps->state, &ps->ppid, &ps->pgrp,
+              &ps->sid, &ps->tty_nr, &ps->tpgid, &ps->flags,
+              &ps->minflt, &ps->cminflt, &ps->majflt, &ps->cmajflt,
+              &ps->utime, &ps->stime);
+       fscanf(fp, "%ld %ld %ld %ld %ld %ld %llu %lu %ld %ld",
+              &ps->cutime, &ps->cstime, &ps->priority, &ps->nice,
+              &ps->num_threads, &ps->itrealvalue, &ps->starttime,
+              &ps->vsize, &ps->rss, &ps->rsslim);
+       /* Filter out '(' and ')' from comm */
+       if ((len = strlen(ps->comm)) > 0)
+               len--;
+       ps->comm[len] = '\0';
+       memmove(ps->comm, ps->comm + 1, len);
+       fclose(fp);
+       return 0;
+}
+
+int
+parsestatus(pid_t pid, struct procstatus *pstatus)
+{
+       char path[PATH_MAX];
+       char buf[BUFSIZ], *off;
+       int fd;
+       ssize_t n;
+
+       snprintf(path, sizeof(path), "/proc/%d/status", pid);
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return -1;
+       n = read(fd, buf, sizeof(buf) - 1);
+       if (n < 0)
+               eprintf("%s: read error:", path);
+       if (!n) {
+               close(fd);
+               return -1;
+       }
+       buf[n] = '\0';
+       close(fd);
+       off = strstr(buf, "Uid:");
+       if (!off)
+               return -1;
+       sscanf(off, "Uid: %u %u", &pstatus->uid, &pstatus->euid);
+       off = strstr(buf, "Gid:");
+       if (!off)
+               return -1;
+       sscanf(off, "Gid: %u %u", &pstatus->gid, &pstatus->egid);
+       return 0;
+}
+
+int
+pidfile(const char *file)
+{
+       char *end;
+
+       errno = 0;
+       strtol(file, &end, 10);
+       if (*end != '\0')
+               return 0;
+       if (errno != 0)
+               return 0;
+       return 1;
+}
diff --git a/source/ubase/libutil/putword.c b/source/ubase/libutil/putword.c
new file mode 100644 (file)
index 0000000..c460703
--- /dev/null
@@ -0,0 +1,16 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+
+#include "../util.h"
+
+void
+putword(const char *s)
+{
+       static int first = 1;
+
+       if (!first)
+               putchar(' ');
+
+       fputs(s, stdout);
+       first = 0;
+}
diff --git a/source/ubase/libutil/recurse.c b/source/ubase/libutil/recurse.c
new file mode 100644 (file)
index 0000000..318987d
--- /dev/null
@@ -0,0 +1,42 @@
+/* See LICENSE file for copyright and license details. */
+#include <dirent.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../util.h"
+
+void
+recurse(const char *path, void (*fn)(const char *))
+{
+       char buf[PATH_MAX];
+       struct dirent *d;
+       struct stat st;
+       DIR *dp;
+
+       if (lstat(path, &st) == -1 || S_ISDIR(st.st_mode) == 0)
+               return;
+
+       if (!(dp = opendir(path)))
+               eprintf("opendir %s:", path);
+
+       while ((d = readdir(dp))) {
+               if (strcmp(d->d_name, ".") == 0 ||
+                   strcmp(d->d_name, "..") == 0)
+                       continue;
+               if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf))
+                       eprintf("path too long\n");
+               if (buf[strlen(buf) - 1] != '/')
+                       if (strlcat(buf, "/", sizeof(buf)) >= sizeof(buf))
+                               eprintf("path too long\n");
+               if (strlcat(buf, d->d_name, sizeof(buf)) >= sizeof(buf))
+                       eprintf("path too long\n");
+               fn(buf);
+       }
+
+       closedir(dp);
+}
diff --git a/source/ubase/libutil/strlcat.c b/source/ubase/libutil/strlcat.c
new file mode 100644 (file)
index 0000000..bf263b8
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "../util.h"
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+       char *d = dst;
+       const char *s = src;
+       size_t n = siz;
+       size_t dlen;
+       /* Find the end of dst and adjust bytes left but don't go past end */
+       while (n-- != 0 && *d != '\0')
+               d++;
+       dlen = d - dst;
+       n = siz - dlen;
+       if (n == 0)
+               return(dlen + strlen(s));
+       while (*s != '\0') {
+               if (n != 1) {
+                       *d++ = *s;
+                       n--;
+               }
+               s++;
+       }
+       *d = '\0';
+       return(dlen + (s - src)); /* count does not include NUL */
+}
+
+size_t
+estrlcat(char *dst, const char *src, size_t siz)
+{
+       size_t ret;
+
+       if ((ret = strlcat(dst, src, siz)) >= siz)
+               eprintf("strlcat: input string too long\n");
+
+       return ret;
+}
diff --git a/source/ubase/libutil/strlcpy.c b/source/ubase/libutil/strlcpy.c
new file mode 100644 (file)
index 0000000..44b618a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "../util.h"
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+       char *d = dst;
+       const char *s = src;
+       size_t n = siz;
+       /* Copy as many bytes as will fit */
+       if (n != 0) {
+               while (--n != 0) {
+                       if ((*d++ = *s++) == '\0')
+                               break;
+               }
+       }
+       /* Not enough room in dst, add NUL and traverse rest of src */
+       if (n == 0) {
+               if (siz != 0)
+                       *d = '\0'; /* NUL-terminate dst */
+               while (*s++)
+                       ;
+       }
+       return(s - src - 1); /* count does not include NUL */
+}
+
+size_t
+estrlcpy(char *dst, const char *src, size_t siz)
+{
+       size_t ret;
+
+       if ((ret = strlcpy(dst, src, siz)) >= siz)
+               eprintf("strlcpy: input string too long\n");
+
+       return ret;
+}
diff --git a/source/ubase/libutil/strtonum.c b/source/ubase/libutil/strtonum.c
new file mode 100644 (file)
index 0000000..c0ac401
--- /dev/null
@@ -0,0 +1,85 @@
+/*     $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $       */
+
+/*
+ * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "../util.h"
+
+#define        INVALID         1
+#define        TOOSMALL        2
+#define        TOOLARGE        3
+
+long long
+strtonum(const char *numstr, long long minval, long long maxval,
+         const char **errstrp)
+{
+       long long ll = 0;
+       int error = 0;
+       char *ep;
+       struct errval {
+               const char *errstr;
+               int err;
+       } ev[4] = {
+               { NULL,         0 },
+               { "invalid",    EINVAL },
+               { "too small",  ERANGE },
+               { "too large",  ERANGE },
+       };
+
+       ev[0].err = errno;
+       errno = 0;
+       if (minval > maxval) {
+               error = INVALID;
+       } else {
+               ll = strtoll(numstr, &ep, 10);
+               if (numstr == ep || *ep != '\0')
+                       error = INVALID;
+               else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+                       error = TOOSMALL;
+               else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+                       error = TOOLARGE;
+       }
+       if (errstrp != NULL)
+               *errstrp = ev[error].errstr;
+       errno = ev[error].err;
+       if (error)
+               ll = 0;
+
+       return (ll);
+}
+
+long long
+enstrtonum(int status, const char *numstr, long long minval, long long maxval)
+{
+       const char *errstr;
+       long long ll;
+
+       ll = strtonum(numstr, minval, maxval, &errstr);
+       if (errstr)
+               enprintf(status, "strtonum %s: %s\n", numstr, errstr);
+       return ll;
+}
+
+long long
+estrtonum(const char *numstr, long long minval, long long maxval)
+{
+       return enstrtonum(1, numstr, minval, maxval);
+}
diff --git a/source/ubase/libutil/tty.c b/source/ubase/libutil/tty.c
new file mode 100644 (file)
index 0000000..bceb01e
--- /dev/null
@@ -0,0 +1,97 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../util.h"
+
+void
+devtotty(int dev, int *tty_maj, int *tty_min)
+{
+       *tty_maj = (dev >> 8) & 0xfff;
+       *tty_min = (dev & 0xff) | ((dev >> 12) & 0xfff00);
+}
+
+int
+ttytostr(int tty_maj, int tty_min, char *str, size_t n)
+{
+       struct stat sb;
+       struct dirent *dp;
+       DIR *dirp;
+       char path[PATH_MAX];
+       int fd;
+       int r = 0;
+
+       switch (tty_maj) {
+       case 136:
+               snprintf(str, n, "pts/%d", tty_min);
+               return 0;
+       case 4:
+               snprintf(str, n, "tty%d", tty_min);
+               return 0;
+       default:
+               str[0] = '?';
+               str[1] = '\0';
+               break;
+       }
+
+       dirp = opendir("/dev");
+       if (!dirp) {
+               weprintf("opendir /dev:");
+               return -1;
+       }
+
+       while ((dp = readdir(dirp))) {
+               if (!strcmp(dp->d_name, ".") ||
+                   !strcmp(dp->d_name, ".."))
+                       continue;
+
+               if (strlcpy(path, "/dev/", sizeof(path)) >= sizeof(path)) {
+                       weprintf("path too long\n");
+                       r = -1;
+                       goto err0;
+               }
+               if (strlcat(path, dp->d_name, sizeof(path)) >= sizeof(path)) {
+                       weprintf("path too long\n");
+                       r = -1;
+                       goto err0;
+               }
+
+               if (stat(path, &sb) < 0) {
+                       weprintf("stat %s:", path);
+                       r = -1;
+                       goto err0;
+               }
+
+               if ((int)major(sb.st_rdev) == tty_maj &&
+                   (int)minor(sb.st_rdev) == tty_min) {
+                       fd = open(path, O_RDONLY | O_NONBLOCK);
+                       if (fd < 0)
+                               continue;
+                       if (isatty(fd)) {
+                               strlcpy(str, dp->d_name, n);
+                               close(fd);
+                               break;
+                       } else {
+                               close(fd);
+                               r = -1;
+                               goto err0;
+                       }
+               }
+       }
+
+err0:
+       if (closedir(dirp) < 0) {
+               weprintf("closedir /dev:");
+               r = -1;
+       }
+
+       return r;
+}
diff --git a/source/ubase/login.1 b/source/ubase/login.1
new file mode 100644 (file)
index 0000000..61cd2d4
--- /dev/null
@@ -0,0 +1,27 @@
+.Dd February 2, 2015
+.Dt LOGIN 1
+.Os ubase
+.Sh NAME
+.Nm login
+.Nd log into the system
+.Sh SYNOPSIS
+.Nm
+.Op Fl p
+.Ar username
+.Sh DESCRIPTION
+.Nm
+logs the
+.Ar username
+into the system. It sets
+.Ev HOME ,
+.Ev SHELL ,
+.Ev USER ,
+.Ev LOGNAME
+and the
+.Ev PATH environment variables and invokes the login shell as specified in
+.Pa /etc/passwd .
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl p
+Preserve the environment.
+.El
diff --git a/source/ubase/login.c b/source/ubase/login.c
new file mode 100644 (file)
index 0000000..25a59e4
--- /dev/null
@@ -0,0 +1,130 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "config.h"
+#include "passwd.h"
+#include "util.h"
+
+/* Write utmp entry */
+static void
+writeutmp(const char *user, const char *tty)
+{
+       struct utmp usr;
+       FILE *fp;
+
+       memset(&usr, 0, sizeof(usr));
+
+       usr.ut_type = USER_PROCESS;
+       usr.ut_pid = getpid();
+       strlcpy(usr.ut_user, user, sizeof(usr.ut_user));
+       strlcpy(usr.ut_line, tty, sizeof(usr.ut_line));
+       usr.ut_tv.tv_sec = time(NULL);
+
+       fp = fopen(UTMP_PATH, "a");
+       if (fp) {
+               if (fwrite(&usr, sizeof(usr), 1, fp) != 1)
+                       if (ferror(fp))
+                               weprintf("%s: write error:", UTMP_PATH);
+               fclose(fp);
+       } else {
+               weprintf("fopen %s:", UTMP_PATH);
+       }
+}
+
+static int
+dologin(struct passwd *pw, int preserve)
+{
+       char *shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell;
+
+       if (preserve == 0)
+               clearenv();
+       setenv("HOME", pw->pw_dir, 1);
+       setenv("SHELL", shell, 1);
+       setenv("USER", pw->pw_name, 1);
+       setenv("LOGNAME", pw->pw_name, 1);
+       setenv("PATH", ENV_PATH, 1);
+       if (chdir(pw->pw_dir) < 0)
+               eprintf("chdir %s:", pw->pw_dir);
+       execlp(shell, shell, "-l", NULL);
+       weprintf("execlp %s:", shell);
+       return (errno == ENOENT) ? 127 : 126;
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-p] username\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct passwd *pw;
+       char *pass, *user;
+       char *tty;
+       uid_t uid;
+       gid_t gid;
+       int pflag = 0;
+
+       ARGBEGIN {
+       case 'p':
+               pflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               usage();
+
+       if (isatty(0) == 0 || isatty(1) == 0 || isatty(2) == 0)
+               eprintf("no tty");
+
+       user = argv[0];
+       errno = 0;
+       pw = getpwnam(user);
+       if (!pw) {
+               if (errno)
+                       eprintf("getpwnam %s:", user);
+               else
+                       eprintf("who are you?\n");
+       }
+
+       uid = pw->pw_uid;
+       gid = pw->pw_gid;
+
+       /* Flush pending input */
+       ioctl(0, TCFLSH, (void *)0);
+
+       pass = getpass("Password: ");
+       if (!pass)
+               eprintf("getpass:");
+       if (pw_check(pw, pass) <= 0)
+               exit(1);
+
+       tty = ttyname(0);
+       if (!tty)
+               eprintf("ttyname:");
+
+       writeutmp(user, tty);
+
+       if (initgroups(user, gid) < 0)
+               eprintf("initgroups:");
+       if (setgid(gid) < 0)
+               eprintf("setgid:");
+       if (setuid(uid) < 0)
+               eprintf("setuid:");
+
+       return dologin(pw, pflag);
+}
diff --git a/source/ubase/lsmod.8 b/source/ubase/lsmod.8
new file mode 100644 (file)
index 0000000..2e8fd1e
--- /dev/null
@@ -0,0 +1,13 @@
+.Dd February 2, 2015
+.Dt LSMOD 8
+.Os ubase
+.Sh NAME
+.Nm lsmod
+.Nd list loaded kernel modules
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+parses
+.Pa /proc/modules
+and shows the loadable kernel modules that are currently loaded.
diff --git a/source/ubase/lsmod.c b/source/ubase/lsmod.c
new file mode 100644 (file)
index 0000000..617fda8
--- /dev/null
@@ -0,0 +1,67 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "text.h"
+#include "util.h"
+
+static void parse_modline(char *buf, char **name, char **size,
+                         char **refcount, char **users);
+
+static void
+usage(void)
+{
+       eprintf("usage: %s\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       const char *modfile = "/proc/modules";
+       FILE *fp;
+       char *buf = NULL;
+       char *name, *size, *refcount, *users;
+       size_t bufsize = 0;
+       size_t len;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc > 0)
+               usage();
+
+       fp = fopen(modfile, "r");
+       if (!fp)
+               eprintf("fopen %s:", modfile);
+
+       printf("%-23s Size  Used by\n", "Module");
+
+       while (agetline(&buf, &bufsize, fp) != -1) {
+               parse_modline(buf, &name, &size, &refcount, &users);
+               if (!name || !size || !refcount || !users)
+                       eprintf("invalid format: %s\n", modfile);
+               len = strlen(users) - 1;
+               if (users[len] == ',' || users[len] == '-')
+                       users[len] = '\0';
+               printf("%-20s%8s%3s %s\n", name, size, refcount,
+                      users);
+       }
+       if (ferror(fp))
+               eprintf("%s: read error:", modfile);
+       free(buf);
+       fclose(fp);
+       return 0;
+}
+
+static void
+parse_modline(char *buf, char **name, char **size,
+             char **refcount, char **users)
+{
+       *name = strtok(buf, " ");
+       *size = strtok(NULL, " ");
+       *refcount = strtok(NULL, " ");
+       *users = strtok(NULL, " ");
+}
diff --git a/source/ubase/lsusb.8 b/source/ubase/lsusb.8
new file mode 100644 (file)
index 0000000..eca2cc8
--- /dev/null
@@ -0,0 +1,13 @@
+.Dd February 2, 2015
+.Dt LSUSB 8
+.Os ubase
+.Sh NAME
+.Nm lsusb
+.Nd list USB devices
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+searches in
+.Pa /sys/bus/usb/devices
+for USB's and connected devices and prints them one by one.
\ No newline at end of file
diff --git a/source/ubase/lsusb.c b/source/ubase/lsusb.c
new file mode 100644 (file)
index 0000000..486eae6
--- /dev/null
@@ -0,0 +1,58 @@
+/* See LICENSE file for copyright and license details. */
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "text.h"
+#include "util.h"
+
+static void
+lsusb(const char *file)
+{
+       FILE *fp;
+       char path[PATH_MAX];
+       char *buf = NULL;
+       size_t size = 0;
+       unsigned int i = 0, busnum = 0, devnum = 0, pid = 0, vid = 0;
+
+       if (strlcpy(path, file, sizeof(path)) >= sizeof(path))
+               eprintf("path too long\n");
+       if (strlcat(path, "/uevent", sizeof(path)) >= sizeof(path))
+               eprintf("path too long\n");
+
+       if (!(fp = fopen(path, "r")))
+               return;
+       while (agetline(&buf, &size, fp) != -1) {
+               if (sscanf(buf, "BUSNUM=%u\n", &busnum) ||
+                   sscanf(buf, "DEVNUM=%u\n", &devnum) ||
+                   sscanf(buf, "PRODUCT=%x/%x/", &pid, &vid))
+                       i++;
+               if (i == 3) {
+                       printf("Bus %03d Device %03d: ID %04x:%04x\n", busnum, devnum,
+                              pid, vid);
+                       break;
+               }
+       }
+       if (ferror(fp))
+               eprintf("%s: read error:", path);
+       free(buf);
+       fclose(fp);
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       recurse("/sys/bus/usb/devices", lsusb);
+       return 0;
+}
diff --git a/source/ubase/mesg.1 b/source/ubase/mesg.1
new file mode 100644 (file)
index 0000000..23d3617
--- /dev/null
@@ -0,0 +1,27 @@
+.Dd February 2, 2015
+.Dt MESG 1
+.Os ubase
+.Sh NAME
+.Nm mesg
+.Nd display (do not display) messages from other users
+.Sh SYNOPSIS
+.Nm
+.Op Fl n | Fl y
+.Sh DESCRIPTION
+.Nm
+controls write access others have to the terminal device associated with
+standard error output. If write access is allowed, then programs such as
+.Xr talk 1
+and
+.Xr write 1
+may display messages on the terminal.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl n
+Disallow messages.
+.It Fl y
+Allow messages.
+.El
+.Sh SEE ALSO
+.Xr talk 1 ,
+.Xr write 1
diff --git a/source/ubase/mesg.c b/source/ubase/mesg.c
new file mode 100644 (file)
index 0000000..58a977a
--- /dev/null
@@ -0,0 +1,53 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [n|y]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct stat sb;
+       mode_t mode;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc > 1)
+               usage();
+
+       if (isatty(2) == 0)
+               eprintf("stderr: not a tty\n");
+
+       if (fstat(2, &sb) < 0)
+               eprintf("fstat stderr:");
+
+       if (argc == 0) {
+               puts(sb.st_mode & (S_IWGRP | S_IWOTH) ? "is y" : "is n");
+               return 0;
+       }
+
+       if (argv[0][0] == 'y' && argv[0][1] == '\0')
+               mode = sb.st_mode | S_IWGRP | S_IWOTH;
+       else if (argv[0][0] == 'n' && argv[0][1] == '\0')
+               mode = sb.st_mode & ~(S_IWGRP | S_IWOTH);
+       else
+               usage();
+
+       if (fchmod(2, mode) < 0)
+               eprintf("fchmod stderr:");
+
+       return 0;
+}
diff --git a/source/ubase/mknod.1 b/source/ubase/mknod.1
new file mode 100644 (file)
index 0000000..5437fbb
--- /dev/null
@@ -0,0 +1,37 @@
+.Dd February 2, 2015
+.Dt MKNOD 1
+.Os ubase
+.Sh NAME
+.Nm mknod
+.Nd create a special device file
+.Sh SYNOPSIS
+.Nm
+.Op Fl m Ar mode
+.Ar name
+.Ar type
+.Ar major
+.Ar minor
+.Sh DESCRIPTION
+.Nm
+creates a special device file named
+.Ar name
+with major number
+.Ar major ,
+and minor number
+.Ar minor .
+.Ar type
+specifies what kind of special file will be created and must be one of:
+.Bl -tag -width Ds
+.It Ar u | c
+A character device.
+.It Ar b
+A block device.
+.El
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl m
+Set the mode of the new file based on the octal value of
+.Ar mode .
+.El
+.Sh SEE ALSO
+.Xr mknod 2
diff --git a/source/ubase/mknod.c b/source/ubase/mknod.c
new file mode 100644 (file)
index 0000000..8de35c7
--- /dev/null
@@ -0,0 +1,45 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-m mode] name type major minor\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       mode_t type, mode = 0644;
+       dev_t dev;
+
+       ARGBEGIN {
+       case 'm':
+               mode = estrtol(EARGF(usage()), 8);
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc != 4)
+               usage();
+
+       if (strlen(argv[1]) != 1 || !strchr("ucb", argv[1][0]))
+               eprintf("mknod: '%s': invalid type\n", argv[1]);
+       type = (argv[1][0] == 'b') ? S_IFBLK : S_IFCHR;
+
+       dev = makedev(estrtol(argv[2], 0), estrtol(argv[3], 0));
+
+       if (mknod(argv[0], type|mode, dev) == -1)
+               eprintf("mknod: '%s':", argv[0]);
+       return 0;
+}
diff --git a/source/ubase/mkswap.8 b/source/ubase/mkswap.8
new file mode 100644 (file)
index 0000000..c847bad
--- /dev/null
@@ -0,0 +1,19 @@
+.Dd February 2, 2015
+.Dt MKSWAP 8
+.Os ubase
+.Sh NAME
+.Nm mkswap
+.Nd set up a Linux swap area
+.Sh SYNOPSIS
+.Nm
+.Ar device
+.Sh DESCRIPTION
+.Nm
+sets up a Linux swap area on a device or in a file. The
+.Ar device
+argument will usually be a disk-partition but it can also be a file. After
+creating the swap area you will typically need to use the
+.Xr swapon 8
+command to start using it.
+.Sh SEE ALSO
+.Xr swapon 8
diff --git a/source/ubase/mkswap.c b/source/ubase/mkswap.c
new file mode 100644 (file)
index 0000000..31f73e6
--- /dev/null
@@ -0,0 +1,89 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define SWAP_UUID_LENGTH 16
+#define SWAP_LABEL_LENGTH 16
+#define SWAP_MIN_PAGES 10
+
+struct swap_hdr {
+       char          bootbits[1024];
+       unsigned int  version;
+       unsigned int  last_page;
+       unsigned int  nr_badpages;
+       unsigned char uuid[SWAP_UUID_LENGTH];
+       char          volume_name[SWAP_LABEL_LENGTH];
+       unsigned int  padding[117];
+       unsigned int  badpages[1];
+};
+
+static void
+usage(void)
+{
+       eprintf("usage: %s device\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int fd;
+       unsigned int pages;
+       long pagesize;
+       struct stat sb;
+       char *buf;
+       struct swap_hdr *hdr;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               usage();
+
+       pagesize = sysconf(_SC_PAGESIZE);
+       if (pagesize <= 0) {
+               pagesize = sysconf(_SC_PAGE_SIZE);
+               if (pagesize <= 0)
+                       eprintf("can't determine pagesize\n");
+       }
+
+       fd = open(argv[0], O_RDWR);
+       if (fd < 0)
+               eprintf("open %s:", argv[0]);
+       if (fstat(fd, &sb) < 0)
+               eprintf("stat %s:", argv[0]);
+
+       buf = ecalloc(1, pagesize);
+
+       pages = sb.st_size / pagesize;
+       if (pages < SWAP_MIN_PAGES)
+               eprintf("swap space needs to be at least %ldKiB\n",
+                       SWAP_MIN_PAGES * pagesize / 1024);
+
+       /* Fill up the swap header */
+       hdr = (struct swap_hdr *)buf;
+       hdr->version = 1;
+       hdr->last_page = pages - 1;
+       strncpy(buf + pagesize - 10, "SWAPSPACE2", 10);
+
+       printf("Setting up swapspace version 1, size = %luKiB\n",
+              (pages - 1) * pagesize / 1024);
+
+       /* Write out the signature page */
+       if (write(fd, buf, pagesize) != pagesize)
+               eprintf("unable to write signature page\n");
+
+       fsync(fd);
+       close(fd);
+       free(buf);
+
+       return 0;
+}
diff --git a/source/ubase/mount.8 b/source/ubase/mount.8
new file mode 100644 (file)
index 0000000..b6fb5e5
--- /dev/null
@@ -0,0 +1,61 @@
+.Dd February 2, 2015
+.Dt MOUNT 8
+.Os ubase
+.Sh NAME
+.Nm mount
+.Nd mount a filesystem
+.Sh SYNOPSIS
+.Nm
+.Op Fl BMRan
+.Op Fl o Ar options
+.Op Fl t Ar fstype
+.Op Ar source
+.Op Ar target
+.Sh DESCRIPTION
+.Nm
+attaches the filesystem specified to the filesystem hierarchy. The
+.Xr umount 8
+command will detach it again.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl B
+Remount a subtree somewhere else (so that its contents are visible in both
+places).
+.It Fl M
+Move a subtree to some other place.
+.It Fl R
+Remount a subtree and all possible submounts somewhere else (so that its
+contents are available in both places).
+.It Fl a
+Mount all filesystems mentioned in
+.Pa /etc/fstab .
+.It Fl n
+Mount without writing in
+.Pa /etc/mtab .
+This is the default action.
+.It Fl o Ar options
+Specify a comma separated string of filesystem specific options.
+.It Fl t Ar fstype
+Set the filesystem type. More than one type may be specified in a comma
+separated list. The list of file system types can be prefixed with "no" to
+specify the file system types for which action should not be taken. For
+example, the
+.Nm
+command:
+.Bd -literal
+# mount -a -t nonfs,ext4
+
+.Ed
+mounts all file systems except those of type NFS and EXT4.
+.Nm
+will attempt to execute a program in your
+.Ev PATH
+mount.XXX where XXX is replaced by the type name. For example, NFS file
+systems are mounted by the program
+.Pa mount.nfs .
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr umount 2 ,
+.Xr swapon 8 ,
+.Xr umount 8
diff --git a/source/ubase/mount.c b/source/ubase/mount.c
new file mode 100644 (file)
index 0000000..2eb175c
--- /dev/null
@@ -0,0 +1,325 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "text.h"
+#include "util.h"
+
+#define FSOPTS_MAXLEN 512
+
+struct {
+       const char *opt;
+       const char *notopt;
+       unsigned long v;
+} optnames[] = {
+       { "defaults",   NULL,           0              },
+       { "remount",    NULL,           MS_REMOUNT     },
+       { "ro",         "rw",           MS_RDONLY      },
+       { "sync",       "async",        MS_SYNCHRONOUS },
+       { "dirsync",    NULL,           MS_DIRSYNC     },
+       { "nodev",      "dev",          MS_NODEV       },
+       { "noatime",    "atime",        MS_NOATIME     },
+       { "nodiratime", "diratime",     MS_NODIRATIME  },
+       { "noexec",     "exec",         MS_NOEXEC      },
+       { "nosuid",     "suid",         MS_NOSUID      },
+       { "mand",       "nomand",       MS_MANDLOCK    },
+       { "relatime",   "norelatime",   MS_RELATIME    },
+       { "bind",       NULL,           MS_BIND        },
+       { NULL,         NULL,           0              }
+};
+
+static unsigned long argflags = 0;
+static char fsopts[FSOPTS_MAXLEN] = "";
+
+static char *
+findtype(const char *types, const char *t)
+{
+       const char *p;
+       size_t len;
+
+       for (len = strlen(t); (p = strstr(types, t)); types = p + len) {
+               if (!strncmp(p, t, len) && (p[len] == '\0' || p[len] == ','))
+                       return (char *)p;
+       }
+       return NULL;
+}
+
+static void
+parseopts(const char *popts, unsigned long *flags, char *data, size_t datasiz)
+{
+       unsigned int i, validopt;
+       size_t optlen, dlen = 0;
+       const char *name, *e;
+
+       name = popts;
+       data[0] = '\0';
+       do {
+               if ((e = strstr(name, ",")))
+                       optlen = e - name;
+               else
+                       optlen = strlen(name);
+
+               validopt = 0;
+               for (i = 0; optnames[i].opt; i++) {
+                       if (optnames[i].opt &&
+                           !strncmp(name, optnames[i].opt, optlen)) {
+                               *flags |= optnames[i].v;
+                               validopt = 1;
+                               break;
+                       }
+                       if (optnames[i].notopt &&
+                           !strncmp(name, optnames[i].notopt, optlen)) {
+                               *flags &= ~optnames[i].v;
+                               validopt = 1;
+                               break;
+                       }
+               }
+
+               if (!validopt && optlen > 0) {
+                       /* unknown option, pass as data option to mount() */
+                       if (dlen + optlen + 2 >= datasiz)
+                               return; /* prevent overflow */
+                       if (dlen)
+                               data[dlen++] = ',';
+                       memcpy(&data[dlen], name, optlen);
+                       dlen += optlen;
+                       data[dlen] = '\0';
+               }
+               name = e + 1;
+       } while (e);
+}
+
+static int
+mounthelper(const char *fsname, const char *dir, const char *fstype)
+{
+       pid_t pid;
+       char eprog[PATH_MAX];
+       char const *eargv[10];
+       int status, i;
+
+       pid = fork();
+       switch(pid) {
+       case -1:
+               break;
+       case 0:
+               snprintf(eprog, sizeof(eprog), "mount.%s", fstype);
+
+               i = 0;
+               eargv[i++] = eprog;
+               if (argflags & MS_BIND)
+                       eargv[i++] = "-B";
+               if (argflags & MS_MOVE)
+                       eargv[i++] = "-M";
+               if (argflags & MS_REC)
+                       eargv[i++] = "-R";
+
+               if (fsopts[0]) {
+                       eargv[i++] = "-o";
+                       eargv[i++] = fsopts;
+               }
+               eargv[i++] = fsname;
+               eargv[i++] = dir;
+               eargv[i] = NULL;
+
+               execvp(eprog, (char * const *)eargv);
+               if (errno == ENOENT)
+                       _exit(1);
+               weprintf("execvp:");
+               _exit(1);
+               break;
+       default:
+               if (waitpid(pid, &status, 0) < 0) {
+                       weprintf("waitpid:");
+                       return -1;
+               }
+               if (WIFEXITED(status))
+                       return WEXITSTATUS(status);
+               else if (WIFSIGNALED(status))
+                       return 1;
+               break;
+       }
+       return 0;
+}
+
+static int
+mounted(const char *dir)
+{
+       FILE *fp;
+       struct mntent *me, mebuf;
+       struct stat st1, st2;
+       char linebuf[256];
+
+       if (stat(dir, &st1) < 0) {
+               weprintf("stat %s:", dir);
+               return 0;
+       }
+       if (!(fp = setmntent("/proc/mounts", "r")))
+               eprintf("setmntent %s:", "/proc/mounts");
+
+       while ((me = getmntent_r(fp, &mebuf, linebuf, sizeof(linebuf)))) {
+               if (stat(me->mnt_dir, &st2) < 0) {
+                       weprintf("stat %s:", me->mnt_dir);
+                       continue;
+               }
+               if (st1.st_dev == st2.st_dev &&
+                   st1.st_ino == st2.st_ino)
+                       return 1;
+       }
+       endmntent(fp);
+
+       return 0;
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-BMRan] [-t fstype] [-o options] [source] [target]\n",
+               argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *types = NULL, data[FSOPTS_MAXLEN] = "", *resolvpath = NULL;
+       char *files[] = { "/proc/mounts", "/etc/fstab", NULL };
+       const char *source, *target;
+       struct mntent *me = NULL;
+       int aflag = 0, status = 0, i, r;
+       unsigned long flags = 0;
+       FILE *fp;
+
+       ARGBEGIN {
+       case 'B':
+               argflags |= MS_BIND;
+               break;
+       case 'M':
+               argflags |= MS_MOVE;
+               break;
+       case 'R':
+               argflags |= MS_REC;
+               break;
+       case 'a':
+               aflag = 1;
+               break;
+       case 'o':
+               estrlcat(fsopts, EARGF(usage()), sizeof(fsopts));
+               parseopts(fsopts, &flags, data, sizeof(data));
+               break;
+       case 't':
+               types = EARGF(usage());
+               break;
+       case 'n':
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1 && aflag == 0) {
+               if (!(fp = fopen(files[0], "r")))
+                       eprintf("fopen %s:", files[0]);
+               concat(fp, files[0], stdout, "<stdout>");
+               fclose(fp);
+               return 0;
+       }
+
+       if (aflag == 1)
+               goto mountall;
+
+       source = argv[0];
+       target = argv[1];
+
+       if (!target) {
+               target = argv[0];
+               source = NULL;
+               if (!(resolvpath = realpath(target, NULL)))
+                       eprintf("realpath %s:", target);
+               target = resolvpath;
+       }
+
+       for (i = 0; files[i]; i++) {
+               if (!(fp = setmntent(files[i], "r"))) {
+                       if (strcmp(files[i], "/proc/mounts") != 0)
+                               weprintf("setmntent %s:", files[i]);
+                       continue;
+               }
+               while ((me = getmntent(fp))) {
+                       if (strcmp(me->mnt_dir, target) == 0 ||
+                          strcmp(me->mnt_fsname, target) == 0 ||
+                          (source && strcmp(me->mnt_dir, source) == 0) ||
+                          (source && strcmp(me->mnt_fsname, source) == 0)) {
+                               if (!source) {
+                                       target = me->mnt_dir;
+                                       source = me->mnt_fsname;
+                               }
+                               if (!fsopts[0])
+                                       estrlcat(fsopts, me->mnt_opts, sizeof(fsopts));
+                                       parseopts(fsopts, &flags, data, sizeof(data));
+                               if (!types)
+                                       types = me->mnt_type;
+                               goto mountsingle;
+                       }
+               }
+               endmntent(fp);
+               fp = NULL;
+       }
+       if (!source)
+               eprintf("can't find %s in /etc/fstab\n", target);
+
+mountsingle:
+       r = mounthelper(source, target, types);
+       if (r == -1)
+               status = 1;
+       if (r > 0 && mount(source, target, types, argflags | flags, data) < 0) {
+               weprintf("mount: %s:", source);
+               status = 1;
+       }
+       if (fp)
+               endmntent(fp);
+       free(resolvpath);
+       return status;
+
+mountall:
+       if (!(fp = setmntent("/etc/fstab", "r")))
+               eprintf("setmntent %s:", "/etc/fstab");
+       while ((me = getmntent(fp))) {
+               /* has "noauto" option or already mounted: skip */
+               if (hasmntopt(me, MNTOPT_NOAUTO) || mounted(me->mnt_dir))
+                       continue;
+               flags = 0;
+               fsopts[0] = '\0';
+               if (strlcat(fsopts, me->mnt_opts, sizeof(fsopts)) >= sizeof(fsopts)) {
+                       weprintf("%s: option string too long\n", me->mnt_dir);
+                       status = 1;
+                       continue;
+               }
+               parseopts(fsopts, &flags, data, sizeof(data));
+               /* if -t types specified:
+                * if non-match, skip
+                * if match and prefixed with "no", skip */
+               if (types &&
+                   ((types[0] == 'n' && types[1] == 'o' &&
+                    findtype(types + 2, me->mnt_type)) ||
+                    (!findtype(types, me->mnt_type))))
+                       continue;
+
+               r = mounthelper(me->mnt_fsname, me->mnt_dir, me->mnt_type);
+               if (r > 0 && mount(me->mnt_fsname, me->mnt_dir, me->mnt_type,
+                                  argflags | flags, data) < 0) {
+                       weprintf("mount: %s:", me->mnt_fsname);
+                       status = 1;
+               }
+       }
+       endmntent(fp);
+
+       return status;
+}
diff --git a/source/ubase/mountpoint.1 b/source/ubase/mountpoint.1
new file mode 100644 (file)
index 0000000..a2464b0
--- /dev/null
@@ -0,0 +1,32 @@
+.Dd February 2, 2015
+.Dt MOUNTPOINT 1
+.Os ubase
+.Sh NAME
+.Nm mountpoint
+.Nd check if a directory is a mountpoint
+.Sh SYNOPSIS
+.Nm
+.Op Fl dq
+.Ar directory
+.Nm
+.Fl x Ar device
+.Sh DESCRIPTION
+.Nm
+checks if the
+.Ar directory
+is mentioned in the
+.Pa /proc/mounts
+file.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl d
+Print the major/minor device number of the filesystem on stdout.
+.It Fl q
+Be quiet, don't print anything.
+.It Fl x
+Print the major/minor device number of the
+.Ar device
+on stdout.
+.El
+.Sh SEE ALSO
+.Xr mount 8
diff --git a/source/ubase/mountpoint.c b/source/ubase/mountpoint.c
new file mode 100644 (file)
index 0000000..8f205a2
--- /dev/null
@@ -0,0 +1,101 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-dqx] target\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int dflag = 0, qflag = 0, xflag = 0;
+       int ret = 0;
+       struct mntent *me = NULL;
+       FILE *fp;
+       struct stat st1, st2;
+
+       ARGBEGIN {
+       case 'd':
+               dflag = 1;
+               break;
+       case 'q':
+               qflag = 1;
+               break;
+       case 'x':
+               xflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               usage();
+
+       if (stat(argv[0], &st1) < 0) {
+               if (qflag)
+                       return 1;
+               eprintf("stat %s:", argv[0]);
+       }
+
+       if (xflag) {
+               if (!S_ISBLK(st1.st_mode)) {
+                       if (qflag)
+                               return 1;
+                       eprintf("stat: %s: not a block device\n",
+                               argv[0]);
+               }
+               printf("%u:%u\n", major(st1.st_rdev),
+                      minor(st1.st_rdev));
+               return 0;
+       }
+
+       if (!S_ISDIR(st1.st_mode)) {
+               if (qflag)
+                       return 1;
+               eprintf("stat %s: not a directory\n", argv[0]);
+       }
+
+       if (dflag) {
+               printf("%u:%u\n", major(st1.st_dev),
+                      minor(st1.st_dev));
+               return 0;
+       }
+
+       fp = setmntent("/proc/mounts", "r");
+       if (!fp) {
+               if (qflag)
+                       return 1;
+               eprintf("setmntent %s:", "/proc/mounts");
+       }
+       while ((me = getmntent(fp)) != NULL) {
+               if (stat(me->mnt_dir, &st2) < 0) {
+                       if (qflag)
+                               return 1;
+                       eprintf("stat %s:", me->mnt_dir);
+               }
+               if (st1.st_dev == st2.st_dev &&
+                   st1.st_ino == st2.st_ino)
+                       break;
+       }
+       endmntent(fp);
+
+       if (me == NULL)
+               ret = 1;
+
+       if (!qflag)
+               printf("%s %s a mountpoint\n", argv[0],
+                      !ret ? "is" : "is not");
+
+       return ret;
+}
diff --git a/source/ubase/nologin.8 b/source/ubase/nologin.8
new file mode 100644 (file)
index 0000000..9ea1328
--- /dev/null
@@ -0,0 +1,21 @@
+.Dd March 26, 2016
+.Dt NOLOGIN 8
+.Os ubase
+.Sh NAME
+.Nm nologin
+.Nd refuse login
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+prints a message informing the user that she
+is not allowed to log in. If /etc/nologin.txt
+exists, its content is printed instead of
+the default message.
+.Pp
+.Nm
+is intended to be specified as the user's
+default shell.
+.Sh EXIT STATUS
+.Nm
+returns a status code indicating failure.
diff --git a/source/ubase/nologin.c b/source/ubase/nologin.c
new file mode 100644 (file)
index 0000000..10b5b66
--- /dev/null
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+       int fd;
+       char buf[BUFSIZ];
+       ssize_t n;
+
+       fd = open("/etc/nologin.txt", O_RDONLY);
+       if (fd >= 0) {
+               while ((n = read(fd, buf, sizeof(buf))) > 0)
+                       write(STDOUT_FILENO, buf, n);
+               close(fd);
+       } else {
+               printf("The account is currently unavailable.\n");
+       }
+       return 1;
+}
diff --git a/source/ubase/pagesize.1 b/source/ubase/pagesize.1
new file mode 100644 (file)
index 0000000..a6a46d8
--- /dev/null
@@ -0,0 +1,12 @@
+.Dd February 2, 2015
+.Dt PAGESIZE 1
+.Os ubase
+.Sh NAME
+.Nm pagesize
+.Nd print system page size
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+prints the size of a page of memory in bytes.  This program is
+useful in constructing portable shell scripts.
\ No newline at end of file
diff --git a/source/ubase/pagesize.c b/source/ubase/pagesize.c
new file mode 100644 (file)
index 0000000..3833b90
--- /dev/null
@@ -0,0 +1,32 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       long pagesz;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       pagesz = sysconf(_SC_PAGESIZE);
+       if (pagesz <= 0) {
+               pagesz = sysconf(_SC_PAGE_SIZE);
+               if (pagesz <= 0)
+                       eprintf("can't determine pagesize\n");
+       }
+       printf("%ld\n", pagesz);
+       return 0;
+}
diff --git a/source/ubase/passwd.1 b/source/ubase/passwd.1
new file mode 100644 (file)
index 0000000..e61bc9d
--- /dev/null
@@ -0,0 +1,15 @@
+.Dd February 2, 2015
+.Dt PASSWD 1
+.Os ubase
+.Sh NAME
+.Nm passwd
+.Nd change a user's password
+.Sh SYNOPSIS
+.Nm
+.Op Ar username
+.Sh DESCRIPTION
+.Nm
+changes the user's password. The user is prompted for their current password.
+If the current password is correctly typed, a new password is requested. The
+new password must be entered twice to avoid typing errors. The superuser is
+not required to provide a user's current password.
diff --git a/source/ubase/passwd.c b/source/ubase/passwd.c
new file mode 100644 (file)
index 0000000..c5916be
--- /dev/null
@@ -0,0 +1,257 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "passwd.h"
+#include "text.h"
+#include "util.h"
+
+static FILE *
+spw_get_file(const char *user)
+{
+       FILE *fp = NULL;
+       char file[PATH_MAX];
+       int r;
+
+       r = snprintf(file, sizeof(file), "/etc/tcb/%s/shadow", user);
+       if (r < 0 || (size_t)r >= sizeof(file))
+               eprintf("snprintf:");
+       fp = fopen(file, "r+");
+       if (!fp)
+               fp = fopen("/etc/shadow", "r+");
+       return fp;
+}
+
+static int
+spw_write_file(FILE *fp, const struct spwd *spw, char *pwhash)
+{
+       struct spwd *spwent;
+       int r = -1, w = 0;
+       FILE *tfp = NULL;
+
+       /* write to temporary file. */
+       tfp = tmpfile();
+       if (!tfp) {
+               weprintf("tmpfile:");
+               goto cleanup;
+       }
+       while ((spwent = fgetspent(fp))) {
+               /* update entry on name match */
+               if (strcmp(spwent->sp_namp, spw->sp_namp) == 0) {
+                       spwent->sp_pwdp = pwhash;
+                       w++;
+               }
+               errno = 0;
+               if (putspent(spwent, tfp) == -1) {
+                       weprintf("putspent:");
+                       goto cleanup;
+               }
+       }
+       if (!w) {
+               weprintf("shadow: no matching entry to write to\n");
+               goto cleanup;
+       }
+       fflush(tfp);
+
+       if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) {
+               weprintf("fseek:");
+               goto cleanup;
+       }
+
+       /* write temporary file to (tcb) shadow file */
+       concat(tfp, "tmpfile", fp, "shadow");
+       ftruncate(fileno(fp), ftell(tfp));
+
+       r = 0; /* success */
+cleanup:
+       if (tfp)
+               fclose(tfp);
+       return r;
+}
+
+static
+int pw_write_file(FILE *fp, const struct passwd *pw, char *pwhash) {
+       struct passwd *pwent;
+       int r = -1, w = 0;
+       FILE *tfp = NULL;
+
+       /* write to temporary file. */
+       tfp = tmpfile();
+       if (!tfp) {
+               weprintf("tmpfile:");
+               goto cleanup;
+       }
+       while ((pwent = fgetpwent(fp))) {
+               /* update entry on name match */
+               if (strcmp(pwent->pw_name, pw->pw_name) == 0) {
+                       pwent->pw_passwd = pwhash;
+                       w++;
+               }
+               errno = 0;
+               if (putpwent(pwent, tfp) == -1) {
+                       weprintf("putpwent:");
+                       goto cleanup;
+               }
+       }
+       if (!w) {
+               weprintf("passwd: no matching entry to write to\n");
+               goto cleanup;
+       }
+       fflush(tfp);
+
+       if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) {
+               weprintf("fseek:");
+               goto cleanup;
+       }
+
+       /* write to passwd file. */
+       concat(tfp, "tmpfile", fp, "passwd");
+       ftruncate(fileno(fp), ftell(tfp));
+
+       r = 0; /* success */
+cleanup:
+       if (tfp)
+               fclose(tfp);
+       return r;
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [username]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL;
+       char *inpass, *p, *salt = PW_CIPHER, *prevhash = NULL;
+       struct passwd *pw;
+       struct spwd *spw = NULL;
+       FILE *fp = NULL;
+       int r = -1, status = 1;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       pw_init();
+       umask(077);
+
+       errno = 0;
+       if (argc == 0)
+               pw = getpwuid(getuid());
+       else
+               pw = getpwnam(argv[0]);
+       if (!pw) {
+               if (errno)
+                       eprintf("getpwnam: %s:", argv[0]);
+               else
+                       eprintf("who are you?\n");
+       }
+
+       /* is using shadow entry ? */
+       if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0') {
+               errno = 0;
+               spw = getspnam(pw->pw_name);
+               if (!spw) {
+                       if (errno)
+                               eprintf("getspnam: %s:", pw->pw_name);
+                       else
+                               eprintf("who are you?\n");
+               }
+       }
+
+       /* Flush pending input */
+       ioctl(0, TCFLSH, (void *)0);
+
+       if (getuid() == 0) {
+               goto newpass;
+       } else {
+               if (pw->pw_passwd[0] == '!' ||
+                   pw->pw_passwd[0] == '*')
+                       eprintf("denied\n");
+               if (pw->pw_passwd[0] == '\0') {
+                       goto newpass;
+               }
+               if (pw->pw_passwd[0] == 'x')
+                       prevhash = salt = spw->sp_pwdp;
+               else
+                       prevhash = salt = pw->pw_passwd;
+       }
+
+       printf("Changing password for %s\n", pw->pw_name);
+       inpass = getpass("Old password: ");
+       if (!inpass)
+               eprintf("getpass:");
+       if (inpass[0] == '\0')
+               eprintf("no password supplied\n");
+       p = crypt(inpass, salt);
+       if (!p)
+               eprintf("crypt:");
+       cryptpass1 = estrdup(p);
+       if (strcmp(cryptpass1, prevhash) != 0)
+               eprintf("incorrect password\n");
+
+newpass:
+       inpass = getpass("Enter new password: ");
+       if (!inpass)
+               eprintf("getpass:");
+       if (inpass[0] == '\0')
+               eprintf("no password supplied\n");
+       p = crypt(inpass, salt);
+       if (!p)
+               eprintf("crypt:");
+       cryptpass2 = estrdup(p);
+       if (cryptpass1 && strcmp(cryptpass1, cryptpass2) == 0)
+               eprintf("password left unchanged\n");
+
+       /* Flush pending input */
+       ioctl(0, TCFLSH, (void *)0);
+
+       inpass = getpass("Retype new password: ");
+       if (!inpass)
+               eprintf("getpass:");
+       if (inpass[0] == '\0')
+               eprintf("no password supplied\n");
+       p = crypt(inpass, salt);
+       if (!p)
+               eprintf("crypt:");
+       cryptpass3 = estrdup(p);
+       if (strcmp(cryptpass2, cryptpass3) != 0)
+               eprintf("passwords don't match\n");
+
+       fp = spw_get_file(pw->pw_name);
+       if (fp) {
+               r = spw_write_file(fp, spw, cryptpass3);
+       } else {
+               fp = fopen("/etc/passwd", "r+");
+               if (fp)
+                       r = pw_write_file(fp, pw, cryptpass3);
+               else
+                       weprintf("fopen:");
+       }
+       if (!r)
+               status = 0;
+
+       if (fp)
+               fclose(fp);
+       free(cryptpass3);
+       free(cryptpass2);
+       free(cryptpass1);
+
+       return status;
+}
diff --git a/source/ubase/passwd.h b/source/ubase/passwd.h
new file mode 100644 (file)
index 0000000..ec8286b
--- /dev/null
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+/* passwd.c */
+int pw_check(const struct passwd *, const char *);
+int pw_init(void);
diff --git a/source/ubase/pidof.1 b/source/ubase/pidof.1
new file mode 100644 (file)
index 0000000..a9d8392
--- /dev/null
@@ -0,0 +1,25 @@
+.Dd February 2, 2015
+.Dt PIDOF 1
+.Os ubase
+.Sh NAME
+.Nm pidof
+.Nd find the process ID of a running program
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar pid1,pid2,...pidN
+.Op Fl s
+.Op Ar program...
+.Sh DESCRIPTION
+.Nm
+finds the process id's of the named programs and prints them to
+stdout.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl o
+Tell pidof to omit processes with that process id. The special pid
+%PPID can be used to name the parent process of the pidof program.
+.It Fl s
+Single shot - this instructs the program to only return one process id.
+.El
+.Sh SEE ALSO
+.Xr killall5 8
diff --git a/source/ubase/pidof.c b/source/ubase/pidof.c
new file mode 100644 (file)
index 0000000..c98bf92
--- /dev/null
@@ -0,0 +1,114 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/types.h>
+
+#include <dirent.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "proc.h"
+#include "queue.h"
+#include "util.h"
+
+struct pidentry {
+       pid_t pid;
+       SLIST_ENTRY(pidentry) entry;
+};
+
+static SLIST_HEAD(, pidentry) omitpid_head;
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-o pid1,pid2,...pidN] [-s] [program...]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       DIR *dp;
+       struct dirent *entry;
+       pid_t pid;
+       struct procstat ps;
+       char cmdline[BUFSIZ], *cmd, *cmdbase = NULL, *p, *arg = NULL;
+       int i, found = 0;
+       int sflag = 0, oflag = 0;
+       struct pidentry *pe;
+
+       ARGBEGIN {
+       case 's':
+               sflag = 1;
+               break;
+       case 'o':
+               oflag = 1;
+               arg = EARGF(usage());
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (!argc)
+               return 1;
+
+       SLIST_INIT(&omitpid_head);
+
+       for (p = strtok(arg, ","); p; p = strtok(NULL, ",")) {
+               pe = emalloc(sizeof(*pe));
+               if (strcmp(p, "%PPID") == 0)
+                       pe->pid = getppid();
+               else
+                       pe->pid = estrtol(p, 10);
+               SLIST_INSERT_HEAD(&omitpid_head, pe, entry);
+       }
+
+       if (!(dp = opendir("/proc")))
+               eprintf("opendir /proc:");
+
+       while ((entry = readdir(dp))) {
+               if (!pidfile(entry->d_name))
+                       continue;
+               pid = estrtol(entry->d_name, 10);
+               if (oflag) {
+                       SLIST_FOREACH(pe, &omitpid_head, entry)
+                               if (pe->pid == pid)
+                                       break;
+                       if (pe)
+                               continue;
+               }
+               if (parsestat(pid, &ps) < 0)
+                       continue;
+               if (parsecmdline(ps.pid, cmdline,
+                                sizeof(cmdline)) < 0) {
+                       cmd = ps.comm;
+                       cmdbase = cmd;
+               } else {
+                       if ((p = strchr(cmdline, ' ')))
+                               *p = '\0';
+                       cmd = cmdline;
+                       cmdbase = basename(cmdline);
+               }
+               /* Workaround for login shells */
+               if (cmd[0] == '-')
+                       cmd++;
+               for (i = 0; i < argc; i++) {
+                       if (strcmp(cmd, argv[i]) == 0 ||
+                           strcmp(cmdbase, argv[i]) == 0) {
+                               putword(entry->d_name);
+                               found++;
+                               if (sflag)
+                                       goto out;
+                       }
+               }
+       }
+
+out:
+       if (found)
+               putchar('\n');
+
+       closedir(dp);
+
+       return 0;
+}
diff --git a/source/ubase/pivot_root.8 b/source/ubase/pivot_root.8
new file mode 100644 (file)
index 0000000..2ce81c5
--- /dev/null
@@ -0,0 +1,17 @@
+.Dd February 2, 2015
+.Dt PIVOT_ROOT 8
+.Os ubase
+.Sh NAME
+.Nm pivot_root
+.Nd change the root filesystem
+.Sh SYNOPSIS
+.Nm
+.Ar newroot putold
+.Sh DESCRIPTION
+.Nm
+moves the root file system of the current process to the
+directory
+.Ar put_old
+and makes
+.Ar new_root
+the new root file system.
diff --git a/source/ubase/pivot_root.c b/source/ubase/pivot_root.c
new file mode 100644 (file)
index 0000000..8bbaa48
--- /dev/null
@@ -0,0 +1,31 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/syscall.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s new-root put-old\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 2)
+               usage();
+
+       if (syscall(SYS_pivot_root, argv[0], argv[1]) < 0)
+               eprintf("pivot_root:");
+
+       return 0;
+}
diff --git a/source/ubase/proc.h b/source/ubase/proc.h
new file mode 100644 (file)
index 0000000..cb44e12
--- /dev/null
@@ -0,0 +1,42 @@
+/* See LICENSE file for copyright and license details. */
+struct procstat {
+       int pid;
+       char comm[PATH_MAX + 2]; /* + 2 for '(' and ')' */
+       unsigned char state;
+       int ppid;
+       int pgrp;
+       int sid;
+       int tty_nr;
+       int tpgid;
+       unsigned flags;
+       unsigned long minflt;
+       unsigned long cminflt;
+       unsigned long majflt;
+       unsigned long cmajflt;
+       unsigned long utime;
+       unsigned long stime;
+       long cutime;
+       long cstime;
+       long priority;
+       long nice;
+       long num_threads;
+       long itrealvalue;
+       unsigned long long starttime;
+       unsigned long vsize;
+       long rss;
+       long rsslim;
+};
+
+struct procstatus {
+       uid_t uid;
+       uid_t euid;
+       gid_t gid;
+       gid_t egid;
+};
+
+int parsecmdline(pid_t pid, char *buf, size_t siz);
+int parsestat(pid_t pid, struct procstat *ps);
+int parsestatus(pid_t pid, struct procstatus *pstatus);
+int proceuid(pid_t pid, uid_t *euid);
+int procuid(pid_t pid, uid_t *euid);
+int pidfile(const char *file);
diff --git a/source/ubase/ps.1 b/source/ubase/ps.1
new file mode 100644 (file)
index 0000000..3a8dd65
--- /dev/null
@@ -0,0 +1,29 @@
+.Dd February 2, 2015
+.Dt PS 1
+.Os ubase
+.Sh NAME
+.Nm ps
+.Nd display process status
+.Sh SYNOPSIS
+.Nm
+.Op Fl aAdef
+.Sh DESCRIPTION
+.Nm
+displays information about active processes.  When given no options,
+.Nm
+prints information about processes of the current user that has a
+controlling terminal.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl a
+Select all processes except both session leaders and processes not
+associated with a terminal.
+.It Fl A
+Select all processes.  Identical to \fB-e\fR.
+.It Fl d
+Select all processes except session leaders.
+.It Fl e
+Select all processes.  Identical to \fB-A\fR.
+.It Fl f
+Do full-format listing.
+.El
diff --git a/source/ubase/ps.c b/source/ubase/ps.c
new file mode 100644 (file)
index 0000000..114983b
--- /dev/null
@@ -0,0 +1,180 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/sysinfo.h>
+
+#include <errno.h>
+#include <libgen.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "proc.h"
+#include "util.h"
+
+static void psout(struct procstat *ps);
+static void psr(const char *file);
+
+enum {
+       PS_aflag = 1 << 0,
+       PS_Aflag = 1 << 1,
+       PS_dflag = 1 << 2,
+       PS_fflag = 1 << 3
+};
+
+static int flags;
+
+static void
+psout(struct procstat *ps)
+{
+       struct procstatus pstatus;
+       char cmdline[BUFSIZ], *cmd;
+       char buf[BUFSIZ];
+       char ttystr[TTY_NAME_MAX], *myttystr;
+       int tty_maj, tty_min;
+       uid_t myeuid;
+       unsigned sutime;
+       time_t start;
+       char stimestr[sizeof("%H:%M")];
+       struct sysinfo info;
+       struct passwd *pw;
+       struct winsize w;
+
+       /* Ignore session leaders */
+       if (flags & PS_dflag)
+               if (ps->pid == ps->sid)
+                       return;
+
+       devtotty(ps->tty_nr, &tty_maj, &tty_min);
+       ttytostr(tty_maj, tty_min, ttystr, sizeof(ttystr));
+
+       /* Only print processes that are associated with
+        * a terminal and they are not session leaders */
+       if (flags & PS_aflag)
+               if (ps->pid == ps->sid || ttystr[0] == '?')
+                       return;
+
+       if (parsestatus(ps->pid, &pstatus) < 0)
+               return;
+
+       /* This is the default case, only print processes that have
+        * the same controlling terminal as the invoker and the same
+        * euid as the current user */
+       if (!(flags & (PS_aflag | PS_Aflag | PS_dflag))) {
+               myttystr = ttyname(0);
+               if (myttystr) {
+                       if (strcmp(myttystr + strlen("/dev/"), ttystr))
+                               return;
+               } else {
+                       /* The invoker has no controlling terminal - just
+                        * go ahead and print the processes anyway */
+                       ttystr[0] = '?';
+                       ttystr[1] = '\0';
+               }
+               myeuid = geteuid();
+               if (myeuid != pstatus.euid)
+                       return;
+       }
+
+       sutime = (ps->stime + ps->utime) / sysconf(_SC_CLK_TCK);
+
+       ioctl(1, TIOCGWINSZ, &w);
+       if (!(flags & PS_fflag)) {
+               snprintf(buf, sizeof(buf), "%5d %-6s   %02u:%02u:%02u %s", ps->pid, ttystr,
+                        sutime / 3600, (sutime % 3600) / 60, sutime % 60,
+                        ps->comm);
+               if (w.ws_col)
+                       printf("%.*s\n", w.ws_col, buf);
+               else
+                       printf("%s\n", buf);
+       } else {
+               errno = 0;
+               pw = getpwuid(pstatus.uid);
+               if (!pw)
+                       eprintf("getpwuid %d:", pstatus.uid);
+
+               if (sysinfo(&info) < 0)
+                       eprintf("sysinfo:");
+
+               start = time(NULL) - info.uptime;
+               start += (ps->starttime / sysconf(_SC_CLK_TCK));
+               strftime(stimestr, sizeof(stimestr),
+                        "%H:%M", localtime(&start));
+
+               /* For kthreads/zombies /proc/<pid>/cmdline will be
+                * empty so use ps->comm in that case */
+               if (parsecmdline(ps->pid, cmdline, sizeof(cmdline)) < 0)
+                       cmd = ps->comm;
+               else
+                       cmd = cmdline;
+
+               snprintf(buf, sizeof(buf), "%-8s %5d %5d  ? %5s %-5s    %02u:%02u:%02u %s%s%s",
+                        pw->pw_name, ps->pid,
+                        ps->ppid, stimestr, ttystr,
+                        sutime / 3600, (sutime % 3600) / 60, sutime % 60,
+                        (cmd == ps->comm) ? "[" : "", cmd,
+                        (cmd == ps->comm) ? "]" : "");
+               if (w.ws_col)
+                       printf("%.*s\n", w.ws_col, buf);
+               else
+                       printf("%s\n", buf);
+       }
+}
+
+static void
+psr(const char *file)
+{
+       char path[PATH_MAX], *p;
+       struct procstat ps;
+       pid_t pid;
+
+       if (strlcpy(path, file, sizeof(path)) >= sizeof(path))
+               eprintf("path too long\n");
+       p = basename(path);
+       if (pidfile(p) == 0)
+               return;
+       pid = estrtol(p, 10);
+       if (parsestat(pid, &ps) < 0)
+               return;
+       psout(&ps);
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: [-aAdef] %s\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       ARGBEGIN {
+       case 'a':
+               flags |= PS_aflag;
+               break;
+       case 'A':
+               flags |= PS_Aflag;
+               break;
+       case 'd':
+               flags |= PS_dflag;
+               break;
+       case 'e':
+               flags |= PS_Aflag;
+               break;
+       case 'f':
+               flags |= PS_fflag;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (!(flags & PS_fflag))
+               printf("  PID TTY          TIME CMD\n");
+       else
+               printf("UID        PID  PPID  C STIME TTY          TIME CMD\n");
+       recurse("/proc", psr);
+       return 0;
+}
diff --git a/source/ubase/queue.h b/source/ubase/queue.h
new file mode 100644 (file)
index 0000000..f8f09bf
--- /dev/null
@@ -0,0 +1,648 @@
+/*     $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $        */
+/*     $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $       */
+
+/*
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)queue.h     8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef        _SYS_QUEUE_H_
+#define        _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists, 
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *slh_first; /* first element */                     \
+}
+#define        SLIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+#define SLIST_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *sle_next;  /* next element */                      \
+}
+/*
+ * Singly-linked List access methods.
+ */
+#define        SLIST_FIRST(head)       ((head)->slh_first)
+#define        SLIST_END(head)         NULL
+#define        SLIST_EMPTY(head)       (SLIST_FIRST(head) == SLIST_END(head))
+#define        SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
+
+#define        SLIST_FOREACH(var, head, field)                                 \
+       for((var) = SLIST_FIRST(head);                                  \
+           (var) != SLIST_END(head);                                   \
+           (var) = SLIST_NEXT(var, field))
+
+#define        SLIST_FOREACH_SAFE(var, head, field, tvar)                      \
+       for ((var) = SLIST_FIRST(head);                         \
+           (var) && ((tvar) = SLIST_NEXT(var, field), 1);              \
+           (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define        SLIST_INIT(head) {                                              \
+       SLIST_FIRST(head) = SLIST_END(head);                            \
+}
+
+#define        SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
+       (elm)->field.sle_next = (slistelm)->field.sle_next;             \
+       (slistelm)->field.sle_next = (elm);                             \
+} while (0)
+
+#define        SLIST_INSERT_HEAD(head, elm, field) do {                        \
+       (elm)->field.sle_next = (head)->slh_first;                      \
+       (head)->slh_first = (elm);                                      \
+} while (0)
+
+#define        SLIST_REMOVE_AFTER(elm, field) do {                             \
+       (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next;  \
+} while (0)
+
+#define        SLIST_REMOVE_HEAD(head, field) do {                             \
+       (head)->slh_first = (head)->slh_first->field.sle_next;          \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do {                      \
+       if ((head)->slh_first == (elm)) {                               \
+               SLIST_REMOVE_HEAD((head), field);                       \
+       } else {                                                        \
+               struct type *curelm = (head)->slh_first;                \
+                                                                       \
+               while (curelm->field.sle_next != (elm))                 \
+                       curelm = curelm->field.sle_next;                \
+               curelm->field.sle_next =                                \
+                   curelm->field.sle_next->field.sle_next;             \
+               _Q_INVALIDATE((elm)->field.sle_next);                   \
+       }                                                               \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *lh_first;  /* first element */                     \
+}
+
+#define LIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+
+#define LIST_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *le_next;   /* next element */                      \
+       struct type **le_prev;  /* address of previous next element */  \
+}
+
+/*
+ * List access methods
+ */
+#define        LIST_FIRST(head)                ((head)->lh_first)
+#define        LIST_END(head)                  NULL
+#define        LIST_EMPTY(head)                (LIST_FIRST(head) == LIST_END(head))
+#define        LIST_NEXT(elm, field)           ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field)                                 \
+       for((var) = LIST_FIRST(head);                                   \
+           (var)!= LIST_END(head);                                     \
+           (var) = LIST_NEXT(var, field))
+
+#define        LIST_FOREACH_SAFE(var, head, field, tvar)                       \
+       for ((var) = LIST_FIRST(head);                          \
+           (var) && ((tvar) = LIST_NEXT(var, field), 1);               \
+           (var) = (tvar))
+
+/*
+ * List functions.
+ */
+#define        LIST_INIT(head) do {                                            \
+       LIST_FIRST(head) = LIST_END(head);                              \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do {                    \
+       if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)  \
+               (listelm)->field.le_next->field.le_prev =               \
+                   &(elm)->field.le_next;                              \
+       (listelm)->field.le_next = (elm);                               \
+       (elm)->field.le_prev = &(listelm)->field.le_next;               \
+} while (0)
+
+#define        LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
+       (elm)->field.le_prev = (listelm)->field.le_prev;                \
+       (elm)->field.le_next = (listelm);                               \
+       *(listelm)->field.le_prev = (elm);                              \
+       (listelm)->field.le_prev = &(elm)->field.le_next;               \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do {                                \
+       if (((elm)->field.le_next = (head)->lh_first) != NULL)          \
+               (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+       (head)->lh_first = (elm);                                       \
+       (elm)->field.le_prev = &(head)->lh_first;                       \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do {                                   \
+       if ((elm)->field.le_next != NULL)                               \
+               (elm)->field.le_next->field.le_prev =                   \
+                   (elm)->field.le_prev;                               \
+       *(elm)->field.le_prev = (elm)->field.le_next;                   \
+       _Q_INVALIDATE((elm)->field.le_prev);                            \
+       _Q_INVALIDATE((elm)->field.le_next);                            \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do {                            \
+       if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)     \
+               (elm2)->field.le_next->field.le_prev =                  \
+                   &(elm2)->field.le_next;                             \
+       (elm2)->field.le_prev = (elm)->field.le_prev;                   \
+       *(elm2)->field.le_prev = (elm2);                                \
+       _Q_INVALIDATE((elm)->field.le_prev);                            \
+       _Q_INVALIDATE((elm)->field.le_next);                            \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type)                                       \
+struct name {                                                          \
+       struct type *sqh_first; /* first element */                     \
+       struct type **sqh_last; /* addr of last next element */         \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head)                                 \
+       { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type)                                            \
+struct {                                                               \
+       struct type *sqe_next;  /* next element */                      \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define        SIMPLEQ_FIRST(head)         ((head)->sqh_first)
+#define        SIMPLEQ_END(head)           NULL
+#define        SIMPLEQ_EMPTY(head)         (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define        SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field)                              \
+       for((var) = SIMPLEQ_FIRST(head);                                \
+           (var) != SIMPLEQ_END(head);                                 \
+           (var) = SIMPLEQ_NEXT(var, field))
+
+#define        SIMPLEQ_FOREACH_SAFE(var, head, field, tvar)                    \
+       for ((var) = SIMPLEQ_FIRST(head);                               \
+           (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1);            \
+           (var) = (tvar))
+
+/*
+ * Simple queue functions.
+ */
+#define        SIMPLEQ_INIT(head) do {                                         \
+       (head)->sqh_first = NULL;                                       \
+       (head)->sqh_last = &(head)->sqh_first;                          \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do {                     \
+       if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)        \
+               (head)->sqh_last = &(elm)->field.sqe_next;              \
+       (head)->sqh_first = (elm);                                      \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do {                     \
+       (elm)->field.sqe_next = NULL;                                   \
+       *(head)->sqh_last = (elm);                                      \
+       (head)->sqh_last = &(elm)->field.sqe_next;                      \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {           \
+       if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+               (head)->sqh_last = &(elm)->field.sqe_next;              \
+       (listelm)->field.sqe_next = (elm);                              \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do {                  \
+       if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+               (head)->sqh_last = &(head)->sqh_first;                  \
+} while (0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do {                    \
+       if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+           == NULL)                                                    \
+               (head)->sqh_last = &(elm)->field.sqe_next;              \
+} while (0)
+
+/*
+ * XOR Simple queue definitions.
+ */
+#define XSIMPLEQ_HEAD(name, type)                                      \
+struct name {                                                          \
+       struct type *sqx_first; /* first element */                     \
+       struct type **sqx_last; /* addr of last next element */         \
+       unsigned long sqx_cookie;                                       \
+}
+
+#define XSIMPLEQ_ENTRY(type)                                           \
+struct {                                                               \
+       struct type *sqx_next;  /* next element */                      \
+}
+
+/*
+ * XOR Simple queue access methods.
+ */
+#define XSIMPLEQ_XOR(head, ptr)            ((__typeof(ptr))((head)->sqx_cookie ^ \
+                                       (unsigned long)(ptr)))
+#define        XSIMPLEQ_FIRST(head)        XSIMPLEQ_XOR(head, ((head)->sqx_first))
+#define        XSIMPLEQ_END(head)          NULL
+#define        XSIMPLEQ_EMPTY(head)        (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
+#define        XSIMPLEQ_NEXT(head, elm, field)    XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
+
+
+#define XSIMPLEQ_FOREACH(var, head, field)                             \
+       for ((var) = XSIMPLEQ_FIRST(head);                              \
+           (var) != XSIMPLEQ_END(head);                                \
+           (var) = XSIMPLEQ_NEXT(head, var, field))
+
+#define        XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar)                   \
+       for ((var) = XSIMPLEQ_FIRST(head);                              \
+           (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1);     \
+           (var) = (tvar))
+
+/*
+ * XOR Simple queue functions.
+ */
+#define        XSIMPLEQ_INIT(head) do {                                        \
+       arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
+       (head)->sqx_first = XSIMPLEQ_XOR(head, NULL);                   \
+       (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first);      \
+} while (0)
+
+#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do {                    \
+       if (((elm)->field.sqx_next = (head)->sqx_first) ==              \
+           XSIMPLEQ_XOR(head, NULL))                                   \
+               (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+       (head)->sqx_first = XSIMPLEQ_XOR(head, (elm));                  \
+} while (0)
+
+#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do {                    \
+       (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL);               \
+       *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
+       (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);  \
+} while (0)
+
+#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {          \
+       if (((elm)->field.sqx_next = (listelm)->field.sqx_next) ==      \
+           XSIMPLEQ_XOR(head, NULL))                                   \
+               (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+       (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm));          \
+} while (0)
+
+#define XSIMPLEQ_REMOVE_HEAD(head, field) do {                         \
+       if (((head)->sqx_first = XSIMPLEQ_XOR(head,                     \
+           (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
+               (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
+} while (0)
+
+#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do {                   \
+       if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head,                 \
+           (elm)->field.sqx_next)->field.sqx_next)                     \
+           == XSIMPLEQ_XOR(head, NULL))                                \
+               (head)->sqx_last =                                      \
+                   XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);         \
+} while (0)
+
+                   
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *tqh_first; /* first element */                     \
+       struct type **tqh_last; /* addr of last next element */         \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head)                                   \
+       { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *tqe_next;  /* next element */                      \
+       struct type **tqe_prev; /* address of previous next element */  \
+}
+
+/* 
+ * tail queue access methods 
+ */
+#define        TAILQ_FIRST(head)               ((head)->tqh_first)
+#define        TAILQ_END(head)                 NULL
+#define        TAILQ_NEXT(elm, field)          ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname)                                     \
+       (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field)                               \
+       (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define        TAILQ_EMPTY(head)                                               \
+       (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field)                                        \
+       for((var) = TAILQ_FIRST(head);                                  \
+           (var) != TAILQ_END(head);                                   \
+           (var) = TAILQ_NEXT(var, field))
+
+#define        TAILQ_FOREACH_SAFE(var, head, field, tvar)                      \
+       for ((var) = TAILQ_FIRST(head);                                 \
+           (var) != TAILQ_END(head) &&                                 \
+           ((tvar) = TAILQ_NEXT(var, field), 1);                       \
+           (var) = (tvar))
+
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field)              \
+       for((var) = TAILQ_LAST(head, headname);                         \
+           (var) != TAILQ_END(head);                                   \
+           (var) = TAILQ_PREV(var, headname, field))
+
+#define        TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)    \
+       for ((var) = TAILQ_LAST(head, headname);                        \
+           (var) != TAILQ_END(head) &&                                 \
+           ((tvar) = TAILQ_PREV(var, headname, field), 1);             \
+           (var) = (tvar))
+
+/*
+ * Tail queue functions.
+ */
+#define        TAILQ_INIT(head) do {                                           \
+       (head)->tqh_first = NULL;                                       \
+       (head)->tqh_last = &(head)->tqh_first;                          \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do {                       \
+       if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)        \
+               (head)->tqh_first->field.tqe_prev =                     \
+                   &(elm)->field.tqe_next;                             \
+       else                                                            \
+               (head)->tqh_last = &(elm)->field.tqe_next;              \
+       (head)->tqh_first = (elm);                                      \
+       (elm)->field.tqe_prev = &(head)->tqh_first;                     \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {                       \
+       (elm)->field.tqe_next = NULL;                                   \
+       (elm)->field.tqe_prev = (head)->tqh_last;                       \
+       *(head)->tqh_last = (elm);                                      \
+       (head)->tqh_last = &(elm)->field.tqe_next;                      \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {             \
+       if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+               (elm)->field.tqe_next->field.tqe_prev =                 \
+                   &(elm)->field.tqe_next;                             \
+       else                                                            \
+               (head)->tqh_last = &(elm)->field.tqe_next;              \
+       (listelm)->field.tqe_next = (elm);                              \
+       (elm)->field.tqe_prev = &(listelm)->field.tqe_next;             \
+} while (0)
+
+#define        TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
+       (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
+       (elm)->field.tqe_next = (listelm);                              \
+       *(listelm)->field.tqe_prev = (elm);                             \
+       (listelm)->field.tqe_prev = &(elm)->field.tqe_next;             \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {                            \
+       if (((elm)->field.tqe_next) != NULL)                            \
+               (elm)->field.tqe_next->field.tqe_prev =                 \
+                   (elm)->field.tqe_prev;                              \
+       else                                                            \
+               (head)->tqh_last = (elm)->field.tqe_prev;               \
+       *(elm)->field.tqe_prev = (elm)->field.tqe_next;                 \
+       _Q_INVALIDATE((elm)->field.tqe_prev);                           \
+       _Q_INVALIDATE((elm)->field.tqe_next);                           \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do {                     \
+       if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)   \
+               (elm2)->field.tqe_next->field.tqe_prev =                \
+                   &(elm2)->field.tqe_next;                            \
+       else                                                            \
+               (head)->tqh_last = &(elm2)->field.tqe_next;             \
+       (elm2)->field.tqe_prev = (elm)->field.tqe_prev;                 \
+       *(elm2)->field.tqe_prev = (elm2);                               \
+       _Q_INVALIDATE((elm)->field.tqe_prev);                           \
+       _Q_INVALIDATE((elm)->field.tqe_next);                           \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)                                       \
+struct name {                                                          \
+       struct type *cqh_first;         /* first element */             \
+       struct type *cqh_last;          /* last element */              \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head)                                 \
+       { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type)                                            \
+struct {                                                               \
+       struct type *cqe_next;          /* next element */              \
+       struct type *cqe_prev;          /* previous element */          \
+}
+
+/*
+ * Circular queue access methods 
+ */
+#define        CIRCLEQ_FIRST(head)             ((head)->cqh_first)
+#define        CIRCLEQ_LAST(head)              ((head)->cqh_last)
+#define        CIRCLEQ_END(head)               ((void *)(head))
+#define        CIRCLEQ_NEXT(elm, field)        ((elm)->field.cqe_next)
+#define        CIRCLEQ_PREV(elm, field)        ((elm)->field.cqe_prev)
+#define        CIRCLEQ_EMPTY(head)                                             \
+       (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field)                              \
+       for((var) = CIRCLEQ_FIRST(head);                                \
+           (var) != CIRCLEQ_END(head);                                 \
+           (var) = CIRCLEQ_NEXT(var, field))
+
+#define        CIRCLEQ_FOREACH_SAFE(var, head, field, tvar)                    \
+       for ((var) = CIRCLEQ_FIRST(head);                               \
+           (var) != CIRCLEQ_END(head) &&                               \
+           ((tvar) = CIRCLEQ_NEXT(var, field), 1);                     \
+           (var) = (tvar))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field)                      \
+       for((var) = CIRCLEQ_LAST(head);                                 \
+           (var) != CIRCLEQ_END(head);                                 \
+           (var) = CIRCLEQ_PREV(var, field))
+
+#define        CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)  \
+       for ((var) = CIRCLEQ_LAST(head, headname);                      \
+           (var) != CIRCLEQ_END(head) &&                               \
+           ((tvar) = CIRCLEQ_PREV(var, headname, field), 1);           \
+           (var) = (tvar))
+
+/*
+ * Circular queue functions.
+ */
+#define        CIRCLEQ_INIT(head) do {                                         \
+       (head)->cqh_first = CIRCLEQ_END(head);                          \
+       (head)->cqh_last = CIRCLEQ_END(head);                           \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {           \
+       (elm)->field.cqe_next = (listelm)->field.cqe_next;              \
+       (elm)->field.cqe_prev = (listelm);                              \
+       if ((listelm)->field.cqe_next == CIRCLEQ_END(head))             \
+               (head)->cqh_last = (elm);                               \
+       else                                                            \
+               (listelm)->field.cqe_next->field.cqe_prev = (elm);      \
+       (listelm)->field.cqe_next = (elm);                              \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {          \
+       (elm)->field.cqe_next = (listelm);                              \
+       (elm)->field.cqe_prev = (listelm)->field.cqe_prev;              \
+       if ((listelm)->field.cqe_prev == CIRCLEQ_END(head))             \
+               (head)->cqh_first = (elm);                              \
+       else                                                            \
+               (listelm)->field.cqe_prev->field.cqe_next = (elm);      \
+       (listelm)->field.cqe_prev = (elm);                              \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do {                     \
+       (elm)->field.cqe_next = (head)->cqh_first;                      \
+       (elm)->field.cqe_prev = CIRCLEQ_END(head);                      \
+       if ((head)->cqh_last == CIRCLEQ_END(head))                      \
+               (head)->cqh_last = (elm);                               \
+       else                                                            \
+               (head)->cqh_first->field.cqe_prev = (elm);              \
+       (head)->cqh_first = (elm);                                      \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do {                     \
+       (elm)->field.cqe_next = CIRCLEQ_END(head);                      \
+       (elm)->field.cqe_prev = (head)->cqh_last;                       \
+       if ((head)->cqh_first == CIRCLEQ_END(head))                     \
+               (head)->cqh_first = (elm);                              \
+       else                                                            \
+               (head)->cqh_last->field.cqe_next = (elm);               \
+       (head)->cqh_last = (elm);                                       \
+} while (0)
+
+#define        CIRCLEQ_REMOVE(head, elm, field) do {                           \
+       if ((elm)->field.cqe_next == CIRCLEQ_END(head))                 \
+               (head)->cqh_last = (elm)->field.cqe_prev;               \
+       else                                                            \
+               (elm)->field.cqe_next->field.cqe_prev =                 \
+                   (elm)->field.cqe_prev;                              \
+       if ((elm)->field.cqe_prev == CIRCLEQ_END(head))                 \
+               (head)->cqh_first = (elm)->field.cqe_next;              \
+       else                                                            \
+               (elm)->field.cqe_prev->field.cqe_next =                 \
+                   (elm)->field.cqe_next;                              \
+       _Q_INVALIDATE((elm)->field.cqe_prev);                           \
+       _Q_INVALIDATE((elm)->field.cqe_next);                           \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do {                   \
+       if (((elm2)->field.cqe_next = (elm)->field.cqe_next) ==         \
+           CIRCLEQ_END(head))                                          \
+               (head)->cqh_last = (elm2);                              \
+       else                                                            \
+               (elm2)->field.cqe_next->field.cqe_prev = (elm2);        \
+       if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) ==         \
+           CIRCLEQ_END(head))                                          \
+               (head)->cqh_first = (elm2);                             \
+       else                                                            \
+               (elm2)->field.cqe_prev->field.cqe_next = (elm2);        \
+       _Q_INVALIDATE((elm)->field.cqe_prev);                           \
+       _Q_INVALIDATE((elm)->field.cqe_next);                           \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/source/ubase/readahead.8 b/source/ubase/readahead.8
new file mode 100644 (file)
index 0000000..5c2eeb8
--- /dev/null
@@ -0,0 +1,15 @@
+.Dd February 2, 2015
+.Dt READAHEAD 8
+.Os ubase
+.Sh NAME
+.Nm preload
+.Nd preload files into disk cache
+.Sh SYNOPSIS
+.Nm
+.Ar file...
+.Sh DESCRIPTION
+.Nm
+preloads files into the kernel's disk cache. The number of pages preloaded
+depends on the kernel but it is usually around 2MB.
+.Sh SEE ALSO
+.Xr readahead 2
diff --git a/source/ubase/readahead.c b/source/ubase/readahead.c
new file mode 100644 (file)
index 0000000..dc7850f
--- /dev/null
@@ -0,0 +1,38 @@
+/* See LICENSE file for copyright and license details. */
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s file...\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       FILE *fp;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc == 0)
+               usage();
+
+       for (; argc > 0; argc--, argv++) {
+               if (!(fp = fopen(argv[0], "r"))) {
+                       weprintf("fopen %s:", argv[0]);
+                       continue;
+               }
+               if (readahead(fileno(fp), 0, -1) < 0)
+                       weprintf("readahead %s:", argv[0]);
+               fclose(fp);
+       }
+       return 0;
+}
diff --git a/source/ubase/reboot.h b/source/ubase/reboot.h
new file mode 100644 (file)
index 0000000..71bfb8a
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Magic values required to use _reboot() system call.
+ */
+
+#define        LINUX_REBOOT_MAGIC1     0xfee1dead
+#define        LINUX_REBOOT_MAGIC2     672274793
+#define        LINUX_REBOOT_MAGIC2A    85072278
+#define        LINUX_REBOOT_MAGIC2B    369367448
+#define        LINUX_REBOOT_MAGIC2C    537993216
+
+
+/*
+ * Commands accepted by the _reboot() system call.
+ *
+ * RESTART     Restart system using default command and mode.
+ * HALT        Stop OS and give system control to ROM monitor, if any.
+ * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command.
+ * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task.
+ * POWER_OFF   Stop OS and remove all power from system, if possible.
+ * RESTART2    Restart system using given command string.
+ * SW_SUSPEND  Suspend system using software suspend if compiled in.
+ * KEXEC       Restart system using a previously loaded Linux kernel
+ */
+
+#define        LINUX_REBOOT_CMD_RESTART        0x01234567
+#define        LINUX_REBOOT_CMD_HALT           0xCDEF0123
+#define        LINUX_REBOOT_CMD_CAD_ON         0x89ABCDEF
+#define        LINUX_REBOOT_CMD_CAD_OFF        0x00000000
+#define        LINUX_REBOOT_CMD_POWER_OFF      0x4321FEDC
+#define        LINUX_REBOOT_CMD_RESTART2       0xA1B2C3D4
+#define        LINUX_REBOOT_CMD_SW_SUSPEND     0xD000FCE2
+#define        LINUX_REBOOT_CMD_KEXEC          0x45584543
diff --git a/source/ubase/respawn.1 b/source/ubase/respawn.1
new file mode 100644 (file)
index 0000000..3502087
--- /dev/null
@@ -0,0 +1,32 @@
+.Dd February 2, 2015
+.Dt RESPAWN 1
+.Os ubase
+.Sh NAME
+.Nm respawn
+.Nd spawn the given command repeatedly
+.Sh SYNOPSIS
+.Nm
+.Op Fl l Ar fifo
+.Op Fl d Ar N
+.Ar cmd Op Ar args...
+.Sh DESCRIPTION
+.Nm
+spawns the given
+.Ar cmd
+in a new session repeatedly.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl d
+Set the delay between invocations of \fIcmd\fR.  It defaults to 0.
+.It Fl l
+Listen on the specified
+.Ar fifo
+for writes. For each write spawn a new instance of
+.Ar cmd .
+This can be used in conjunction with a process supervisor to restart a
+particular program. The
+.Fl l
+and
+.Fl d
+options are incompatible. All writes are discarded.
+.El
\ No newline at end of file
diff --git a/source/ubase/respawn.c b/source/ubase/respawn.c
new file mode 100644 (file)
index 0000000..77670f5
--- /dev/null
@@ -0,0 +1,106 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+sigterm(int sig)
+{
+       if (sig == SIGTERM) {
+               kill(0, SIGTERM);
+               _exit(0);
+       }
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-l fifo] [-d N] cmd [args...]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *fifo = NULL;
+       unsigned int delay = 0;
+       pid_t pid;
+       char buf[BUFSIZ];
+       int savederrno;
+       int fd;
+       ssize_t n;
+       fd_set rdfd;
+
+       ARGBEGIN {
+       case 'd':
+               delay = estrtol(EARGF(usage()), 0);
+               break;
+       case 'l':
+               fifo = EARGF(usage());
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               usage();
+
+       if (fifo && delay > 0)
+               usage();
+
+       setsid();
+
+       signal(SIGTERM, sigterm);
+
+       if (fifo) {
+               /* TODO: we should use O_RDONLY and re-open the fd on EOF */
+               fd = open(fifo, O_RDWR | O_NONBLOCK);
+               if (fd < 0)
+                       eprintf("open %s:", fifo);
+       }
+
+       while (1) {
+               if (fifo) {
+                       FD_ZERO(&rdfd);
+                       FD_SET(fd, &rdfd);
+                       n = select(fd + 1, &rdfd, NULL, NULL, NULL);
+                       if (n < 0)
+                               eprintf("select:");
+                       if (n == 0 || FD_ISSET(fd, &rdfd) == 0)
+                               continue;
+                       while ((n = read(fd, buf, sizeof(buf))) > 0)
+                               ;
+                       if (n < 0)
+                               if (errno != EAGAIN)
+                                       eprintf("read %s:", fifo);
+               }
+               pid = fork();
+               if (pid < 0)
+                       eprintf("fork:");
+               switch (pid) {
+               case 0:
+                       execvp(argv[0], argv);
+                       savederrno = errno;
+                       weprintf("execvp %s:", argv[0]);
+                       _exit(savederrno == ENOENT ? 127 : 126);
+                       break;
+               default:
+                       waitpid(pid, NULL, 0);
+                       break;
+               }
+               if (!fifo)
+                       sleep(delay);
+       }
+       /* not reachable */
+       return 0;
+}
diff --git a/source/ubase/rmmod.8 b/source/ubase/rmmod.8
new file mode 100644 (file)
index 0000000..318c9b3
--- /dev/null
@@ -0,0 +1,33 @@
+.Dd February 2, 2015
+.Dt RMMOD 8
+.Os ubase
+.Sh NAME
+.Nm rmmod
+.Nd remove a module from the Linux kernel
+.Sh SYNOPSIS
+.Nm
+.Op Fl fw
+.Ar module...
+.Sh DESCRIPTION
+.Nm
+removes one or more modules from the kernel.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl f
+This option can be extremely dangerous: it has no effect unless
+CONFIG_MODULE_FORCE_UNLOAD was set when the kernel was compiled.
+With this option, you can remove modules which are being used, or
+which are not designed to be removed, or have been marked as unsafe.
+.It Fl w
+Normally,
+.Nm
+will refuse to unload modules which are in
+use. With this option,
+.Nm
+will isolate the module, and wait until the module is no longer used. Noone
+new will be able to use the module, but it's up to you to make sure the
+current users eventually finish with it.
+.El
+.Sh SEE ALSO
+.Xr insmod 8 ,
+.Xr lsmod 8
\ No newline at end of file
diff --git a/source/ubase/rmmod.c b/source/ubase/rmmod.c
new file mode 100644 (file)
index 0000000..b5ce5b7
--- /dev/null
@@ -0,0 +1,50 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/syscall.h>
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-fw] module...\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *mod, *p;
+       int i;
+       int flags = O_NONBLOCK;
+
+       ARGBEGIN {
+       case 'f':
+               flags |= O_TRUNC;
+               break;
+       case 'w':
+               flags &= ~O_NONBLOCK;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               usage();
+
+       for (i = 0; i < argc; i++) {
+               mod = argv[i];
+               p = strrchr(mod, '.');
+               if (p && !strcmp(p, ".ko"))
+                       *p = '\0';
+               if (syscall(__NR_delete_module, mod, flags) < 0)
+                       eprintf("delete_module:");
+       }
+
+       return 0;
+}
diff --git a/source/ubase/rtc.h b/source/ubase/rtc.h
new file mode 100644 (file)
index 0000000..e4135a7
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * The struct used to pass data via the following ioctl. Similar to the
+ * struct tm in <time.h>, but it needs to be here so that the kernel
+ * source is self contained, allowing cross-compiles, etc. etc.
+ */
+
+struct rtc_time {
+       int tm_sec;
+       int tm_min;
+       int tm_hour;
+       int tm_mday;
+       int tm_mon;
+       int tm_year;
+       int tm_wday;
+       int tm_yday;
+       int tm_isdst;
+};
+
+#define RTC_RD_TIME    _IOR('p', 0x09, struct rtc_time) /* Read RTC time   */
+#define RTC_SET_TIME   _IOW('p', 0x0a, struct rtc_time) /* Set RTC time    */
diff --git a/source/ubase/stat.1 b/source/ubase/stat.1
new file mode 100644 (file)
index 0000000..fdb3433
--- /dev/null
@@ -0,0 +1,24 @@
+.Dd February 2, 2015
+.Dt STAT 1
+.Os ubase
+.Sh NAME
+.Nm stat
+.Nd display file status
+.Sh SYNOPSIS
+.Nm
+.Op Fl L
+.Op Ar file...
+.Sh DESCRIPTION
+.Nm
+displays information about the given
+.Ar files
+or stdin if no
+.Ar files
+are specified.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl L
+Follow links.
+.El
+.Sh SEE ALSO
+.Xr stat 2
diff --git a/source/ubase/stat.c b/source/ubase/stat.c
new file mode 100644 (file)
index 0000000..220a659
--- /dev/null
@@ -0,0 +1,89 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+show_stat_terse(const char *file, struct stat *st)
+{
+       printf("%s ", file);
+       printf("%lu %lu ", (unsigned long)st->st_size,
+              (unsigned long)st->st_blocks);
+       printf("%04o %u %u ", st->st_mode & 0777, st->st_uid, st->st_gid);
+       printf("%llx ", (unsigned long long)st->st_dev);
+       printf("%lu %lu ", (unsigned long)st->st_ino, (unsigned long)st->st_nlink);
+       printf("%d %d ", major(st->st_rdev), minor(st->st_rdev));
+       printf("%ld %ld %ld ", st->st_atime, st->st_mtime, st->st_ctime);
+       printf("%lu\n", (unsigned long)st->st_blksize);
+}
+
+static void
+show_stat(const char *file, struct stat *st)
+{
+       char buf[100];
+
+       printf("  File: â€˜%s’\n", file);
+       printf("  Size: %lu\tBlocks: %lu\tIO Block: %lu\n", (unsigned long)st->st_size,
+              (unsigned long)st->st_blocks, (unsigned long)st->st_blksize);
+       printf("Device: %xh/%ud\tInode: %lu\tLinks %lu\n", major(st->st_dev),
+              minor(st->st_dev), (unsigned long)st->st_ino, (unsigned long)st->st_nlink);
+       printf("Access: %04o\tUid: %u\tGid: %u\n", st->st_mode & 0777, st->st_uid, st->st_gid);
+       strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&st->st_atime));
+       printf("Access: %s\n", buf);
+       strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&st->st_mtime));
+       printf("Modify: %s\n", buf);
+       strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&st->st_ctime));
+       printf("Change: %s\n", buf);
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-L] [-t] [file...]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct stat st;
+       int i, ret = 0;
+       int (*fn)(const char *, struct stat *) = lstat;
+       char *fnname = "lstat";
+       void (*showstat)(const char *, struct stat *) = show_stat;
+
+       ARGBEGIN {
+       case 'L':
+               fn = stat;
+               fnname = "stat";
+               break;
+       case 't':
+               showstat = show_stat_terse;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc == 0) {
+               if (fstat(0, &st) < 0)
+                       eprintf("stat <stdin>:");
+               show_stat("<stdin>", &st);
+       }
+
+       for (i = 0; i < argc; i++) {
+               if (fn(argv[i], &st) == -1) {
+                       weprintf("%s %s:", fnname, argv[i]);
+                       ret = 1;
+                       continue;
+               }
+               showstat(argv[i], &st);
+       }
+
+       return ret;
+}
\ No newline at end of file
diff --git a/source/ubase/su.1 b/source/ubase/su.1
new file mode 100644 (file)
index 0000000..305308d
--- /dev/null
@@ -0,0 +1,39 @@
+.Dd February 2, 2015
+.Dt SU 1
+.Os ubase
+.Sh NAME
+.Nm su
+.Nd run a command with a substitute user and group ID
+.Sh SYNOPSIS
+.Nm
+.Op Fl lp
+.Op Ar username
+.Sh DESCRIPTION
+.Nm
+allows to run commands with a substitute user and group ID. When called
+without arguments,
+.Nm
+defaults to running an interactive shell as root. For backward compatibility
+.Nm
+defaults to not change the current directory and to only set the environment
+variables
+.Ev HOME
+and
+.Ev SHELL
+(plus
+.Ev USER
+and
+.Ev LOGNAME
+if the target
+.Ar username
+is not root).
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl l
+Starts the shell as login shell with an environment similar to a real
+login.
+.It Fl p
+Preserves the whole environment. This option is ignored if the
+.Fl l
+option is specified.
+.El
\ No newline at end of file
diff --git a/source/ubase/su.c b/source/ubase/su.c
new file mode 100644 (file)
index 0000000..329238f
--- /dev/null
@@ -0,0 +1,125 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/types.h>
+
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "passwd.h"
+#include "util.h"
+
+extern char **environ;
+
+static int lflag = 0;
+static int pflag = 0;
+
+static int
+dologin(struct passwd *pw)
+{
+       char *shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell;
+       char *term = getenv("TERM");
+       clearenv();
+       setenv("HOME", pw->pw_dir, 1);
+       setenv("SHELL", shell, 1);
+       setenv("USER", pw->pw_name, 1);
+       setenv("LOGNAME", pw->pw_name, 1);
+       setenv("TERM", term ? term : "linux", 1);
+       if (strcmp(pw->pw_name, "root") == 0)
+               setenv("PATH", ENV_SUPATH, 1);
+       else
+               setenv("PATH", ENV_PATH, 1);
+       if (chdir(pw->pw_dir) < 0)
+               eprintf("chdir %s:", pw->pw_dir);
+       execlp(shell, shell, "-l", NULL);
+       weprintf("execlp %s:", shell);
+       return (errno == ENOENT) ? 127 : 126;
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-lp] [username]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *usr = "root", *pass;
+       char *shell;
+       struct passwd *pw;
+       char *newargv[2];
+       uid_t uid;
+
+       ARGBEGIN {
+       case 'l':
+               lflag = 1;
+               break;
+       case 'p':
+               pflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               ;
+       else if (argc == 1)
+               usr = argv[0];
+       else
+               usage();
+
+       errno = 0;
+       pw = getpwnam(usr);
+       if (!pw) {
+               if (errno)
+                       eprintf("getpwnam: %s:", usr);
+               else
+                       eprintf("who are you?\n");
+       }
+
+       uid = getuid();
+       if (uid) {
+               pass = getpass("Password: ");
+               if (!pass)
+                       eprintf("getpass:");
+               if (pw_check(pw, pass) <= 0)
+                       exit(1);
+       }
+
+       if (initgroups(usr, pw->pw_gid) < 0)
+               eprintf("initgroups:");
+       if (setgid(pw->pw_gid) < 0)
+               eprintf("setgid:");
+       if (setuid(pw->pw_uid) < 0)
+               eprintf("setuid:");
+
+       if (lflag) {
+               return dologin(pw);
+       } else {
+               shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell;
+               newargv[0] = shell;
+               newargv[1] = NULL;
+               if (!pflag) {
+                       setenv("HOME", pw->pw_dir, 1);
+                       setenv("SHELL", shell, 1);
+                       if (strcmp(pw->pw_name, "root") != 0) {
+                               setenv("USER", pw->pw_name, 1);
+                               setenv("LOGNAME", pw->pw_name, 1);
+                       }
+               }
+               if (strcmp(pw->pw_name, "root") == 0)
+                       setenv("PATH", ENV_SUPATH, 1);
+               else
+                       setenv("PATH", ENV_PATH, 1);
+               execve(pflag ? getenv("SHELL") : shell,
+                      newargv, environ);
+               weprintf("execve %s:", shell);
+               return (errno == ENOENT) ? 127 : 126;
+       }
+       return 0;
+}
\ No newline at end of file
diff --git a/source/ubase/swaplabel.8 b/source/ubase/swaplabel.8
new file mode 100644 (file)
index 0000000..13274c5
--- /dev/null
@@ -0,0 +1,18 @@
+.Dd February 2, 2015
+.Dt SWAPLABEL 8
+.Os ubase
+.Sh NAME
+.Nm swaplabel
+.Nd set the label of a swap filesystem
+.Sh SYNOPSIS
+.Nm
+.Op Fl L Ar label
+.Ar device
+.Sh DESCRIPTION
+.Nm
+is used to change the label of a swap device or file.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl L Ar label
+Change the label.
+.El
diff --git a/source/ubase/swaplabel.c b/source/ubase/swaplabel.c
new file mode 100644 (file)
index 0000000..a9895d5
--- /dev/null
@@ -0,0 +1,80 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define SWAP_MAGIC1 "SWAPSPACE2"
+#define SWAP_MAGIC2 "SWAP-SPACE"
+#define SWAP_MAGIC_LENGTH (10)
+#define SWAP_MAGIC_OFFSET (sysconf(_SC_PAGESIZE) - SWAP_MAGIC_LENGTH)
+#define SWAP_LABEL_LENGTH (16)
+#define SWAP_LABEL_OFFSET (1024 + 4 + 4 + 4 + 16)
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-L label] device\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int setlabel = 0;
+       int fd;
+       char magic[SWAP_MAGIC_LENGTH];
+       char *label;
+       char *device;
+       int i;
+
+       ARGBEGIN {
+       case 'L':
+               setlabel = 1;
+               label = EARGF(usage());
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               usage();
+       device = argv[0];
+
+       fd = open(device, O_RDWR);
+       if (fd < 0)
+               eprintf("open %s:", device);
+
+       if (lseek(fd, SWAP_MAGIC_OFFSET, SEEK_SET) != SWAP_MAGIC_OFFSET)
+               eprintf("failed seeking to magic position:");
+       if (read(fd, magic, SWAP_MAGIC_LENGTH) != SWAP_MAGIC_LENGTH)
+               eprintf("reading magic failed:");
+       if (memcmp(magic, SWAP_MAGIC1, 10) && memcmp(magic, SWAP_MAGIC2, 10))
+               eprintf("%s: is not a swap partition\n", device);
+       if (lseek(fd, SWAP_LABEL_OFFSET, SEEK_SET) != SWAP_LABEL_OFFSET)
+               eprintf("failed seeking to label position:");
+
+       if (!setlabel) {
+               label = emalloc(SWAP_LABEL_LENGTH);
+               if (read(fd, label, SWAP_LABEL_LENGTH) != SWAP_LABEL_LENGTH)
+                       eprintf("reading label failed:");
+               for (i = 0; i < SWAP_LABEL_LENGTH && label[i] != '\0'; i++)
+                       if (i == (SWAP_LABEL_LENGTH - 1) && label[i] != '\0')
+                               eprintf("invalid label\n");
+               printf("label: %s\n", label);
+               free(label);
+       } else {
+               if (strlen(label) + 1 > SWAP_LABEL_LENGTH)
+                       eprintf("label too long\n");
+               if (write(fd, label, strlen(label) + 1) != (ssize_t)strlen(label) + 1)
+                       eprintf("writing label failed:");
+       }
+
+       fsync(fd);
+       close(fd);
+       return 0;
+}
diff --git a/source/ubase/swapoff.8 b/source/ubase/swapoff.8
new file mode 100644 (file)
index 0000000..45dbe7a
--- /dev/null
@@ -0,0 +1,17 @@
+.Dd February 2, 2015
+.Dt SWAPOFF 8
+.Os ubase
+.Sh NAME
+.Nm swapoff
+.Nd disable devices and files for paging and swapping
+.Sh SYNOPSIS
+.Nm
+.Fl a | Ar device
+.Sh DESCRIPTION
+.Nm
+disables swapping on the specified devices and files.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl a
+Disable swapping on all known swap devices and files as found in /etc/fstab.
+.El
diff --git a/source/ubase/swapoff.c b/source/ubase/swapoff.c
new file mode 100644 (file)
index 0000000..5f32e9d
--- /dev/null
@@ -0,0 +1,59 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/swap.h>
+
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s -a | device\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int i;
+       int ret = 0;
+       int all = 0;
+       struct mntent *me;
+       FILE *fp;
+
+       ARGBEGIN {
+       case 'a':
+               all = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if ((!all && argc < 1) || (all && argc > 0))
+               usage();
+
+       if (all) {
+               fp = setmntent("/etc/fstab", "r");
+               if (!fp)
+                       eprintf("setmntent %s:", "/etc/fstab");
+               while ((me = getmntent(fp)) != NULL) {
+                       if (strcmp(me->mnt_type, MNTTYPE_SWAP) == 0) {
+                               if (swapoff(me->mnt_fsname) < 0) {
+                                       weprintf("swapoff %s:", me->mnt_fsname);
+                                       ret = 1;
+                               }
+                       }
+               }
+               endmntent(fp);
+       } else {
+               for (i = 0; i < argc; i++) {
+                       if (swapoff(argv[i]) < 0) {
+                               weprintf("swapoff %s:", argv[i]);
+                               ret = 1;
+                       }
+               }
+       }
+       return ret;
+}
diff --git a/source/ubase/swapon.8 b/source/ubase/swapon.8
new file mode 100644 (file)
index 0000000..695a7bf
--- /dev/null
@@ -0,0 +1,24 @@
+.Dd February 2, 2015
+.Dt SWAPON 8
+.Os ubase
+.Sh NAME
+.Nm swapon
+.Nd enable devices and files for paging and swapping
+.Sh SYNOPSIS
+.Nm
+.Op Fl dp
+.Fl a | Ar device
+.Sh DESCRIPTION
+.Nm
+is used to specify devices on which paging and swapping are to take place.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl a
+Make all devices marked as ``swap'' in
+.Pa /etc/fstab
+available, except for those with the ``noauto'' option.
+.It Fl d
+Discard freed swap pages before they are reused.
+.It Fl p
+Set higher priority than the default to the new swap area.
+.El
diff --git a/source/ubase/swapon.c b/source/ubase/swapon.c
new file mode 100644 (file)
index 0000000..672c64e
--- /dev/null
@@ -0,0 +1,67 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/swap.h>
+
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-dp] -a | device\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int i;
+       int ret = 0;
+       int flags = 0;
+       int all = 0;
+       struct mntent *me;
+       FILE *fp;
+
+       ARGBEGIN {
+       case 'a':
+               all = 1;
+               break;
+       case 'd':
+               flags |= SWAP_FLAG_DISCARD;
+               break;
+       case 'p':
+               flags |= SWAP_FLAG_PREFER;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if ((!all && argc < 1) || (all && argc > 0))
+               usage();
+
+       if (all) {
+               fp = setmntent("/etc/fstab", "r");
+               if (!fp)
+                       eprintf("setmntent %s:", "/etc/fstab");
+               while ((me = getmntent(fp)) != NULL) {
+                       if (strcmp(me->mnt_type, MNTTYPE_SWAP) == 0
+                           && (hasmntopt(me, MNTOPT_NOAUTO) == NULL)) {
+                               if (swapon(me->mnt_fsname, flags) < 0) {
+                                       weprintf("swapon %s:", me->mnt_fsname);
+                                       ret = 1;
+                               }
+                       }
+               }
+               endmntent(fp);
+       } else {
+               for (i = 0; i < argc; i++) {
+                       if (swapon(argv[i], flags) < 0) {
+                               weprintf("swapon %s:", argv[i]);
+                               ret = 1;
+                       }
+               }
+       }
+       return ret;
+}
diff --git a/source/ubase/switch_root.8 b/source/ubase/switch_root.8
new file mode 100644 (file)
index 0000000..764119e
--- /dev/null
@@ -0,0 +1,31 @@
+.Dd February 2, 2015
+.Dt SWITCH_ROOT 8
+.Os ubase
+.Sh NAME
+.Nm switch_root
+.Nd switch to another filesystem as the root of the mount tree
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar console
+.Ar newroot init
+.Sh DESCRIPTION
+.Nm
+removes all files and directories on the current root filesystem and
+overmounts it with
+.Ar newroot .
+If a
+.Ar console
+is specified, redirect stdio and stderr to it. After the switch, execute
+.Ar init .
+.Pp
+.Nm
+can only be run as PID 1 in an initramfs or tmpfs with a regular and
+executable /sbin/init.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl c
+Redirect stdio and stderr to
+.Ar console
+after switching to
+.Ar newroot .
+.El
diff --git a/source/ubase/switch_root.c b/source/ubase/switch_root.c
new file mode 100644 (file)
index 0000000..204f7d1
--- /dev/null
@@ -0,0 +1,131 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define RAMFS_MAGIC            0x858458f6      /* some random number */
+#define TMPFS_MAGIC            0x01021994
+
+static void
+delete_content(const char *dir, dev_t curdevice)
+{
+       char path[PATH_MAX];
+       DIR *d;
+       struct stat st;
+       struct dirent *dent;
+
+       /* don't dive into other filesystems */
+       if (lstat(dir, &st) < 0 || st.st_dev != curdevice)
+               return;
+       if (!(d = opendir(dir)))
+               return;
+       while ((dent = readdir(d))) {
+               if (strcmp(dent->d_name, ".") == 0 ||
+                   strcmp(dent->d_name, "..") == 0)
+                       continue;
+
+               /* build path and dive deeper */
+               if (strlcpy(path, dir, sizeof(path)) >= sizeof(path))
+                       eprintf("path too long\n");
+               if (path[strlen(path) - 1] != '/')
+                       if (strlcat(path, "/", sizeof(path)) >= sizeof(path))
+                               eprintf("path too long\n");
+               if (strlcat(path, dent->d_name, sizeof(path)) >= sizeof(path))
+                       eprintf("path too long\n");
+
+               if (lstat(path, &st) < 0)
+                       weprintf("lstat %s:", path);
+
+               if (S_ISDIR(st.st_mode)) {
+                       delete_content(path, curdevice);
+                       if (rmdir(path) < 0)
+                               weprintf("rmdir %s:", path);
+               } else {
+                       if (unlink(path) < 0)
+                               weprintf("unlink %s:", path);
+               }
+       }
+       closedir(d);
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-c console] [newroot] [init] (PID 1)\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char *console = NULL;
+       dev_t curdev;
+       struct stat st;
+       struct statfs stfs;
+
+       ARGBEGIN {
+       case 'c':
+               console = EARGF(usage());
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       /* check number of args and if we are PID 1 */
+       if (argc != 2 || getpid() != 1)
+               usage();
+
+       /* chdir to newroot and make sure it's a different fs */
+       if (chdir(argv[0]))
+               eprintf("chdir %s:", argv[0]);
+
+       if (stat("/", &st))
+               eprintf("stat %s:", "/");
+
+       curdev = st.st_dev;
+       if (stat(".", &st))
+               eprintf("stat %s:", ".");
+       if (st.st_dev == curdev)
+               usage();
+
+       /* avoids trouble with real filesystems */
+       if (stat("/init", &st) || !S_ISREG(st.st_mode))
+               eprintf("/init is not a regular file\n");
+
+       statfs("/", &stfs);
+       if ((unsigned)stfs.f_type != RAMFS_MAGIC && (unsigned)stfs.f_type != TMPFS_MAGIC)
+               eprintf("current filesystem is not a RAMFS or TMPFS\n");
+
+       /* wipe / */
+       delete_content("/", curdev);
+
+       /* overmount / with newroot and chroot into it */
+       if (mount(".", "/", NULL, MS_MOVE, NULL))
+               eprintf("mount %s:", ".");
+
+       if (chroot("."))
+               eprintf("chroot failed\n");
+
+       /* if -c is set, redirect stdin/stdout/stderr to console */
+       if (console) {
+               close(0);
+               if (open(console, O_RDWR) == -1)
+                       eprintf("open %s:", console);
+               dup2(0, 1);
+               dup2(0, 2);
+       }
+
+       /* execute init */
+       execv(argv[1], argv);
+       eprintf("can't execute '%s':", argv[1]);
+       return 1;
+}
diff --git a/source/ubase/sysctl.8 b/source/ubase/sysctl.8
new file mode 100644 (file)
index 0000000..4624b18
--- /dev/null
@@ -0,0 +1,24 @@
+.Dd February 2, 2015
+.Dt SYSCTL 8
+.Os ubase
+.Sh NAME
+.Nm sysctl
+.Nd configure kernel parameters at runtime
+.Sh SYNOPSIS
+.Nm
+.Op Fl p Ar file
+.Ar variable Ns Oo Ar =value Oc Ns ...
+.Sh DESCRIPTION
+.Nm
+modifies kernel parameters at runtime. The parameters available are those
+listed under
+.Pa /proc/sys/ .
+Procfs is required for sysctl support in Linux. You can use
+.Nm
+to both read and write sysctl data.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl p
+Load the sysctl key=value pairs from
+.Ar file .
+.El
diff --git a/source/ubase/sysctl.c b/source/ubase/sysctl.c
new file mode 100644 (file)
index 0000000..a98a637
--- /dev/null
@@ -0,0 +1,213 @@
+/* See LICENSE file for copyright and license details. */
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "text.h"
+#include "util.h"
+
+static void
+replacestr(char *s, int a, int b)
+{
+       for (; *s; s++)
+               if (*s == a)
+                       *s = b;
+}
+
+static int
+getsysctl(char *variable, char **value)
+{
+       char path[PATH_MAX];
+       char *p;
+       char *buf, *tmp, c;
+       int fd;
+       ssize_t n;
+       size_t sz, i;
+
+       replacestr(variable, '.', '/');
+
+       strlcpy(path, "/proc/sys/", sizeof(path));
+       if (strlcat(path, variable, sizeof(path)) >= sizeof(path)) {
+               replacestr(variable, '/', '.');
+               return -1;
+       }
+
+       replacestr(variable, '/', '.');
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return -1;
+
+       i = 0;
+       sz = 1;
+       buf = NULL;
+       while (1) {
+               n = read(fd, &c, 1);
+               if (n < 0) {
+                       close(fd);
+                       free(buf);
+                       return -1;
+               }
+               if (n == 0)
+                       break;
+               if (i == sz - 1) {
+                       sz *= 2;
+                       tmp = realloc(buf, sz);
+                       if (!tmp) {
+                               close(fd);
+                               free(buf);
+                               return -1;
+                       }
+                       buf = tmp;
+               }
+               buf[i++] = c;
+       }
+       buf[i] = '\0';
+
+       p = strrchr(buf, '\n');
+       if (p)
+               *p = '\0';
+
+       *value = buf;
+
+       close(fd);
+
+       return 0;
+}
+
+static int
+setsysctl(char *variable, char *value)
+{
+       char path[PATH_MAX];
+       int fd;
+       ssize_t n;
+
+       replacestr(variable, '.', '/');
+
+       strlcpy(path, "/proc/sys/", sizeof(path));
+       if (strlcat(path, variable, sizeof(path)) >= sizeof(path)) {
+               replacestr(variable, '/', '.');
+               return -1;
+       }
+
+       replacestr(variable, '/', '.');
+
+       fd = open(path, O_WRONLY);
+       if (fd < 0)
+               return -1;
+
+       n = write(fd, value, strlen(value));
+       if ((size_t)n != strlen(value)) {
+               close(fd);
+               return -1;
+       }
+
+       close(fd);
+
+       return 0;
+}
+
+static int
+parsepair(char *pair)
+{
+       char *p;
+       char *variable;
+       char *value;
+
+       for (p = pair; *p; p++) {
+               if (p[0] == '.' && p[1] == '.') {
+                       weprintf("malformed input: %s\n", pair);
+                       return -1;
+               }
+       }
+       p = strchr(pair, '=');
+       if (p) {
+               if (p[1] == '\0') {
+                       weprintf("malformed input: %s\n", pair);
+                       return -1;
+               }
+               *p = '\0';
+               value = &p[1];
+       } else {
+               value = NULL;
+       }
+       variable = pair;
+       if (value) {
+               if (setsysctl(variable, value) < 0) {
+                       weprintf("failed to set sysctl for %s\n", variable);
+                       return -1;
+               }
+       } else {
+               if (getsysctl(variable, &value) < 0) {
+                       weprintf("failed to get sysctl for %s\n", variable);
+                       return -1;
+               }
+               printf("%s = %s\n", variable, value);
+               free(value);
+       }
+
+       return 0;
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-p file] variable[=value]...\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       FILE *fp;
+       char *buf = NULL, *p;
+       char *file = NULL;
+       size_t size = 0;
+       int i;
+       int r = 0;
+
+       ARGBEGIN {
+       case 'p':
+               file = EARGF(usage());
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (!file && argc < 1)
+               usage();
+
+       if (!file) {
+               for (i = 0; i < argc; i++)
+                       if (parsepair(argv[i]) < 0)
+                               r = 1;
+       } else {
+               fp = fopen(file, "r");
+               if (!fp)
+                       eprintf("fopen %s:", file);
+               while (agetline(&buf, &size, fp) != -1) {
+                       p = buf;
+                       for (p = buf; *p == ' ' || *p == '\t'; p++)
+                               ;
+                       if (*p == '#' || *p == '\n')
+                               continue;
+                       for (p = buf; *p; p++) {
+                               if (*p == '\n') {
+                                       *p = '\0';
+                                       break;
+                               }
+                       }
+                       p = buf;
+                       if (parsepair(p) < 0)
+                               r = 1;
+               }
+               if (ferror(fp))
+                       eprintf("%s: read error:", file);
+               free(buf);
+               fclose(fp);
+       }
+
+       return r;
+}
diff --git a/source/ubase/text.h b/source/ubase/text.h
new file mode 100644 (file)
index 0000000..8ea09f8
--- /dev/null
@@ -0,0 +1,13 @@
+/* See LICENSE file for copyright and license details. */
+
+struct linebuf {
+       char **lines;
+       long nlines;
+       long capacity;
+};
+#define EMPTY_LINEBUF {NULL, 0, 0,}
+void getlines(FILE *, struct linebuf *);
+
+ssize_t agetline(char **, size_t *, FILE *);
+
+void concat(FILE *, const char *, FILE *, const char *);
diff --git a/source/ubase/truncate.1 b/source/ubase/truncate.1
new file mode 100644 (file)
index 0000000..591532c
--- /dev/null
@@ -0,0 +1,36 @@
+.Dd February 2, 2015
+.Dt TRUNCATE 1
+.Os ubase
+.Sh NAME
+.Nm truncate
+.Nd shrink or extend the size of a file to the specified size
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Fl s Ar size
+.Ar file...
+.Sh DESCRIPTION
+.Nm
+shrinks or extends the size of each
+.Ar file
+specified size. A
+.Ar file
+argument that does not exist is created. If a
+.Ar file
+is larger than the specified
+.Ar size ,
+the extra data is lost. If a
+.Ar file
+is shorter, it is extended and the extended part (hole) reads as zero bytes.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl c
+Do not create any files.
+.It Fl s Ar size
+Set or adjust the file size by
+.Ar size
+bytes.
+.El
+.Sh SEE ALSO
+.Xr ftruncate 2 ,
+.Xr truncate 2
\ No newline at end of file
diff --git a/source/ubase/truncate.c b/source/ubase/truncate.c
new file mode 100644 (file)
index 0000000..9947397
--- /dev/null
@@ -0,0 +1,53 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-c] -s size file...\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int cflag = 0, sflag = 0;
+       int fd, i, ret = 0;
+       long size = 0;
+
+       ARGBEGIN {
+       case 's':
+               sflag = 1;
+               size = estrtol(EARGF(usage()), 10);
+               break;
+       case 'c':
+               cflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1 || sflag == 0)
+               usage();
+
+       for (i = 0; i < argc; i++) {
+               fd = open(argv[i], O_WRONLY | (cflag ? 0 : O_CREAT), 0644);
+               if (fd < 0) {
+                       weprintf("open: cannot open `%s' for writing:", argv[i]);
+                       ret = 1;
+                       continue;
+               }
+               if (ftruncate(fd, size) < 0) {
+                       weprintf("ftruncate: cannot open `%s' for writing:", argv[i]);
+                       ret = 1;
+               }
+               close(fd);
+       }
+       return ret;
+}
diff --git a/source/ubase/umount.8 b/source/ubase/umount.8
new file mode 100644 (file)
index 0000000..c172efb
--- /dev/null
@@ -0,0 +1,40 @@
+.Dd February 2, 2015
+.Dt UMOUNT 8
+.Os ubase
+.Sh NAME
+.Nm umount
+.Nd unmount file systems
+.Sh SYNOPSIS
+.Nm
+.Op Fl fln
+.Ar target...
+.Nm
+.Op Fl fln
+.Fl a
+.Sh DESCRIPTION
+.Nm
+detaches the
+.Ar target
+filesystem(s). A file system is specified by giving the directory where it
+has been mounted. Giving the special device on which the file system
+lives may also work, but is obsolete, mainly because it will fail in
+case this device was mounted on more than one directory.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl a
+All of the file systems described in
+.Pa /proc/mounts
+are unmounted. The proc filesystem is not unmounted.
+.It Fl f
+Force unmount (in case of an unreachable NFS server).
+.It Fl l
+Lazy unmount. Detach the filesystem from the fs hierarchy now, and cleanup
+all references to the filesystem as soon as it is not busy anymore.
+.It Fl n
+Unmount without writing in
+.Pa /etc/mtab .
+This is the default action.
+.El
+.Sh SEE ALSO
+.Xr umount 2 ,
+.Xr mount 8
diff --git a/source/ubase/umount.c b/source/ubase/umount.c
new file mode 100644 (file)
index 0000000..7f9e174
--- /dev/null
@@ -0,0 +1,86 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/mount.h>
+
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+static int
+umountall(int flags)
+{
+       FILE *fp;
+       struct mntent *me;
+       int ret;
+       char **mntdirs = NULL;
+       int len = 0;
+
+       fp = setmntent("/proc/mounts", "r");
+       if (!fp)
+               eprintf("setmntent %s:", "/proc/mounts");
+       while ((me = getmntent(fp))) {
+               if (strcmp(me->mnt_type, "proc") == 0)
+                       continue;
+               mntdirs = erealloc(mntdirs, ++len * sizeof(*mntdirs));
+               mntdirs[len - 1] = estrdup(me->mnt_dir);
+       }
+       endmntent(fp);
+       while (--len >= 0) {
+               if (umount2(mntdirs[len], flags) < 0) {
+                       weprintf("umount2 %s:", mntdirs[len]);
+                       ret = 1;
+               }
+               free(mntdirs[len]);
+       }
+       free(mntdirs);
+       return ret;
+}
+
+static void
+usage(void)
+{
+       weprintf("usage: %s [-lfn] target...\n", argv0);
+       weprintf("usage: %s -a [-lfn]\n", argv0);
+       exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int i;
+       int aflag = 0;
+       int flags = 0;
+       int ret = 0;
+
+       ARGBEGIN {
+       case 'a':
+               aflag = 1;
+               break;
+       case 'f':
+               flags |= MNT_FORCE;
+               break;
+       case 'l':
+               flags |= MNT_DETACH;
+               break;
+       case 'n':
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1 && aflag == 0)
+               usage();
+
+       if (aflag == 1)
+               return umountall(flags);
+
+       for (i = 0; i < argc; i++) {
+               if (umount2(argv[i], flags) < 0) {
+                       weprintf("umount2 %s:", argv[i]);
+                       ret = 1;
+               }
+       }
+       return ret;
+}
\ No newline at end of file
diff --git a/source/ubase/unshare.1 b/source/ubase/unshare.1
new file mode 100644 (file)
index 0000000..655745e
--- /dev/null
@@ -0,0 +1,58 @@
+.Dd February 2, 2015
+.Dt UNSHARE 1
+.Os ubase
+.Sh NAME
+.Nm unshare
+.Nd run program with some namespaces unshared from parent
+.Sh SYNOPSIS
+.Nm
+.Op Fl muinpU
+.Ar cmd
+.Op Ar args...
+.Sh DESCRIPTION
+.Nm
+unshares the indicated namespaces from the parent process and then executes
+the specified program. The namespaces to be unshared are indicated via
+options.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl i
+Unshare the System V IPC namespace, so that the calling process has a
+private copy of the System V IPC namespace which is not shared with
+any other process. This flag has the same effect as the
+.Xr clone 2
+.Dv CLONE_NEWIPC
+flag.
+.It Fl m
+Unshare the mount namespace, so that the calling process has a private
+copy of its namespace which is not shared with any other process.
+This flag has the same effect as the
+.Xr clone 2
+.Dv CLONE_NEWNS
+flag.
+.It Fl n
+Unshare the network namespace, so that the calling process is moved
+into a new network namespace which is not shared with any previously
+existing process. This flag has the same effect as the
+.Xr clone 2
+.Dv CLONE_NEWNET
+flag.
+.It Fl u
+Unshare the UTS IPC namespace, so that the calling process has a
+private copy of the UTS namespace which is not shared with any other
+process. This flag has the same effect as the
+.Xr clone 2
+.Dv CLONE_NEWUTS
+flag.
+.It Fl p
+Create the process in a new PID namespace. This flag has the same
+effect as the
+.Xr clone 2
+.Dv CLONE_NEWPID
+flag.
+.It Fl U
+The process will have a distinct set of UIDs, GIDs and capabilities.
+.El
+.Sh SEE ALSO
+.Xr clone 2 ,
+.Xr unshare 2
diff --git a/source/ubase/unshare.c b/source/ubase/unshare.c
new file mode 100644 (file)
index 0000000..832c711
--- /dev/null
@@ -0,0 +1,54 @@
+/* See LICENSE file for copyright and license details. */
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-muinpU] cmd [args...]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int flags = 0;
+
+       ARGBEGIN {
+       case 'm':
+               flags |= CLONE_NEWNS;
+               break;
+       case 'u':
+               flags |= CLONE_NEWUTS;
+               break;
+       case 'i':
+               flags |= CLONE_NEWIPC;
+               break;
+       case 'n':
+               flags |= CLONE_NEWNET;
+               break;
+       case 'p':
+               flags |= CLONE_NEWPID;
+               break;
+       case 'U':
+               flags |= CLONE_NEWUSER;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               usage();
+
+       if (unshare(flags) < 0)
+               eprintf("unshare:");
+
+       if (execvp(argv[0], argv) < 0)
+               eprintf("execvp:");
+
+       return 0;
+}
diff --git a/source/ubase/uptime.1 b/source/ubase/uptime.1
new file mode 100644 (file)
index 0000000..b4e8538
--- /dev/null
@@ -0,0 +1,16 @@
+.Dd February 2, 2015
+.Dt UPTIME 1
+.Os ubase
+.Sh NAME
+.Nm uptime
+.Nd tell how long the system has been running
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+gives a one line display of the following information. The current time, how
+long the system has been running, how many users are currently logged on and
+the system load averages for the past 1, 5 and 15 minutes.
+.Sh SEE ALSO
+.Xr ps 1 ,
+.Xr utmp 5
diff --git a/source/ubase/uptime.c b/source/ubase/uptime.c
new file mode 100644 (file)
index 0000000..e0dd6fa
--- /dev/null
@@ -0,0 +1,73 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/sysinfo.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <utmpx.h>
+
+#include "config.h"
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct utmpx utx;
+       FILE *ufp;
+       struct sysinfo info;
+       time_t tmptime;
+       struct tm *now;
+       unsigned int days, hours, minutes;
+       int nusers = 0;
+       size_t n;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (sysinfo(&info) < 0)
+               eprintf("sysinfo:");
+       time(&tmptime);
+       now = localtime(&tmptime);
+       printf(" %02d:%02d:%02d up ", now->tm_hour, now->tm_min, now->tm_sec);
+       info.uptime /= 60;
+       minutes = info.uptime % 60;
+       info.uptime /= 60;
+       hours = info.uptime % 24;
+       days = info.uptime / 24;
+       if (days)
+               printf("%d day%s, ", days, days > 1 ? "s" : "");
+       if (hours)
+               printf("%2d:%02d, ", hours, minutes);
+       else
+               printf("%d min, ", minutes);
+
+       if ((ufp = fopen(UTMP_PATH, "r"))) {
+               while ((n = fread(&utx, sizeof(utx), 1, ufp)) > 0) {
+                       if (!utx.ut_user[0])
+                               continue;
+                       if (utx.ut_type != USER_PROCESS)
+                               continue;
+                       nusers++;
+               }
+               if (ferror(ufp))
+                       eprintf("%s: read error:", UTMP_PATH);
+               fclose(ufp);
+               printf(" %d user%s, ", nusers, nusers > 1 ? "s" : "");
+       }
+
+       printf(" load average: %.02f, %.02f, %.02f\n",
+              info.loads[0] / 65536.0f,
+              info.loads[1] / 65536.0f,
+              info.loads[2] / 65536.0f);
+
+       return 0;
+}
diff --git a/source/ubase/util.h b/source/ubase/util.h
new file mode 100644 (file)
index 0000000..6ab609e
--- /dev/null
@@ -0,0 +1,69 @@
+/* See LICENSE file for copyright and license details. */
+#include "arg.h"
+
+#define UTF8_POINT(c) (((c) & 0xc0) != 0x80)
+
+#undef MIN
+#define MIN(x,y)  ((x) < (y) ? (x) : (y))
+#undef MAX
+#define MAX(x,y)  ((x) > (y) ? (x) : (y))
+#undef LIMIT
+#define LIMIT(x, a, b)  (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
+
+#define LEN(x) (sizeof (x) / sizeof *(x))
+
+/* eprintf.c */
+extern char *argv0;
+
+/* agetcwd.c */
+char *agetcwd(void);
+
+/* apathmax.c */
+void apathmax(char **, long *);
+
+/* ealloc.c */
+void *ecalloc(size_t, size_t);
+void *emalloc(size_t size);
+void *erealloc(void *, size_t);
+char *estrdup(const char *);
+
+/* eprintf.c */
+void enprintf(int, const char *, ...);
+void eprintf(const char *, ...);
+void weprintf(const char *, ...);
+
+/* estrtol.c */
+long estrtol(const char *, int);
+
+/* estrtoul.c */
+unsigned long estrtoul(const char *, int);
+
+/* explicit_bzero.c */
+#undef explicit_bzero
+void explicit_bzero(void *, size_t);
+
+/* putword.c */
+void putword(const char *);
+
+/* recurse.c */
+void recurse(const char *, void (*)(const char *));
+
+/* strlcat.c */
+#undef strlcat
+size_t strlcat(char *, const char *, size_t);
+size_t estrlcat(char *, const char *, size_t);
+
+/* strlcpy.c */
+#undef strlcpy
+size_t strlcpy(char *, const char *, size_t);
+size_t estrlcpy(char *, const char *, size_t);
+
+/* strtonum.c */
+#undef strtonum
+long long strtonum(const char *, long long, long long, const char **);
+long long enstrtonum(int, const char *, long long, long long);
+long long estrtonum(const char *, long long, long long);
+
+/* tty.c */
+void devtotty(int, int *, int *);
+int ttytostr(int, int, char *, size_t);
diff --git a/source/ubase/vtallow.1 b/source/ubase/vtallow.1
new file mode 100644 (file)
index 0000000..b567bf7
--- /dev/null
@@ -0,0 +1,21 @@
+.Dd December 5, 2014
+.Dt VTALLOW 1
+.Os ubase
+.Sh NAME
+.Nm vtallow
+.Nd enable or disable the VT switch
+.Sh SYNOPSIS
+.Nm vtallow
+.Ar n | Ar y
+.Sh DESCRIPTION
+.Nm
+controls the VT switch lock.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Ar n
+Disable VT switching.
+.It Ar y
+Enable VT switching.
+.El
+.Sh SEE ALSO
+.Xr chvt 1
diff --git a/source/ubase/vtallow.c b/source/ubase/vtallow.c
new file mode 100644 (file)
index 0000000..c43a2b9
--- /dev/null
@@ -0,0 +1,52 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define CONSOLE "/dev/console"
+
+#define VT_LOCKSWITCH   0x560B  /* disallow vt switching */
+#define VT_UNLOCKSWITCH 0x560C  /* allow vt switching */
+
+static void
+usage(void)
+{
+       eprintf("usage: %s n | y\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int fd;
+       int allow;
+
+       ARGBEGIN {
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc != 1)
+               usage();
+
+       if (!strcmp(argv[0], "y"))
+               allow = 1;
+       else if (!strcmp(argv[0], "n"))
+               allow = 0;
+       else
+               usage();
+
+       if ((fd = open(CONSOLE, O_WRONLY)) < 0)
+               eprintf("open %s:", CONSOLE);
+       if (ioctl(fd, allow ? VT_UNLOCKSWITCH : VT_LOCKSWITCH) < 0)
+               eprintf("cannot %s VT switch:",
+                       allow ? "unlock" : "lock");
+       close(fd);
+       return 0;
+}
diff --git a/source/ubase/watch.1 b/source/ubase/watch.1
new file mode 100644 (file)
index 0000000..db08ed0
--- /dev/null
@@ -0,0 +1,27 @@
+.Dd February 2, 2015
+.Dt WATCH 1
+.Os ubase
+.Sh NAME
+.Nm watch
+.Nd execute a program periodically, showing output fullscreen
+.Sh SYNOPSIS
+.Nm
+.Op Fl n Ar interval
+.Op Fl t
+.Ar command
+.Sh DESCRIPTION
+.Nm
+runs
+.Ar command
+repeatedly, displaying its output one screenfull at a time. This allows you
+to watch the program output change over time. By default the program is run
+every 2 seconds and will run until terminated.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl n Ar interval
+Specify the update interval.
+.It Fl t
+Turn off the header showing the interval, command and current time at the top
+of the display, as well as the following blank line. This is the default
+action.
+.El
diff --git a/source/ubase/watch.c b/source/ubase/watch.c
new file mode 100644 (file)
index 0000000..51ac67f
--- /dev/null
@@ -0,0 +1,58 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-t] [-n interval] command\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char cmd[BUFSIZ];
+       char *end;
+       useconds_t interval = 2 * 1E6;
+       float period;
+       int i;
+
+       ARGBEGIN {
+       case 't':
+               break;
+       case 'n':
+               period = strtof(EARGF(usage()), &end);
+               if (*end != '\0' || errno != 0)
+                       eprintf("invalid interval\n");
+               if (period < 0)
+                       period = 0.1f;
+               interval = period * 1E6;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc < 1)
+               usage();
+
+       if (strlcpy(cmd, argv[0], sizeof(cmd)) >= sizeof(cmd))
+               eprintf("command too long\n");
+       for (i = 1; i < argc; i++) {
+               if (strlcat(cmd, " ", sizeof(cmd)) >= sizeof(cmd))
+                       eprintf("command too long\n");
+               if (strlcat(cmd, argv[i], sizeof(cmd)) >= sizeof(cmd))
+                       eprintf("command too long\n");
+       }
+
+       for (;;) {
+               printf("\x1b[2J\x1b[H"); /* clear */
+               fflush(NULL);
+               system(cmd);
+               usleep(interval);
+       }
+       return 0;
+}
diff --git a/source/ubase/who.1 b/source/ubase/who.1
new file mode 100644 (file)
index 0000000..bae98c6
--- /dev/null
@@ -0,0 +1,28 @@
+.Dd February 2, 2015
+.Dt WHO 1
+.Os ubase
+.Sh NAME
+.Nm who
+.Nd print who has logged on
+.Sh SYNOPSIS
+.Nm
+.Op Fl lm
+.Sh DESCRIPTION
+.Nm
+prints a list of who has logged on, their controlling tty, and the
+time at which they logged on.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl l
+Print LOGIN processes as well.
+.It Fl m
+Only show users on current tty.
+.El
+.Sh SEE ALSO
+.Xr utmp 5
+.Sh BUGS
+.Nm
+relies on the utmp file to be updated responsibly. This
+doesn't always happen, which can cause
+.Nm
+to print completely bogus data.
diff --git a/source/ubase/who.c b/source/ubase/who.c
new file mode 100644 (file)
index 0000000..6a28c8c
--- /dev/null
@@ -0,0 +1,64 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "config.h"
+#include "util.h"
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-ml]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct utmp usr;
+       FILE *ufp;
+       char timebuf[sizeof "yyyy-mm-dd hh:mm"];
+       char *tty, *ttmp;
+       int mflag = 0, lflag = 0;
+       time_t t;
+
+       ARGBEGIN {
+       case 'm':
+               mflag = 1;
+               tty = ttyname(0);
+               if (!tty)
+                       eprintf("ttyname: stdin:");
+               if ((ttmp = strrchr(tty, '/')))
+                       tty = ttmp+1;
+               break;
+       case 'l':
+               lflag = 1;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc > 0)
+               usage();
+
+       if (!(ufp = fopen(UTMP_PATH, "r")))
+               eprintf("fopen: %s:", UTMP_PATH);
+
+       while (fread(&usr, sizeof(usr), 1, ufp) == 1) {
+               if (!*usr.ut_name || !*usr.ut_line ||
+                   usr.ut_line[0] == '~')
+                       continue;
+               if (mflag != 0 && strcmp(usr.ut_line, tty) != 0)
+                       continue;
+               if (!!strcmp(usr.ut_name, "LOGIN") == lflag)
+                       continue;
+               t = usr.ut_time;
+               strftime(timebuf, sizeof timebuf, "%Y-%m-%d %H:%M", localtime(&t));
+               printf("%-8s %-12s %-16s\n", usr.ut_name, usr.ut_line, timebuf);
+       }
+       fclose(ufp);
+       return 0;
+}