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