Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why bash background task ignores SIGINT?

I noticed that sleep can't be killed by SIGINT when spawned by:

(sleep 1000 &)

I wonder why is so.
All of the following are killed by SIGINT:

sleep 1000
sleep 1000 &
(sleep 1000)
( ( (sleep 1000) ) )
( ( (sleep 1000)& ) )

so I figure it must have something to do with non-interactive bash (brackets are required to enter subshell) and task have to be run in the background.

I wrote a short C program to test the behaviour and found that sa_handler is set to SIG_IGN -- explaining phenomenon, but why exactly is so?

I haven't found any information whether it is an intended feature (though considering length of manual I may have simply missed it) and if so, what was the reason behide it.

I include the C code for those interested:

#include <stdlib.h>
#include <stdio.h>

#include <signal.h>

int main() {
    struct sigaction oldact;
    if(sigaction(SIGINT, NULL, &oldact) != 0) {
        printf("Error in sigaction\n");
        exit(1);
    }

    if(oldact.sa_flags & SA_SIGINFO) {
        printf("Using sa_sigaction\n");
    } else {
        if(oldact.sa_handler == SIG_DFL) {
            printf("Default action\n");
        } else if(oldact.sa_handler == SIG_IGN) {
            printf("Ignore signal\n");
        } else {
            printf("Other action\n");
        }
    }
    return 0;
}

EDIT:

pilcrow answer is great and I accepted it. I wanted to add as to why posix says so that, according to signal(7) both SIGINT and SIGQUIT are from keyboard. So it kinda makes sens to ignore them in processes detached from one (and not job controled by bash).

EDIT2:

Checkout Mark Plotnick comment for true explanation WHY.

like image 465
yjay Avatar asked Oct 16 '22 05:10

yjay


1 Answers

This construct, (sleep 1000 &) puts your sleep command in a grandchild subshell with no job control:

your-shell
      \
      a compound-list in a subshell
          \
         an asynchronous list in a subshell

The first subshell, ( compound-list ) (a grouping command construct), simply runs the backgrounded command & (an asynchronous list) and then exits. The asynchronous list is run in its own subshell.

That final subshell is too far removed from your initial shell for job control to be meaningful.

Per POSIX, "[i]f job control is disabled ... when the shell executes an asynchronous list, the commands in the list shall inherit from the shell a signal action of ignored (SIG_IGN) for the SIGINT and SIGQUIT signals."

Thus your sleep is run with SIGINT set to ignore.

like image 167
pilcrow Avatar answered Oct 19 '22 01:10

pilcrow