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

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.

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.

exit(1) in child process returns 256 in the parent process in Linux

I am new to OS programming. I am trying to figure out the mechanism of process creation using fork() system call in Linux. Hence I have written the following code.
# include <stdio.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <unistd.h>
# include <stdlib.h>
int main() {
pid_t pid;
int status;
pid = fork();
if(pid<0) {
fprintf(stderr,"Error: Child can't be created\n");
} else if(pid == 0) {
printf("Hi I am child : %d\n",getpid());
printf("Child finished\n");
exit(1);
} else {
printf("Hi! I am parent : %d\n",getpid());
pid = wait(&status);
printf("After that parent get : %d of child %d\n",status,pid);
printf("Parent finished\n");
}
return 0;
}
According to my knowledge, the parent process gets the exit status of its child process after its termination via the wait() system call. When I am passing 0 to the exit() function in the child process, the parent is also getting 0 as the status. But when I pass 1, the parent process is getting 256. Here is a snapshot of the output for reference.
My question is why it is happenning and how? Please explain.
Thank you in advance.
The value that wait puts in status is not just the exit status. To get the exit status from it, you need to use the WIFEXITED and WEXITSTATUS macros. eg:
# include <stdio.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <unistd.h>
# include <stdlib.h>
int
main(void)
{
pid_t pid;
int status;
pid = fork();
if( pid < 0 ){
perror("fork");
return 1;
} else if( pid == 0 ){
printf("Hi I am child : %ld\n", (long)getpid());
printf("Child finished\n");
exit(1);
} else {
printf("Hi! I am parent : %ld\n", (long)getpid());
pid = wait(&status);
if( WIFEXITED(status) ){
printf("parent get: %d of child %ld\n",
WEXITSTATUS(status), (long)pid
);
}
printf("Parent finished\n");
}
return 0;
}

Calling every child process at once to kill?

I have to write an program which will generate a random amount of processes, and then will kill them one after one, after they all were created.
My problem is that I can't stop the child processes after being created.
Also, I try to call the termination-output to stdout from a child process, but don't really know how to solve it (because pid = 0 is for every child process).
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
//int status;
srand(time(NULL));
int amount = (rand())%9+1;
pid_t fatherid = getpid();
printf("Hello I am a parent process, my PID is %d and I will now create %d children.\n",fatherid,amount);
pid_t pid = 1;
pid_t pidarr[amount];
for(int i = 0;i<amount;i++){
if(pid != 0){
pid = fork();
pidarr[i] = pid;
if(pid ==0){
printf("Hello I am a child process, my PID is %d and my parent has the PID %d.\n",getpid(),fatherid);
}
sleep(1);
}
}
if(pid != 0){
wait(NULL);
}
for(int i = (amount-1);i >= 0;i--){
if(pidarr[(i-1)] != 0){
printf("Hello I am a child process %d, I will terminate now.\n",getpid());
}
sleep(rand()%4);
if(pid != 0){
kill(pidarr[i],SIGKILL);
printf("Child Process %d was terminated.\n",pidarr[i]);
}
}
if(pid != 0){
printf("All child processes were terminated. I will terminate myself now.\n");
}
return EXIT_SUCCESS;
}
the following code shows how to handle fork and child processes.
the code compiles cleanly, is tested and works
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
int main( void )
{
//int status;
srand(time(NULL));
int amount = (rand())%9+1;
pid_t fatherid = getpid();
printf("Hello I am a parent process, my PID is %d and I will now create %d children.\n",fatherid,amount);
pid_t pid;
pid_t pidarr[amount];
for(int i = 0;i<amount;i++)
{
pid = fork();
if( -1 == pid )
{ //then, fork() error
perror( "fork() failed" );
exit(1);
}
// implied else, fork() successful
//pidarr[i] = pid;
if(!pid )
{ // then child process
printf("Hello I am a child process, my PID is %d and my parent has the PID %d.\n",getpid(),fatherid);
exit(0); // exit child process
}
// implied else, parent process
pidarr[i] = pid;
sleep(1);
} // end for
for(int i = (amount-1); i >= 0; i--)
{
kill(pidarr[i],SIGKILL);
printf("Child Process %d was terminated.\n",pidarr[i]);
}
printf("All child processes were terminated. I will terminate myself now.\n");
return(0);
} // end function: main
I am not sure about other parts of your logic (e.g. the if clause inside the fork loop), but
if(pid != 0){
wait(NULL);
}
looks suspiciously as of the parent process waits for a child to exit so that it doesn't get to the code which would kill the children at all (unless they exit on their own, but then the killing seems pointless).
Some issues in your code:
1) As #Peter Schneider points out,
parent process waits for a child to exit so that it doesn't get to the code which would kill the children
So first of all, you have to get rid of:
if(pid != 0){
wait(NULL);
}
2) The for loop that kills the children has to be executed only by the parent process, so the if clause embraces the for:
if(pid != 0){
for(int i = (amount-1);i >= 0;i--){
kill(pidarr[i],SIGKILL);
printf("Child Process %d was terminated.\n",pidarr[i]);
}
}
3) The child processes have to wait doing something until parent kills them, so append the following else clause to the above if:
else{
while(1){
printf("I am a child process %d. Will sleep for 2 senconds\n",getpid());
sleep(2);
}
}
4) the following code makes no sense, because when children are killed they simply stop working.
if(pidarr[(i-1)] != 0){
printf("Hello I am a child process %d, I will terminate now.\n",getpid());
}
If you want children to do something when the signal from kill() gets to them, you will have to use signals.

How to terminate a child process which is running another program by doing exec

I'm doing fork in my main program,and doing exec in the child process which will run another program. Now i want to terminate the child(i.e., the program invoked by exec) and return back to the main program(or parent program). how could i achieve this.. I tried with ctrl+c but its killing parent process and child also.please help me.
/*This is main.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
void sig_int(void);
void sig_term(void);
pid_t pid,ppid;
int main(char argc,char **argv){
int n;
char ch;
printf("***********Application to start or stop services**********\n");
do
{
printf("Enter 1 to start service no.1\n");
printf("Enter 2 to start service no.2\n");
printf("Enter 3 to start service no.3\n");
scanf("%d",&n);
if(fork() == 0)
{
switch(n)
{
case 1: printf("starting service no. 1..\n");
printf("checking whether the given service is already running...\n");
// system("./det.sh ./test")
pid = getpid();
printf("child process pid = %d\n",pid);
// signal(SIGINT,(void *)sig_int);
// signal(SIGTERM,(void *)sig_term);
//execl("/var/vR_main","vR_main",argv[1],argv[2],argv[3],argv[4],NULL);
execl("./test","test",0,0);//will run test.c
break;
case 2: printf("starting service no. 2..\n");
break;
case 3: printf("starting service no. 3..\n");
break;
}
}
else
{
int status;
wait(&status);
if (WIFEXITED(status))
printf("CHILD exited with %d\n", WEXITSTATUS(status));
if (WIFSIGNALED(status))
printf("signaled by %d\n", WTERMSIG(status));
if (WIFSTOPPED(status))
printf("stopped by %d\n", WSTOPSIG(status));
// sleep(2);
ppid = getpid();
printf("%d\n",ppid);
// wait();
printf("\nDo you want to continue...y/n:");
scanf(" %c",&ch);
}
}while(ch == 'y');
return 0;
}
void sig_int(void)
{
printf("caught signal\n");
kill(pid,SIGKILL);
// signal(SIGINT,SIG_DFL);
// exit(0);
}
void sig_term(void)
{
printf("killing the process\n");
signal(SIGINT,SIG_DFL);
// exit(0);
}
/*This is test.c*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void sig_int(void);
void sig_term(void);
pid_t pid;
int main()
{
// int a=10,b=40,c=50,max;
pid = getpid();
printf("exec pid = %d\n",pid);
while (1)
{
signal(SIGINT,(void *)sig_int);
signal(SIGTERM,(void *)sig_term);
}
// max=a>b?a>c?a:c:b>c?b:c;
// printf("%d\n",max);
}
void sig_int(void)
{
printf("caught signal\n");
// signal(SIGINT,SIG_DFL);
kill(pid,SIGKILL);
// exit(0);
}
void sig_term(void)
{
printf("killing the process\n");
signal(SIGINT,SIG_DFL);
// exit(0);
}
Now I want to kill "test application" (invoked by exec),and return to the parent process or the "else block" to continue the program.
You need to do the following:
Do a kill(pid, SIGTERM) first - this gives the child process an opportunity to terminate gracefully
Wait a period of time (use sleep). The period of time depends on the time the child process takes to close down gracefully.
Use waitpid(pid, &status, WNOHANG) checking the return value. If the process has not aborted do step 4
Do a kill(pid, SIGKILL) then harvest the zombie by doing waitpid(pid, &status, 0).
These steps ensure that you give the child process to have a signal handler to close down and also ensures that you have no zombie processes.
Either in or outside your program, it is possible to use kill. By including <signal.h>, you can kill a process with a given PID (use the fork return value to do this).
#include <signal.h>
int pid;
switch (pid = fork())
{
case -1:
/* some stuff */
break;
case 0:
/* some stuff */
break;
default:
/* some stuff */
kill(pid, SIGTERM);
}
It is also possible to use kill command in the shell. To find the PID of your child process, you can run ps command.
man kill
The kill() function shall send a signal to a process or a group of processes specified by pid. The signal to be sent is specified by sig and is either one from the list given in <signal.h> or 0. If sig is 0 (the null signal), error checking is performed but no signal is actually sent. The null signal can be used to check the validity of pid.
POSIX defines the kill(2) system call for this:
kill(pid, SIGKILL);

C pipe() and fork()

I have a problem with a simple program im making with fork and pipes for learning purpose. I want a child that send the ppid to the parent to output the value of ppid and do this twice. However,the result is two ppid output are the same.Why?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2]; /* for the pipe */
int n,pid,ppid,val;
int p[5],q[5];
if (pipe(fd) < 0) {
printf("Pipe creation error\n");
exit(1);
}
for(val=0;val<2;val++){
pid = fork();
if (pid < 0) {
printf("Fork failed\n");
exit(1);
} else if (pid == 0) { /* child */
ppid = getpid();
printf("child %d pid:%d \n",val+1,ppid);
write(fd[1], &ppid, sizeof(ppid));
sleep(1);
close(fd[1]);
} else { /* parent */
//printf("Parent: pid: ");
close(fd[1]);
printf("%d \n",val+1);
sleep(1);
n = read(fd[0], &ppid ,sizeof(ppid));
printf("%d \n",ppid);
// fflush(stdout);
close(fd[0]);
wait(NULL);
// printf("<parent> I have completed!\n");
exit(0);
}
}
}
There may be potential problem in the program design. Since the parent waits for the child
in the first iteration, the child executes the for loop for val=1 and spawns another process
through fork. Eventually there are three process of which two of them will have the same pid
as one of them is executing the for twice.

Resources