Fork and Execlp - c

I trying a program with fork and execlp where parent address space is replaced with "ls" command.
#include<stdio.h>
main()
{
int pid,j=10,fd;
pid=fork();
if(pid==0)
{
printf("\nI am the child\n");
execlp("/bin/ls","ls",NULL);
printf("\nStill I am the child\n");
}
else if (pid > 0)
{
printf("\n I am the parent\n");
wait();
}
}
When I execute the program the last line of child
printf("\nStill I am the child\n");
is not printed. Why?

exec family functions do not return when successful.
http://pubs.opengroup.org/onlinepubs/009604499/functions/exec.html
The exec family of functions shall replace the current process image with a new process image. The new image shall be constructed from a regular, executable file called the new process image file. There shall be no return from a successful exec, because the calling process image is overlaid by the new process image.
If one of the exec functions returns to the calling process image, an error has occurred; the return value shall be -1, and errno shall be set to indicate the error.

exec functions will not merely execute your command. They will actually replace the execution context of the process by your selected executable (in your case /bin/ls).
In other words, since the ls function ends by terminating its process (thorugh 'exit' or returning the main function or whatever), your child process will be killed at the end of the execution of ls.
You can actually use this printf call to print some errors, for instance:
if(pid==0)
{
printf("\nI am the child\n");
execlp("/bin/ls","ls",NULL);
printf("\nError: Could not execute function %s\n", "/bin/ls");
_exit(0); //make sure you kill your process, it won't disappear by itself.
}

The reason is simple : The exec() functions only return if an error has have occurred. For the same refer man pages of exec() functions.
What exactly is happening when exec() functions are called :
execl() does not create a new process - it modifies the
VADS and associated contents - in addition, execution context
is also modified.
old execution context is no longer used - a new execution context is created.
a new, fresh context is created for the newly loaded application and
control is passed to the scheduler- scheduler resumes the same child
process with the newly available execution context - using this, a jump
is executed to the entry point of the new application, in user-space -
the new application starts executing in the same child process.
system stack is overwritten with new hw context for
resuming the main() of the new program in user-space.
execution context and code/data/heap/stack of old application in the
child process are completely destroyed - no longer available.
only time execve() or execl() will return to the same application/code
of the current process is when execve() or execl() fails to load
a new application in the current process - meaning, the only time
execv()/execvl() or family of calls will return is when there
is error in completing execv()/execl()/family of calls.
Note: you must validate the return value of exec() family system call APIs for
errors / error codes - based on the error/error codes,
you may terminate the current process or take some other
action.

after the function execlp() does not get executed as per the documentation of execlp
hence your printf() statement "Still I'm the child" does not get executed ...!!

You are taking process id as int type but actually , to store process id you should use pid_t
When you use exec family function the entire address space of called process replaces the calling process. So,now the last printf statement is not there in the new process , actually even the process id of the process is also not changed

Related

How to get the return value of a child process in C after parent process ends?

I'm new so bear with me. I compiled an exe from a c file which uses fork(). I have the parent process return a value, let's say "A," and the child process returns "B." When the program ends and I "echo $?" the last return, I get "A."
I do have waitpid() implemented for the child to finish. But is there a way to output "B"(whatever that value is) from the shell after the process has finished?

what's the difference between all fork function return values?

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#define LG_LIGNE 256
int main(void){
char ligne[LG_LIGNE];
while(1){//afficher un symbole d'invite(prompt)
fprintf(stderr, "-->");
//lire une ligne de commandes
if(fgets(ligne,LG_LIGNE,stdin)==NULL)
break;
//supprimer le retour chariot final
ligne[strlen(ligne)-1]='\0';
//lancer un processeur
if(fork()==0){
//processus fils
//executer la commande
execlp(ligne, ligne, NULL);
//msg d'erreur si on echoue
perror(ligne);
exit(EXIT_FAILURE);
}
else{//process père
//attendre la fin de son fils
waitpid(-1,NULL,0);
//et reprendre la boucle
}
}
fprintf(stderr,"\n");
return EXIT_SUCCESS;
}
I understand what each return value means but I didn't quite understand it here, what being in the child/father process technically means, and I couldn't see the use of waitpid, what will happen if I didn't use it?
Fork() creates a copy of the process's memory and state, and spawns a child process to run in it. Both the child and parent run in their own process, with their own copy of the address space and virtual memory. So how can you tell which is the parent and which is the child since both tasks resume execution after the fork() call with identical state and memory?
You can tell which is the parent and which is the child based on the return code from fork(). If the return value is zero, then you are the child process, and this process usually tests the return code for zero, and branches off to do the child's work.
Meanwhile, the parent also returns from the same fork() call but with the return code set to the process id of its child (which is non-zero). The parent may choose to save this process id, but it is only really useful if the parent is creating multiple children and wants to track them through their process ids.
Before the parent finishes, it typically waits for its child processses to complete first. This is what the waitpid call does. You can pass in the child process's id, in which case the waitpid call does not return until that child process completes. Passing -1 into waitpid tells it to wait until ONE of your child tasks completes.
The return code from waitpid contains the process id of the child task that completed. For more information, this has already been answered here https://stackoverflow.com/a/21249082/6693299
For a complete explanation of what your program does, read the excellent detailed description by H.S. that answers the same question here https://stackoverflow.com/a/46741392/6693299
Your first question -
what being in the child/father process technically means
The fork() creates a child process by duplicating the calling process.
The process that invoked fork() is the parent process and the newly created process is the child process.
So fork() splits a process in two, and returns 0 to the child process and the PID of the child process to the parent process, or -1 if the fork failed.
The child process and the parent process run in separate memory spaces. At the time of fork() both memory spaces have the same content.
There is concept called Copy on Write, its good to have knowledge about it-
Copy on Write is an optimization where the page tables are set up so that the parent and child process start off sharing all of the same memory, and only the pages that are written to by either process are copied when needed.
If the process does not modify any memory and immediately executes a new process, replacing the address space entirely. Thus, it would be wasteful to copy all of the process's memory during a fork, and instead the copy-on-write technique is used.
For example, in your program you have immediately calling execlp after fork:
if(fork()==0){
//processus fils
//executer la commande
execlp(ligne, ligne, NULL);
Your second question -
I couldn't see the use of waitpid, what will happen if I didn't use it?
To explain this, I have modified your program and added statement to print parent and child process pid's and commented out this statement-
waitpid(-1,NULL,0);
Output of program is -
parent process pid : 22325
-->ls
child process pid : 22326
< here the output of ls command >
Now, if we see the output of ps command with grep'ed parent process id -
# ps -eaf | grep 22325
root 22325 21555 0 10:39 pts/4 00:00:00 ./a.out
root 22326 22325 0 10:39 pts/4 00:00:00 [ls] <defunct>
root 22339 21644 0 10:39 pts/5 00:00:00 grep 22325
Here, in the output the first column is UID, second is PID and third id PPID (parent pid).
You can see the child process (pid - 22326) is marked as < defunct >.
A "defunct" process (also referred to as "zombie" process) is a process which has finished execution, it will have an exit status to report to its parent process. Because of this last little bit of information, the process will remain in the operating system’s process table as a zombie process, indicating that it is not to be scheduled for further execution, but that it cannot be completely removed (and its process ID cannot be reused) until it has been determined that the exit status is no longer needed.
And here comes the use of waitpid() -
The wait() and waitpid() functions shall obtain status information pertaining to one of the caller's child processes.
waitpid() suspends the calling process until the system gets status information on the child. If the system already has status information on an appropriate child when waitpid() is called, waitpid() returns immediately. waitpid() is also ended if the calling process receives a signal whose action is either to execute a signal handler or to end the process.
For other details on waitpid() like syntax (status, options) and return value you can check its man page.
If I uncomment the waitpid() in your program and compile and run it, the output -
parent process id : 23069
-->ls
child process id : 23070
<here the output of ls command>
Now, if we see the output of ps command with grep'ed parent process id -
# ps -eaf | grep 23069
root 23069 21555 0 10:51 pts/4 00:00:00 ./a.out
root 23108 21644 0 10:51 pts/5 00:00:00 grep 23069
There is no zombie process. The child process which runs ls command has been finished and the parent process read its exit status.
Hope this answer your both the questions.

execvp/fork -- how to catch unsuccessful executions?

Right now I'm writing a C program that must execute a child process. I'm not doing multiple child processes simultaneously or anything, so this is fairly straightforward. I am definitely executing the built-in shell programs (i.e. things like cat and echo) successfully, but I also need to be able to tell when one of these programs fails to execute successfully. I'm trying this with the following simplified code:
int returnStatus; // The return status of the child process.
pid_t pid = fork();
if (pid == -1) // error with forking.
{
// Not really important for this question.
}
else if (pid == 0) // We're in the child process.
{
execvp(programName, programNameAndCommandsArray); // vars declared above fork().
// If this code executes the execution has failed.
exit(127); // This exit code was taken from a exec tutorial -- why 127?
}
else // We're in the parent process.
{
wait(&returnStatus); // Wait for the child process to exit.
if (returnStatus == -1) // The child process execution failed.
{
// Log an error of execution.
}
}
So for example, if I try to execute rm fileThatDoesntExist.txt, I would like to consider that a failure since the file didn't exist. How can I accomplish this? Also, while that execvp() call successfully executes built-in shell programs, it doesn't execute programs in the current directory of the executable (i.e. the program that this code is running inside of); Is there something else that I have to do in order to get it to run programs in the current directory?
Thanks!
This is a classic problem with a very elegant solution. Before forking, create a pipe in the parent. After fork, the parent should close the writing end of the pipe, and block attempting to read from the reading end. The child should close the reading end and set the close-on-exec flag, using fcntl, for the writing end.
Now, if the child calls execvp successfully, the writing end of the pipe will be closed with no data, and read in the parent will return 0. If execvp fails in the child, write the error code to the pipe, and read in the parent will return nonzero, having read the error code for the parent to handle.
wait(2) gives you more than just the exit status of the child process. In order to get the real exit status, you need to use the WIFEXITED() macro to test if the child exited normally (as opposed to abnormally via a signal etc.), and then use the WEXITSTATUS() macro to get the real exit status:
wait(&status);
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
// Program succeeded
}
else
{
// Program failed but exited normally
}
}
else
{
// Program exited abnormally
}
In order for execvp(3) to run a program in the current directory, you either need to add the current directory to your $PATH environment (generally not a good idea), or pass it the full path, e.g. use ./myprogram instead of just myprogram.
In terms of failure detection, if an exec() function replaces the current process with a new one, then the current process is gone; it doesn't matter if the executed program decides that what it has been asked to do is a success or failure. However, the parent process from before the fork can discover the child's exit code which would likely have the command success/failure information.
In terms of finding executables, execvp() duplicates the action of the shell, searching the current path. If it is not finding executables in the working directory, it is likely that directory is not in the search path (or the files are not actually executable). You can try specifying them by a full path name.
If you simply want to run a command and wait for the result, you might want to use the system() function which handles this for you, instead of building it yourself with fork/exec.

killing a child process if execvp fails

I have a C program similar in structure to: http://www.csl.mtu.edu/cs4411/www/NOTES/process/fork/exec.html (that is, it's a shell that runs one command with execvp when entered and loops indefinitely until "exit" is entered).
What is the best way to kill a child process immediately if an unrecognized command is passed to execvp? For example, if I typed "ehco" instead of "echo" how could I quickly kill this child process? I've noticed if I enter a command not in my PATH and then immediately type exit it doesn't exit until I type exit again later.
The linked example already does the right thing: the child should unconditionally call _exit() after execvp(). The execvp() will only return if it fails.
(In other words, you don't kill the child process from the parent; you wait for the child process to exit, and write the child process so that it kills itself if the exec fails).

capturing commandline output directly in a buffer

I want to execute a command using system() command or execl and want to capture the output directly in a buffer in C. Is ther any possibility to capture the output in a buffer using dup() system call or using pipe(). I dont want to use any file in between using mkstemp or any other temporary file. please help me in this.Thanks in advance.
I tried it with fork() creating two process and piping the output and it is working.However I dont want to use fork system call since i am going to run the module infinitely using seperate thread and it is invoking lot of fork() and system is running out of resources sometimes after.
To be clear about what i am doing is capturing an output of a shell script in a buffer processing the ouput and displaying it in a window which i have designed using ncurses.Thankyou.
Here is some code for capturing the output of program; it uses exec() instead of system(), but that is straightforward to accomodate by invoking the shell directly:
How can I implement 'tee' programmatically in C?
void tee(const char* fname) {
int pipe_fd[2];
check(pipe(pipe_fd));
const pid_t pid = fork();
check(pid);
if(!pid) { // our log child
close(pipe_fd[1]); // Close unused write end
FILE* logFile = fname? fopen(fname,"a"): NULL;
if(fname && !logFile)
fprintf(stderr,"cannot open log file \"%s\": %d (%s)\n",fname,errno,strerror(errno));
char ch;
while(read(pipe_fd[0],&ch,1) > 0) {
//### any timestamp logic or whatever here
putchar(ch);
if(logFile)
fputc(ch,logFile);
if('\n'==ch) {
fflush(stdout);
if(logFile)
fflush(logFile);
}
}
putchar('\n');
close(pipe_fd[0]);
if(logFile)
fclose(logFile);
exit(EXIT_SUCCESS);
} else {
close(pipe_fd[0]); // Close unused read end
// redirect stdout and stderr
dup2(pipe_fd[1],STDOUT_FILENO);
dup2(pipe_fd[1],STDERR_FILENO);
close(pipe_fd[1]);
}
}
A simple way is to use popen ( http://www.opengroup.org/onlinepubs/007908799/xsh/popen.html), which returns a FILE*.
You can try popen(), but your fundamental problem is running too many processes. You have to make sure your commands finish, otherwise you will end up with exactly the problems you're having. popen() internally calls fork() anyway (or the effect is as if it did).
So, in the end, you have to make sure that the program you want to run from your threads exits "soon enough".
You want to use a sequence like this:
Call pipe once per stream you want to create (eg. stdin, stdout, stderr)
Call fork
in the child
close the parent end of the handles
close any other handles you have open
set up stdin, stdout, stderr to be the appropriate child side of the pipe
exec your desired command
If that fails, die.
in the parent
close the child side of the handles
Read and write to the pipes as appropriate
When done, call waitpid() (or similar) to clean up the child process.
Beware of blocking and buffering. You don't want your parent process to block on a write while the child is blocked on a read; make sure you use non-blocking I/O or threads to deal with those issues.
If you are have implemented a C program and you want to execute a script, you want to use a fork(). Unless you are willing to consider embedding the script interpreter in your program, you have to use fork() (system() uses fork() internally).
If you are running out of resources, most likely, you are not reaping your children. Until the parent process get the exit code, the OS needs keeps the child around as a 'zombie' process. You need to issue a wait() call to get the OS to free up the final resources associated with the child.

Resources