Some app launchers these days run each app in a new systemd scope, which puts the app process and any child processes into their own cgroup. For example I use rofi which does this, and I noticed that fuzzel does also. That is handy for tracking and cleaning up child processes!
You can see how processes are organized by running,
$ systemctl --user status
I think that’s a quite useful way to see processes organized. Looking at it I noticed a couple of scopes that shouldn’t still be running.
Just for fun I wanted to use this to try to script a better killall
. For example if I run $ killscope slack
I want the script to:
- find processes with the name “slack”
- find the names of the systemd scopes that own those processes (for example,
app-niri-rofi-2594858.scope
) - kill processes in each scope with a command like,
systemctl --user stop app-niri-rofi-2594858.scope
Step 2 turned out to be harder than I liked. Does anyone know of an easy way to do this? Ideally I’d like a list of all scopes with information for all child processes in JSON or another machine-readable format.
systemctl --user status
gives me all of the information I want, listing each scope with the command for each process under it. But it is not structured in an easily machine-readable format. Adding --output json
does nothing.
systemd-cgls
shows the same cgroup information that is shown in systemctl --user status
. But again, I don’t see an option for machine-readable output.
systemd-cgtop
is interesting, bot not relevant.
Anyway, I got something working by falling back on the classic commands. ps
can show the cgroup for each process:
$ ps x --format comm=,cgroup= | grep '^slack\b'
slack 0::/user.slice/user-1000.slice/user@1000.service/app.slice/app-niri-rofi-2594858.scope
slack 0::/user.slice/user-1000.slice/user@1000.service/app.slice/app-niri-rofi-2594858.scope
slack 0::/user.slice/user-1000.slice/user@1000.service/app.slice/app-niri-rofi-2594858.scope
...
The last path element of the cgroup happens to be the scope name. That can be extracted with awk -F/ '{print $NF}'
Then unique scope names can be fed to xargs
. Here is a shell function that puts everything together:
function killscope() {
local name="$1"
ps x --format comm=,cgroup= \
| grep "^$name\b" \
| awk -F/ '{print $NF}' \
| sort | uniq \
| xargs -r systemctl --user stop
}
It could be better, and it might be a little dangerous. But it works!
cgroup names can be read from /proc/PID/cgroup.
Nice! Thanks!