How to catch SIGINT and ignore it in the child process? - c

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;
}

Related

Signals not working as intended (C language, linux)

I tried to answer this question:
Write a program C that creates two children. The second child process
is blocked until the reception of the signal SIGUSR1 sent from the
parent process. While the first child process is blocked until the
reception of the signal SIGUSR2 (that will kill him) sent from the
second child process. The parent is terminated after the termination
of his children.
However the execution is not working as intended with my code below, and only the parent printfs are displayed. Can you tell me what's wrong with my code?
My code:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
void this(int sig) {
printf("this is this");
}
int main() {
int pid = fork();
int pid2;
if (pid < 0) {
exit(-1);
} else if (pid == 0) {
printf("FIrst child is paused");
pause();
printf("ERror");
} else {
pid2 = fork();
if (pid2 < 0) {
exit(-2);
} else if (pid2 == 0) {
signal(SIGUSR1, &this);
printf("Second child is paused");
pause();
kill(pid,SIGUSR2);
printf("signal sent to first child");
} else {
printf("this is the parent");
kill(pid2, SIGUSR1);
printf("signal sent to second child");
wait(NULL);
exit(-3);
}
}
}
You make no provision to ensure that the parent's signal is delivered to the second child only when that child is ready for it. Because process startup takes some time, chances are good that the signal is indeed delivered sooner. In that case, the second child will be terminated (default disposition of SIGUSR1) or it will block indefinitely in pause() (if the signal is received after the handler is installed but before pauseing). In neither case will the second child signal the first.
Signal masks and signal dispositions are inherited across a fork, so you can address that by blocking SIGUSR1 in the parent before forking, and then using sigsuspend() in the child instead of pause(), which will enable you to atomically unblock the signal and start waiting for it.
The same is not an issue for the first child because you're looking for it to exercise the default disposition for SIGUSR2 (termination), and it does not matter for the specified behavior whether that happens before that child reaches or blocks in pause().
Additionally,
the parent waits only for one child, but the prompt seems to say that it must wait for both. Perhaps you dropped the second wait() because the parent was not terminating, but if so, that was a missed clue that one of the children was not terminating.
printf is not async-signal-safe, so calling it from a signal handler invokes undefined behavior.
you should put a newline at the end of your printf formats. This will make your output much more readable, and it will also ensure that the output is delivered to the screen promptly. That could end up being useful as you debug. Alternatively, use puts() instead of printf() since you are outputting only fixed strings. puts() will add a newline automatically.
The absence of newlines probably explains why the first child's output from before it pauses is never printed. If the second child were reaching the indefinite pause state then it would also explain why that child's pre-pause output was not being printed.

How to set the paused process to background?

I am new in C. I am trying to make a shell - like program. I am currently making a signal handler, which means, when the process is running and somebody pressed ctrl + Z the process should pause and go to background while shell has to continue. The problem here is: parent process is making wait(NULL), but child is not ending the program so basically parent waits the child which is not ending the program yet. How to make it so that parent continues to work foreground. (you can see my code How to redirect signal to child process from parent process? here)
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
pid_t pid;
void send_signal(int signum){
kill(pid, signum);
}
void init_signals(){
signal(SIGINT, send_signal);
signal(SIGTSTP, send_signal);
}
int main(){
init_signals();
pid = fork();
if(pid > 0){
//Parent Process
printf("PARENT: %d\n", getpid());
waitpid(pid, NULL, WUNTRACED);
printf("Parent out of wait, i think this is what you are expecting\n");
} else {
struct sigaction act = {{0}};
act.sa_handler = send_signal;
act.sa_flags = SA_RESETHAND;
sigaction(SIGTSTP, &act, NULL);
sigaction(SIGINT, &act, NULL);
perror("sigaction ");
printf("CHILD: %d\n", getpid());
// Child Process
while(1){
usleep(300000);
}
}
return 0;
}
I think above code can serve your purpose. Let me explain it.
In your code [How to redirect signal to child process from parent process? you have handled signal and from hander context sending same signal.When you pressed Ctrl + c or Ctrl + z both parent and child receives signal. Now as per the handler code
void send_signal(int signum) {
kill(pid, signum);
}
when handler will execute in parent's context pid will be equal to child's pid so it will send signal to child but when handler runs in child context pid value will be 0, so it sends signal to whole process group i.e. parent as well as child. this make you code to run handler recursively for infinite times. Due to this you are not getting desired result.
I have modified two things to get desired result.
child context
In child context restore the signal action to the default upon entry to the signal handler so that when child receives signal for second time signal default action can be performed.
parent context
use waitpid() instead of wait().
pid_t waitpid(pid_t pid, int *status, int options);
The waitpid() system call suspends execution of the calling process until a child specified by pid argument has changed state. By default, waitpid() waits only for terminated children, but this behavior is modifiable via the options argument.
`WUNTRACED` also return if a child has stopped
Due to WUNTRACED parent process will return when child will be stopped or terminated.
I hope it will serve you purpose ask me if it don't.

Does tcsetpgrp() succeds when the caller belong to a background process?

According to the POSIX specification, tcsetpgrp can cause SIGTTOU to be sent to the group of the calling process if it is a background process.
However I can't understand if in such case the foreground group is changed.
Also, if the foreground group is actually changed despite the signal generation, I wonder what happens to the session and to the terminal if the new foreground group is the one that is going to receive the SIGTTOU.
TL:DR:
No the foreground group does not change. This makes sense since the signal is supposed to be sent when the process is changing a setting on the terminal -- an output operation. The signal would also not be delivered to the process (now the foreground group) if the change succeeded, because then it could get stuck without someone to send SIGCONT.
Longer answer:
A simple example:
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
void sig(int signo) {
const char* msg = strsignal(signo); // XXX: Not async-signal-safe.
write(STDOUT_FILENO, msg, strlen(msg));
write(STDOUT_FILENO, "\n", 1);
}
int main() {
char cntl_tty[L_ctermid];
ctermid(cntl_tty);
signal(SIGTTOU, sig);
signal(SIGCONT, sig);
int fd = open(cntl_tty, O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
if (tcsetpgrp(fd, getpgrp()) == -1) {
perror("tcsetpgrp");
} else {
puts("foregrounded");
}
return 0;
}
When this code is started as a background process and SIGTTOU is handled, this loops forever printing that the signal is received. The perror is never called, which implies that the kernel restarts the system call. Sending SIGCONT does not matter. The foregrounding never succeeds. However when foregrounding the code through the shell, "foregrounded" is printed as expected.
When the signal disposition for SIGTTOU is changed to SIG_IGN, "foregrounded" is printed immediately.

parent shell not getting SIGINT when child has a handler

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;
}

using alarm to get parent process to wait on child process for a number of seconds

I need to create a c file that takes in two arguments WAIT, and TIME.
The code should first fork() a child process which does work for W seconds and then exits. The parent process should wait on the child, but for T seconds maximum.
After T seconds of waiting, the parent process should stop waiting and print the the message "Timed out after T seconds.", and exit.
On the other hand, if the child terminates before the timeout, the parent should print the message "Child done." and exit.
I want to try and use the alarm() function to do this but I am stuck on how exactly.
#include <stdio.h>
int int main(int argc, char const *argv[])
{
int W = atoi(argv[1]);
int T = atoi(argv[2]);
pid_t pid = fork();
if (pid ==0){
sleep(W);
}
else{
alarm(T);
//REST OF CODE HERE
}
return 0;
}
I do not have much because I am trying to wrap my head around how to make the parent wait on the child for the given number of seconds.
Your help is appreciated.
I think what you're looking for is SIGCHLD ...this is a signal sent to the parent when a child changes status. There is a system call expressly designed to wait for this signal, appropriately called wait. There are 4 status changes that can trigger the signal, and the wait manpage documents how to check for them:
The child terminates normally
The child was terminated by a signal
The child was stopped by a signal
The child was continued by a signal
wait will block the parent until it receives a signal--either SIGCHLD or another unignored signal. This means your strategy of using alarm should work perfectly (however, you should note that the default action for SIGALRM is to terminate the program, so if that's not what you want, you'll need to change it):
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int W;
int T;
int status;
pid_t pid;
if(argc != 3) return 1;
W = atoi(argv[1]);
T = atoi(argv[2]);
pid = fork();
if(pid == 0)
{
sleep(W);
}
else
{
alarm(T);
if(wait() == -1)
{
/*
This will not actually run unless you override
the default action for SIGALRM
*/
}
else
{
/*Child changed state*/
/*Check how with WIF... macros*/
}
}
return 0;
}
You have one other option that may be a bit more work, but you might want to explore it. There is another system call named sigtimedwait which can wait for a set of signals (including SIGCHLD) with a timeout.

Resources