From 2111575e3c20fdf6f737d24e40721cb5a24ab9ae Mon Sep 17 00:00:00 2001 From: Trumeet Date: Wed, 16 Feb 2022 14:43:35 -0800 Subject: Add ReapperRun --- ReapperRun/.gitignore | 3 +++ ReapperRun/Makefile | 26 ++++++++++++++++++++++++ ReapperRun/README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ ReapperRun/main.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 ReapperRun/.gitignore create mode 100644 ReapperRun/Makefile create mode 100644 ReapperRun/README.md create mode 100644 ReapperRun/main.c 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 +#include +#include +#include +#include +#include +#include +#include + +#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 +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 */ -- cgit v1.2.3