skip to Main Content

I’m encountering a discrepancy in the behavior of my code when using kill(pid, SIGINT) between Debian 12 and CentOS 7 in a virtual machine. Previously, this code worked as expected in Fedora 39.

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <cstdlib>
#include <cstring>
#include <sys/wait.h>

using namespace std;

void sigint_handler(int signal) {
    cout << "The child process received the SIGINT signal. Ending..." << endl;
    exit(EXIT_SUCCESS);
}

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        cerr << "Error creating the child process." << endl;
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // Child process
        cout << "Child process created. PID: " << getpid() << endl;
        
        // Signal handler for SIGINT
        signal(SIGINT, sigint_handler);

        // Executing ping for 10 iterations, although parent process should stop it earlier
        execl("/bin/sh", "sh", "-c", "ping -c 10 google.com", NULL);
    } else {
        // Parent process
        cout << "parent process. PID from child: " << pid << endl;
        sleep(3); // Wait 3 seconds before sending the SIGINT

        cout << "Sending SIGINT to the child process..." << endl;
        if (kill(pid, SIGINT) == -1) {
            cerr << "Error sending SIGINT to the child process." << endl;
            exit(EXIT_FAILURE);
        }

        int status;
        waitpid(pid, &status, 0); // Wait to the child process to end
        if (WIFEXITED(status)) {
            cout << "Child process end with status: " << WEXITSTATUS(status) << endl;
        } else {
            cout << "Child process end in an incorrect way" << endl;
        }
    }

    return 0;
}

After migrating to Debian 12, the code snippet above doesn’t behave as expected. Instead of stopping the ping process after sending the SIGINT signal, it continues executing all 10 iterations. However, when running the same code in a CentOS VM, it behaves correctly, stopping after a few iterations.

Why is this happening?

Thanks in advance.

Additional information:

g++ version in Debian 12: g++ (Debian 12.2.0-14) 12.2.0

g++ version in CentOS 7 VM: g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)

Edit 1:

I have also tried with system("ping -c 10 google.com") instead of execl() without success. Same with sigaction() and it doesn’t work.

In addition to that, the problem isn’t related to the different implementations of ping in Debian and Red Hat based distros, because I have tried with my original code and it doesn’t stop.

In my original code, instead of ping, I call a program.o that has a infinite loop that should be stopped with Ctrl+C. In Fedora and CentOS my program runs perfectly while in Debian it doesn’t.

3

Answers


  1. Chosen as BEST ANSWER

    I have found the issue looking at the processes launched using pgrep -l -u $USER.

    If we consider that the binary of the program is called program and PID from child: 10001, in CentOS we have this:

    $ pgrep -l -u $USER
    10000 program
    10001 ping
    

    While in Debian we have this:

    $ pgrep -l -u $USER
    10000 program
    10001 sh
    10002 ping
    

    That means that in Debian, in order to make the program work just like in CentOS, we should change the pid to pid + 1 because of that sh process with ID 10001 in the middle.

    The only thing that would be useful to know is why in Debian that sh process is created while in CentOS is not.


  2. This does not work as it seems you expect:

    // Signal handler for SIGINT
    signal(SIGINT, sigint_handler);
    
    // Executing ping for 10 iterations, although parent process should stop it earlier
    execl("/bin/sh", "sh", "-c", "ping -c 10 google.com", NULL);
    

    execl will reset the signal action back to the default in the new process. This is setting the signal handler to sigint_handler only for an extremely short window.

    Therefore, how ping reacts to SIGINT is completely dependent on how it sets up its signal handlers. Presumably, the different VMs have different implementations or versions of ping. iputils and inetutils both have a ping.

    Login or Signup to reply.
  3. It depends the way the shell constructs the tree of (sub)processes when interpreting commands. It is possible that /bin/sh is not the same shell on different Linux distributions.

    Anyway, why do you launch ping under control of shell and not launching it directly?

    execlp("ping","ping","-c","10","google.com",NULL);
    

    More: you may not use SIGINT whose semantic for shells may also differs greatly (some may just wait for sub command to terminate). Prefer the use of SIGTERM.

    Also note that exec-ing reset signal handlers to default behaviour if sets the a catching function before, and prefer the use of sigaction which have reliable behaviour (it is not the case fo signal).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search