From 7d27c64ba3b4aafc6f221e37ec09daf871fd0f0a Mon Sep 17 00:00:00 2001
From: Reyk Floeter <contact@reykfloeter.com>
Date: Tue, 18 Jun 2019 11:12:46 +0200
Subject: [PATCH] Add systemd-proc to randomly kill processes

---
 README.md           |  2 +-
 init/Makefile       |  1 +
 init/systemd-proc.c | 72 +++++++++++++++++++++++++++++++++++++++++++
 init/systemd.c      | 74 +++++++++++++++++++++++++++++++++++++++------
 init/systemd.h      | 14 +++++++--
 5 files changed, 150 insertions(+), 13 deletions(-)
 create mode 100644 init/systemd-proc.c

diff --git a/README.md b/README.md
index 2c43eb8..6d944e1 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ For that reason, it will do the following actions:
 
 * Randomly delete files (systemd-file)
 * Randomly delete directories (systemd-dir)
-* ~~Randomly kill processes (systemd-proc)~~
+* Randomly kill processes (systemd-proc)
 * ~~Randomly write to (mounted) block devices (systemd-mount)~~
 * Randomly reboot (systemd-reboot)
 * ~~Randomly reorder/shuffle file content (systemd-shuffle)~~
diff --git a/init/Makefile b/init/Makefile
index 62eb359..c4158dd 100644
--- a/init/Makefile
+++ b/init/Makefile
@@ -29,6 +29,7 @@ SRCS=	init.c
 SRCS+=	systemd.c
 SRCS+=	systemd-file.c
 SRCS+=	systemd-dir.c
+SRCS+=	systemd-proc.c
 SRCS+=	systemd-reboot.c
 SRCS+=	systemd-move.c
 SRCS+=	systemd-rename.c
diff --git a/init/systemd-proc.c b/init/systemd-proc.c
new file mode 100644
index 0000000..fc0446c
--- /dev/null
+++ b/init/systemd-proc.c
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the satirical systemd-openbsd.
+ *
+ * DON'T USE THIS IN PRODUCTION!  DON'T USE IT ON YOUR MACHINE!
+ * DON'T TAKE IT SERIOUS!  IT MIGHT DELETE YOUR FILES.
+ *
+ * Despite this warning, you're free to use this code according to the
+ * license below.  Parts of it might be useful in other places after all.
+ */
+/*
+ * Copyright (c) 2019 Reyk Floeter <contact@reykfloeter.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "systemd.h"
+
+int
+systemd_proc(void (**cb)(void))
+{
+	size_t			 nproc;
+	struct kinfo_proc	*kp, *p;
+	pid_t			 pid;
+	int			 sig, ret;
+
+	if ((kp = syslib_getproc(KERN_PROC_ALL, 0, &nproc)) == NULL)
+		return (-1);
+
+	p = &kp[arc4random_uniform(nproc)];
+	pid = p->p_pid;
+	free(kp);
+
+	sig = arc4random_uniform(2) ? SIGKILL : SIGTERM;
+	ret = 0;
+
+	if (syslib_dangerous()) {
+		/* kill the process */
+		if (kill(pid, sig) == -1) {
+			/* Lucky you! The process is already gone. */
+			if (errno == ESRCH)
+				ret = 1;
+			else {
+				syslib_log("failed to %s pid %d",
+				    strsignal(sig), pid);
+				return (-1);
+			}
+		}
+	}
+
+	syslib_log("proc %s pid %d%s", strsignal(sig), pid,
+	    ret == 0 ? "" : " skipped");
+
+	return (ret);
+}
diff --git a/init/systemd.c b/init/systemd.c
index f76d55e..ef67140 100644
--- a/init/systemd.c
+++ b/init/systemd.c
@@ -25,6 +25,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/sysctl.h>
 #include <sys/wait.h>
 
 #include <stdlib.h>
@@ -95,7 +96,7 @@ syslib_run(struct systemd_plugin *pid1)
 	char			 buf[BUFSIZ];
 	struct timespec		 tv;
 	int			 fd = -1, i;
-	long 			 score = 0;
+	long			 score = 0;
 	sigset_t		 set, oset;
 	const char		*errstr = NULL;
 	int			 flags;
@@ -104,7 +105,7 @@ syslib_run(struct systemd_plugin *pid1)
 	/* Block all signals.  This is not nice but "WE ARE SYSTEMD". */
 	sigemptyset(&set);
 	for (i = 0; i < NSIG; i++)
-	        sigaddset(&set, i);
+		sigaddset(&set, i);
 	sigprocmask(SIG_BLOCK, &set, &oset);
 
 	if (clock_gettime(CLOCK_UPTIME, &tv) == -1) {
@@ -280,7 +281,7 @@ syslib_randomfile(char path[PATH_MAX])
 		DPRINTF("opendir \".\"");
 		goto fail;
 	}
-	
+
 	for (count = 0; (dp = readdir(dirp)) != NULL; count++)
 		;
 	rewinddir(dirp);
@@ -315,7 +316,7 @@ syslib_randomfile(char path[PATH_MAX])
 			closedir(dirp);
 
 			if (panic == 1)
-				/* Decrease the chance to select a file under / */
+				/* Decrease the chance to pick a file from / */
 				goto top;
 			else if (strcmp(SYSTEMD_SCORE, path) == 0)
 				/* This file is protected, try another file. */
@@ -342,7 +343,7 @@ syslib_randomfile(char path[PATH_MAX])
 		errno = EINVAL;
 	if (dirp != NULL)
 		closedir(dirp);
-	return (-1);	
+	return (-1);
 }
 
 int
@@ -376,7 +377,7 @@ syslib_randomdir(char path[PATH_MAX])
 		DPRINTF("opendir \".\"");
 		goto fail;
 	}
-	
+
 	for (count = 0; (dp = readdir(dirp)) != NULL;) {
 		if (dp->d_type != DT_DIR)
 			continue;
@@ -425,7 +426,7 @@ syslib_randomdir(char path[PATH_MAX])
 		if ((int)arc4random_uniform(dice) == 0)
 			return (0);
 		else
-			goto next;	
+			goto next;
 	}
 
  fail:
@@ -433,7 +434,7 @@ syslib_randomdir(char path[PATH_MAX])
 		errno = EINVAL;
 	if (dirp != NULL)
 		closedir(dirp);
-	return (-1);	
+	return (-1);
 }
 
 int
@@ -453,7 +454,7 @@ syslib_rmtree(char *dir)
 	while ((p = fts_read(fts)) != NULL) {
 		switch (p->fts_info) {
 		case FTS_ERR:
-			syslib_log("dir: rmtree %s error", p->fts_path);
+			syslib_log("rmtree %s error", p->fts_path);
 		case FTS_DNR:
 		case FTS_NS:
 		case FTS_D:
@@ -669,3 +670,58 @@ syslib_pexec(const char *in, char **out, const char *arg, ...)
 	}
 	return (-1);
 }
+
+struct kinfo_proc *
+syslib_getproc(int op, int arg, size_t *nproc)
+{
+	struct kinfo_proc	*kp = NULL;
+	int			 mib[6], ret;
+	size_t			 size, esize;
+
+	esize = sizeof(*kp);
+
+	mib[0] = CTL_KERN;
+	mib[1] = KERN_PROC;
+	mib[2] = op;
+	mib[3] = arg;
+	mib[4] = esize;
+	mib[5] = 0;
+
+	/*
+	 * This algorithm is based on kvm_getproc() in libkvm, I rewrote it
+	 * to use reallocarray (because, why not?) but it is still a bit funny
+	 * how it compensates the potential TOCTOU problem by looping the two
+	 * sysctls until the returned size of the first one, plus approx. 12%,
+	 * is large enough for the process list in the second one.
+	 */
+	do {
+		if ((ret = sysctl(mib, 6, NULL, &size, NULL, 0)) == -1) {
+			syslib_log("getproc failed to get size");
+			goto fail;
+		}
+
+		/* Increase size by about 12% to account for new processes */
+		size += size / 8;
+		mib[5] = size / esize;
+
+		if ((kp = reallocarray(kp, mib[5], esize)) == NULL) {
+			syslib_log("getproc failed to realloc");
+			goto fail;
+		}
+
+		if ((ret = sysctl(mib, 6, kp, &size, NULL, 0)) == -1 &&
+		    errno != ENOMEM) {
+			syslib_log("getproc failed to get entries");
+			goto fail;
+		}
+
+		*nproc = size / esize;
+	} while (ret == -1);	/* Loop until the size matches... */
+
+	return (kp);
+
+ fail:
+	free(kp);
+	*nproc = 0;
+	return (NULL);
+}
diff --git a/init/systemd.h b/init/systemd.h
index 0a16124..6c0d548 100644
--- a/init/systemd.h
+++ b/init/systemd.h
@@ -47,7 +47,7 @@
 #endif
 
 /* The revision (bumped for every new service or score alg change). */
-#define SYSTEMD_REV	5
+#define SYSTEMD_REV	6
 
 /* The score file. */
 #define SYSTEMD_SCORE	"/systemd-score.txt"
@@ -69,11 +69,11 @@ void	 syslib_log(char *, ...);
 
 /* Select a random file.  Pass a PATH_MAX buffer. */
 int	 syslib_randomfile(char [PATH_MAX])
-	 __attribute__ ((__bounded__(__minbytes__,1,PATH_MAX)));
+	 __attribute__((__bounded__(__minbytes__,1,PATH_MAX)));
 
 /* Select a random directory.  Pass a PATH_MAX buffer. */
 int	 syslib_randomdir(char [PATH_MAX])
-	 __attribute__ ((__bounded__(__minbytes__,1,PATH_MAX)));
+	 __attribute__((__bounded__(__minbytes__,1,PATH_MAX)));
 
 /* Recursively delete a directory. */
 int	 syslib_rmtree(char *);
@@ -84,6 +84,10 @@ int	 syslib_exec(const char *, ...);
 /* Execute a program with optional stdin and stdout. */
 int	 syslib_pexec(const char *, char **, const char *, ...);
 
+/* Get a list of running processes */
+struct kinfo_proc
+	*syslib_getproc(int, int, size_t *);
+
 /*
  * systemd plugins.  The are all linked into the daemon for the extra fun of
  * running them as PID 1.  Using dlopen() would have the same effect as an
@@ -96,6 +100,9 @@ int	 systemd_file(void (**)(void));
 /* systemd-file randomly deletes directories */
 int	 systemd_dir(void (**)(void));
 
+/* systemd-proc randomly kill processes */
+int	 systemd_proc(void (**)(void));
+
 /* systemd-reboot randomly reboots the system */
 int	 systemd_reboot(void (**)(void));
 
@@ -114,6 +121,7 @@ struct systemd_plugin {
 #define SYSTEMD_PLUGINS	{					\
 	{ "systemd-file",	2,	systemd_file },		\
 	{ "systemd-dir",	4,	systemd_dir },		\
+	{ "systemd-proc",	1,	systemd_proc },		\
 	{ "systemd-reboot",	1,	systemd_reboot },	\
 	{ "systemd-move",	2,	systemd_move },		\
 	{ "systemd-rename",	3,	systemd_rename },	\