1

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?

lord.garbage
  • 2,373
  • If you launch firefox from a dwm keybind (using the spawn function), it will parent the process... – jasonwryan Aug 27 '14 at 09:07
  • Basically I would like to do this for every application. Hence, this would involve setting keybinds for every relevant application in dwm's config.h. I'd prefer to avoid this. – lord.garbage Aug 27 '14 at 09:09

4 Answers4

5

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)

Volker Siegel
  • 17,283
  • 2
    Cheers. As far as I know you can actually do this since Linux kernel 3.4 by setting PR_SET_CHILD_SUBREAPER which circumvents init picking up orphan processes. But I think it's just to complicated for my case. (cf. man prctl). – lord.garbage Aug 27 '14 at 12:17
  • 1
    Scary! shudder ...in many ways... – Volker Siegel Aug 27 '14 at 12:18
  • init with other PIDs as well. Upstart creates an init for each user session, IIRC, which serves as the parent for that session's orphaned processes. – muru Aug 27 '14 at 12:35
  • The question for me is: How can is it possible to get the following process tree from the shell: systemd--bash--firefox. All methods I try will ultimately lead to a process tree of the following form systemd--firefox when I spawn firefox from the shell. – lord.garbage Aug 27 '14 at 13:28
  • That looks like you did an exec, replacing the bash process by the firefox process? – Volker Siegel Aug 27 '14 at 13:30
  • Exactly. But somehow launchers like dmenu manage to "demonize" a /bin/bash process which is the parent of the program. In the pstrees I posted chromium has not init as its parent but rather bash which in turn has init as parent. This bash session however is not attached to any terminal (that's why I say "demonized") and I wonder how this works... – lord.garbage Aug 27 '14 at 13:36
  • I just tried sleep 101 & sleep 102 & sleep 103 & in a terminal - I get a shell that is parent to these sleep processes, I think. Do you mean it's different if there is no terminal? – Volker Siegel Aug 27 '14 at 13:41
  • If I run sleep in a shell, in foreground, I also see the shell as a parent in pstree. – Volker Siegel Aug 27 '14 at 13:43
  • Yes, but as soon as you exit the terminal with exit you kill the shell with it. Hence, the now orphaned processes get adopted by init. Which is exactly what we talked about before and how it is not possible to reparent. But how then is the aforementioned pstree output for chromium possible. – lord.garbage Aug 27 '14 at 13:50
  • With exit, you exit the shell. Try a script that runs some commands in background, and then one in foreground. And run that in background, disown it, exit the terminal. (somewhat confusing...) – Volker Siegel Aug 27 '14 at 13:53
  • dmenu_run, the script that launches the programm, actually just contains: dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & where the first two commands are just there to pipe the name of the program to the shell. – lord.garbage Aug 27 '14 at 13:53
  • I remember vaguely that there may be a special case when the shell has just that one command to run, and may use exec only then. Let's see... – Volker Siegel Aug 27 '14 at 13:56
  • Hmm.. I tried strace -f -o sh-one-command.strace bash -c '/bin/true' and strace -f -o sh-two-commands.strace bash -c '/bin/false; /bin/true' - they look about the same regarding exec. Not sure where I have seen that... – Volker Siegel Aug 27 '14 at 14:03
5

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 exited\n", 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.

countermode
  • 7,533
  • 5
  • 31
  • 58
0

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

  • 3
    More details are needed. Blogs come and go, SE stays here. Please include the walkthrough from the linked article into your post. – Deer Hunter Sep 29 '14 at 13:04
  • As @DeerHunter feared, the blog entry is now gone, it seems. Here's an archived version: http://web.archive.org/web/20160504140857/http://monkeypatch.me/blog/move-a-running-process-to-a-new-screen-shell.html – kini Nov 07 '16 at 23:09
0

With reptyr, you can reparent a running program to a new terminal. Here is a longer explanation how it works.

Albert
  • 699