Will ctrl+c send SIGINT signals to both parent and child processes in Linux? - linux

In the terminal, I executed a main parent process which will fork a child process. In both the parent and child processes I implemented a SIGINT signal handler.
So when I press "ctrl+c", will both the handlers be called at the same time? Or do I need to call the child process's signal handler explicitly in the parent process's handler?
I looked up this post:
How does Ctrl-C terminate a child process?
which says that "The SIGINT signal is generated by the terminal line discipline, and broadcast to all processes in the terminal's foreground process group". I just didn't quite understand what does "foreground process group" means.
Thanks,

In both the parent and child processes I implemented a SIGINT signal
handler. So when I press "ctrl+c", will both the handlers be called at
the same time?
Yes, they both will receive SIGINT.
Or do I need to call the child process's signal handler explicitly in
the parent process's handler?
"Calling" another process' signal handler doesn't make sense. If the both the process have a handler installed then they will be called once they receive the signal SIGINT.
I just didn't quite understand what does "foreground process group"
means.
Typically, a process associated with a controlling terminal is foreground process and its process group is called foreground process group. When you start a process from the command line, it's a foreground process:
E.g.
$ ./script.sh # foreground process
$ ./script & # background process
I suggest you read about tty and The TTY demystified for a detailed explanation.

setpgid POSIX C process group minimal example
This illustrates how the signal does get sent to the child, if the child didn't change its process group with setpgid.
main.c
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
volatile sig_atomic_t is_child = 0;
void signal_handler(int sig) {
char parent_str[] = "sigint parent\n";
char child_str[] = "sigint child\n";
signal(sig, signal_handler);
if (sig == SIGINT) {
if (is_child) {
write(STDOUT_FILENO, child_str, sizeof(child_str) - 1);
} else {
write(STDOUT_FILENO, parent_str, sizeof(parent_str) - 1);
}
}
}
int main(int argc, char **argv) {
pid_t pid, pgid;
(void)argv;
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);
pid = fork();
assert(pid != -1);
if (pid == 0) {
/* Change the pgid.
* The new one is guaranteed to be different than the previous, which was equal to the parent's,
* because `man setpgid` says:
* > the child has its own unique process ID, and this PID does not match
* > the ID of any existing process group (setpgid(2)) or session.
*/
is_child = 1;
if (argc > 1) {
setpgid(0, 0);
}
printf("child pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0));
assert(kill(getppid(), SIGUSR1) == 0);
while (1);
exit(EXIT_SUCCESS);
}
/* Wait until the child sends a SIGUSR1. */
pause();
pgid = getpgid(0);
printf("parent pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)pgid);
/* man kill explains that negative first argument means to send a signal to a process group. */
kill(-pgid, SIGINT);
while (1);
}
GitHub upstream.
Compile with:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wpedantic -o setpgid setpgid.c
Run without setpgid
Without any CLI arguments, setpgid is not done:
./setpgid
Possible outcome:
child pid, pgid = 28250, 28249
parent pid, pgid = 28249, 28249
sigint parent
sigint child
and the program hangs.
As we can see, the pgid of both processes is the same, as it gets inherited across fork.
Then whenever you hit:
Ctrl + C
It outputs again:
sigint parent
sigint child
This shows how:
to send a signal to an entire process group with kill(-pgid, SIGINT)
Ctrl + C on the terminal sends a kill to the entire process group by default
Quit the program by sending a different signal to both processes, e.g. SIGQUIT with Ctrl + \.
Run with setpgid
If you run with an argument, e.g.:
./setpgid 1
then the child changes its pgid, and now only a single sigint gets printed every time from the parent only:
child pid, pgid = 16470, 16470
parent pid, pgid = 16469, 16469
sigint parent
And now, whenever you hit:
Ctrl + C
only the parent receives the signal as well:
sigint parent
You can still kill the parent as before with a SIGQUIT:
Ctrl + \
however the child now has a different PGID, and does not receive that signal! This can seen from:
ps aux | grep setpgid
You will have to kill it explicitly with:
kill -9 16470
This makes it clear why signal groups exist: otherwise we would get a bunch of processes left over to be cleaned manually all the time.
Tested on Ubuntu 18.04.

Related

Is it possible to start a linux process, getting the PID, but start running later on certain condition?

I'm thinking about some tool that can pause the program at start.
For example, my_bin starts running at once.
$ ./my_bin
With this tool
$ magic_tool ./my_bin
my_bin will start. I can get the PID. Then I can start the actual running later.
I've just tested my suggestion in the comments and it worked! This is the code in my magic_tool.c:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main (int argc, char *argv[])
{
pid_t pid;
printf("Executing %s to wrap %s.\n", argv[0], argv[1]);
pid = fork();
if (pid == -1)
return -1;
if (pid == 0) {
raise(SIGSTOP);
execl(argv[1], "", NULL);
} else {
printf("PID == %d\n", pid);
}
return 0;
}
I wrote another test program target.c:
#include <stdio.h>
int main ()
{
puts("It works!\n");
return 0;
}
Running ./magic_tool ./target printed a PID and returned to shell. Only after running kill -SIGCONT <printed_pid> was It works! printed. You'll probably want to have PID saved somewhere else and also perform some checks in the magic_tool, but I think this is nonetheless a good proof of concept.
EDIT:
I was playing around with this a bit more and for some reason it didn't always work (see why below). The solution is simple - just follow a proper fork off and die pattern a bit more closely in magic_tool.c:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main (int argc, char *argv[])
{
pid_t pid;
printf("Executing %s to wrap %s.\n", argv[0], argv[1]);
pid = fork();
if (pid == -1)
return -1;
if (pid == 0) {
setsid();
pid = fork();
if (pid == -1)
return -1;
if (pid == 0) {
raise(SIGSTOP);
if (execl(argv[1], "", NULL))
return -1;
}
printf("PID == %d\n", pid);
}
return 0;
}
I found an explanation in this answer:
When you start the root process from your shell, it is a process group leader, and its descendants are members of that group. When that leader terminates, the process group is orphaned. When the system detects a newly-orphaned process group in which any member is stopped, then every member of the process group is sent a SIGHUP followed by a SIGCONT.
So, some of your descendant processes are still stopped when the leader terminates, and thus everyone receives a SIGHUP followed by a SIGCONT, which for practical purposes mean they die of SIGHUP.
Exactly which descendants are still stopped (or even just merrily advancing toward exit()) is a timing race.
The answer also links to IEEE Std 1003.1-2017 _Exit entry which contains more details on the matter.
This is mostly a very similar idea as #gst, but done entirely in the shell, you can spawn a subshell (this forks and create a new pid) and have the subshell send itself SIGSTOP signal, when the subshell receives a SIGCONT signal and resumes, the subshell exec the intended program (this replaces the subshell with the intended program without creating a new pid). So that the main shell can continue doing stuff, the subshell should run on background with &.
In a nutshell:
(kill -STOP $BASHPID && exec ./my_bin) &
subpid=$! # get the pid of above subshell
... do something else ...
kill -CONT $subpid # resume
Another idea that wouldn't suffer from race condition between the main process sending SIGCONT and the subshell SIGSTOP-ing itself is to use a file descriptor to implement the wait instead:
exec {PIPEFD}<> <(:) # set PIPEFD to the file descriptor of an anonymous pipe
(read -u $PIPEFD && exec ./my_bin) &
subpid=$! # get the pid of above subshell
... do something else ...
echo >&$PIPEFD # resume

execvp appears to change pid of the process

I created a program that does:
1. fork
2. from the son, create execvp with csh. The execvp csh runs a script a.sh that prints in infinite loop.
My problem is that I cant stop or kill the process from the father (using kill(SIGKILL,pid) from the father process didn't work).
I think that the problem is in execvp.
when I print the pid from the script(echo $BASHPID ) I get a different pid from the one that I get before the execvp. i know the pid after execvp is supposed to remain the same, but it seems like it doesn't.
here is the problematic code:
int ExeExternal(char* args[MAX_ARG], char* cmdString, int* fg_pid, char** L_Fg_Cmd){
//int child_status;
int pID;
pID = fork();
switch(pID) {
case -1: //error
perror("fork");
return 1;
case 0 : // Child Process
setpgrp();
printf("son getpid() : %d",getpid());
fflush(stdout);
char* argument_for_cshs[5];
char* cmd="csh";
argument_for_cshs[0]="csh";
argument_for_cshs[1]="-f";
argument_for_cshs[2]="-c";
argument_for_cshs[3]=cmdString;
argument_for_cshs[4]=NULL;
execvp(argument_for_cshs[0],argument_for_cshs);
//if return => there is a problem
any solution?

Not able to kill a fork'ed process

I am forking a child, and trying to kill it.
pid_t *child_pid;
int main(){
child_pid = mmap(NULL, sizeof(pid_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);$
int a = fork();
if (a != 0) {
printf("child # %d\n", a);
*child_pid = a;
system("sleep 100");
} else {
sleep(1);
printf("Trying to kill %d\n", *child_pid);
int ret = kill(*child_pid,SIGKILL);
printf("killled with %d\n", ret);
}
}
However, the kill command gets stuck at:
child # 4752
Trying to kill 4752
In the meantime, calling ps shows this:
4752 pts/4 00:00:00 simple <defunct>
You are killing yourself. fork() returns 0 if you are in the forked process, or the child process id (PID) in the 'master' process.
So, the upper branch of your if() clause is performed in the master process, where you copy the child's process ID (stored in a) to child_pid.
In the lower branch you are in the child process, where take the child_pid, which is your own and then happily kill() yourself... That's why you never get the line 'Killed with...'
As paxdiablo pointed out, since this is a child process it will remain a zombie until you fetch the exit status with wait() or the master process exits.
BTW, I'm not sure what you want to do with this code:
If you want to exit the child process gracefully you can just do an exit().
If you want to kill your child process, keep track of the child PID (returned by fork()) and then kill() it from your master process.
If you want to kill the master process from your child (odd as that may sound) be careful, as that may take the child process(es) with it. You have to detach the child process from the master process (see the manual page for daemon()).
The child process is dead, it's just that the process entry will hang around until someone collects the exit code.
If the parent doesn't do it, it will eventually be inherited by init, which will reap it at some point.

How to tell if a downstream process in a Unix pipe has crashed

I have a Linux process (let's call it the main process) whose standard output is piped to another process (called the downstream process) by means of the shell's pipe operator (|). The main process is set up to receive SIGPIPE signals if the downstream process crashes. Unfortunately, SIGPIPE is not raised until the main process writes to stdout. Is there a way to tell sooner that the downstream process has terminated?
One approach is to write continuously to the downstream process, but that seems wasteful. Another approach is to have a separate watchdog process that monitors all relevant processes, but that is complex. Or perhaps there is some way to use select() to trigger the signal. I am hoping that the main process can do all this itself.
It appears the stdout file descriptor becomes "ready for reading" when the receiver crashes:
$ gcc -Wall select-downstream-crash.c -o select-downstream-crash
$ gcc -Wall crash-in-five-seconds.c -o crash-in-five-seconds
$ ./select-downstream-crash | ./crash-in-five-seconds
... five seconds pass ...
stdout is ready for reading
Segmentation fault
select-downstream-crash.c
#include <err.h>
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
int main(void)
{
fd_set readfds;
int rc;
FD_ZERO(&readfds);
FD_SET(STDOUT_FILENO, &readfds);
rc = select(STDOUT_FILENO + 1, &readfds, NULL, NULL, NULL);
if (rc < 0)
err(1, "select");
if (FD_ISSET(STDOUT_FILENO, &readfds))
fprintf(stderr, "stdout is ready for reading\n");
return 0;
}
crash-in-five-seconds.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
sleep(5);
putchar(*(char*)NULL);
return 0;
}
I tried this on Linux, but don't know if it'll work elsewhere. It would be nice to find some documentation explaining this observation.
If the main process forks the other processes, then it will get SIGCHLD notifications when they exit.

Externally disabling signals for a Linux program

On Linux, is it possible to somehow disable signaling for programs externally... that is, without modifying their source code?
Context:
I'm calling a C (and also a Java) program from within a bash script on Linux. I don't want any interruptions for my bash script, and for the other programs that the script launches (as foreground processes).
While I can use a...
trap '' INT
... in my bash script to disable the Ctrl C signal, this works only when the program control happens to be in the bash code. That is, if I press Ctrl C while the C program is running, the C program gets interrupted and it exits! This C program is doing some critical operation because of which I don't want it be interrupted. I don't have access to the source code of this C program, so signal handling inside the C program is out of question.
#!/bin/bash
trap 'echo You pressed Ctrl C' INT
# A C program to emulate a real-world, long-running program,
# which I don't want to be interrupted, and for which I
# don't have the source code!
#
# File: y.c
# To build: gcc -o y y.c
#
# #include <stdio.h>
# int main(int argc, char *argv[]) {
# printf("Performing a critical operation...\n");
# for(;;); // Do nothing forever.
# printf("Performing a critical operation... done.\n");
# }
./y
Regards,
/HS
The process signal mask is inherited across exec, so you can simply write a small wrapper program that blocks SIGINT and executes the target:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGINT);
sigprocmask(SIG_BLOCK, &sigs, 0);
if (argc > 1) {
execvp(argv[1], argv + 1);
perror("execv");
} else {
fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
}
return 1;
}
If you compile this program to noint, you would just execute ./noint ./y.
As ephemient notes in comments, the signal disposition is also inherited, so you can have the wrapper ignore the signal instead of blocking it:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
struct sigaction sa = { 0 };
sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &sa, 0);
if (argc > 1) {
execvp(argv[1], argv + 1);
perror("execv");
} else {
fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
}
return 1;
}
(and of course for a belt-and-braces approach, you could do both).
The "trap" command is local to this process, never applies to children.
To really trap the signal, you have to hack it using a LD_PRELOAD hook. This is non-trival task (you have to compile a loadable with _init(), sigaction() inside), so I won't include the full code here. You can find an example for SIGSEGV on Phack Volume 0x0b, Issue 0x3a, Phile #0x03.
Alternativlly, try the nohup and tail trick.
nohup your_command &
tail -F nohup.out
I would suggest that your C (and Java) application needs rewriting so that it can handle an exception, what happens if it really does need to be interrupted, power fails, etc...
I that fails, J-16 is right on the money. Does the user need to interract with the process, or just see the output (do they even need to see the output?)
The solutions explained above are not working for me, even by chaining the both commands proposed by Caf.
However, I finally succeeded in getting the expected behavior this way :
#!/bin/zsh
setopt MONITOR
TRAPINT() { print AAA }
print 1
( ./child & ; wait)
print 2
If I press Ctrl-C while child is running, it will wait that it exits, then will print AAA and 2. child will not receive any signals.
The subshell is used to prevent the PID from being shown.
And sorry... this is for zsh though the question is for bash, but I do not know bash enough to provide an equivalent script.
This is example code of enabling signals like Ctrl+C for programs which block it.
fixControlC.c
#include <stdio.h>
#include <signal.h>
int sigaddset(sigset_t *set, int signo) {
printf("int sigaddset(sigset_t *set=%p, int signo=%d)\n", set, signo);
return 0;
}
Compile it:
gcc -fPIC -shared -o fixControlC.so fixControlC.c
Run it:
LD_LIBRARY_PATH=. LD_PRELOAD=fixControlC.so mysqld

Resources