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