UNIX signal handling. Wait in SIGCHLD handler. C - c

I've a parent and a child processes. In the parent I established a signal handler for a SIGCHLD. I send SIGTSTP signal to the child, that trigers SIGCHLD and in SIGCHLD siganl handler in parent I call wait function to get a status of the stopped child. But instead of returning immediately it blocks. Then I send a SIGCONT signal to the child and wait returns with errno set to Interuppted system call. I can't understand what I'm missing.
pid_t pid;
static void sig_chld(int signo);
int main() {
struct sigaction act, savechld;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = sig_chld;
if (sigaction(SIGCHLD, &act, &savechld) < 0){
return errno;
}
pid = fork();
switch (pid){
case -1:{
perror("fork failed");
return errno;
}
case 0:{ //child
if (sigaction(SIGCHLD, &savechld, NULL) < 0)
return errno;
execlp(path, name_of_executable, (char*)NULL);
return errno;
}
default:{
for (;;)
pause();
}
}
return 0;
}
void sig_chld(int signo) {
int statol;
if (wait(&statol) < 0){
perror("waitt error");
exit(errno);
}
if (WIFSTOPPED(statol)){
puts("Child is stopped");
} else if (WIFEXITED(statol)){
puts("Child exited");
} else if (WIFCONTINUED(statol)){
puts("Child continued");
} else if (WIFSIGNALED(statol)){
puts("Child is signaled");
int sig = WTERMSIG(statol);
psignal(sig, NULL);
}
}

You have to use waitpid() instead of wait(), and you need to specify the option WUNTRACED to also have stopped children reported with waitpid(), like this:
if (waitpid(-1, &statol, WUNTRACED) < 0) {
Now waitpid() should return immediately and your macro WIFSTOPPED(&statol) should be true.

Related

Unable to handle signals on Linux from inside of forked process

I am trying to execute a fork function and use a signal handler in the child process, however when I run the program and try to send the signal the parent process gets the signal instead of the child process. The code:
pid_t id = fork();
if(id == 0)
{
struct sigaction ss;
ss.sa_handler = &sigstop;
sigaction(SIGTSTP, &ss, NULL);
int status;
status = execvp(arguments[0], arguments);
if(status == -1)
perror("MSH_ERROR (unknown command");
exit(EXIT_SUCCESS);
}
else
{
wait(NULL);
}

How could I know if all processes have been already created before sending signals?

I'm writing a Unix program where the parent process has to send signals to children and a grandson. How could I know if all processes have been already created before sending signals? Because sometimes they don't exist yet. Thanks a lot!
void t(int sig)
{
kill(SIGKILL, pidc1);
kill(SIGKILL, pidc2);
kill(SIGKILL, pidg2);
kill(SIGKILL, pidc3);
}
void handler()
{
write(1, "Signal SIGUSR1\n", 15);
}
pid_t pidc1, pidc2, pidc3, pidg2;
int main(int argc, char **argv)
{
struct sigaction action;
int status;
action.sa_flags = 0;
action.sa_handler = handler;
sigaction(SIGUSR1, &action, NULL);
pidc1 = fork();
if(pidc1 == 0)
{
printf("Child 1\n");
}
pidc2 = fork();
if(pidc2 == 0)
{
printf("Child 2\n");
pidg2 = fork();
if(pidg2 == 0)
{
printf("Grandson 2\n");
}
wait(&status);
}
pidc3 = fork();
if(pidc3 == 0)
{
printf("Child 3\n");
}
kill(pidg2, SIGUSR1);
kill(pidc3, SIGUSR1);
signal(SIGALRM, t);
alarm(10);
wait(&status);
}
Preliminary note: The child code parts in your example program fall through to their parent's code, which is certainly not intended; I'll assume something like return sleep(5); at the end of each block. Also note that the printf()s may malfunction with fork()s and buffered output.
Barmar wrote:
If you need to wait for the grandchild processes to be created, you need some kind of communication from the child to the parent, so it can send the grandchild's PID. Shared memory and a mutex would be a way to do this.
That's absolutely correct. (The direct children are no problem, since the parent knows their PIDs.) Another way to communicate the grandchild's PID is a pipe; your example main() could become:
int main(int argc, char **argv)
{
int status;
sigaction(SIGUSR1, &(struct sigaction){.sa_handler = handler}, NULL);
setbuf(stdout, NULL); // printf() may malfunction without this
pidc1 = fork();
if (pidc1 == 0)
{
printf("Child 1\n"); return sleep(5);
}
int pipefd[2];
pipe(pipefd); // for communicating the grandson's PID
pidc2 = fork();
if (pidc2 == 0)
{
printf("Child 2\n");
pidg2 = fork();
if (pidg2 == 0)
{
printf("Grandson 2\n"); return sleep(5);
}
write(pipefd[1], &pidg2, sizeof pidg2); // write pidg2 to write end
wait(&status); return sleep(5);
}
pidc3 = fork();
if(pidc3 == 0)
{
printf("Child 3\n"); return sleep(5);
}
read(pipefd[0], &pidg2, sizeof pidg2); // read pidg2 from pipe's read end
kill(pidg2, SIGUSR1);
kill(pidc3, SIGUSR1);
}

waitpid() return value 0 along with errno EINTR

I am trying to write a program in which I am forking a child from a parent, and handling SIGCHLD signals using a handler, in which I use waitpid(). When I execute it, however, I am sometimes getting a return value of 0 from waitpid, along with errno being set to EINTR. What does that mean?
Here is my SIGCHLD handler:
pid_t pid;
int status;
while((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0)
{
printf("Handler reaped child %d\n", (int)pid);
if(WIFEXITED(status))
{
deletejob(job_list, pid);
}
else if(WIFSIGNALED(status))
{
deletejob(job_list, pid);
}
else if(WIFSTOPPED(status))
{
struct job_t *job = getjobpid(job_list, pid);
job->state = ST;
}
}
printf("%d %d\n", pid, errno);
if(errno != ECHILD)
{
unix_error("waitpid error");
}
return;
Here is the parent function, in which I fork the child:
pid_t pid;
sigset_t block_set;
int file_descriptor;
if(tok.outfile == NULL)
{
file_descriptor = STDOUT_FILENO;
}
else
{
file_descriptor = open(tok.outfile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
}
sigemptyset(&block_set);
sigaddset(&block_set, SIGCHLD);
sigaddset(&block_set, SIGINT);
sigaddset(&block_set, SIGTSTP);
sigprocmask(SIG_BLOCK, &block_set, NULL);
pid = fork();
if(pid == 0)
{
sigprocmask(SIG_UNBLOCK, &block_set, NULL);
setpgid(0, 0);
dup2(file_descriptor, 1);
while(execve(tok.argv[0], tok.argv, environ) < 0)
{
exit(0);
}
}
else
{
if(bg == 1)
{
addjob(job_list, pid, BG, cmdline);
sigprocmask(SIG_UNBLOCK, &block_set, NULL);
int jid = pid2jid(pid);
printf("[%d] (%d) %s\n", jid, pid, cmdline);
}
else if(bg == 0)
{
addjob(job_list, pid, FG, cmdline);
sigprocmask(SIG_UNBLOCK, &block_set, NULL);
}
if(bg == 0)
{
struct job_t *job = getjobpid(job_list, pid);
while(pid == fgpid(job_list))
{
sleep(1);
}
}
}
}
return;
When called with WNOHANG, waitpid() returns 0 when there are no more children left to reap. Your SIGCHLD handler gets called when a child process exits, so you know there'll always be at least one to reap. But because multiple signals don't get queued, it's possible there might be more than one child process to reap.
So what this while loop does:
while((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0)
is to basically say "call waitpid() to reap the child I know is waiting, and then keep calling it over and over to reap any additional children which may happen to be available, until it returns 0, at which point I know I've reaped all the children that are available."
Returning 0 here is therefore not an error, it's a deliberate device to reap an unknown number of children. waitpid() is not setting errno, in this case, so that EINTR must have been set when some previous system call got interrupted.
Generally speaking, you should not check errno unless a function returns an error, although there are some unusual cases (strtol() is one, where a return of 0 could mean the parsed number was 0, or could mean that there was an error) where the return value does not unambiguously indicate an error. In these cases you can set errno to 0 prior to calling the function, and in the event the return value suggests there might be an error, you can check errno to see if it's been set.

I have a handler to sigchld signal,but when sigtimedwait() returns success handler not called. Why?

int exe(int sec)
{
const char *buf;
int timeout_sec=sec;
int i=0,j=0;
sigset_t mask;
sigset_t orig_mask;
sigset_t pset;
siginfo_t siginfo;
struct timespec timeout;
pid_t pid;
int fd,status=-1;
int fl;
sig_init();
sigemptyset (&mask);
sigaddset (&mask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
perror ("sigprocmask");
goto error;
}
if (( pid=fork())<0)
{
perror ("fork");
goto error;
}
else if (pid == 0)
{
printf("In child pid=%d\n",getpid());
if(execv(ptr[0],ptr)==-1)
{
printf("execv failed in %s: error: %s\n", __FUNCTION__, strerror(errno));
_exit(1);
}
printf("Exiting child\n");
_exit(0);
}
else
{
timeout.tv_sec = timeout_sec;
timeout.tv_nsec = 0;
do {
if ((ss=sigtimedwait(&mask, &siginfo, &timeout)) < 0) {
if (errno == EINTR) {
// Interrupted by a signal other than SIGCHLD.
printf("Continue\n");
continue;
}
else if (errno == EAGAIN) {
printf("%s %s failed in:%s KILLING\n",ptr[0],ptr[1],__FUNCTION__);
kill (pid, SIGKILL);
}
else {
perror ("sigtimedwait");
goto error;
}
}
break;
} while (1);
if (sigprocmask(SIG_SETMASK, &orig_mask, NULL) < 0) {
perror ("sigprocmask");
goto error;
}
sleep(10);
}
return 0;
error:
printf("%s failed\n",__FUNCTION__);
return -1;
}
Here I am trying to block the SIGCHLD and the in child process executing execv, for SIGCHLD status I have a handler for it(not mentioned here in code) which is working fine when sigtimedwait() returning error but when sigtimedwait() success the handler is not called, and if I am using waitpid directly in parent instead of handler then in both of cases it is woking fine.
Please help me out. I just found one clue in man page of sigtimedwait i.e. "sigwaitinfo() removes the delivered signal from the calling process's list of pending signals and returns the signal number as its function result"
Curious to know the reason and too much in need.Thanks :)

How can I handle SIGCHLD?

I need to handle SIGCHLD properly. How can I use it with my existing code? at the moment I cant wait for the child process unless I use 0 instead of WNOHANG|WUNTRACED.
status = 0;
pid_t child, endID;
if(amp == 1)
signal( SIGCHLD, SIG_IGN );
child = fork();
if (child < 0) {
perror("fork() error\n");
exit(EXIT_FAILURE);
} else if (child == 0) {
// do sth here
perror("error\n");
} else {
//sleep(1)
If I remove sleep then parent is executed 1st.. why?
Here is a start (but read below):
static void
child_handler(int sig)
{
pid_t pid;
int status;
/* EEEEXTEERMINAAATE! */
while((pid = waitpid(-1, &status, WNOHANG)) > 0)
;
}
/* Establish handler. */
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = child_handler;
sigaction(SIGCHLD, &sa, NULL);
Of course, this is all pointless. If the parent simply ignores SIGCHLD, the children are silently reaped and won't turn into zombies.
Quoting TLPI:
Explicitly setting the disposition of SIGCHLD to SIG_IGN causes any
child process that subsequently terminates to be immediately removed
from the system instead of being converted into a zombie.
So something like this should do the trick for you:
signal(SIGCHLD, SIG_IGN); /* Silently (and portably) reap children. */

Resources