How can I know when has execvp finished? - c

Im currently working on a class work in the Introduction to Operative Systems course, and we are asked to build a simple command interpreter in C.
For this interpreter, if we put '&' before a comand we execute it in the background and if we put ';' we execute it as usual (in the foreground). We are using fork and process management functions, so when we are in the parent process, if we typed ';' we simply wait until the child process (executing the comand, for example pwd), and if we put '&' we just dont wait and we break from the switch-case on where we are.
this is the code:
err = get_arguments(buf, n, arg, MAXARG); //if we type & sleep 5 we get the sleep 5 in the arg
id = fork();
switch (id){
case -1:
errore("fork");
break;
case 0: /* child process */
/* execute the command using execvp */
pid = getpid();
pid_parent = getppid();
if(buf[0] == '&') {
printf("-U------- Child: just created (PID %d - Parent PID: %d): now executing'%s ...' in the background...\n",
pid, pid_parent, arg[0]);
} else if(buf[0] == ';') {
printf("-U------- Umea naiz: sortu berria (PID %d - Parent PID: %d): orain '%s ...' exekutatzera aurreko planoan...\n",
pid, pid_parent, arg[0]);
}
execvp(arg[0], arg);
printf("-U------- Child (PID %d): i'm here only if an error has ocurred\n",
pid);
errore("exec");
break;
default: /* parent */
pid_child = id;
pid = getpid();
printf("-G------- Parent (PID %d): child process '%s ...' (PID %d) started \n",
pid, arg[0], pid_child);
if(buf[0] == '&') { // if we want to execute in the background
break; //break and don't wait for the child to stop
} else if(buf[0] == ';') { // if it's in the foreground
/* wait until child finishes */
pid_finished = wait(&status);
if ( pid_finished != pid_child) {
errore("wait");
} else {
if (WIFEXITED(status)){ // get the return code
child_return_code = WEXITSTATUS(status);
printf("-G------- Parent (PID %d): a child process (PID %d) has finished with the %d return code\n",
pid, pid_finished, child_return_code);
}
};
}
break;
}
}
for (n = 0; n < BUFSIZE; n++) buf[n] = '\0';
write(1, "prompt$ ", 9); //write the prompt again
When I write ; pwd for example, it obviously works great. How ever, i want the parent process to know when a child process that has been executed in the background, has finished. And when if happens, print it's id and the return code.
I don't know how to do this, because when execvp is used, the code that comes after it only gets executed when an error happens. So how can I know when this child process has finished in the background?
My code was in spanish so maybe something is spelled wrong, I tried my best.
Thank you so much!

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.

Background process killing with Parent process in C

I have the following code in my main function
pid_t pid;
pid = fork(); //Two processes are made
if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
int status;
if (waitpid(pid, &status, 0) == pid && WIFEXITED(status))
{
printf("Exitstatus [");
for (int i = 0; i < noOfTokens; i++)
{
printf("%s ", commands[i]);
}
printf("\b] = %d\n", WEXITSTATUS(status));
}
}
else if (pid == 0) //Child process. Executes commands and prints error if something unexpected happened
{
if (runBGflag==1) insertElement(getpid(),ptr);
execvp(commands[0], commands);
printf ("exec: %s\n", strerror(errno));
exit(1);
}
In a nutshell, a child process is made and if the runBackGround flag is set, the parent process will not wait for the child process to exit, but rather continue running. If a background process is made, the PID of the background process is stored in a list. At a later point, this function is called
void delete_zombies(void)
{
pid_t kidpid;
int status;
char buffer[1337];
while ((kidpid = waitpid(-1, &status, WNOHANG)) > 0)
{
removeElement(kidpid,buffer,1337);
printf("Child %ld terminated\n", kidpid);
printf("its command was %s\n",buffer);
}
}
This function simply checks if any child processes have died and in that case deletes them. It will then search for the childs PID in the list, remove it and print it out.
The problem is, the delete_zombies function will find that a child has died and will then try to remove it from the list, but it only finds an empty list, as if the child process never inserted its PID into the list.
This is really strange, because delete_zombies only finds a dead child process, when there was one created with the background flag set, so we know insertElement must have been called, but strangely when the parent checks in the list nothing is there
Is the cause for that, that child process and parent process have seperate lists, or is the PID maybe wrong?

Terminating background process on next foreground process

I'm having trouble looping through my shell and terminating a background process. I can't figure out the proper usage of waitpid after looking at a few questions like this for hours, so I'll describe my problem.
I need to run a sleep command for a few seconds, save that pid, run an ls command and keep my shell running while the sleep runs in the background. The next foreground process that executes, (ls) afterwards will clean up the background process after executing. So I wrote something like this
while(1) {
//Prompt user input
//Get input from user
//Parse & tokenize
//Determine if this process is to run in the background
if((strcmp(symbols[0], "&") == 0) || (strcmp(symbols[0], "&\n") == 0)) {
backgroundFlag = true;
} else {
backgroundFlag = false;
foregroundFlag = true;
}
pid_t childPid = fork();
if(backgroundFlag == true && childPid != 0) {
backgroundPid = childPid;
}
//Determine the process
if(childPid == 0) {
//Run in the background
if(backgroundFlag == true) {
printf(">>>IN CHILD(background): backgroundPid = %d\n", getpid());
backgroundPid = getpid();
} else {
foregroundFlag = true;
//debug
printf("\n>>>IN CHILD: pid = %d\n", getpid());
}
//Save this for child pid
childPid = getpid();
//Execute the command
execvp(command1[0], args1-1);
} else {
printf("\n>>>In Parent: pid = %d\n", getpid());
//this is the parent
if(backgroundFlag == false && backgroundPid == 0) {
wait(NULL);
foregroundFlag = false;
printf("\n...Child reaped: pid = %d\n", childPid);
} else if (backgroundFlag == true && backgroundPid > 0) {
pid_t return_pid = waitpid(backgroundPid, 0, WNOHANG);
if(return_pid == backgroundPid) {
printf("\n...background child reaped: pid = %d\n", backgroundPid);
backgroundPid = 0;
}
}
}
}
but the shell just runs the process, hangs and never gives me a reaped message. In that case when I hit enter, it will restart the parent to the top of the loop, but never reap the background process. What exactly am I doing wrong? I tried looping with backgroundPid and -1, WNOHANG, and a few other flags, but I just keep getting the same thing - either it will run and hang never prompting again for input until hitting ENTER, or it will run first and not allow any other foreground processes to go.
Either way it never reaps the background.
This is what i get as result of doing sleep(3) & (it's a custom sleep that echoes), then ls before completion, then ls again
progShell> ./customecho testing &
COMMAND #1: ./customecho
...The above command will be executed in background
>>>In Parent: pid = 7774
Freeing input...
backgroundPid - 7793
looping...
>>>IN CHILD(background): backgroundPid = 7793
progShell> ls
COMMAND #1: ls
>>>In Parent: pid = 7774
Freeing input...
backgroundPid - 7793
looping...
progShell>
>>>IN CHILD: pid = 7794
Makefile progShell progShell.c customecho.c customecho
customecho PID=7793: testing
Then only after hitting ENTER do i get this...
no termination...
Freeing input...
backgroundPid - 7793
looping...
Edit: played around for a while, and managed to fix a bit of the code -
while(1) {
//Determine if this process is to run in the background
if((strcmp(symbols[0], "&") == 0) || (strcmp(symbols[0], "&\n") == 0)) {
backgroundFlag = true;
backgroundFinishedFlag = false;
} else {
backgroundFlag = false;
foregroundFlag = true;
}
printf("Background finished: %d\n", backgroundFinishedFlag);
printf("Background flag: %d\n", backgroundFlag);
//Create a new process
pid_t childPid = fork();
//Determine if this process is to run in the background, and assign appropriately per process.
if(backgroundFlag == true && childPid == 0) {
backgroundPid = getpid();
printf("Background process (PID=%d)\n", backgroundPid);
} else if(backgroundFlag == true) {
backgroundPid = childPid;
}
//Determine the process we are in - this is child
if(childPid == 0) {
//debug
//Run in the background
if(backgroundFlag == true) {
printf(">>>IN CHILD(background): backgroundPid = %d\n", getpid());
} else {
//Run in the foreground
printf("\n>>>IN CHILD: pid = %d\n", getpid());
}
//Save this for child pid
childPid = getpid();
//Execute the command
execvp(command1[0], args1-1);
} else {
//this is the parent
//debug
printf("\n>>>In Parent: pid = %d\n", getpid());
printf("\t this parent's child is - %d and background id is %d\n", childPid, backgroundPid);
//There is a foreground process - execute it first.
if(foregroundFlag) {
waitpid(childPid, NULL, 0);
foregroundFlag = false;
printf("\n...Child reaped: pid = %d\n", childPid);
}
//Determine if there is a background process to be reaped
if(backgroundFinishedFlag == false && backgroundPid > 0 && (waitpid(backgroundPid, NULL, WNOHANG) > 0)) {
printf("\nTerminating background process now!\n");
backgroundFinishedFlag = true;
backgroundPid = 0;
}
}
}
It terminates child in background upon completion of next foreground process...
Just one problem is, when the background process is finished, I would like it to prompt for input again - is there any signal catch I can use to do this? Ex... after the sleep prints, it overwrites the "shellProg> " prompt looking like input and leaves a blank space to use for input.
shellProg> sne PID=9886: testing
(i hit enter or any input here otherwise no prompt)
Freeing input...
backgroundPid - 9886
looping...
shellProg>

Why is execvp() executing twice using fork()?

I am implementing a shell.
When attempting a command other than changing directories, execvp() runs, the child terminates and a new child is created. When I change directories, the child does not terminate and a new child is created. Here is a sample of my code:
for(;;) {
printf("bash: ");
parse();
...
pid_t pid = fork()
if (pid == 0)
if (!strcmp(line[0], "cd"))
if (!line[1]) (void) chdir(getenv("HOME"));
else (void) chdir(line[1]);
else execvp(line[0], line);
...
if (pid > 0) {
while (pid == wait(NULL));
printf("%d terminated.\n", pid);
}
}
cd ../; ls; runs correctly, except I have to Ctrl+D twice to end the program.
Though, if I pipe the same information (ie. mybash < chdirtest), it runs correctly once, terminates the child, runs again except in the original directly, then terminates the final child.
cd should not be invoked through a child process, the shell itself should change its current directory (that's the property of an internal command: modify the process of the shell itself).
A (primitve) shell should looks like:
for(;;) {
printf("bash: ");
parse();
// realize internal commands (here "cd")
if (!strcmp(line[0], "cd")) {
if (!line[1]) (void) chdir(getenv("HOME"));
else (void) chdir(line[1]);
continue; // jump back to read another command
}
// realize external commands
pid_t pid = fork()
if (pid == 0) {
execvp(line[0], line);
exit(EXIT_FAILURE); // wrong exec
}
// synchro on child
if (pid > 0) {
while (pid == wait(NULL));
printf("%d terminated.\n", pid);
}
}

Parent/Child process print

I'm writing a C program that creates a child process. After creating the child process, the parent process should ouput two messages: "I am the parent" then it should print "The parent is done". Same for child process "I am child" and "The child is done". However I want to make sure, the second message of the child is always done before the second message of the parent. How can I achieve to print "The child is done" and "The parent is done" rather than printing their pid?
#include <unistd.h>
#include <stdio.h>
main()
{
int pid, stat_loc;
printf("\nmy pid = %d\n", getpid());
pid = fork();
if (pid == -1)
perror("error in fork");
else if (pid ==0 )
{
printf("\nI am the child process, my pid = %d\n\n", getpid());
}
else
{
printf("\nI am the parent process, my pid = %d\n\n", getpid());
sleep(2);
}
printf("\nThe %d is done\n\n", getpid());
}
You could have a flag variable, that is set in the parent, but then the child clears it. Then simply check for that for the last output.
Something like
int is_parent = 1; // Important to create and initialize before the fork
pid = fork();
if (pid == -1) { ... }
if (pid == 0)
{
printf("\nI am the child process, my pid = %d\n\n", getpid());
in_parent = 0; // We're not in the parent anymore
}
else { ... }
printf("\nThe %s is done\n\n", is_parent ? "parent" : child");
Call wait(2) in the parent process for the child to complete.
else
{
wait(0);
printf("\nI am the parent process, my pid = %d\n\n", getpid());
}
You should check if wait() succeeds and main()'s return type should be int.

Resources