wait for children and grand-children - c

How can you wait until all children and grand-children have exited, without blocking in a signal handler? This is my attempt so far.
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int run = 1;
void handler(int sig, siginfo_t *info, void *uap)
{
int exit_code;
printf("sigchld pid %d\n", info->si_pid);
pid_t pid = waitpid(-1, &exit_code, 0);
if (pid == -1) {
perror("waitpid()\n");
} else {
printf("waitpid returned %d\n", pid);
}
// set run = 0 when all children exit
printf("end of sigchild handler\n");
}
void main() {
struct sigaction chld;
chld.sa_sigaction = handler;
chld.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
sigaction(SIGCHLD, &chld, NULL);
//procmask sigchld?
if (!fork ()) {
if (!fork ()) {
sleep(2);
printf ("grand-son exit: %d\n", getpid());
exit (0);
}
sleep(1);
printf ("son exit: %d\n", getpid());
exit (0);
}
while(run)
sleep(1);
printf("ciao\n");
}

While it is true that SIGCHLD and waitpid, etc., only work for immediate children, on UNIX systems you can often "cheat" a little bit with inherited resources passed from parent to child to grandchild, and closed upon process termination.
For example, the original process might open a pipe, and perhaps set the read end of it close-on-exec, so that children and grandchildren inherit the write end. When the original process is ready to wait for all descendants to terminate, it closes its write end of the pipe and blockingly reads or selects for readability on the remaining descriptor. When the last descendant has terminated, the read end of the pipe will deliver an EOF.
This tactic is not guaranteed — a child or grandchild might cautiously close inherited file descriptors — but it often works well enough.

Related

waitpid stops waiting after signal is sent

I am currently working on a C project for university. Among other things I should signal the parent process using SIGUSR1.
The problem I'm facing at the moment is that I also need to wait for the child process to terminate so I can safely shut down everything in the end (removing shared Memory etc.).
At the moment I am using sigaction() to respond to the signal and waitpid() to wait for the child to terminate (that was the plan anyways ^^). But when I signal the parent using kill(), waitpid() stops waiting and runs the remainder of the parent even though the child is still running.
I feel like I'm missing something obvious but I can't figure it out.
Any help is greatly appreciated,
stay safe
Tim
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
void handle_sigusr1(int sig) {
printf("Recieved signal %i.\n", sig);
}
int main() {
pid_t pid;
pid = fork();
if (pid == -1) {
perror("fork:");
return EXIT_FAILURE;
}
else if (pid == 0) {
printf("Hello from the child.\n");
kill(getppid(), SIGUSR1);
sleep(3);
printf("Hello again from the child.\n");
return EXIT_SUCCESS;
}
else {
printf("Hello from the parent.\n");
struct sigaction sa;
sa.sa_handler = &handle_sigusr1;
sigaction(SIGUSR1, &sa, NULL);
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status))
printf("Exit status: %i\n", WEXITSTATUS(status));
printf("Finished waiting for child.\n");
return EXIT_SUCCESS;
}
}
Output:
Hello from the parent.
Hello from the child.
Recieved signal 10.
Exit status: 0
Finished waiting for child.
tim#schlepptop:signalTest$ Hello again from the child.
PS: WEXITSTATUS(status) is usually 0 but sometimes it's also something like 16 or 128.
Per POSIX waitpid() documentation:
RETURN VALUE
... If wait() or waitpid() returns due to the delivery of a signal to the calling process, -1 shall be returned and errno set to [EINTR]. ...
You need to check the return value:
int status
do
{
errno = 0;
int rc = waitpid(pid, &status, 0);
if ( rc != -1 )
{
break;
}
}
while ( errno == EINTR );

Trouble synchronously catching SIGCHLD using sigwaitinfo on linux

I've got a parent process from which I'm spawning 10 child processes using fork(2). After all are successfully up and running, I begin synchronously waiting for them to finish by calling sigwaitinfo(2) in the parent process.
Here's a minimal version of the code:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <mqueue.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <time.h>
int main(void) {
sigset_t set;
sigemptyset(&set); // initialize empty set
sigaddset(&set, SIGCHLD);
sigset_t old_set;
sigprocmask(SIG_UNBLOCK, &set, &old_set); // unblock SIGCHLD
if(sigismember(&old_set, SIGCHLD) == 1) {
fprintf(stdout, "parent: SIGCHLD was previously blocked");
}
for(size_t i = 0; i < 10; i++) {
pid_t pid = fork();
if(pid == -1) {
perror("error forking");
return EXIT_FAILURE;
}
if(pid == 0) {
sleep(10);
return EXIT_SUCCESS;
} else {
fprintf(stdout, "parent: spawned new child (%d)\n", pid);
}
}
size_t n_children_finished = 0;
siginfo_t info; // buffer for info about received signal
while(n_children_finished < 10) {
fprintf(stdout, "parent: waiting for SIGCHLD...\n");
int sig = sigwaitinfo(&set, &info);
// even though it's a set we've only registered SIGCHLD, so no need to check return value
n_children_finished++;
fprintf(stdout, "parent: child (PID: %d) finished\n", info.si_pid);
}
fprintf(stdout, "parent: all children finished!\n");
return EXIT_SUCCESS;
}
output:
parent: spawned new child (224386)
parent: spawned new child (224387)
parent: spawned new child (224388)
parent: spawned new child (224389)
parent: spawned new child (224390)
parent: spawned new child (224391)
parent: spawned new child (224392)
parent: spawned new child (224393)
parent: spawned new child (224394)
parent: spawned new child (224395)
parent: waiting for SIGCHLD...
I understand that this is a very poor and unreliable approach (some child processes may finish before I setup the synchronous handler), but in practice I know that each of the 10 processes last long enough so that they terminate some time after the synchronous handler is in place in the parent.
The problem is that the first sigwaitinfo(2) call in the while loop hangs forever and never returns, despite the parent's children successfully terminating after its call. I can't figure out why.

wait() function in c

I have two process. Their names are parent and child process. I want parent process wait child process without wait() function. How can I do this?
My code is here.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
intmain(int argc, char *argv[]) {
printf("hello world (pid:%d)\n", (int) getpid());
int rc = fork(); if (rc < 0) { // fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) { // child (new process)
printf("hello, I am child (pid:%d)\n", (int) getpid());
} else { // parent goes down this path (original process)
printf("hello, I am parent of %d (wc:%d) (pid:%d)\n", rc, wc, (int) getpid());
}
return 0; }
You could write a loop that keeps checking if the child is running. You can use kill function to check if a process is alive. (This won't work, you can send a signal to a zombie process)
You could define a signal handler for SIGCHLD that sets a variable, and check that variable in a loop.
If you don't call some form of wait the child will become a zombie process though.

why SIGHUP signal not received when becoming an Orphaned Process Group

In the manual for GNU libc about orphaned process groups, it mentioned :
“process groups that continue running even after the session leader
has terminated are marked as orphaned process groups.
When a process group becomes an orphan, its processes are sent a SIGHUP
signal. Ordinarily, this causes the processes to terminate. However,
if a program ignores this signal or establishes a handler for it
(see Signal Handling), it can continue running as in the orphan process
group even after its controlling process terminates; but it still
cannot access the terminal any more. ”
I write a test program, but when the process group becomes an orphan, its process didn't receive the SIGHUP signal. I am wondering why?
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
static void
sig_hup(int signo) //**never get called ???**
{
printf("SIGHUP received, pid = %ld\n", (long)getpid());
}
static void
pr_ids(char *name)
{
printf("%s: pid = %ld, ppid = %ld, pgrp = %ld, tpgrp = %ld\n",
name, (long)getpid(), (long)getppid(), (long)getpgrp(),
(long)tcgetpgrp(STDIN_FILENO));
fflush(stdout);
}
int
main(void)
{
char c;
pid_t pid;
pr_ids("parent");
pid = fork();
if (pid > 0) { // parent
sleep(5);
exit(0); // parent exit;
} else {
pr_ids("child");
setsid(); //create new session, and "child" becomes the session leader
pid = fork();
if(pid>0) {
sleep(20);
exit(0); // "child" exit
// so the process group become an orphan process group
}
else{
pr_ids("grandson");
signal(SIGHUP, sig_hup); // establish signal handler
sleep(60); // now becoming orphan process group
printf("end\n");
}
}
exit(0);
}
Orphaned process groups get SIGHUP followed by SIGCONT if they're stopped when they become orphaned.
Sleep is not enough, you need:
kill(getpid(), SIGSTOP); //or raise(SIGSTOP);
In addition to that, POSIX doesn't require that SIGHUP and SIGCONT be sent if the orphaning was caused by setsid() or setprgrp() because then it wasn't caused by an exiting process innocently unaware of job control (see http://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html ).
However, with kill(getpid(), SIGSTOP) instead of that sleep(60) in the child, you will get a stopped orphan with your program even if you don't call setsid().
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
static void
sig_hup(int signo) //**never get called ???**
{
printf("SIGHUP received, pid = %ld\n", (long)getpid());
}
static void
pr_ids(char *name)
{
printf("%s: pid = %ld, ppid = %ld, pgrp = %ld, tpgrp = %ld\n",
name, (long)getpid(), (long)getppid(), (long)getpgrp(),
(long)tcgetpgrp(STDIN_FILENO));
fflush(stdout);
}
int
main(void)
{
pid_t pid;
pr_ids("parent");
pid = fork();
if (pid > 0) { // parent
sleep(5);
_exit(0); // parent exit;
} else {
pr_ids("child");
/*setsid(); //create new session, and "child" becomes the session leader*/
pid = fork();
if(pid>0) {
sleep(2);
exit(0); // "child" exit
// so the process group become an orphan process group
}
else{
pr_ids("grandson");
signal(SIGHUP, sig_hup); // establish signal handler
kill(getpid(), SIGSTOP);
printf("end\n");
}
}
exit(0);
}
should get you a SIGHUP in the child after the parent dies (5s).
That document section is talking specifically about the loss of a controlling terminal of a process that had one—usually by a modem hangup, or the virtual equivalent (ending an ssh session, etc). (I think the phrasing in the document could be improved here). When you use setsid() here, you give up access to the controlling terminal by the time setsid() returns, so there is no controlling terminal to lose from there forward.
You could open() a tty device (such as a pty slave) to gain a controlling terminal (note that you may have to do some additional operation as well—FreeBSD requires a TIOCSCTTY ioctl), then lose it again, and then you should get the SIGHUP signal.

Parent process doesn't complete after child is terminated in C

I'm having trouble with a process forking exercise. I want to fork a child process and have it hang after announcing it has been forked, and wait for a signal to terminate, after which the parent process must announce it is terminating and then exit.
I can get the processes forked and have the parent wait for the hanging child to be killed by the signal, but it seems to kill the parent as well. I tried killing the child process specifically by its PID, but with no success.
Thanks for any help!
Code:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
void catchInt (int signum)
{
printf("\nMy sincerest apologies, master\n");
/*kill(0, SIGINT);*/
exit(0);
}
void ignoreInt (int signum)
{
wait(NULL);
}
int main () {
pid_t pid;
/* fork process */
pid = fork();
if (pid < 0) /* error handler */
{
fprintf(stderr, "Fork Failed");
exit(-1);
}
else if (pid == 0) /* child */
{
printf("Child reporting in\n");
signal(SIGINT, catchInt);
for ( ;; )
pause();
}
else /* parent */
{
/* parent will wait for the child to complete */
signal(SIGINT, ignoreInt);
wait(NULL);
printf("You're welcome\n");
exit(0);
}
}
Even assuming you fix the code so it compiles (you've not defined tempPID), there are problems:
You set the child to go to sleep until a signal arrives.
You set the parent to wait until the child dies.
So, you have a state where neither process is going to do anything more.
You probably need the parent to send a signal to the child:
kill(pid, SIGINT);
It is not clear that you need the parent to set a signal handler.
You probably want the child to set a signal handler.
You probably don't want the infinite loop in the child.
Oh, and void main() is incorrect - int main() or int main(void) or int main(int argc, char **argv) are the approved declarations for main().
And it is tidier if you return a value (0) from main(). The C99 standard does permit you to drop off the end of main() and will treat that as returning zero, but only if the function is properly declared as returning an int.
The header for wait() and relatives in POSIX is <sys/wait.h>.
And, because I'm a sucker, here's code that compiles and might even do what you want:
#include <stdio.h>
#include <signal.h>
#include <unistd.h> /* getpid() */
#include <stdlib.h>
#include <sys/wait.h>
void catchInt(int signum)
{
printf("Child's PID is %d\n", (int)getpid());
printf("My sincerest apologies, master\n");
exit(1);
}
int main()
{
pid_t pid = fork();
if (pid < 0) /* error handler */
{
fprintf(stderr, "Fork Failed");
exit(-1);
}
else if (pid == 0) /* child */
{
printf("Child reporting in\n");
signal(SIGINT, catchInt);
pause();
}
else /* parent */
{
sleep(1);
kill(pid, SIGINT);
wait(NULL);
printf("You're welcome\n");
}
return(0);
}
Just figured out what I was doing wrong, I should have realized SIGINT is sent to every process, and so the parent was simply being sent an unhandled SIGINT, causing it to exit. Thanks for all the help (my apologies on the sloppy coding, I really shouldn't wait until the program is completed to clean that up), the code's been edited above and works as intended.
Thanks again.

Resources