2

The following scenario:

  • process_1.bin starts doSomeThing.sh

  • doSomeThing.sh should kill process_1.bin first but keep running itself and do some other things

I have tried fork, exec, screen, p_threads, daemon, and some without success. Every time the doSomeThing.sh starts and kills the process_1.bin, it kills itself because the parent process is killed.

doSomeThing.sh tells me: "leaving program after kill signal!"

Unfortunately, this does not work either:

        pid_t pid;
        pid = fork();
        if (pid < 0)
            exit(EXIT_FAILURE);

        if (pid > 0)            // Success: Let the parent terminate
            exit(EXIT_SUCCESS);

        if (setsid() < 0)
            exit(EXIT_FAILURE);

        pid = fork();
        if (pid < 0)
            exit(EXIT_FAILURE);

        if (pid > 0)
            exit(EXIT_SUCCESS);
    
    //     umask(0);  

    // chdir("/");

        switch(pid)
        {
        case -1: // Fork() has failed
            perror ("fork");
            break;
        case 0: // This is processed by the child
// deamon(0,0);            
          system("/root/doSomeThing.sh");
// system("screen -dmS doSomeThing /root/doSomething.sh")

//     char *name[] = {
//    "/bin/bash",
//    "-c",
//    "/root/doSomeThing.sh",
//    NULL
//    };
//     if(execvp(name[0], name) < 0)
//       perror("execvp");

            exit(0);
            break;
        default:    // This is processed by the parrent
            break;
        }

How can I direct the doSomeThing.sh to kill the process_1.bin but still stay alive itself?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
oemer_1907
  • 21
  • 3
  • If you want good control of signals, you probably cannot use `system`. Fork your own process to execute the script (eg, via `exec("/bin/sh" ....` or similar) and take control of the signal handling. – William Pursell Jan 20 '22 at 14:55
  • I have already tried: ``` pid_t pid; pid = fork(); if (pid < 0) exit(EXIT_FAILURE); if (pid > 0) // Success: Let the parent terminate exit(EXIT_SUCCESS); if (setsid() < 0) exit(EXIT_FAILURE); pid = fork(); if (pid < 0) exit(EXIT_FAILURE); if (pid > 0) exit(EXIT_SUCCESS); char *name[] = { "/bin/bash", "-c", "/root/doSomeThing.sh", NULL }; switch(pid) { ... case 0: execvp(name[0], name) break; ... } ``` – oemer_1907 Jan 20 '22 at 15:03
  • Can you show the code in `doSomething.sh`? It's not a particularly good idea for child processes to kill their parent processes (patricide and matricide are frowned upon in most circles). Is `process1.bin` the C program you show some fragmentary code for? Is `doSomething.sh` a shell script? How does it determine the PID of its parent process? – Jonathan Leffler Jan 20 '22 at 15:13
  • Post the code in your comment into the question. When using backticks around ```code``` in a comment, make sure there is no space after the backtick: ``` spaced ``` vs ```unspaced```. – Jonathan Leffler Jan 20 '22 at 15:13
  • @Jonathan Leffler, Re "*patricide and matricide are frowned upon in most circles*", And killing one's children isn't? Bad analogy. – ikegami Jan 20 '22 at 15:14
  • 1
    @ikegami — In programming circles, parental processes sometimes do have to commit infanticide; and depressingly often parental processes have to wait for their children to die. Multi-process programming is a morbid business! – Jonathan Leffler Jan 20 '22 at 15:16
  • 1
    If your call to `fork()` returns a positive value (the pid of the child), you already exit the current process (i.e. the parent). Why would you want to kill it another time? What happens if your child process doesn't execute the script but does something else instead (sleep 300, find 1 billion primes)? Does it die? – Ronald Jan 20 '22 at 15:16
  • @Jonathan Leffler, I didn't realize you were just repeating yourself. It sounded like a analogy to real life. – ikegami Jan 20 '22 at 15:19
  • This is about 2 update processes. The first update process is written in C and communicates with an API to get the latest version from the server. As soon as a new package is available, the c update process runs a shell script which plays the update. The shell script kills all processes to play the update (including the C update process). Once the C update process is killed by the shell update process, it sends a sigterm to all children. => The shell script is also killed *if instead he just calculates without killing his father - then he just keeps running until the end. – oemer_1907 Jan 20 '22 at 15:29
  • I've just discovered that `$PPID` is a POSIX-standard shell variable; I've never needed it before. You live, you learn. – Jonathan Leffler Jan 20 '22 at 15:35

2 Answers2

2

Apart from ignoring SIGTERM as suggested by Jonathan Leffler, you could make the child process a daemon so that its life will become independant of the life of its parent. Linux has a command to start a process as a daemon daemonize or a library function for a program willing to detach from its parent and its controlling terminal daemon.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • 1
    And if you want to do it directly in C: [**Creating a daemon in Linux**](https://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux) The steps listed in the answers there will work on POSIX-based systems in general, not just Linux. – Andrew Henle Feb 11 '22 at 23:50
1

Your child process should not be dying. Here's a pair of shell scripts simulating what you describe.

process1.bin

#!/bin/sh

echo "$0: at work"
trap 'echo "$0: Death threat received"; exit 1' 1 2 3 13 15

sh doSomething.sh &

echo "$0: so far, so good"
wait
echo "$0: it's sad when all your children die"
ps -f
echo "$0: exiting normally"
exit 0

doSomething.sh

#!/bin/sh
#
# Commit parenticide and continue, preferably without dying.

#PPID=$(ps -fp$$ | colnum -c 3)

echo "Before"
ps -f

(
set -x
kill $PPID
)

sleep 1

echo ""
echo "After"
ps -f

sleep 5
echo "$0: Goodbye, Cruel World!"

One time when I ran it, I got the output:

$ ./process1.bin
./process1.bin: at work
./process1.bin: so far, so good
Before
  UID   PID  PPID   C STIME   TTY           TIME CMD
  501   649   641   0  9:58AM ttys000    0:00.14 -bash
  501 29760   649   0  8:47AM ttys000    0:00.01 sh
  501 29793 29760   0  8:47AM ttys000    0:00.01 /bin/sh ./process1.bin
  501 29794 29793   0  8:47AM ttys000    0:00.01 sh doSomething.sh
  501   661   657   0  9:58AM ttys001    0:00.05 -bash
  501   693   688   0  9:58AM ttys002    0:00.05 -bash
  501   738   731   0  9:58AM ttys007    0:00.31 -bash
  501   769   766   0  9:58AM ttys008    0:00.05 -bash
  501   848   847   0  9:58AM ttys009    0:00.05 -bash
  501   884   881   0  9:58AM ttys010    0:00.12 -bash
  501   946   920   0  9:58AM ttys011    0:00.15 -bash
+ kill 29793
./process1.bin: Death threat received
$ 
After
  UID   PID  PPID   C STIME   TTY           TIME CMD
  501   649   641   0  9:58AM ttys000    0:00.14 -bash
  501 29760   649   0  8:47AM ttys000    0:00.01 sh
  501 29794     1   0  8:47AM ttys000    0:00.01 sh doSomething.sh
  501   661   657   0  9:58AM ttys001    0:00.05 -bash
  501   693   688   0  9:58AM ttys002    0:00.05 -bash
  501   738   731   0  9:58AM ttys007    0:00.31 -bash
  501   769   766   0  9:58AM ttys008    0:00.05 -bash
  501   848   847   0  9:58AM ttys009    0:00.05 -bash
  501   884   881   0  9:58AM ttys010    0:00.12 -bash
  501   946   920   0  9:58AM ttys011    0:00.15 -bash
doSomething.sh: Goodbye, Cruel World!

$

Note that my $ prompt appears after 'Death threat received'. The following output is from doSomething; I hit return after 'Goodbye, Cruel World!' to get the final $ prompt.

Testing with Bash on a MacBook Pro running macOS Catalina 10.15.7.


In a comment, oemer_1907 says:

The shell script kills all processes to play the update (including the C update process). Once the C update process is killed by the shell update process, it sends a SIGTERM to all its children. Hence, the shell script is also killed. If instead it just calculates without killing its parent, then it just keeps running until the end.

Well, if the parent process sends SIGTERM to all its children, your child script, doSomething.sh, must ignore the SIGTERM signal using either of these:

trap "" 15
trap "" TERM

before it sends a signal to its parent. Or the parent process could arrange not to send the signal to this child. Or it could arrange for the child to ignore SIGTERM before it is executed:

signal(SIGTERM, SIG_IGN);
…exec child…

Or …

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thank you very much. However, I want to solve it on the top level (Process_1) using the C language. Is it possible to stop the sending of the signals on doSomeThing.sh already on the process_1 page ( e.g. signal 15) ? – oemer_1907 Jan 20 '22 at 16:12
  • So, call `signal(SIGTERM, SIG_IGN);` in the child (of necessity, after the fork) before using `execvp()`. – Jonathan Leffler Jan 20 '22 at 16:21
  • Incidentally, note that since you didn't provide an MCVE ([Minimal, Complete, Verifiable Example](https://stackoverflow.com/help/mcve) — or MRE or whatever name SO now uses) or an SSCCE ([Short, Self-Contained, Correct Example](http://sscce.org/) — the same idea by a different name), I couldn't modify your C code. Please don't provide fragments of code; provide an MCVE. – Jonathan Leffler Jan 20 '22 at 16:26
  • Thank you very much. The problem is now solved. The fork() with system () works very well. My mistake was that I named both processes the same. -update.bin -update.sh when killing the process it was not looking for PPID but for the process with the name "update". So the update.sh killed itself (there was no kill signal from update.bin). it works now - i renamed it to: - updater.bin - update.sh MANY THANKS for the fast, nice and informative help. – oemer_1907 Jan 21 '22 at 11:02