In the main process I listen to SIGCHLD:
signal(SIGCHLD, &my_handler);
Then I fork(), execv() and let it run in background (/bin/cat for example).
When I try from terminal to send SIGSTOP to the child process, my_handler() gets called. But when I try to send SIGCONT to it, the the handler isn't called on macOS but it's executed on my Ubuntu.
Man:
SIGCHLD: child status has changed.
Am I missing something? Is it an expected behaviour? I wrote my app on Ubuntu and expected it to work on mac as well.
I tried with sigaction() as well, but with the same results.
Here's a sample code to demonstrate:
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void my_handler(int signum)
{
printf("\t SIGCHLD received\n");
fflush(stdout);
}
void my_kill(pid_t pid, int signum)
{
printf("Sending %d\n", signum);
fflush(stdout);
kill(pid, signum);
printf("Sent %d\n\n", signum);
fflush(stdout);
}
int main()
{
pid_t pid;
char *cat_args[2] = {"/bin/cat", NULL};
signal(SIGCHLD, &my_handler);
pid = fork();
if (pid == 0)
{
execv("/bin/cat", cat_args);
}
else
{
my_kill(pid, SIGSTOP);
my_kill(pid, SIGCONT);
wait(NULL);
}
return 0;
}
With the output on macOS:
Sending 17
SIGCHLD received
Sent 17
Sending 19
Sent 19
That behavior is optional. An implementation need not generate a SIGCHLD upon continuation. The language used in POSIX.1-2008 (2016 edition) is "may" rather than "shall":
When a stopped process is continued, a SIGCHLD signal may be generated for its parent process, unless the parent process has set the SA_NOCLDSTOP flag.
- System Interfaces, 2.4.3 Signal Actions
...a SIGCHLD signal may be generated for the calling process whenever any of its stopped child processes are continued.
-
System Interfaces sigaction "Description"
Emphases added.
Am I missing something?
Probably not.
Is it an expected behaviour?
Probably yes.
OSX is based on 4.4 BSD, and this BSD simply does not support sending a SIGCHLD to the parent when the child continues. Earlier versions of Linux lacked this support as well.
Related
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.
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.
I'm studying C signals and I'm having trouble with an exercise that requires to write a program which sends back any signal it receives from a process back to the sender process.
I thought about using the function signal() or the function sigaction(), but they ask you to specify the signum, and I would need them to handle EVERY signal my program receives.
Is there any way to do so without calling the sigaction() for every signal number?
Thank you in advance for your answers
You don't specify what OS you are using but this is simple on POSIX systems. I can't speak to Windows.
The call you want is sigwaitinfo.
You need to block all signals and then use sigwaitinfo to process any received signals synchronously. Sigwaitinfo's return code is the signal received and when you pass a non-null siginfo_t struct as the second parameter you get the same information you would get from a signal handler returning a siginfo_t structure, including the pid of the sending process. Take the received signal and pid and use kill to send the signal back to the originating process. Make sure you aren't sending the signal to yourself before calling kill lest you end up in a loop of sending and receiving a signal to yourself.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#define errExit(msg) do {perror(msg); exit(EXIT_FAILURE);} while (0)
int main(int argc, char *argv[])
{
siginfo_t signalInfo;
sigset_t allSignals;
printf("echo pid = %d\n", getpid());
// block every signal but SIGKILL & SIGSTOP which can't be blocked
sigfillset(&allSignals);
if (sigprocmask(SIG_SETMASK, &allSignals, NULL) == -1)
errExit("sigprocmask");
while(1)
{
// accept signals until SIGTERM delivered
int sig = sigwaitinfo(&allSignals, &signalInfo);
if (sig == -1)
errExit("sigwaitinfo");
if (sig == SIGTERM)
{
printf("buh-bye\n");
exit(EXIT_SUCCESS);
}
printf("echo received signal %s (%d) from pid = %d\n", strsignal(sig), sig, signalInfo.si_pid);
/*** NOT doing this for testing because it will kill the sending shell
// echo signal back to sender but not to yourself
// which would be stupid and lead to an endless loop
if (signalInfo.si_pid != getpid())
kill(signalInfo.si_pid, sig);
***/
}
}
TEST
--- terminal 1 ---
//current shell's pid
> echo $$
7646
//send signals after starting echo pgm in another terminal
> kill -SIGUSR1 7754
> kill -SIGUSR2 7754
> kill -SIGTERM 7754
--- terminal 2 ---
> echosig
echo pid = 7754
echo received signal User defined signal 1 (10) from pid = 7646
echo received signal User defined signal 2 (12) from pid = 7646
buh-bye
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'm playing with waitpid() and signal() and I'm looking for reliable test cases for returning WIFSIGNALED(status) = WIFSTOPPED(status) = WIFCONTINUED (status) = true but can't find any...
Care to tell me how can I make sure those return true so I can debug my code?
Also, a few hints about what signals should I catch with signal() to test those macros would be helpful...
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define NELEMS(x) (sizeof (x) / sizeof (x)[0])
static void testsignaled(void) {
kill(getpid(), SIGINT);
}
static void teststopped(void) {
kill(getpid(), SIGSTOP);
}
static void testcontinued(void) {
kill(getpid(), SIGSTOP);
/* Busy-work to keep us from exiting before the parent waits.
* This is a race.
*/
alarm(1);
while(1) {}
}
int main(void) {
void (*test[])(void) = {testsignaled, teststopped, testcontinued};
pid_t pid[NELEMS(test)];
int i, status;
for(i = 0; i < sizeof test / sizeof test[0]; ++i) {
pid[i] = fork();
if(0 == pid[i]) {
test[i]();
return 0;
}
}
/* Pause to let the child processes to do their thing.
* This is a race.
*/
sleep(1);
/* Observe the stoppage of the third process and continue it. */
wait4(pid[2], &status, WUNTRACED, 0);
kill(pid[2], SIGCONT);
/* Wait for the child processes. */
for(i = 0; i < NELEMS(test); ++i) {
wait4(pid[i], &status, WCONTINUED | WUNTRACED, 0);
printf("%d%s%s%s\n", i, WIFCONTINUED(status) ? " CONTINUED" : "", WIFSIGNALED(status) ? " SIGNALED" : "", WIFSTOPPED(status) ? " STOPPED" : "");
}
return 0;
}
Handling WIFSIGNALED is easy. The child process can commit suicide with the kill() system call. You can also check for core dumps - some signals create them (SIGQUIT, IIRC); some signals do not (SIGINT).
Handling WIFSTOPPED may be harder. The simple step to try is for the child to send itself SIGSTOP with the kill() system call again. Actually, I think that should work. Note that you may want to check on SIGTTIN and SIGTTOU and SIGTSTOP - I believe they count for WIFSTOPPED. (There's also a chance that SIGSTOP only works sanely when sent by a debugger to a process it is running via the non-POSIX system call, ptrace().)
Handling WIFCONTINUED is something that I think the parent has to do; after you detect a process has been stopped, your calling code should make it continue by sending it a SIGCONT signal (kill() again). The child can't deliver this itself; it has been stopped. Again, I'm not sure whether there are extra wrinkles to worry about - probably.
A framework something like the below will allow you check the results of the wait() and waitpid() calls.
pid_t pid = fork();
if (pid == 0) {
/* child */
sleep(200);
}
else {
/* parent */
kill(pid, SIGSTOP);
/* do wait(), waitpid() stuff */
}
You do not actually have to catch the signals (using signal() or related function) that are sent. signal() installs a handler that overrides the default behavior for the specific signal - so if you want to check for a signal terminating your process, pick one that has that default behavior - "man -s7 signal" will give you details a signal's default behavior.
For the macros you have mentioned use SIGSTOP for WIFSTOPPED(status), SIGCONT for WIFCONTINUED (status) and SIGINT for WIFSIGNALED(status)
If you want more flexibility for testing, you could use kill (see "man kill") to send signals to your process. kill -l will list all the signals that can be sent.
in your tests you can fork() and send specific signal to your child processes? In this scenario your child processes are test cases?
EDIT
my answer is about coding a C test. you fork, get the pid of your child process (the process
with signal handlers installed), then you can send signal to it by using kill(2).
In this way you can test the exit status