xx77aBs
xx77aBs

Reputation: 4768

Child process receives parent's SIGINT

I have one simple program that's using Qt Framework. It uses QProcess to execute RAR and compress some files. In my program I am catching SIGINT and doing something in my code when it occurs:

signal(SIGINT, &unix_handler);

When SIGINT occurs, I check if RAR process is done, and if it isn't I will wait for it ... The problem is that (I think) RAR process also gets SIGINT that was meant for my program and it quits before it has compressed all files.

Is there a way to run RAR process so that it doesn't receive SIGINT when my program receives it?

Thanks

Upvotes: 19

Views: 14892

Answers (3)

hagello
hagello

Reputation: 3255

Just make the subprocess ignore SIGINT:

child_pid = fork();
if (child_pid == 0) {
   /* child process */
   signal(SIGINT, SIG_IGN);
   execl(...);
}

man sigaction:

During an execve(2), the dispositions of handled signals are reset to the default; the dispositions of ignored signals are left unchanged.

Upvotes: 0

Nemo
Nemo

Reputation: 71515

If you are generating the SIGINT with Ctrl+C on a Unix system, then the signal is being sent to the entire process group.

You need to use setpgid or setsid to put the child process into a different process group so that it will not receive the signals generated by the controlling terminal.


[Edit:]

Be sure to read the RATIONALE section of the setpgid page carefully. It is a little tricky to plug all of the potential race conditions here.

To guarantee 100% that no SIGINT will be delivered to your child process, you need to do something like this:

#define CHECK(x) if(!(x)) { perror(#x " failed"); abort(); /* or whatever */ }
/* Block SIGINT. */
sigset_t mask, omask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
CHECK(sigprocmask(SIG_BLOCK, &mask, &omask) == 0);

/* Spawn child. */
pid_t child_pid = fork();
CHECK(child_pid >= 0);
if (child_pid == 0) {
    /* Child */
    CHECK(setpgid(0, 0) == 0);
    execl(...);
    abort();
}
/* Parent */
if (setpgid(child_pid, child_pid) < 0 && errno != EACCES)
    abort(); /* or whatever */
/* Unblock SIGINT */
CHECK(sigprocmask(SIG_SETMASK, &omask, NULL) == 0);

Strictly speaking, every one of these steps is necessary. You have to block the signal in case the user hits Ctrl+C right after the call to fork. You have to call setpgid in the child in case the execl happens before the parent has time to do anything. You have to call setpgid in the parent in case the parent runs and someone hits Ctrl+C before the child has time to do anything.

The sequence above is clumsy, but it does handle 100% of the race conditions.

Upvotes: 35

Arnold Spence
Arnold Spence

Reputation: 22272

What are you doing in your handler? There are only certain Qt functions that you can call safely from a unix signal handler. This page in the documentation identifies what ones they are.

The main problem is that the handler will execute outside of the main Qt event thread. That page also proposes a method to deal with this. I prefer getting the handler to "post" a custom event to the application and handle it that way. I posted an answer describing how to implement custom events here.

Upvotes: 0

Related Questions