#------------------------------------------------------------------------------
# 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)
#------------------------------------------------------------------------------
include source/Rules.mk
include source/sh/Rules.mk
+include source/ubase/Rules.mk
+#include source/sbase/Rules.mk
.PHONY: all $(BINS)
--- /dev/null
+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>
--- /dev/null
+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
--- /dev/null
+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/
--- /dev/null
+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)
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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
--- /dev/null
+.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 .
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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"
--- /dev/null
+/* 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"
--- /dev/null
+# 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
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* (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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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);
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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);
+}
--- /dev/null
+.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.
--- /dev/null
+/* 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, " ");
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* See LICENSE file for copyright and license details. */
+/* passwd.c */
+int pw_check(const struct passwd *, const char *);
+int pw_init(void);
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+.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
--- /dev/null
+/* 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
--- /dev/null
+.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
--- /dev/null
+/* 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
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 *);
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+.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.
--- /dev/null
+/* 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;
+}