Wait for an unknown child process - c

I need to execute n child processes, n being a parameter of the program, those n child process will execute one Linux command each, and when they end the program need to create another child process to substitute it.
The problem is that I don't really know how to wait for an unknown process. As each child can have different commands those commands can last different times, so the main process needs to be available to detect when one of the child process has ended and create another one in it's place.
I tried using a for loop with a pid_t array of n size which contains all the pids but it doesn't work. I also tried using wait but once a child process finishes the program stops. This is what I have:
int main(int argc, char *argv[]){
int opt;
int num_procesos=1;
int status;
pid_t pid;
if(argc > 3){
error(argv[0],EXIT_FAILURE);
}
while((opt = getopt(argc,argv,"p")) != -1){
switch(opt){
case 'p':
if(argv[2]==NULL){
error(argv[0],EXIT_FAILURE);
}
num_procesos = atoi(argv[2]);
if(num_procesos < 1 || num_procesos > 8){
fprintf(stderr,"Error: el número de procesos en ejecución tiene que estar entre 1 y 8.");
error(argv[0],EXIT_FAILURE);
}
break;
default:
num_procesos = 1;
break;
}
}
pid_t pids[num_procesos];
for(int i=0;i<num_procesos;i++){
switch(pid = fork()){
case -1:
perror("fork()");
exit(EXIT_FAILURE);
break;
case 0:
leer_y_ejecutar();
}
}
int j=0;
while(j==0){
for(int i=0;i<num_procesos;i++){
if(wait(&status)==0){
switch(pid = fork()){
case -1:
perror("fork()");
exit(EXIT_FAILURE);
break;
case 0:
leer_y_ejecutar();
}
}else{
perror("wait()");
exit(EXIT_FAILURE);
}
}
}
return EXIT_SUCCESS;
}
Don't mind about the function leer_y_ejecutar() it's just part of the rest of my code. If any test is necessary it can be substituted with a printf or something else.

From the wait(2) manual :
The wait() system call suspends execution of the calling thread until one of its children terminates. The call wait(&wstatus) is equivalent to waitpid(-1, &wstatus, 0);
...
waitpid(): on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more child(ren) specified by pid exist, but have not yet changed
state, then 0 is returned. On error, -1 is returned.\
The signature for waitpid is pid_t waitpid(pid_t pid, int *wstatus, int options);, so essentially, this piece of code :
while(j==0){
for(int i=0;i<num_procesos;i++){
// from here
if(wait(&status)==0){
switch(pid = fork()){
case -1:
perror("fork()");
exit(EXIT_FAILURE);
break;
case 0:
leer_y_ejecutar();
}
// to here
}else{
perror("wait()");
exit(EXIT_FAILURE);
}
}
}
Is never read, because wait(&status) == 0 cannot happen because WNOHANG has not been specified.
As a potential solution, I'd suggest changing it for wait(&status) > 0.

Related

Not fully understanding fork for switch program output

I have been trying to understand the output of this program, but still I don’t quite get it.
main()
{
int pid, i;
pid = getpid();
for (i = 0; i < 25; i++)
{
switch (fork())
{
case 0:
if (pid % 2 == 0)
{
exit(0);
break;
}
default:
if (pid % 2 != 0)
{
exit(0);
}
}
}
printf("I am the process %d and my father is the process %d\n", getpid(), getppid());
while (wait(NULL) > 0) {}
return 0;
}
When I run this, it returns:
I am the process 11110 and my father is the process 26453
However, if you were to run the above code without both "% 2", it won't return anything.
I am very confused about this. The way I thought it would work (for the code without "% 2") is, for each for iteration:
the child (pid==0) would finish its process (killing the child process) and always break from the switch (not affecting the for loop)
the father/main process will wait until the child dies
next for iteration
Is the above approach correct? If so, how would it be with "% 2"?
Without % 2 you'd get:
switch (fork())
{
case 0:
if (pid == 0)
{
exit(0);
break;
}
default:
if (pid != 0)
{
exit(0);
}
}
Since pid is not 0, the parent would exit(0) immediately after the first fork(), so you won't see a print statement.

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.

Trouble with status of pid after waitpid in my own implemented shell

I am trying to implement my own shell as a homework. In the shell I need a new command called 'status' which displays the current status of pids for example:
When I type it must display
myshell>>status
PID PGID STATUS PROG
1412 1412 pwd exit(0)
1454 1454 /opt/firefox/bin/firefox running
1462 1462 ls exit(1)
1463 1463 xterm stopped
However in my shell I have no child process error for exited process and it writes signaled(29) for stopped process.You can see my output from here
This is my process list struct
typedef struct
{
pid_t ppid;
pid_t ppgid;
char *prog;
char status[30];
}Process;
This is my fork for executing new process:
if(forkexec){
int pid=fork();
iterator=iterator+1;
processList[iterator].prog=ptr;
processList[iterator].ppid=pid;
processList[iterator].ppgid=getpgid(pid);
strcpy(ptr,worte[0]);
switch (pid){
case -1:
perror("Fehler bei fork");
return(-1);
case 0: //child process
if(umlenkungen(k))
exit(1);
if(!setpgid(0, 0))
{
do_execvp(k->u.einfach.wortanzahl, k->u.einfach.worte); //for executing program
}
abbruch("interner Fehler 001"); //error
default: //parent process
if(k->endeabwarten){
if(!setpgid(pid, 0))
{
tcsetpgrp(0,getpgid(pid));
waitpid(pid, NULL, WUNTRACED);
tcsetpgrp(0,getpgid(shellpid));
}
}
return 0;
}
}
In child process it calls do_execvp function which is:
void do_execvp(int argc, char **args){
if(execvp(*args, args)==-1)
{
perror("exec-Fehler");
fprintf(stderr, "bei Aufruf von \"%s\"\n", *args);
exit(1);
}
}
For new status command I have, it means if the user enter status this part will run:
if (strcmp(worte[0], "status")==0) {
int i;
fputs("PID: PGID: PROGRAM: STATUS: \n",stdout);
for(i=0; i<=iterator; i++)
{
find_status(i);
printf("%d %d %s %s\n", processList[i].ppid,processList[i].ppgid,processList[i].prog,processList[i].status);
}
return 0;
}
When I am iterating the process list to print with upper code I also call find_status function which is:
void find_status(int current)
{
pid_t w;
int status;
char stat[30];
w=waitpid(processList[current].ppid, &status, WNOHANG | WUNTRACED | WCONTINUED);
switch(w){
case -1:
strcpy(stat, "No child process");
break;
case 0:
strcpy(stat,"running");
default:
if (WIFEXITED(status)!=0) {
sprintf(stat, "exit(%d)", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)!=0) {
sprintf(stat, "signaled(%d)", WTERMSIG(status));
} else if (WIFSTOPPED(status)!=0) {
strcpy(stat,"stopped");
}
break;
}
strcpy(processList[current].status, stat);
}
By the way iterator variable is global variable which holds the index of the last element in the process list. Process list is also global variable. So, where is my mistake in the code why it is not displaying exited and stopped process' status? Thank you.
Impossible to be sure since you haven't shown all the code, but I speculate that you've already waited for those processes.
In your fork code, the parent branch (default case) has a waitpid call. Is that code being executed? Once you have successfully waited for a child process (at least one that has in fact exited), it will be removed from the kernel process table, and you can't (successfully) later call waitpid again: it no longer exists.
To discover what exactly is going on, you should print the errno value when waitpid fails (even better, print strerror(errno) so that you don't have to go lookup the errno value). This will tell you exactly why waitpid failed, not just that it failed.

Multiple execlp not working

I need some help here. I need to execute all three execlp() once I run the program but what happen is that only case 0 is executed.I changed pid to 1 and case1 gets executed and so on. Tried putting it in a for loop but does not work. I changed break to continue but still the same - only one process is executed. Any suggestions?
main(){
pid_t pid;
pid= fork();
int i;
if(pid==0){
for (i=0; i<3; i++){
switch (i){
case 0:
execlp("/bin/cat", "cat", "wctrial.txt", NULL);
break;
case 1:
execlp("/bin/mkdir", "mkdir", "mydirectory", NULL);
break;
case 2:
execlp("/bin/wc", "wctrial.txt", NULL);
break;
}
}
}else{
wait(NULL);
printf("Child process completed!");
exit(0);
}
}
According to man execlp:
The exec() family of functions replaces the current process image with a new process image.
(emphasis is mine)
Therefore, once you called successfully execlp, the process doesn't re-execute the old code.
case 0:
execlp("/bin/cat", "cat", "wctrial.txt", NULL);
/* shouldn't go here */
break;
If you want to execute the three programs, you can create three processes. For instance (loops unrolled):
pid_t son;
son = fork();
if (son == -1) /* report */
else if (son == 0) execlp("/bin/cat", "cat", "wctrial.txt", NULL);
else wait(NULL);
son = fork();
if (son == -1) /* report */
else if (son == 0) execlp("/bin/mkdir", "mkdir", "mydirectory", NULL);
else wait(NULL);
/* ... */
See also Kirilenko's answer. The solution is to use system(..) instead of execlp(..).
Man page here.

Waiting for all child processes to be created - C

Let's suppose I have a code like the following
switch (fork()) {
case -1:
//error checking
break;
case 0:
//child code
break;
default:
int i;
for (i = 0; i < n; i++) {
switch (fork()) {
case -1:
//error checking
break;
case 0:
//exec
break;
default:
//parent that waits for all childs to be created
break;
}
}
}
How do I make the second parent process wait for all the other processes to be created exactly ...
I was told I had to make a loop but I don't know how to implement it exactly. Supposing there are n child processes.
I think you may have misunderstood the requirement slightly. The term 'second parent' doesn't make a lot of sense to me.
What makes most sense as a requirement is:
Parent process launches N children.
Each child does its appropriate stuff.
The parent process must then wait for all N children to complete.
Then it can report its own completion (or get on with other work, or ...).
In outline, you would then have:
int pid;
for (int i = 0; i < N; i++)
{
switch (pid = fork())
{
case 0:
be_childish(i);
/*NOTREACHED*/
break;
case -1:
// Print error report
break;
default:
printf("Started PID %d\n", pid);
break;
}
}
int status;
while ((pid = wait(&status)) > 0)
{
printf("PID %d exited (status 0x%.4X)\n", pid, status);
}
printf("All done!\n");
Note the /*NOTREACHED*/ comment. I assume that the child process exits from within the be_childish() function. The code could ensure no damage by including an exit(1); or perhaps _exit(1); or _Exit(1);. It is rather important that a child process does not continue the loop.

Resources