aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrumeet <yuuta@yuuta.moe>2022-02-16 14:43:35 -0800
committerTrumeet <yuuta@yuuta.moe>2022-02-16 14:43:35 -0800
commit2111575e3c20fdf6f737d24e40721cb5a24ab9ae (patch)
tree3f6c1105237d4b47e6350204968ecd657e75337f
parent7400fe9569778d8f9b1527f3f801e0364c539161 (diff)
downloadgists-2111575e3c20fdf6f737d24e40721cb5a24ab9ae.tar
gists-2111575e3c20fdf6f737d24e40721cb5a24ab9ae.tar.gz
gists-2111575e3c20fdf6f737d24e40721cb5a24ab9ae.tar.bz2
gists-2111575e3c20fdf6f737d24e40721cb5a24ab9ae.zip
Add ReapperRun
-rw-r--r--ReapperRun/.gitignore3
-rw-r--r--ReapperRun/Makefile26
-rw-r--r--ReapperRun/README.md56
-rw-r--r--ReapperRun/main.c52
4 files changed, 137 insertions, 0 deletions
diff --git a/ReapperRun/.gitignore b/ReapperRun/.gitignore
new file mode 100644
index 0000000..2601db2
--- /dev/null
+++ b/ReapperRun/.gitignore
@@ -0,0 +1,3 @@
+main
+test
+*.o
diff --git a/ReapperRun/Makefile b/ReapperRun/Makefile
new file mode 100644
index 0000000..8007624
--- /dev/null
+++ b/ReapperRun/Makefile
@@ -0,0 +1,26 @@
+.POSIX:
+CFLAGS += -std=c99
+CFLAGS += -Wall
+
+release: main
+
+all: main test
+
+t:
+ make all LDFLAGS="-fsanitize=address $(LDFLAGS)" CFLAGS+="-fsanitize=address -O0 -g3 -rdynamic $(CFLAGS)"
+ ./main ./test
+
+main: main.o
+ $(CC) $(LDFLAGS) $(LDLIBS) $^ -o $@
+
+main.o: main.c
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+test: test.o
+ $(CC) $(LDFLAGS) $(LDLIBS) $^ -o $@
+
+test.o: main.c
+ $(CC) $(CFLAGS) -o $@ -DTEST -c $<
+
+clean:
+ rm -rf main main.o test test.o
diff --git a/ReapperRun/README.md b/ReapperRun/README.md
new file mode 100644
index 0000000..59b602b
--- /dev/null
+++ b/ReapperRun/README.md
@@ -0,0 +1,56 @@
+# ReaperRun
+
+A simple wrapper that adds `PR_SET_CHILD_SUBREAPER` flag to a program.
+
+## Background
+
+i3wm can start daemons and programs (e.g. `nm-applet` and `xterm`), but it doesn't set the subreapper flag. Since Linux 3.14, service managers can use this flag to act as "init", reaping orphan children. i3wm starts processes (the `exec` command in configuration file) by double-forking, and the intermediate fork immediately exits after forking the child, making the children orphan. systemd-user uses this flag to reap orphan children, but i3wm is not started using systemd-user but a display manager (e.g. SDDM in my case). SDDM does not set this flag as well, making all processes under i3 reaped by init (1). This messes up the process tree.
+
+Read more:
+
+* [https://github.com/i3/i3/issues/4274](https://github.com/i3/i3/issues/4274)
+
+* [https://blog.lilydjwg.me/2014/2/23/let-s-adopt-orphaned-processes.43035.html](https://blog.lilydjwg.me/2014/2/23/let-s-adopt-orphaned-processes.43035.html)
+
+The trick is simple: Just add the subreaper flag to i3 or any of its parents. According to prctl(2), this flag will be preserved across execve(2), so it is best to make a wrapper to set the flag and exec i3 rather than patching i3.
+
+## Usage
+
+1. Run `make release`. The output is `main`.
+
+2. Ask your display manager to run `/path/to/reapperrun /path/to/i3` instead of running i3 directly. That's it.
+
+## Effect
+
+Tree of init before:
+
+```
+at-spi-bus-laun,768 --launch-immediately
+ |-dbus-daemon,781 --config-file=/usr/share/defaults/at-spi2/accessibility.conf --nofork --print-address 3
+at-spi2-registr,8096 --use-gnome-session
+blueman-applet,8059 /usr/bin/blueman-applet
+blueman-tray,8110 /usr/bin/blueman-tray
+chromium,8485
+fcitx5,8064
+i3bar,8048 --bar_id=bar-0 --socket=/run/user/10001/i3/ipc-socket.8025
+keepassxc,8073
+fish,6582
+nm-applet,8061
+i3,8025
+telegram-deskto,8248
+xterm,8119
+```
+
+Tree of init after:
+```
+i3,9348
+ |-blueman-applet,9381 /usr/bin/blueman-applet
+ |-blueman-tray,9433 /usr/bin/blueman-tray
+ |-fcitx5,9391
+ |-i3bar,9371 --bar_id=bar-0 --socket=/run/user/10001/i3/ipc-socket.9348
+ |-keepassxc,9395
+ |-nm-applet,9383
+ `-xterm,9436
+```
+
+You can see that all processes are under i3 now.
diff --git a/ReapperRun/main.c b/ReapperRun/main.c
new file mode 100644
index 0000000..ec209d8
--- /dev/null
+++ b/ReapperRun/main.c
@@ -0,0 +1,52 @@
+/*
+ * Starts any programs with PR_SET_CHILD_SUBREAPER on Linux.
+ * Author: Yuuta Liang
+ */
+
+#define _POSIX_C_SOURCE 200809L
+#define _GNU_SOURCE
+#include <sys/prctl.h>
+#include <err.h>
+#include <errno.h>
+#include <assert.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#ifndef TEST
+int main(int argc, char **argv) {
+ int r;
+ assert(argc);
+ if (argc == 1)
+ errx(64, "Usage: %s /path/to/binary [arg1] [arg2] ...",
+ argv[0]);
+ /* path is not freed on intend. */
+ char *path = realpath(argv[1], NULL);
+ if (!path)
+ err(errno, "realpath(%s)", argv[1]);
+ if ((r = prctl(PR_SET_CHILD_SUBREAPER, 1) < 0))
+ err(errno, "prctl(PR_SET_CHILD_SUBREAPER)");
+ if ((r = execve(path, &argv[1], environ)))
+ err(errno, "exec(%s)", path);
+}
+#else /* ifndef TEST */
+#include <stdio.h>
+int main(void) {
+ printf("Parent PID: %d\n", getpid());
+ if (!fork()) {
+ printf("Fork PID: %d\n", getpid());
+ unsigned int counter = 0;
+ goto f;
+f:
+ counter ++;
+ if (!fork()) {
+ printf("Child PID: %d\n", getpid());
+ pause();
+ }
+ if (counter < 20) goto f;
+ exit(0);
+ }
+ pause();
+}
+#endif /* ifndef TEST */