Is reparenting from the shell possible?
This question is closely related to How to "correctly" start an application from a shell but tries to tackle a more specific problem. How can I spawn an application from a shell and thereby making it a child of another process. Here is what I mean exemplified with two graphics:
systemd-+-acpid
|-bash---chromium-+-chrome-sandbox---chromium-+-chrome-sandbox---nacl_helper
| | `-chromium---5*[chromium-+-{Chrome_ChildIOT}]
| | |-{Compositor}]
| | |-{HTMLParserThrea}]
| | |-{OptimizingCompi}]
| | `-3*[{v8:SweeperThrea}]]
| |-chromium
| |-chromium-+-chromium
| | |-{Chrome_ChildIOT}
| | `-{Watchdog}
| |-{AudioThread}
| |-3*[{BrowserBlocking}]
| |-{BrowserWatchdog}
| |-5*[{CachePoolWorker}]
| |-{Chrome_CacheThr}
| |-{Chrome_DBThread}
| |-{Chrome_FileThre}
| |-{Chrome_FileUser}
| |-{Chrome_HistoryT}
| |-{Chrome_IOThread}
| |-{Chrome_ProcessL}
| |-{Chrome_SafeBrow}
| |-{CrShutdownDetec}
| |-{IndexedDB}
| |-{LevelDBEnv}
| |-{NSS SSL ThreadW}
| |-{NetworkChangeNo}
| |-2*[{Proxy resolver}]
| |-{WorkerPool/1201}
| |-{WorkerPool/2059}
| |-{WorkerPool/2579}
| |-{WorkerPool/2590}
| |-{WorkerPool/2592}
| |-{WorkerPool/2608}
| |-{WorkerPool/2973}
| |-{WorkerPool/2974}
| |-{chromium}
| |-{extension_crash}
| |-{gpu-process_cra}
| |-{handle-watcher-}
| |-{inotify_reader}
| |-{ppapi_crash_upl}
| `-{renderer_crash_}
|-2*[dbus-daemon]
|-dbus-launch
|-dhcpcd
|-firefox-+-4*[{Analysis Helper}]
| |-{Cache I/O}
| |-{Cache2 I/O}
| |-{Cert Verify}
| |-3*[{DOM Worker}]
| |-{Gecko_IOThread}
| |-{HTML5 Parser}
| |-{Hang Monitor}
| |-{Image Scaler}
| |-{JS GC Helper}
| |-{JS Watchdog}
| |-{Proxy R~olution}
| |-{Socket Thread}
| |-{Timer}
| |-{URL Classifier}
| |-{gmain}
| |-{localStorage DB}
| |-{mozStorage #1}
| |-{mozStorage #2}
| |-{mozStorage #3}
| |-{mozStorage #4}
| `-{mozStorage #5}
|-gpg-agent
|-login---bash---startx---xinit-+-Xorg.bin-+-xf86-video-inte
| | `-{Xorg.bin}
| `-dwm-+-dwmstatus
| `-xterm---bash-+-bash
| `-pstree
|-systemd---(sd-pam)
|-systemd-journal
|-systemd-logind
|-systemd-udevd
|-wpa_actiond
`-wpa_supplicant
The process tree shows chromium and firefox as children of the init
process that starts at boot and has PID 1
. But what I want to achieve is to start firefox and chromium as children of dwm
. Hence, I want a similar behaviour to what you can see under the weston
part of the following process tree where firefox has weston-desktop
as its parent:
systemd-+-acpid
|-bash---chromium-+-chrome-sandbox---chromium-+-chrome-sandbox---nacl_helper
| | `-chromium-+-3*[chromium-+-{Chrome_ChildIOT}]
| | | |-{Compositor}]
| | | |-{HTMLParserThrea}]
| | | |-{OptimizingCompi}]
| | | `-3*[{v8:SweeperThrea}]]
| | `-4*[chromium-+-{Chrome_ChildIOT}]
| | |-{CompositorRaste}]
| | |-{Compositor}]
| | |-{HTMLParserThrea}]
| | |-{OptimizingCompi}]
| | `-3*[{v8:SweeperThrea}]]
| |-{AudioThread}
| |-3*[{BrowserBlocking}]
| |-{BrowserWatchdog}
| |-5*[{CachePoolWorker}]
| |-{Chrome_CacheThr}
| |-{Chrome_DBThread}
| |-{Chrome_FileThre}
| |-{Chrome_FileUser}
| |-{Chrome_HistoryT}
| |-{Chrome_IOThread}
| |-{Chrome_ProcessL}
| |-{Chrome_SafeBrow}
| |-{Chrome_SyncThre}
| |-{CrShutdownDetec}
| |-{IndexedDB}
| |-{NSS SSL ThreadW}
| |-{NetworkChangeNo}
| |-2*[{Proxy resolver}]
| |-{WorkerPool/2315}
| |-{WorkerPool/2316}
| |-{WorkerPool/2481}
| |-{chromium}
| |-{extension_crash}
| |-{gpu-process_cra}
| |-{handle-watcher-}
| |-{inotify_reader}
| |-{renderer_crash_}
| `-{sandbox_ipc_thr}
|-2*[dbus-daemon]
|-dbus-launch
|-dhcpcd
|-gpg-agent
|-login---bash---startx---xinit-+-Xorg.bin-+-xf86-video-inte
| | `-{Xorg.bin}
| `-dwm-+-dwmstatus
| `-xterm---bash
|-login---bash---weston-launch---weston-+-Xwayland---4*[{Xwayland}]
| |-weston-desktop--+-firefox-+-firefox
| | | |-4*[{Analysis Helper}]
| | | |-{Cache2 I/O}
| | | |-{Cert Verify}
| | | |-{DNS Resolver #1}
| | | |-{DNS Resolver #2}
| | | |-2*[{DOM Worker}]
| | | |-{Gecko_IOThread}
| | | |-{HTML5 Parser}
| | | |-{Hang Monitor}
| | | |-{Image Scaler}
| | | |-{ImageDecoder #1}
| | | |-{ImageDecoder #2}
| | | |-{ImageDecoder #3}
| | | |-{JS GC Helper}
| | | |-{JS Watchdog}
| | | |-{Socket Thread}
| | | |-{Timer}
| | | |-{URL Classifier}
| | | |-{gmain}
| | | |-{localStorage DB}
| | | |-{mozStorage #1}
| | | |-{mozStorage #2}
| | | |-{mozStorage #3}
| | | |-{mozStorage #4}
| | | `-{mozStorage #5}
| | `-weston-terminal---bash---pstree
| `-weston-keyboard
|-systemd---(sd-pam)
|-systemd-journal
|-systemd-logind
|-systemd-udevd
|-tmux---bash
|-wpa_actiond
`-wpa_supplicant
One possible solution would be to use nsenter
from util-linux
. I could enter the namespace of the dwm
process and fork a new firefox process which would then be the child of dwm
. However, that seems like a lot of work. Is there some easier way to do this?
You can not start a process as the child of the shell, and then “reparent” it so another process becomes it’s parent.
So you need to use a parent process that explicitly starts the children.
init
with PID 1 is an exception, processes can become it’s child as it collects processes that lost their original parent process.
(With upstart, there can be multiple init
processes, they do not share the PID 1, but otherwise the roles are very similar.)
(See also PR_SET_CHILD_SUBREAPER in man 2 prctl
)
What you ask for is simply impossible. By the design of the Unix and Linux internal process management init
becomes the parent of all processes whose parents die. This is because processes must have parents (also by design), and init is always there, for if init
dies, the system shuts down. But beyond that there is no such thing as “re-parenting” processes.
EDIT
However: As lord.garbage pointed out, there’s the arcane prctl()
system call which is wicked cool and makes any program that uses it unportable. Suppose we don’t care. Using the PR_SET_CHILD_SUBREAPER
option it can wait()
not only for its own children (as before) but also for all of their descendants, should their parents die prematurely. Thus a process using this feature can assume the role of init
for its descendants. The following code is a proof of concept:
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int
main (int argc, const char* const argv[], char* const envp[])
{
pid_t pid;
if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) < 0) {
perror("prctl");
return 4;
}
pid = fork();
if (pid < 0) {
perror("fork");
return 4;
}
if (pid == 0) {
// child
char* const argv[] = { "/usr/bin/konsole", "-e", "/bin/bash", NULL };
if (execve("/usr/bin/konsole", argv, envp) < 0) {
perror("execve");
}
}
// parent
while (1) {
pid_t wpid;
int s;
wpid = waitpid(-1, &s, 0);
if (wpid > 0) {
printf("child with pid %u has exitedn", wpid);
}
}
return 0;
}
Run some programs in the background that does not need shell attendance, exit konsole, run ps
, exit the programs, and see what happens. Replace konsole
by anything your heart desires.
Now, in order to achieve what you want, use the prctl()
call as in the PoC and then execve()
to dwm
. And hope that dwm
wait()
s for unspecific children lest they end up as zombies.
Final note: There’s still no such thing as re-parenting. I.e. you still cannot arbitrarily assign a parent to a process.
I do not believe you can send a process to the dwm process as parent. But if you could start a screen or shell as a child process of dwm, then you could reparent your desired processes into that. See this link for details: http://monkeypatch.me/blog/move-a-running-process-to-a-new-screen-shell.html