When you press Ctrl-C, foreground processes receive SIGINT:
$ bash -c 'sleep 100; echo program died'
^C
$ echo $?
130
However, if a program installs a SIGINT handler, parent program doesn't receive the signal. Why?
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
void sig_int(int sig_num)
{
exit(1);
}
static struct sigaction sigact = { .sa_handler=sig_int };
int main(int argc, char *argv[])
{
sigaction(SIGINT,&sigact,NULL);
sleep(100);
return 0;
}
bash didn't die:
$ bash -c './a.out; echo program died'
^Cprogram died
Related to Bash not trapping interrupts during rsync/subshell exec statements , but all answers there are workarounds.
The shell ignore SIGINT if not sent directly from the terminal
This long post explain what is happening in details. Here I'll try to summarise the most important concepts and propose a working solution.
It turns out that the shell is programmed to ignore the SIGINT if it is not directly sent from the terminal (by hitting CTRL-C). If a subprocess intercepts it then it must exit by explicitly killing itself with SIGINT, quoting the post:
"If you don't catch SIGINT, the system automatically does the right
thing for you: Your program exits and the calling program gets the
right "I-exited-on-SIGINT" status after waiting for your exit.
But once you catch SIGINT, you have to take care of the proper way to
exit after whatever cleanup you do in your SIGINT handler.
Decide whether the SIGINT is used for exit/abort purposes and hence a
shellscript calling this program should discontinue. This is hopefully
obvious. If you just need to do some cleanup on SIGINT, but then exit
immediately, the answer is "yes".
If so, you have to tell the calling program about it by exiting with
the "I-exited-on-SIGINT" status.
There is no other way of doing this than to kill yourself with a
SIGINT signal. Do it by resetting the SIGINT handler to SIG_DFL, then
send yourself the signal.
void sigint_handler(int sig)
{
[do some cleanup]
signal(SIGINT, SIG_DFL);
kill(getpid(), SIGINT);
}
SIGINT Handler
Here is a working version of the handler that intercepts the signal and correctly kill itself (and thus it doesn't print 'program died').
OTOH, If you send a different signal the handler run the exit function and you will see again 'program died' printed on the screen.
void sig_int(int sig_num)
{
if (sig_num == SIGINT) {
printf("received SIGINT\n");
signal(SIGINT, SIG_DFL);
kill(getpid(), SIGINT);
} else {
exit(1);
}
}
static struct sigaction sigact = { .sa_handler=sig_int };
int main(int argc, char *argv[])
{
sigaction(SIGINT,&sigact,NULL);
printf("go to sleep\n");
sleep(3);
printf("awaken\n");
return 0;
}
Related
I have two problems/questions here!
1) I tried to catch CTRL+Z and handle it but nothing happens!!
2) how can i test the SIGCONT ? (it doesn't have a shortcut like CTRL+ ..)
Is there anything wrong with my code? This my code below :
void sigstop()
{
printf(" Suspended\n");
}
void sigcont()
{
printf(" Its Back\n");
}
void sigint()
{
printf(" Interrupt\n");
//exit(0);
}
int main(int argc, char **argv){
printf("Starting the program\n");
signal(SIGSTOP,sigstop);
signal(SIGCONT,sigcont);
signal(SIGINT, sigint);
while(1) {
sleep(2);
}
return 0;
}
1) I tried to catch CTRL+Z and handle it but nothing happens!!
CTRL + Z sends SIGTSTP. So you need to setup handler for SIGTSTP (not SIGSTOP). SIGSTOP and SIGKILL signals can't caught or handled; so your handler for SIGSTOP will be ignored.
2) how can i test the SIGCONT ? (it doesn't have a shortcut like CTRL+ ..)
Shells typically have a built-in command called fg. So once you suspend your process with CTRL + Z, you'd be able to send SIGCONT via fg.
But you can always use the shell builtin kill (or the kill command) to send the desired signal to your process.
Couple of other issues in your code:
signal function should be of the signal void func(int sig) { .. }. So you need to fix your signal functions.
printf is not async-signal-safe and thus it can't be safely called from a signal handler.
I need some help on C program - it is a reverse shell (https://github.com/arturgontijo/remoteShell/blob/master/reverseShell.c) I made few changes, like put that all in a loop and some sleep pattern + put some argument to pass directly IP and PORT now that thing works very good it's stable (problem that cannot autocomplete stuff with TAB I don't really care) BUT what I really care is that this thing will break if on target machine I press CTRL+C the program just exits itself. Now I used this example to block CTRL+C calls:
/* Signal Handler for SIGINT */
void sigintHandler(int sig_num)
{
/* Reset handler to catch SIGINT next time.
Refer http://en.cppreference.com/w/c/program/signal */
signal(SIGINT, sigintHandler);
printf("\n Cannot be terminated using Ctrl+C \n");
fflush(stdout);
}
signal(SIGINT, sigintHandler);
I got this example online and put it on my loop as well, but still from client pressing ctrl+C breaks program. I wonder dup2() is responsible for that or something because on simple C program this actually worked fine.
You can use the sigetops family of functions to manipulate the signals sent into your application.
So for your example you could use:
#include <signal.h>
#include <unistd.h>
int main(int argc, char **argv)
{
sigset_t block_set;
sigemptyset(&block_set);
sigaddset(&block_set, SIGINT);
sigprocmask(SIG_BLOCK, &block_set, NULL);
while(1) {
sleep(1);
}
}
Running Example: https://repl.it/repls/RelevantImaginarySearchservice
You can unblock the signal at a later time by calling
sigprocmask(SIG_UNBLOCK, &block_set, NULL);
I have the following test C program with UNIX system calls:
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void abide(int sig_num) {
printf("I, sleeper, will abide to this signal %d!\n", sig_num);
fflush(stdout);
exit(0);
}
void refuse(int sig_num) {
signal(SIGINT, refuse);
printf("I, sleeper, REFUSE this signal %d!\n", sig_num);
fflush(stdout);
}
int main(int argc, char *argv[]) {
if (argc > 1 && strcmp(argv[1], "refuse") == 0) {
signal(SIGINT, refuse);
} else if (argc > 1 && strcmp(argv[1], "deaf") == 0) {
printf("I, sleeper, have been made deaf...\n");
} else {
signal(SIGINT, abide);
}
printf("I, sleeper, am now sleeping for 10s...\n");
sleep(10);
printf("I, sleeper, has terminated normally.\n");
return 0;
}
Then I have another program which acts as a little shell. At my testing point, it forks and makes the child program execute the program above (with appropriate arguments). This shell is also ignoring Ctrl+C commands by use of
signal(SIGINT, SIG_IGN);
The results are the following:
MyShell> ./sleeper
I, sleeper, am now sleeping for 10s...
^CI, sleeper, will abide to this signal!
MyShell> ./sleeper refuse
I, sleeper, am now sleeping for 10s...
^CI, sleeper, REFUSE this signal!
I, sleeper, has terminated normally.
MyShell> ./sleeper deaf
I, sleeper, have been made deaf...
I, sleeper, am now sleeping for 10s...
^C^C^C^C <---- not terminating
The first run seems correct. The second one is a bit strange as we are effectively ignoring the signal, but the program terminates anyway. Maybe it's because we're calling sleep() which is interrupted.
But it's the third result that confuses me. In a regular shell the program terminates, but in my custom shell nothing happens. It keeps running. Shouldn't the sleeper program's default signal handler (which terminates it also) execute just as abide() does?
Thanks for any clarification!
Solved it. The problem was a bit subtle. After using fork(), child processes apparently inherit their parents signal handlers, even if you use the exec() system calls afterwards. So the child process for sleeper was using the ignore handler. The solution was simply to add the default handler
signal(SIGINT, SIG_DFL)
between the calls to fork() and exec().
I have a main that runs program from the command line arguments. The command line program is forked and run in the child process. When SIGINT is sent, I want to catch it and ask the user to confirm that he/she want to quit. If yes, both parent and child end, else child keeps running.
My problem is that I can't get the child to start running back up, when user says no.
I have tried SIGSTOP & SIGCONT but these actually just cause the processes to stop.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
extern char **environ;
void sigint_handler(int sig);
void sigint_chldhandler(int sig);
int main( int argc, char** argv)
{
int pid;
signal(SIGINT,sigint_handler);
if((pid=fork())==0)
{
printf("%d\n",pid);
execve(argv[1],argv,environ);
}
int status;
waitpid(pid,&status,0);
}
void sigint_handler(int sig)
{
printf("Do you want to quit?Yes/No:\n");
char buf[4];
fgets(buf, sizeof(char)*4, stdin);
printf("child pid:%d\n",getpid());
printf("parent pid:%d\n",getppid());
if(strcmp(buf,"Yes")==0)
{
kill(-getpid(),SIGKILL);
printf("Exiting!\n");
exit(0);
}
}
Unless you rig the child's signal handling, it will be terminated by the interrupt when the signal is sent, regardless of what happens in the parent. Therefore, you will need to be rather more sophisticated. I think you will need something along the lines of:
Parent process sets its SIGINT signal handler.
Parent forks.
Child process sets its SIGINT handling to SIG_IGN.
Child executes specified command.
Parent waits for SIGINT to arrive, probably while running waitpid().
When it arrives, it sends SIGSTOP to the child.
It asks the question and gets the response.
If the response is to continue, then it sends SIGCONT to the child and returns to its waiting mode.
If the response is to stop, then it sends first SIGCONT and then SIGTERM (or another signal other than SIGINT) to the child to kill it. (Using SIGKILL is not sensible; the child should be given a chance to exit in response to SIGTERM or SIGHUP. If the child doesn't take the death threat seriously, then you can send it SIGKILL.)
When the parent has established that the child has exited, it can exit in its own turn.
Note that if the child process is running something like vim, which alters the terminal settings dramatically, then sending it SIGKILL will leave the terminal in a cockeyed state. It is fiddly setting it back to a sane state; it is better to give the program a chance to reset the terminal settings in its own right.
SIGINT comes to parent process and to child process (to process group).
Parent process calls your handler.
Child processes this signal by default.
You can use this, for example:
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
int main()
{
pid_t pid;
char c;
switch(pid = fork())
{
case -1:
printf("!!!");
return -1;
break;
case 0:
printf("child started\n");
while(1) { };
break;
default:
while(1)
{
c = getchar();
if(c == 'q')
{
//your conditions
kill(pid, SIGKILL);
return 0;
}
}
break;
}
return 0;
}
I want to detect the kill signal of my program inorder to execute some C instruction before leaving my program. my program is running on linux
Is it possible to do that? If yes how I can do it?
You can register a signal handler using sigaction(). Note that you cannot handle SIGKILL or SIGSTOP though.
No, SIGKILL can not be handled, maybe you want to catch CTRL+C, then:
#include <stdio.h>
#include <signal.h>
volatile sig_atomic_t stop;
void
inthand(int signum)
{
stop = 1;
}
int
main(int argc, char **argv)
{
signal(SIGINT, inthand);
while (!stop)
printf("a");
printf("exiting safely\n");
return 0;
}
Will do the trick
If SIGKILL or SIGTERM is sent to your process you cannot mask or ignore the signal. Other signals you can handle and mask it.