0

If $PID_PARENT launched $PID_CHILD, how can I detach (disown?) $PID_CHILD from $PID_PARENT so that, when I kill $PID_PARENT, $PID_CHILD keeps running like nothing happened?

Concretely, my $PID_PARENT is the process running Jenkins (or the Java process that runs the server that runs Jenkins) and $PID_CHILD is a reeeeeeally long job that I don't want to have to restart after restarting Jenkins (which is needed for some maintenance). Essentially, I want to stop Jenkins but not stop the long job it started and I know both PIDs.

UPDATE 1: I found out about disown and tried from a login shell (so not the parent PID shell):

disown $PID_CHILD

but got

-bash: disown: 13924: no such job

The $PID_CHILD correct and doing

ps -o ppid= $PID_CHILD

returns $PID_PARENT

UPDATE 2: As per @Rui's answer, I made a temporary hack job in Jenkins that only runs, from the parent shell this time:

disown 13924 

but still got

disown: 13924: no such job – amphibient

2

One way is to have the child process disassociate itself from the parent. This will require suitable code in the child process, or a wrapper script that performs the disassociation before executing the real code:

#!/usr/bin/env perl
use strict;
use warnings;
die "Usage: $0 command [args ..]\n" unless @ARGV;
# diassociate this process (some folks also do a double-fork thing)
use POSIX "setsid";
chdir("/") || die "can't chdir to /: $!";
open( STDIN,  "< /dev/null" ) || die "can't read /dev/null: $!";
open( STDOUT, "> /dev/null" ) || die "can't write to /dev/null: $!";
defined( my $pid = fork() ) || die "can't fork: $!";
exit if $pid;    # non-zero now means I am the parent
( setsid() != -1 ) || die "Can't start a new session: $!";
open( STDERR, ">&STDOUT" ) || die "can't dup stdout: $!";
# and replace ourself with whatever we were called with
exec @ARGV;

which if saved as solitary can be tested via something like:

% ./solitary logger greppable
% grep greppable /var/log/system.log
Jun 27 10:52:15 hostn jhqdoe[20966]: greppable
% 

with the use of logger(1) because the standard filehandles were all closed as part of the disassociation. These may need to be redirected elsewhere for your application.

Note that this may not be possible if Jenkins (or systemd or whatever) uses a PID namespace that the process cannot escape no matter how it forks itself, in which case you would need some container-level solution, or to revisit what you're trying to do.

  • +1 for mentioning Colombus' egg and the joys of systemd. – Rui F Ribeiro Jun 27 '18 at 18:47
  • boggles my mind there isn't a quick way to do this, which seems like a pretty stock function... – amphibient Jun 27 '18 at 19:47
  • how do you pass the PID to your script? -- i don't see how you actually set $pid – amphibient Jun 27 '18 at 19:50
  • @amphibient there is no pid to set. Jenkins (or whatever) runs solitary blah blah blah where blah blah blah is usually what gets run, and solitary disassociates (via all those various system calls) then replaces itself with blah blah blah. There is a daemon(3) call but that may or may not be portable. – thrig Jun 27 '18 at 20:14
  • so if there are 7 processes spawned off by Jenkins, they would all get disowned? – amphibient Jun 27 '18 at 20:38
1

I think you should be able to simply do

$ disown $PID

Then if you terminate your shell session, the process will still be running.

  • is $PID the PID of the child or the parent ? – amphibient Jun 27 '18 at 15:52
  • that didn't work, see the update to the OP – amphibient Jun 27 '18 at 16:06
  • disown is a ksh93 extension that is not available in most shells. – schily Jun 27 '18 at 16:13
  • @schily using here it with bash – Rui F Ribeiro Jun 27 '18 at 16:18
  • You may later create a script that uses /bin/sh and that does not support it. – schily Jun 27 '18 at 16:31
1

You need to do your disown from the shell that called the process. Hence calling from other shell you get the message no such job.

To actually disown a process and tell it to ignore the hangup signal, you would do from the shell that called the process:

disown -h $PID

From disown help:

$ disown --help disown: disown [-h] [-ar] [jobspec ... | pid ...]
Remove jobs from current shell.

    Removes each JOBSPEC argument from the table of active jobs.  Without
    any JOBSPECs, the shell uses its notion of the current job.

    Options:
      -a    remove all jobs if JOBSPEC is not supplied
      -h    mark each JOBSPEC so that SIGHUP is not sent to the job if the
                    shell receives a SIGHUP
      -r    remove only running jobs

    Exit Status:
    Returns success unless an invalid option or JOBSPEC is given.
  • Is there a way to emulate disown from the shell that called the process from the login shell that is not the shell that called the process (kind of like sudo does anything as a user)? – amphibient Jun 27 '18 at 16:23
  • @amphibient I think not, but let´s wait if someone says something in contrary or has a clever idea. – Rui F Ribeiro Jun 27 '18 at 16:36
  • I am pretty surprised there isn't a ready way to do this, seems like a pretty standard function... – amphibient Jun 27 '18 at 16:47
1

When creating a background process that persists after a Jenkins build completes, you will need to watch out for the Jenkins Process Tree Killer. When a build exits, the process tree killer attempts to kill all processes related to that build, even if the processes have been disowned from the build process and are no longer child processes of the build process.

There are instructions in the link above for how to disable the process tree killer for particular jobs or for Jenkins as a whole.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.