I actually did this with TrueOS (formerly known as PC-BSD) just under a year ago.
vanilla TrueOS
On vanilla TrueOS, the pcdm
service runs the graphical user interface.
- The Mewburn
rc
system runs the /usr/local/etc/rc.d/pcdm
script, which is not long-lived.
- An infinitely-looping and quite convoluted shell script named
PCDMd
spawns xinit
telling it to run a program named PCDM-session
.
PCDM-session
is a desktop chooser and login program that writes out a nonce shell script in /tmp
. It spawns /usr/local/share/PCDM/pcdm-session
. It also spins off and orphans a dbus-daemon
, run as the superuser, on the side if it isn't passed information about a Desktop Bus to talk to.
/usr/local/share/PCDM/pcdm-session
chains to sh /tmp/PCDM-session.blah
, the script written out earlier.
- Said shell script in its turn runs everything in
$HOME/.xprofile
, which is where all of the things such as gpg-agent
are forked off, and finally runs the chosen desktop's session via dbus-launch
(run as the logged-in user and spinning off another dbus-daemon
process).
This results in a fairly messy process tree, both for anything forked off by $HOME/.xprofile
(which runs everything from /usr/local/share/pcbsd/xstartup/
) and for anything started under the desktop session process and later orphaned via fork-and-exit-parent (like, alas, thunderbird
). All of the D-Bus brokers are not pretty, either; a lot of stuff ends up with process #1 as its parent process ID. The naming does not help make this straighforward, either. pcdm
invokes PCDMd
invokes PCDM-session
invokes pcdm-session
invokes PCDM-session.blah
.
(I tried to let the TrueOS people know that this was unnecessarily complex, especially the wholly unnecessary hidden extra, and broken, service manager inside the PCDM-session
program that I have left out of the above description.)
local-reaper
and userenv
from the nosh toolset
In the nosh toolset there is a little chain-loading utility named local-reaper
. Its sole task is to mark (or un-mark) itself as a subreaper and then chain-load another program image in the same process.
There's nothing Linux-specific about it, the subreaper mechanism being available on several operating systems. It works on Linux and FreeBSD/TrueOS/DragonFlySBD, and has no hard-wired system call numbers or anything of that sort. ☺
Also in the nosh tool set are the userenv
and setuidgid
tools. These actually do everything that the TrueOS pcdm-session
program does, and more besides, including setting up the DBUS_SESSION_BUS_ADDRESS
variable to point to the per-user Desktop Bus broker. The entire TrueOS pcdm-session
program is in fact replaceable with
#!/bin/sh -
exec >>"$5" 2>&1
chown -v -h -- "$1" "${XAUTHORITY}"
exec \
local-reaper true \
setuidgid --supplementary "$1" \
userenv --set-path --set-other --set-tools --set-timezone --set-locale --set-xdg \
sh "$4"
putting local-reaper
to use
To use it, I had to find processes that satisfied three conditions:
- They had to be an indirect parent of the processes that were getting orphaned.
- They had to be long-lived.
- They had to be amenable to suddenly gaining child processes that they weren't expecting.
Not all processes were suitable. I actually uncovered a bug in the TrueOS PCDM-session
program, where it was waiting for the wrong process ID, whilst diagnosing whether the PCDM-session
process was suitable to be a subreaper.
(I told the TrueOS people about this bug, too. It's actually a one character typing error.)
Fortunately, shell programs almost always are suitable to be reapers, and there were several shell processes in this tree. So also, it turned out, was xinit
.
improved process tree
So what happens now is;
- I run things under a proper service manager. The server manager process is itself a subreaper.
- Service management spawns a significantly shorter shell script that spawns
local-reaper true xinit …
, making the xinit
process a subreaper too.
xinit
is told to run local-reaper true helper-script
, making the shell interpreter that interprets helper-script a third subreaper.
- helper-script runs
dbus-launch --exit-with-session PCDM-session
. It sticks around, running a shell interpreter, waiting for the PCDM-session
process to finish. dbus-launch
spins off two side-processes that it orphans.
PCDM-session
cannot be made a subreaper. Its behaviour is unchanged: it writes out a nonce shell script in /tmp
and spawns /usr/local/share/PCDM/pcdm-session
. But the spinning off of the Desktop Bus side-processed has already been done for it.
- The revised
/usr/local/share/PCDM/pcdm-session
marks itself as a subreaper and chains to sh /tmp/PCDM-session.blah
, the script written out earlier.
- Said shell script in its turn still runs everything in
$HOME/.xprofile
and thus everything in /usr/local/share/pcbsd/xstartup/
, and finally runs the chosen desktop's session, but directly and no longer via dbus-launch
. It sticks around, running a shell interpreter, waiting for the desktop's session process to finish.
The effects of this are as follows:
- Stuff forked off from any of the scripts in
/usr/local/share/pcbsd/xstartup/
is reparented to the process running sh /tmp/PCDM-session.blah
.
- Desktop applications such as
thunderbird
that are run via fork-and-exit-parent as grandchildren of the desktop session process are reparented to the process running sh /tmp/PCDM-session.blah
.
- The side processes forked off and orphaned by
dbus-launch
are re-parented to the helper-script
process.
- There is no user
dbus-daemon
side process forked off; as everything is pointed to a per-user Desktop Bus broker that is running in another part of the process tree, as a user-level service under proper service management.
- Nothing gets as far as being reparented to
xinit
. Making it a subreaper turned out to be unnecessary.
The process tree, starting from process #1, looks like this:
/sbin/system-manager --
|-- cyclog --max-file-size 262144 --max-total-size 1048576 . (system-manager)
`-- service-manager (system-manager)
…
|-- per-user-manager
| |-- cyclog --max-file-size 262144 --max-total-size 1048576 .
| `-- service-manager
| |-- cyclog JdeBP/socket-servers/
| |-- cyclog JdeBP/dbus-servers/
| |-- dbus-daemon --config-file ./per-user.conf --nofork --address=unix:path=/run/user/JdeBP//bus
…
|-- /bin/sh - ./helper run
`-- xinit /bin/exec local-reaper true ./helper session -- :0 -auth -retro
|-- X :0 -auth -retro (Xorg)
`-- /bin/sh - ./helper session
|-- PCDM-session -once
| `-- PCDM-session -once
| `-- sh /tmp/PCDM-session.ca1015
| |-- /usr/local/bin/gpg-agent …
| | `-- scdaemon --multi-server
| |-- /usr/local/bin/lxsession -s LXDE
| | |-- openbox --config-file …
| | |-- lxpanel --profile LXDE
| | |-- pcmanfm --desktop --profile LXDE
| | `-- xscreensaver -no-splash
| |-- /usr/local/bin/python2.7 /usr/local/share/system-config-printer/applet.py
| |-- life-preserver-tray
| |-- /usr/local/bin/pc-systemupdatertray
| |-- zeitgeist-datahub
| |-- pc-mixer
| |-- pc-mounttray
| |-- /usr/local/libexec/menu-cache/menu-cached …
| `-- thunderbird
|-- dbus-launch --exit-with-session PCDM-session -once
`-- /usr/local/bin/dbus-daemon --fork … --session