c fork() and kill() at the same time not working? - c

Main program: Start a certain amount of child processes then send SIGINT right away.
int main()
{
pid_t childs[CHILDS];
char *execv_argv[3];
int n = CHILDS;
execv_argv[0] = "./debugging_procs/wait_time_at_interrupt";
execv_argv[1] = "2";
execv_argv[2] = NULL;
for (int i = 0; i < n; i++)
{
childs[i] = fork();
if (childs[i] == 0)
{
execv(execv_argv[0], execv_argv);
if (errno != 0)
perror(strerror(errno));
_exit(1);
}
}
if (errno != 0)
perror(strerror(errno));
// sleep(1);
for (int i = 0; i < n; i++)
kill(childs[i], SIGINT);
if (errno != 0)
perror(strerror(errno));
// Wait for all children.
while (wait(NULL) > 0);
return 0;
}
Forked program: Wait for any signal, if SIGINT is sent, open a certain file and write SIGINT and the current pid to it and wait the amount specified of seconds (in this case, I send 2 from the main program).
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
void sigint_handler(int signum)
{
int fd = open("./aux/log1", O_WRONLY | O_APPEND);
char buf[124];
(void)signum;
sprintf(buf, "SIGINT %d\n", getpid());
write(fd, buf, strlen(buf));
close(fd);
}
int main(int argc, char **argv)
{
int wait_time;
wait_time = (argv[1]) ? atoi(argv[1]) : 5;
signal(SIGINT, &sigint_handler);
// Wait for any signal.
pause();
sleep(wait_time);
return 0;
}
The problem is, that the log file that the children should write, doesn't have n lines, meaning that not all children wrote to it. Sometimes nobody writes anything and the main program doesn't wait at all (meaning that sleep() isn't called in this case).
But if I uncomment sleep(1) in the main program, everything works just as I expected.
I suspect that the child processes don't get enough time to listen to SIGINT.
The program I'm working on is a task control and when I run a command like:
restart my_program; restart my_program I get an unstable behaviour. When I call restart, a SIGINT is sent, then a new fork() is called then another SIGINT is sent, just like the example above.
How can I make sure all children will parse SIGINT without the sleep(1) line? I'm testing my program if it can handle programs that don't exit right away after SIGINT is sent.
If I add for example, printf("child process started\n"); at the top of the child program, it doesn't get printed and the main program doesn't wait for anything, unless I sleep for a second. This happens even with only 1 child process.

Everything is working as it should. Some of your child processes get killed by the signal, before they set up the signal handler, or even before they start executing the child binary.
In your parent process, instead of just wait()ing until there are no more child processes, you could examine the identity and exit status of each of the processes reaped. Replace while (wait(NULL) > 0); with
{
pid_t p;
int status;
while ((p = wait(&status)) > 0) {
if (WIFEXITED(status))
printf("Child %ld exit status was %d.\n", (long)p, WEXITSTATUS(status));
else
if (WIFSIGNALED(status))
printf("Child %ld was killed by signal %d.\n", (long)p, WTERMSIG(status));
else
printf("Child %ld was lost.\n", (long)p);
fflush(stdout);
}
}
and you'll see that the "missing" child processes were terminated by the signals. This means that the child process was killed before it was ready to catch the signal.
I wrote my own example program pairs, with complete error checking. Instead of a signal handler, I decided to use sigprocmask() and sigwaitinfo(), just to show another way to do the same thing (and to not be limited to async-signal safe functions in a signal handler).
parent.c:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
const char *signal_name(const int signum)
{
static char buffer[32];
switch (signum) {
case SIGINT: return "INT";
case SIGHUP: return "HUP";
case SIGTERM: return "TERM";
default:
snprintf(buffer, sizeof buffer, "%d", signum);
return (const char *)buffer;
}
}
static int compare_pids(const void *p1, const void *p2)
{
const pid_t pid1 = *(const pid_t *)p1;
const pid_t pid2 = *(const pid_t *)p2;
return (pid1 < pid2) ? -1 :
(pid1 > pid2) ? +1 : 0;
}
int main(int argc, char *argv[])
{
size_t count, r, i;
int status;
pid_t *child, *reaped, p;
char dummy;
if (argc < 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s COUNT PATH-TO-BINARY [ ARGS ... ]\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program will fork COUNT child processes,\n");
fprintf(stderr, "each child process executing PATH-TO-BINARY.\n");
fprintf(stderr, "Immediately after all child processes have been forked,\n");
fprintf(stderr, "they are sent a SIGINT signal.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (sscanf(argv[1], " %zu %c", &count, &dummy) != 1 || count < 1) {
fprintf(stderr, "%s: Invalid count.\n", argv[1]);
return EXIT_FAILURE;
}
child = malloc(count * sizeof child[0]);
reaped = malloc(count * sizeof reaped[0]);
if (!child || !reaped) {
fprintf(stderr, "%s: Count is too large; out of memory.\n", argv[1]);
return EXIT_FAILURE;
}
for (i = 0; i < count; i++) {
p = fork();
if (p == -1) {
if (i == 0) {
fprintf(stderr, "Cannot fork child processes: %s.\n", strerror(errno));
return EXIT_FAILURE;
} else {
fprintf(stderr, "Cannot fork child %zu: %s.\n", i + 1, strerror(errno));
count = i;
break;
}
} else
if (!p) {
/* Child process */
execvp(argv[2], argv + 2);
{
const char *errmsg = strerror(errno);
fprintf(stderr, "Child process %ld: Cannot execute %s: %s.\n",
(long)getpid(), argv[2], errmsg);
exit(EXIT_FAILURE);
}
} else {
/* Parent process. */
child[i] = p;
}
}
/* Send all children the INT signal. */
for (i = 0; i < count; i++)
kill(child[i], SIGINT);
/* Reap and report each child. */
r = 0;
while (1) {
p = wait(&status);
if (p == -1) {
if (errno == ECHILD)
break;
fprintf(stderr, "Error waiting for child processes: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (r < count)
reaped[r++] = p;
else
fprintf(stderr, "Reaped an extra child process!\n");
if (WIFEXITED(status)) {
switch (WEXITSTATUS(status)) {
case EXIT_SUCCESS:
printf("Parent: Reaped child process %ld: EXIT_SUCCESS.\n", (long)p);
break;
case EXIT_FAILURE:
printf("Parent: Reaped child process %ld: EXIT_FAILURE.\n", (long)p);
break;
default:
printf("Parent: Reaped child process %ld: Exit status %d.\n", (long)p, WEXITSTATUS(status));
break;
}
fflush(stdout);
} else
if (WIFSIGNALED(status)) {
printf("Parent: Reaped child process %ld: Terminated by %s.\n", (long)p, signal_name(WTERMSIG(status)));
fflush(stdout);
} else {
printf("Parent: Reaped child process %ld: Lost.\n", (long)p);
fflush(stdout);
}
}
if (r == count) {
/* Sort both pid arrays. */
qsort(child, count, sizeof child[0], compare_pids);
qsort(reaped, count, sizeof reaped[0], compare_pids);
for (i = 0; i < count; i++)
if (child[i] != reaped[i])
break;
if (i == count)
printf("Parent: All %zu child processes were reaped successfully.\n", count);
}
return EXIT_SUCCESS;
}
child.c:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
const char *signal_name(const int signum)
{
static char buffer[32];
switch (signum) {
case SIGINT: return "INT";
case SIGHUP: return "HUP";
case SIGTERM: return "TERM";
default:
snprintf(buffer, sizeof buffer, "%d", signum);
return (const char *)buffer;
}
}
int main(void)
{
const long mypid = getpid();
sigset_t set;
siginfo_t info;
int result;
printf("Child: Child process %ld started!\n", mypid);
fflush(stdout);
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGHUP);
sigaddset(&set, SIGTERM);
sigprocmask(SIG_BLOCK, &set, NULL);
result = sigwaitinfo(&set, &info);
if (result == -1) {
printf("Child: Child process %ld failed: %s.\n", mypid, strerror(errno));
return EXIT_FAILURE;
}
if (info.si_pid == 0)
printf("Child: Child process %ld terminated by signal %s via terminal.\n", mypid, signal_name(result));
else
if (info.si_pid == getppid())
printf("Child: Child process %ld terminated by signal %s sent by the parent process %ld.\n",
mypid, signal_name(result), (long)info.si_pid);
else
printf("Child: Child process %ld terminated by signal %s sent by process %ld.\n",
mypid, signal_name(result), (long)info.si_pid);
return EXIT_SUCCESS;
}
Compile both using e.g.
gcc -Wall -O2 parent.c -o parent
gcc -Wall -O2 child.c -o child
and run them using e.g.
./parent 100 ./child
where the 100 is the number of child processes to fork, each running ./child.
Errors are output to standard error. Each line from parent to standard output begins with Parent:, and each line from any child to standard output begins with Child:.
On my machine, the last line in the output is always Parent: All # child processes were reaped successfully., which means that every child process fork()ed, was reaped and reported using wait(). Nothing was lost, and there were no issues with fork() and kill().
(Do note that if you specify more child processes than you are allowed to fork, the parent program does not consider that an error, and just uses the allowed number of child processes for the test.)
On my machine, forking and reaping 100 child processes is enough work for the parent process, so that every child process gets to the part where it is ready to catch the signal.
On the other hand, the parent can handle 10 child processes (running ./parent 10 ./child) so fast that every one of the child processes gets killed by the INT signal before they are ready to handle the signal.
Here is the output from a pretty typical case when running ./parent 20 ./child:
Child: Child process 19982 started!
Child: Child process 19983 started!
Child: Child process 19984 started!
Child: Child process 19982 terminated by signal INT sent by the parent process 19981.
Child: Child process 19992 started!
Child: Child process 19983 terminated by signal INT sent by the parent process 19981.
Child: Child process 19984 terminated by signal INT sent by the parent process 19981.
Parent: Reaped child process 19982: EXIT_SUCCESS.
Parent: Reaped child process 19985: Terminated by INT.
Parent: Reaped child process 19986: Terminated by INT.
Parent: Reaped child process 19984: EXIT_SUCCESS.
Parent: Reaped child process 19987: Terminated by INT.
Parent: Reaped child process 19988: Terminated by INT.
Parent: Reaped child process 19989: Terminated by INT.
Parent: Reaped child process 19990: Terminated by INT.
Parent: Reaped child process 19991: Terminated by INT.
Parent: Reaped child process 19992: Terminated by INT.
Parent: Reaped child process 19993: Terminated by INT.
Parent: Reaped child process 19994: Terminated by INT.
Parent: Reaped child process 19995: Terminated by INT.
Parent: Reaped child process 19996: Terminated by INT.
Parent: Reaped child process 19983: EXIT_SUCCESS.
Parent: Reaped child process 19997: Terminated by INT.
Parent: Reaped child process 19998: Terminated by INT.
Parent: Reaped child process 19999: Terminated by INT.
Parent: Reaped child process 20000: Terminated by INT.
Parent: Reaped child process 20001: Terminated by INT.
Parent: All 20 child processes were reaped successfully.
Of the 20 child processes, 16 were killed by INT signal before they executed the first printf() (or fflush(stdout)) line. (We could add a printf("Child: Child process %ld executing %s\n", (long)getpid(), argv[2]); fflush(stdout); to parent.c just before the execvp() line, to see if any of the child processes get killed before they execute at all.)
Of the four remaining child processes (19982, 19983, 19984, and 19992), one (19982) was terminated after the first printf() or fflush(), but before it managed to run setprocmask(), which blocks the signal and prepares the child for catching it.
Only those three remaining child processes (19983, 19984, and 19992) caught the INT signal sent by the parent process.
As you can see, just adding complete error checking, and adding sufficient output (and fflush(stdout); where useful, as standard output is buffered by default), lets you run several test cases, and construct a much better overall picture of what is happening.
The program I'm working on is a task control and when I run a command like: restart my_program; restart my_program I get an unstable behaviour. When I call restart, a SIGINT is sent, then a new fork() is called then another SIGINT is sent, just like the example above.
In that case, you are sending the signal before the new fork is ready, so the default disposition of the signal (Termination, for INT) defines what happens.
The solutions to this underlying problem vary. Note that it is at the core of many init system issues. It is easy to solve if the child (my_program here) co-operates, but difficult in all other cases.
One simple co-operation method is to have the child send a signal to its parent process, whenever it is ready for action. To avoid killing parent processes that are unprepared for such information, a signal that is ignored by default (SIGWINCH, for example) can be used.
The option of sleeping for some duration, so that the new child process has enough time to become ready for action, is a common, but pretty unreliable method of mitigating this issue. (In particular, the required duration depends on the child process priority, and the overall load on the machine.)

Try using the waitpid() command in the for loop. This way the next child will only write once the first child is done

Related

Signal handling

I'm currently coding a function that execute external command for an assignment. Here's what I've done, the problem is that the program is taking too long and is interrupted by a SIGALRM.
Thanks for any help!
volatile sig_atomic_t sig = 0;
void ext(int signum){
if(signum==SIGINT || signum==SIGTERM) sig=1;
}
int extern(char **line){
pid_t p;
int status;
struct sigaction as = {0};
as.sa_handler=ext;
if (sigaction(SIGINT, &as, 0)==-1||sigaction(SIGTERM, &as, 0)==-1){
perror("sigaction");
exit(1);
}
switch(p=fork()){
case -1: perror("fork"); exit(1);
case 0 : if(execvp(line[0], line)<0) {perror("exec"); exit(1);} break;
default : //EDIT
if(waitpid(p, &status, 0)>=0){
if (WIFEXITED(status)) return WEXITSTATUS(status);
} else {
perror("wait");
exit(1);
}
break;
}
return 0;
}
Signal dispositions are not carried across a call to execve. They will be reset as soon as execvp executes.
Even if the signal arrives in the child before execpv executes, the parent and the child process have their own copy of
volatile sig_atomic_t sig = 0;
The signal handler in the child changing this value would not cause the parent's copy to change.
Establishing a signal handler is not the correct approach to take here.
Instead, your approach of using waitpid is the correct one, but alongside checking for a normal termination of the program with WIFEXITED(status), you should include another branch that checks WIFSIGNALED(status), which will be true if the child process terminated due to a signal.
WTERMSIG(status) is used to determine which signal terminated the child process.
Here is a general example where the child process randomly exits successfully, or otherwise raises a signal where the default disposition is to terminate the program:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
int main(void)
{
pid_t child = fork();
if (-1 == child) {
perror("fork");
return EXIT_FAILURE;
}
if (0 == child) {
/* randomly signal or return successfully */
srand((unsigned) time(NULL));
switch (rand() % 4) {
case 1: raise(SIGINT); break;
case 2: raise(SIGTERM); break;
case 3: raise(SIGKILL); break;
}
return EXIT_SUCCESS;
}
int status;
if (-1 == waitpid(child, &status, 0)) {
perror("wait");
return EXIT_FAILURE;
}
if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
if (SIGINT == sig || SIGTERM == sig)
printf("Child <%ld> exited by signal SIGINT or SIGTERM.\n", (long) child);
else
printf("Child <%ld> exited by signal #%d.\n", (long) child, sig);
} else if (WIFEXITED(status)) {
printf("Child <%ld> exited normally with status %d.\n",
(long) child,
WEXITSTATUS(status));
}
}
Output from running this program a few times:
Child <41268> exited by signal SIGINT or SIGTERM.
Child <41272> exited by signal SIGINT or SIGTERM.
Child <41276> exited by signal #9.
Child <41280> exited normally with status 0.

prctl(PR_SET_PDEATHSIG) race condition

As I understand, the best way to achieve terminating a child process when its parent dies is via prctl(PR_SET_PDEATHSIG) (at least on Linux): How to make child process die after parent exits?
There is one caveat to this mentioned in man prctl:
This value is cleared for the child of a fork(2) and (since Linux 2.4.36 / 2.6.23) when executing a set-user-ID or set-group-ID binary, or a binary that has associated capabilities (see capabilities(7)). This value is preserved across execve(2).
So, the following code has a race condition:
parent.c:
#include <unistd.h>
int main(int argc, char **argv) {
int f = fork();
if (fork() == 0) {
execl("./child", "child", NULL, NULL);
}
return 0;
}
child.c:
#include <sys/prctl.h>
#include <signal.h>
int main(int argc, char **argv) {
prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
// ...
return 0;
}
Namely, the parent count die before prctl() is executed in the child (and thus the child will not receive the SIGKILL). The proper way to address this is to prctl() in the parent before the exec():
parent.c:
#include <unistd.h>
#include <sys/prctl.h>
#include <signal.h>
int main(int argc, char **argv) {
int f = fork();
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
execl("./child", "child", NULL, NULL);
}
return 0;
}
child.c:
int main(int argc, char **argv) {
// ...
return 0;
}
However, if ./child is a setuid/setgid binary, then this trick to avoid the race condition doesn't work (exec()ing the setuid/setgid binary causes the PDEATHSIG to be lost as per the man page quoted above), and it seems like you are forced to employ the first (racy) solution.
Is there any way if child is a setuid/setgid binary to prctl(PR_SET_PDEATH_SIG) in a non-racy way?
It is much more common to have the parent process set up a pipe. Parent process keeps the write end open (pipefd[1]), closing the read end (pipefd[0]). Child process closes the write end (pipefd[1]), and sets the read end (pipefd[1]) nonblocking.
This way, the child process can use read(pipefd[0], buffer, 1) to check if the parent process is still alive. If the parent is still running, it will return -1 with errno == EAGAIN (or errno == EINTR).
Now, in Linux, the child process can also set the read end async, in which case it will be sent a signal (SIGIO by default) when the parent process exits:
fcntl(pipefd[0], F_SETSIG, desired_signal);
fcntl(pipefd[0], F_SETOWN, getpid());
fcntl(pipefd[0], F_SETFL, O_NONBLOCK | O_ASYNC);
Use a siginfo handler for desired_signal. If info->si_code == POLL_IN && info->si_fd == pipefd[0], the parent process either exited or wrote something to the pipe. Because read() is async-signal safe, and the pipe is nonblocking, you can use read(pipefd[0], &buffer, sizeof buffer) in the signal handler whether the parent wrote something, or if parent exited (closed the pipe). In the latter case, the read() will return 0.
As far as I can see, this approach has no race conditions (if you use a realtime signal, so that the signal is not lost because an user-sent one is already pending), although it is very Linux-specific. After setting the signal handler, and at any point during the lifetime of the child process, the child can always explicitly check if the parent is still alive, without affecting the signal generation.
So, to recap, in pseudocode:
Construct pipe
Fork child process
Child process:
Close write end of pipe
Install pipe signal handler (say, SIGRTMIN+0)
Set read end of pipe to generate pipe signal (F_SETSIG)
Set own PID as read end owner (F_SETOWN)
Set read end of pipe nonblocking and async (F_SETFL, O_NONBLOCK | O_ASYNC)
If read(pipefd[0], buffer, sizeof buffer) == 0,
the parent process has already exited.
Continue with normal work.
Child process pipe signal handler:
If siginfo->si_code == POLL_IN and siginfo->si_fd == pipefd[0],
parent process has exited.
To immediately die, use e.g. raise(SIGKILL).
Parent process:
Close read end of pipe
Continue with normal work.
I do not expect you to believe my word.
Below is a crude example program you can use to check this behaviour yourself. It is long, but only because I wanted it to be easy to see what is happening at runtime. To implement this in a normal program, you only need a couple of dozen lines of code. example.c:
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
if (!done)
done = signum;
}
static int install_done(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
static int deathfd = -1;
static void death(int signum, siginfo_t *info, void *context)
{
if (info->si_code == POLL_IN && info->si_fd == deathfd)
raise(SIGTERM);
}
static int install_death(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_sigaction = death;
act.sa_flags = SA_SIGINFO;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(void)
{
pid_t child, p;
int pipefd[2], status;
char buffer[8];
if (install_done(SIGINT)) {
fprintf(stderr, "Cannot set SIGINT handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (pipe(pipefd) == -1) {
fprintf(stderr, "Cannot create control pipe: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
child = fork();
if (child == (pid_t)-1) {
fprintf(stderr, "Cannot fork child process: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (!child) {
/*
* Child process.
*/
/* Close write end of pipe. */
deathfd = pipefd[0];
close(pipefd[1]);
/* Set a SIGHUP signal handler. */
if (install_death(SIGHUP)) {
fprintf(stderr, "Child process: cannot set SIGHUP handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Set SIGTERM signal handler. */
if (install_done(SIGTERM)) {
fprintf(stderr, "Child process: cannot set SIGTERM handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* We want a SIGHUP instead of SIGIO. */
fcntl(deathfd, F_SETSIG, SIGHUP);
/* We want the SIGHUP delivered when deathfd closes. */
fcntl(deathfd, F_SETOWN, getpid());
/* Make the deathfd (read end of pipe) nonblocking and async. */
fcntl(deathfd, F_SETFL, O_NONBLOCK | O_ASYNC);
/* Check if the parent process is dead. */
if (read(deathfd, buffer, sizeof buffer) == 0) {
printf("Child process (%ld): Parent process is already dead.\n", (long)getpid());
return EXIT_FAILURE;
}
while (1) {
status = __atomic_fetch_and(&done, 0, __ATOMIC_SEQ_CST);
if (status == SIGINT)
printf("Child process (%ld): SIGINT caught and ignored.\n", (long)getpid());
else
if (status)
break;
printf("Child process (%ld): Tick.\n", (long)getpid());
fflush(stdout);
sleep(1);
status = __atomic_fetch_and(&done, 0, __ATOMIC_SEQ_CST);
if (status == SIGINT)
printf("Child process (%ld): SIGINT caught and ignored.\n", (long)getpid());
else
if (status)
break;
printf("Child process (%ld): Tock.\n", (long)getpid());
fflush(stdout);
sleep(1);
}
printf("Child process (%ld): Exited due to %s.\n", (long)getpid(),
(status == SIGINT) ? "SIGINT" :
(status == SIGHUP) ? "SIGHUP" :
(status == SIGTERM) ? "SIGTERM" : "Unknown signal.\n");
fflush(stdout);
return EXIT_SUCCESS;
}
/*
* Parent process.
*/
/* Close read end of pipe. */
close(pipefd[0]);
while (!done) {
fprintf(stderr, "Parent process (%ld): Tick.\n", (long)getpid());
fflush(stderr);
sleep(1);
fprintf(stderr, "Parent process (%ld): Tock.\n", (long)getpid());
fflush(stderr);
sleep(1);
/* Try reaping the child process. */
p = waitpid(child, &status, WNOHANG);
if (p == child || (p == (pid_t)-1 && errno == ECHILD)) {
if (p == child && WIFSIGNALED(status))
fprintf(stderr, "Child process died from %s. Parent will now exit, too.\n",
(WTERMSIG(status) == SIGINT) ? "SIGINT" :
(WTERMSIG(status) == SIGHUP) ? "SIGHUP" :
(WTERMSIG(status) == SIGTERM) ? "SIGTERM" : "an unknown signal");
else
fprintf(stderr, "Child process has exited, so the parent will too.\n");
fflush(stderr);
break;
}
}
if (done) {
fprintf(stderr, "Parent process (%ld): Exited due to %s.\n", (long)getpid(),
(done == SIGINT) ? "SIGINT" :
(done == SIGHUP) ? "SIGHUP" : "Unknown signal.\n");
fflush(stderr);
}
/* Never reached! */
return EXIT_SUCCESS;
}
Compile and run the above using e.g.
gcc -Wall -O2 example.c -o example
./example
The parent process will print to standard output, and the child process to standard error. The parent process will exit if you press Ctrl+C; the child process will ignore that signal. The child process uses SIGHUP instead of SIGIO (although a realtime signal, say SIGRTMIN+0, would be safer); if generated by the parent process exiting, the SIGHUP signal handler will raise SIGTERM in the child.
To make the termination causes easy to see, the child catches SIGTERM, and exits the next iteration (a second later). If so desired, the handler can use e.g. raise(SIGKILL) to terminate itself immediately.
Both parent and child processes show their process IDs, so you can easily send a SIGINT/SIGHUP/SIGTERM signal from another terminal window. (The child process ignores SIGINT and SIGHUP sent from outside the process.)
Your last code snippet still contains a race condition:
int main(int argc, char **argv) {
int f = fork();
if (fork() == 0) {
// <- !!!race time!!!
prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
execl("./child", "child", NULL, NULL);
}
return 0;
}
Meaning that in the child, after the fork, until the prctl() has visible effects (think: returns), there is a time-window where the parent may exit.
To fix this race you have to save the PID of the parent before the fork and check it after the prctl() call, e.g.:
pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
; // continue parent execution
} else {
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
if (r == -1) { perror(0); exit(1); }
// test in case the original parent exited just
// before the prctl() call
if (getppid() != ppid_before_fork)
exit(1);
// continue child execution ...
(see also)
Regarding executing a setuid/setgid program: You can then pass the ppid_before_fork by other means (e.g. in the argument or environment vector) and execute the prctl() (including the comparison) after the exec, i.e. inside the execed binary.
I don't know this for sure, but clearing the parent death signal on execve when invoking a set-id binary looks like an intentional restriction for security reasons. I'm not sure why, considering that you can use kill to send signals to setuid programs that share your real user ID, but they wouldn't have bothered making that change in 2.6.23 if there wasn't a reason to disallow it.
Since you control the code of the set-id child, here is a kludge: make the call to prctl, then immediately afterward, call getppid and see if it returns 1. If it does, then either the process was started directly by init (which is not as rare as it used to be) or the process was reparented to init before it had a chance to call prctl, which means its original parent is dead and it should exit.
(This is a kludge because I know of no way to rule out the possibility that the process was started directly by init. init never exits, so you have one case where it should exit and one case where it shouldn't and no way to tell which. But if you know from the larger design that the process will not be started directly by init, it should be reliable.)
(You must call getppid after prctl, or you have only narrowed the race window, not eliminated it.)

exit() the program from parent before child process has terminated

I have a C server. This server has to handle multiple connections and user's input (through a simple ncurses GUI). So I created two childs.
My problem comes when from the main menu of the user interface, I need to exit the program (then terminate the second child process -which handles the connections- from the first child process).
I'll try to explain myself with a little example:
int main(){
pid_t pid;
int status1, status2;
if((pid = fork()) < 0){
perror("main fork failure:");
exit(1);
}
if(pid == 0){
pid = fork();
if(pid == 0){
/*
some stuff the second child does while
the first child is already running
*/
}
/* this is the first child */
int choice;
choice = menu();
switch(choice){
case 1:
break;
case 2:
/*
HERE I have to exit (from the first child first,
and from the program then): how can I kill the
second child that is running to prevent
zombie processes?
*/
// kill() which pid?
exit(2);
break;
}
wait(&status2);
}
wait(&status1);
return 0;
}
So, how can I kill it if I don't know the second child pid from the first child?
In your code, you reuse the variable pid, but fortunately, the non-zero pid is the one you need to signal.
Hence:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
extern int menu(void);
static void wait_for_pid(int pid)
{
int status;
int corpse;
while ((corpse = wait(&status)) >= 0 && corpse != pid)
printf("Unexpected child %d exited with status 0x%.4X\n", corpse, status);
if (corpse == pid)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
else
printf("Child %d died without its death being tracked\n", pid);
}
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
perror("main fork failure:");
exit(1);
}
if (pid == 0)
{
if ((pid = fork()) < 0)
{
perror("child fork failure:");
exit(1);
}
if (pid == 0)
{
pause(); /* Do nothing until signalled */
exit(0);
}
/* this is the first child */
int choice = menu();
switch (choice)
{
case 1:
/* action 1 */
break;
case 2:
kill(pid, SIGTERM);
exit(2);
/*NOTREACHED*/
}
wait_for_pid(pid);
exit(0);
}
wait_for_pid(pid);
return 0;
}
The loop in the wait_for_pid() function should be overkill for the child, but the parent process could have children it doesn't know about under some circumstances — unlikely but not impossible circumstances.
The use of pause() in the second child is simply writing some code; it is not useful and would not therefore be what you'd write there. Writing the comment /* action 1 */ is likewise dummy code; you'd replace it with code that does something useful. I'd probably have functions to call for the first child and the second child, rather than embedding much code in main(). I assume that it's written as shown to create an MCVE (Minimal, Complete, Verifiable Example); thank you for keeping the code small.
The code above was untested because there was no menu() function. The code below has a menu function — not that it is very interactive.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
extern int menu(void);
int menu(void)
{
printf("Dozing...\n");
sleep(1);
printf("Menu option 2 chosen\n");
return 2;
}
static void wait_for_pid(int pid)
{
int status;
int corpse;
int curpid = getpid();
printf("%d: waiting for children to die\n", curpid);
while ((corpse = wait(&status)) >= 0 && corpse != pid)
printf("%d: Unexpected child %d exited with status 0x%.4X\n", curpid, corpse, status);
if (corpse == pid)
printf("%d: Child %d exited with status 0x%.4X\n", curpid, corpse, status);
else
printf("%d: Child %d died without its death being tracked\n", curpid, pid);
}
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
perror("main fork failure:");
exit(1);
}
if (pid == 0)
{
if ((pid = fork()) < 0)
{
perror("child fork failure:");
exit(1);
}
if (pid == 0)
{
printf("Second child (%d) - pausing\n", (int)getpid());
pause(); /* Do nothing until signalled */
printf("Second child (%d) - awake despite no signal handling\n", (int)getpid());
exit(0);
}
/* this is the first child */
printf("First child (%d) - menuing\n", (int)getpid());
int choice = menu();
switch (choice)
{
case 1:
/* action 1 */
break;
case 2:
printf("kill(%d, SIGTERM)\n", pid);
kill(pid, SIGTERM);
wait_for_pid(pid);
exit(2);
/*NOTREACHED*/
}
/* Reached on menu choices != 2 */
/* Probably needs a loop around the menu() - end loop before wait_for_pid() */
wait_for_pid(pid);
exit(0);
}
wait_for_pid(pid);
return 0;
}
When run, a sample output sequence was:
19489: waiting for children to die
First child (19490) - menuing
Dozing...
Second child (19491) - pausing
Menu option 2 chosen
kill(19491, SIGTERM)
19490: waiting for children to die
19490: Child 19491 exited with status 0x000F
19489: Child 19490 exited with status 0x0200
All of which looks as would be expected. You can see the death from SIGTERM in the status 0x000F (SIGTERM is normally 15, and is 15 on macOS Sierra, though AFAIK no standard demands that it is 15). You can see the first child exited normally with status 2 from the 0x0200. You can see that the parent started waiting before the children did anything. And you can see the debugging techniques — copious printing and including the PID most of the time.

Getting return value of external program [duplicate]

I know that it is possible to read commands output with a pipe? But what about getting return value ? For example i want to execute:
execl("/bin/ping", "/bin/ping" , "-c", "1", "-t", "1", ip_addr, NULL);
How can i get returned value of ping command to find out if it returned 0 or 1?
Here is an example I wrote long time ago. Basically, after you fork a child process and you wait its exit status, you check the status using two Macros. WIFEXITED is used to check if the process exited normally, and WEXITSTATUS checks what the returned number is in case it returned normally:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int number, statval;
printf("%d: I'm the parent !\n", getpid());
if(fork() == 0)
{
number = 10;
printf("PID %d: exiting with number %d\n", getpid(), number);
exit(number) ;
}
else
{
printf("PID %d: waiting for child\n", getpid());
wait(&statval);
if(WIFEXITED(statval))
printf("Child's exit code %d\n", WEXITSTATUS(statval));
else
printf("Child did not terminate with exit\n");
}
return 0;
}
You can use waitpid to get the exit status of you child process as:
int childExitStatus;
waitpid( pID, &childExitStatus, 0); // where pID is the process ID of the child.
exec function familly does not return, the return int is here only when an error occurs at launch time (like not finding file to exec).
You have to catch return value from the signal sent to the process that forked before calling exec.
call wait() or waitpid() in your signal handler (well, you can also call wait() in your process without using any signal handler if it has nothing else to do).
Had trouble understanding and applying the existing answers.
In AraK's answer, if the application has more than one child process running, it is not possible to know which specific child process produced the exit status obtained. According the man page,
wait() and waitpid()
The wait() system call suspends execution of the calling process until one of its children terminates. The call wait(&status)
is equivalent to:
waitpid(-1, &status, 0);
The **waitpid()** system call suspends execution of the calling process until a **child specified by pid** argument has changed state.
So, to obtain the exit status of a specific child process we should rewrite the answer as :
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int number, statval;
int child_pid;
printf("%d: I'm the parent !\n", getpid());
child_pid = fork();
if(child_pid == -1)
{
printf("could not fork! \n");
exit( 1 );
}
else if(child_pid == 0)
{
execl("/bin/ping", "/bin/ping" , "-c", "1", "-t", "1", ip_addr, NULL);
}
else
{
printf("PID %d: waiting for child\n", getpid());
waitpid( child_pid, &statval, WUNTRACED
#ifdef WCONTINUED /* Not all implementations support this */
| WCONTINUED
#endif
);
if(WIFEXITED(statval))
printf("Child's exit code %d\n", WEXITSTATUS(statval));
else
printf("Child did not terminate with exit\n");
}
return 0;
}
Feel free to turn this answer to an edit of AraK's answer.
You can wait on the child process and get its exit status.
The system call is wait(pid), try to read about it.
Instead of exec family, you can use popen. Then read the output using fgets or fscanf.
char buff[MAX_BUFFER_SIZE];
FILE *pf = popen("your command", "r");
fscanf(pf, "%s", buff);
pclose(pf);

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.

Resources