Child process cannot read after the exiting of parent process - c

After forking and executing a child program by function execvp(), the parent process exit. However this cause the function fgets() in the child process return immediately without waiting for input from stdin.
I guess the exiting of the parent process will send some signals to the child process which make the fgets() function return. Could some one explain more for me?
Code of child program:
/* cc child.c -o child */
int main () {
char buffer[10];
fgets(buffer, 10, stdin);
printf("This is what child program read:\n%s", buffer);
}
Code of parent program:
/* cc parent.c -o parent */
int main (int argc, char **argv) {
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0) {
execvp(*(argv+1), argv+1);
}
else {
// while(1); if while(1) or wait() is used, child process can wait for input
exit(1);
}
}
In zsh shell:
zsh>: ./parent ./child
zsh>: This is what child program read: // read nothing and print nothing

The terminal is controlled by the foreground process group. When the shell invokes the parent, it makes the parent the leader of the foreground process group. The child inherits that group and has access to the terminal.
However, when the parent exits, the shell takes back control of the terminal and becomes the leader of the foreground process group. The child is no longer in the foreground process group, so it has no access to the terminal. In this case, fgets() will return NULL and an error code will be set.
If you take input from someplace other than the terminal, such as a pipe, then you'll see that the program works as expected. For example:
$ echo test | ./parent ./child
So this problem only occurs when input comes from the terminal.
In retrospect, answering this question would have been more straighforward if the fgets error code was checked. In this case, fgets returns null, but then you need to check feof() and/or ferror() to determine if this means that the end of the file was reached (stdin closed) or there was an error. In this case, the NULL meant there was an EIO error.
Earlier wrong answer (see comment thread for explanation, leaving this here because of the lengthy discussion):
When you fork, the child process inherits stdin etc. When the parent process exits, it closes stdin, so the child tries to read from a closed descriptor and gets nothing. By adding the call to wait(), you keep stdin open and this allows your child program to work as expected.

You should check the return value from fgets() before printing buffer. Check if fgets doesnot return NULL, and then only print the buffer.

Related

Child not reading output from another child that put it in the pipe

I've been working on this school assignment forever now, and I'm super close to finishing.
The assignment is to create a bash shell in C, which sounds basic enough, but it has to support piping, IO redirect, and flags within the piped commands. I have it all working except for one thing; the | piping child isn't getting any of the data written to the pipe by the user command process child. If I were to remove the child fork for pipechild, and have everything from if(pipe_cmd[0] != '\0') run as the parent, it would work just fine (minus ending the program because of execlp). If I were to use printf() inside the pipe section, the output would be in the right file or terminal, which just leaves the input from the user command process child not getting to where it needs to be as a culprit.
Does anyone see an issue on how I'm using the pipe? It all felt 100% normal to me, given the definition of a pipe.
int a[2];
pipe(a);
//assume file_name is something like file.txt
strcat(file_name, "file.txt");
strcat(pipe_cmd, "wc");
if(!fork())
{
if(pipe_cmd[0] != '\0') // if there's a pipe
{
close(1); //close normal stdout
dup(a[1]); // making stdout same as a[1]
close(a[0]); // closing other end of pipe
execlp("ls","ls",NULL);
}
else if(file_name[0] != '\0') // if just a bare command with a file redirect
{
int rootcmd_file = open(file_name, O_APPEND|O_WRONLY|O_CREAT, 0644);
dup2(rootcmd_file, STDOUT_FILENO);
execlp("ls","ls",NULL); // writes ls to the filename
}
// if no pipe or file name write...
else if(rootcmd_flags[0] != '\0') execlp("ls","ls",NULL)
else execlp("ls","ls",NULL);
} else wait(0);
if(pipe_cmd[0] != '\0') // parent goes here, if pipe.
{
pipechild = fork();
if(pipechild != 0) // *PROBLEM ARISES HERE- IF THIS IS FORKED, IT WILL HAVE NO INFO TAKEN IN.
{
close(0); // closing normal stdin
dup(a[0]); // making our input come from the child above
close(a[1]); // close other end of pipe
if(file_name[0] != '\0') // if a filename does exist, we must reroute the output to the pipe
{
close(1); // close normal stdout
int fileredir_pipe = open(file_name, O_APPEND|O_WRONLY|O_CREAT, 0644);
dup2(fileredir_pipe, STDOUT_FILENO); //redirects STDOUT to file
execlp("wc","wc",NULL); // this outputs nothing
}
else
{
// else there is no file.
// executing the pipe in stdout using execlp.
execlp("wc","wc",NULL); // this outputs nothing
}
}
else wait(0);
}
Thanks in advance. I apologize for some of the code being withheld. This is still an active assignment and I don't want any cases of academic dishonesty. This post was risky enough.
} else wait(0);
The shown code forks the first child process and then waits for it to terminate, at this point.
The first child process gets set up with a pipe on its standard output. The pipe will be connected to the second child process's standard input. The fatal flaw in this scheme is that the second child process isn't even started yet, and won't get started until the first process terminates.
Pipes have limited internal buffering. If the first process generates very little output chances are that its output will fit inside the tiny pipe buffer, it'll write its output and then quietly terminate, none the wiser.
But if the pipe buffer becomes full, the process will block and wait until something reads from the pipe and clears it. It will wait as long as it takes for that to happen. And wait, and wait, and wait. And since the second child process hasn't been started yet, and the parent process is waiting for the first process to terminate it will wait, in vain, forever.
This overall logic is fatally flawed for this reason. The correct logic is to completely fork and execute all child processes, close the pipe descriptors in the parent (this is also important), and then wait for all child processes to terminate. wait must be the very last thing that happens here, otherwise things will break in various amazing and mysterious ways.

Pipe function in Linux shell write in C

My mini-shell program accepts pipe command, for example, ls -l | wc -l and uses excevp to execute these commands.
My problem is if there is no fork() for execvp, the pipe command works well but the shell terminates afterward. If there is a fork() for execvp, dead loop happens. And I cannot fix it.
code:
void run_pipe(char **args){
int ps[2];
pipe(ps);
pid_t pid = fork();
pid_t child_pid;
int child_status;
if(pid == 0){ // child process
close(1);
close(ps[0]);
dup2(ps[1], 1);
//e.g. cmd[0] = "ls", cmd[1] = "-l"
char ** cmd = split(args[index], " \t");
//if fork here, program cannot continue with infinite loop somewhere
if(fork()==0){
if (execvp(cmd[0],cmd)==-1){
printf("%s: Command not found.\n", args[0]);
}
}
wait(0);
}
else{ // parent process
close(0);
close(ps[1]);
dup2(ps[0],0);
//e.g. cmd[0] = "wc", cmd[1] = "-l"
char ** cmd = split(args[index+1], " \t");
//if fork here, program cannot continue with infinite loop somewhere
if(fork()==0){
if (execvp(cmd[0],cmd)==-1){
printf("%s: Command not found.\n", args[0]);
}
}
wait(0);
waitpid(pid, &child_status, 0);
}
}
I know fork() is needed for excevp in order to not terminate the shell program, but I still cannot fix it. Any help will be appreciated, thank you!
How should I make two children parallel?
pid = fork();
if( pid == 0){
// child
} else{ // parent
pid1 = fork();
if(pid1 == 0){
// second child
} else // parent
}
is this correct?
Yes, execvp() replaces the program in which it is called with a different one. If you want to spawn another program without ending execution of the one that does the spawning (i.e. a shell) then that program must fork() to create a new process, and have the new process perform the execvp().
Your program source exhibits a false parallelism that probably either confuses you or reflects a deeper confusion. You structure the behavior of the first child forked in just the same way as the behavior of the parent process after the fork, but what should be parallel is the behavior of the first child and the behavior of the second child.
One outcome is that your program has too many forks. The initial process should fork exactly twice -- once for each child it wants to spawn -- and neither child should fork because it's already a process dedicated to one of the commands you want to run. In your actual program, however, the first child does fork. That case is probably rescued by the child also wait()ing for the grandchild, but it's messy and poor form.
Another outcome is that when you set up the second child's file descriptors, you manipulate the parent's, prior to forking, instead of manipulating the child's after forking. Those changes will persist in the parent process, which I'm pretty confident is not what you want. This is probably why the shell seems to hang: when run_pipe() returns (the shell's standard input has been changed to the read end of the pipe).
Additionally, the parent process should close both ends of the pipe after the children have both been forked, for more or less the same reason that the children must each close the end they are not using. In the end, there will be exactly one open copy of the file descriptor for each end of the pipe, one in one child and the other in the other. Failing to do this correctly can also cause a hang under some circumstances, as the processes you fork may not terminate.
Here's a summary of what you want the program to do:
The original process sets up the pipe.
The original process forks twice, once for each command.
Each subprocess manipulates its own file descriptors to use the correct end of the pipe as the appropriate standard FD, and closes the other end of the pipe.
Each subprocess uses execvp() (or one of the other functions in that family) to run the requested program
the parent closes its copies of the file descriptors for both ends of the pipe
the parent uses wait() or waitpid() to collect two children.
Note, too, that you should check the return values of all your function calls and provide appropriate handling for errors.

How to retain child process's access to the terminal after the parent closes

I'm trying to write a program that creates a child process and then closes itself. I then need the child to remain connected to the terminal.
The child process is working as intended (waits for terminal input, then prints it out) until the parent closes . When the parent process is terminated the child no longer waits for input and the "fgets error" message is printed out in the terminal indefinitely.
This is strange, as it seems that after the parent terminated the stdin closed but stdout of the child remained connected to the terminal.
int main(){
int pid1;
pid1=fork();
if (pid1==-1){
perror("fork");
exit(EXIT_FAILURE);
}
if (pid1==0){
while(1){
char *data;
char *result;
result=fgets(data,100,stdin);
if (result!=NULL){
printf("%s\n",data);
}
else{
printf("fgets error\n");
sleep(1);
}
}
}
else
{
//The sleep below is for debugging purposes
sleep(5);
exit(EXIT_SUCCESS);
}
}
Is there a way to keep the stdin/stdout of the child process directed from/to the terminal after it's parent has terminated?
Any help/explanation would be greatly appreciated.
As the other answer by #Jonathan-Leffler points out, that is caused by the shell taking ownership of stdin when the parent process finishes. The problem is that, while stdout can be shared by as many process as you want (output is just mixed), stdin cannot be easily shared.
A possible workaround, without modifying your program (let it be ./test) may be:
$ cat | ./test
Now, the shell will wait for cat to finish, but cat will not finish until you type EOF (^D) or writing to stdout fails. But even if your parent process finishes, the duplicated fd in the child process will continue to consume the catted data.

Why does fflush() affect the output of forked processes?

I'm trying to learn UNIX programming and came across a question regarding fork() and I couldn't interpret the output of the 2 programs below.
I understand that fork() creates an identical process of the currently running process, but where does it start? For example, if I have these two programs below, what will be the output and how does it work ?
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main (int argc, char **argv)
{
int retval;
printf ("This is most definitely the parent process\n");
// now here fork will create a child process
// i need to know from which line child process starts execution
retval = fork ();
printf ("Which process printed this?\n");
return (0);
}
What will be the difference in above program and the one below with
respect to child process execution:
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main (int argc, char **argv)
{
int retval;
printf ("This is most definitely the parent process\n");
fflush (stdout);
// how does fflush change the output of above program and why ?
// even though no string operations are being used
retval = fork ();
printf ("Which process printed this?\n");
return (0);
}
I think both of them should print :
This is most definitely the parent process
Which process printed this?
Which process printed this?
But the first one is printing:
This is most definitely the parent process
Which process printed this?
This is most definitely the parent process
Which process printed this?
I understand that fork() creates an identical process of the currently
running process, but where does it start?
If fork(2) is successful (i.e. does not return -1), it starts in the line that calls fork(2). fork(2) returns twice: it returns 0 in the child, and a positive number C in the parent, where C is the process ID of the newborn child.
The reason you're seeing This is most definitely the parent process twice is related to stdio's buffering. Stdio buffers output in userspace buffers that are flushed only when some condition occurs (for example, the buffer becomes full). The buffering mode dictates when and how buffers are flushed.
Usually, if output is being written to an interactive device such as a terminal (or pseudoterminal), stdio is line-buffered, which means that the buffers are flushed when a newline is found or fflush(3) is called.
OTOH, if output is redirected to a file or other non-interactive devices (for example, output is redirected to a pipe), stdio is fully-buffered, which means that buffers are flushed only when they become full or fflush(3) is called.
So, without fflush(3), executing the code in a terminal device will print this:
This is most definitely the parent process
Which process printed this?
Which process printed this?
Which is expected. However, if you pipe it through cat(1), you will see this (or some other variant, depends on execution order):
This is most definitely the parent process
Which process printed this?
This is most definitely the parent process
Which process printed this?
This is because output is fully buffered when redirected to a pipe. The string This is most definitely the parent process isn't enough to fill and flush the buffer, so when the parent forks, the child (which gets a copy of the parent's memory space) will get a copy of the output buffer, which already contains the string This is most definitely the parent process. So both processes end up printing that string.
If you always call fflush(3) before forking, this won't happen because the buffer is empty when the parent's memory space is copied to the child.
Execution will continue at (or just after) the fork call. You can use the return value to check if you're the parent or the child process:
RETURN VALUE
On success, the PID of the child process is returned in the parent, and 0 is returned
in the child. On failure, -1 is returned in the parent, no child process is created,
and errno is set appropriately.
(Source: man fork)
For example, if you have the following program:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv) {
printf("Foo.\n");
int retval = fork();
printf("Bar from %s (%d).\n", (retval == 0) ? "child" : "parent", retval);
return 0;
}
The output would be something like:
Foo.
Bar from parent (18464).
Bar from child (0).
...assuming output is line buffered.
when calling fork() there are three possible return conditions.
Do read the man page for fork()
the three return conditions are
-1 -- the fork() failed
0 -- the child is executing
some positive number -- the pid of the child, the parent is executing
The code needs to be something like this:
pid_t pid;
pid = fork();
if ( 0 > pid )
{ // then handle error
}
else if ( 0 == pid )
{ // then child executing
}
else // if ( 0 < pid )
{ // then parent executing
}

How should parent close pipe file descriptor when child process exits

I am creating a TCP service that forks a new process each time a client connects. Before the fork I set up a pipe so the child can send statistics gathered during the connection back to the parent. The parent closes the writing end and the child closes the reading end, and the parent maintains an array of reading-end file descriptors, one per child.
I am not sure what to do with these file descriptors when the child finishes with the connection and exits. Does the child need to notify the parent via the pipe that it is about to exit so the parent can close the pipe? Or can the parent detect the broken pipe automatically after the child exits and close it?
The code in the parent program is running a loop with select() detecting activity on the listening socket and on the read ends of the children's pipes. Each child may send multiple messages to the parent as it runs.
In general, what should the parent process do with a pipe file descriptor when a child exits?
First pass: before it was clear that there was a loop using select() and that children sent multiple messages.
If the parent process maintains an array of file descriptors, it needs to also associate each file descriptor with a child process. If the children send a single small statistics message before they die, then when the main program waits for dead children, it knows which child died, so it can then close the file descriptor for the child that it just spotted dieing (after making sure the pipe is empty by doing one or more final reads).
An alternative mechanism uses select() or poll() or a related function that reports when a read operation on a file descriptor would not hang. When it detects EOF (zero bytes read) from a pipe, it knows the child died. However, this is probably fiddlier to deal with.
It isn't entirely clear from your question whether there's a single message from the child process as it exits, or whether there is a 'stream of consciousness' statistics reports as the child is working. If there's a single message (that's smaller than the pipe buffer size), then life is easy. If there's a stream of messages or the message is longer than the pipe buffer size, you have to think more carefully about coordination — you can't detect messages only when the child dies.
Second pass: after the extra information became available.
If you're already using select() in a loop, then when a child dies, you will get a 'pipe ready for reading' indication from select() and you will get 0 bytes from read() which indicates EOF on that pipe. You should then close that pipe (and wait for one or more children with waitpid(), probably using W_NOHANG — there should be at least one corpse to be collected — so you don't have zombies kicking around for protracted times).
A strict answer to your last question is: when the only child with the write end of a pipe dies, the parent should close the read end of that pipe to release resources for later reuse.
In your case, the parent process shall close writing end of the pipe right after fork. Then it can read its statistic data until EOF (end-of-file) and then close the reading end of the pipe.
broken pipe happens when you write to the pipe but no fd is open for reading from that pipe. So it doesn't apply to your case. In your case, since your parent is reading from the pipe, it should read EOF when child exits (if you have closed the write end in your parent process correctly, otherwise it will just block since it assumes there will still be things to read in the future). Then you can safely close the read fd in your parent process.
In general
If parent writes and child reads, you do need to worry about broken pipe which is when the child closes the read fd, and parent gets SIGPIPE as it keeps writing to the pipe. SIGPIPE by default terminates the process, so you may want to set up a signal handler to make it do whatever you want (if you don't want it to just terminate).
Let's see the see the cases differently for Parent having 1 child and children.
Parent having 1 child. When child process exits and parent is waiting on read end, parent will also exit. Here's the code
//fd[0] //read end
//fd[1] //write end
#include <unistd.h>
#include <stdio.h>
#include <errno.h> //For errno
#include <stdlib.h> //exit()
void DumpAndExit(char* str){
perror (str);
printf ("errno: %d", errono);
exit(0);
}
int main(){
int fd[2], pid = -1;
if (pipe(fd) < 0)
DumpAndExit ("pipe");
if (pid = fork() < 0) {
DumpAndExit ("fork");
}
else if (pid == 0) { //Child
close(fd[0]); //Close read end
printf("In Child \n");
sleep(2);
exit(0);
} else { //Parent
close(fd[1]); //close write
waitpid(-1); //Parent will wait for child
printf("Parent waiting\n");
char buf[4] = {};
read(fd[0], buf, sizeof(buf)); //reads from pipe
printf ("Read from child: %s", buf);
}
}
# ./a.out
In child
Parent waiting
Read from child:
#
In very simple words:
Every process have a PCB(struct task_struct) which has all information of process, In case of fork() it will have child's contexts as well. Means pointers to child's PCB.
Since pipe ie int fd[2] is created on stack of parent, then duplicated to child's stack. When child exits, its PCB is cleared, PCB of parent is updated and Parent knows there's no one connected at other end of pipe.

Resources