FILE * streams with dup2()-ed descriptors — stdin not working - c

After forking a child and dub2()-ing its stdin descriptor to the read-end of a pipe (its write-end is in the parent process) reading with read(0,...) (descriptor based) works fine. But reading with fgets(stdin,...) (stream based) does not work. Why?
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(){
char string[]="MY TEST STRING";
pid_t pid;
int bufSize=80;
char rBuf[bufSize];
int downlink[2], wrlen=0, rdlen=0, status;
memset(rBuf,0,bufSize);
if (pipe (downlink) == -1){
printf("Error with pipe()\n");
exit(4);
}
pid=fork();
if (pid>0){ //parent
wrlen=wrlen+write(downlink[1], string, strlen(string)+1);
//dprintf(downlink[1],"%s", string);
sleep(6);
}
else if (pid == 0){ // child
dup2(downlink[0],STDIN_FILENO);
//rdlen=read(downlink[0], rBuf, bufSize); //works
//rdlen=read(STDIN_FILENO, rBuf, bufSize); //works
//scanf("%s", rBuf);fflush(stdin); //doesn't work, reads up to first blank
//scanf(stdin,"%s", rBuf);fflush(stdin); //doesn't work, reads up to first blank
fgets(rBuf, bufSize, stdin);fflush(stdin); //doesn't work
printf("c: %s", rBuf), fflush(stdout);
//status =execl("/usr/bin/octave","octave","--no-gui",NULL);
//status =execl("/usr/bin/man","man",NULL);
//printf("c: status%d", status), fflush(stdout);
}
else{ //error
printf("Error with fork()\n");
exit(4);
}
return 0;
}
In this code the fork()ed child is supposed to read from stdin (which is dub2()ed to downlink[0](=read-end of pipe from writing parent)) and printf() to stdout the received contets.
If the reading happens with read() (descriptor based) everything works fine. When reading with fgets() or scanf() (stream based) no data is printed.
What am I missing here?

fgets() reads a line, but your parent process never sends a line of text. So you need to add a newline to your string
char string[]="MY TEST STRING\n";
read() however just reads whatever is in the pipe when it becomes available - it does not try to read all the data it can up till a newline character, which is why you get data back when using read()
Even when you do not send a newline, fgets() would return when the write end of the pipe gets closed. However the pipe you create in your parent process gets copied into the child process.
That means that when the parent process exits, its write end of the pipe is closed - but not the write end of the pipe in the child process - leading to the pipe still being open when the parent exit.
So make sure you close() the write end of the pipe in your child process, as you don't need it:
else if (pid == 0){ // child
close(downlink[1]);
dup2(downlink[0],STDIN_FILENO);

Related

Why parent is not able to read stdin after a fork?

#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
int main(){
char buf[10];
pid_t pid = fork();
if (pid < 0){
printf("error fork\n");
exit(1);
}
if (pid == 0){
fgets(buf,5,stdin);
printf("Child : %s\n",buf);
}
else{
wait(NULL);
char* r = fgets(buf,5,stdin);
if (r == NULL){
printf("Parent : eof = %i\n",feof(stdin));
}
else {
printf("Parent : %s\n",buf);
}
}
return 0;
}
My program is very simple : a process is forked; the child process reads 4 characters from stdin and when it finishes, the parent process reads 4 characters from stdin.
Normally, if I write characters in stdin (before the fork) the child process should read the first 4 characters and then the parent process should read the next 4 characters. It seems quit logical as fork() duplicates the parent process, including the file descriptors and opened files.
But, when I execute
echo 'aaaaaaaaa' | ./my_program
I get
Child : aaaa Parent : eof = 1
It seems that stdin has been emptied by the child process when it finished.
I having hard time explaining this behavior.
Can you help me ? :)
Standard input is usually (Is stdout line buffered, unbuffered or indeterminate by default?) line buffered by default. Check this answer to see what exactly this entails.
If you want your program to work as expected, explicitly set your standard input to be unbuffered (before the fork() call). This can be done like so:
setbuf(stdin, NULL);
Also see Should I set stdout and stdin to be unbuffered in C? for more implications of setting stdin to be unbuffered.
Try comment out after "else{"
//wait(NULL);

Redirecting stdout to pipe and reading from it from a single process

I'm porting a Linux program to a system which has no fork(), so everything runs in a single process. This program creates pipe, redirects stdout to pipe's input, forks, children calls printf and parent reads data from pipe. I removed fork, and tried to test it on Linux - the program is hanged on read() which waits for data on pipe. Here's a small reproducer:
#include <stdio.h>
#include <unistd.h>
const char in[7] = "qwerty";
int main ()
{
int fds[2], stdout_sav;
char str[8] = {0};
pipe(fds);
if ((stdout_sav = dup(1)) < 0) return 1; // save stdout
if (dup2(fds[1], 1) < 0) return 2; // stdout -> pipe
close(fds[1]);
if (printf(in) <= 0) return 3;
//fsync(1); // behavior does not change if I add a fsync here
read(fds[0], str, sizeof(in)); // HERE: read waits for data
close(fds[0]);
if (dup2(stdout_sav, 1) < 0) return 4; // restore stdout
close(stdout_sav);
printf("Received %s\n", str);
return 0;
}
As it does not work on Linux, I suppose there is wrong logic in the code, but I don't see any problems with it.
I had an idea that a single process won't write to pipe if there are no readers, however, this is not true as the following code works OK:
pipe(fds);
write(fds[1], in, sizeof(in));
close(fds[1]);
read(fds[0], str, sizeof(in));
close(fds[0]);
printf("Received %s\n", str);
You seem to have forgotten that when connected to a pipe stdout is fully buffered.
You need to explicitly flush stdout for the data to actually be written to the pipe. Since the data isn't flushed, there's nothing in the pipe to be read, and the read call blocks.
So after the printf call you need fflush(stdout).

Why does read() block and wait forever in parent process despite the writing end of pipe being closed?

I'm writing a program with two processes that communicate through a pipe. The child process reads some parameters from the parent, executes a shell script with them and returns the results to the parent process line by line.
My code worked just fine until I wrote the while(read()) part at the end of the parent process. The child would execute the shell script, read its echo's from popen() and print them to standard output.
Now I tried to write the results to the pipe as well and read them in the while() loop at the parent's end but it blocks and neither will the child process print the result to standard output. Apparently it won't even reach the point after reading the data from the pipe sent by the parent.
If I comment out the while() at the parent process, the child will print the results and return, and the program will end smoothly.
Why does the while(read()) block even if I closed the writing end of the pipe in both parent and child processes?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
int read_from_file(char **directory, int *octal) {
FILE *file = fopen("input", "r");
if (file == NULL) {
perror("error opening file");
exit(1);
}
fscanf(file, "%s %d", *directory, octal);
}
int main(int argc, char *argv[]) {
char *directory = malloc(256);
int *octal = malloc(sizeof *octal);
pid_t pid;
int pfd[2];
char res[256];
if (pipe(pfd) < 0) {
perror("Error opening pipe");
return 1;
}
if ((pid = fork()) < 0)
perror("Error forking");
if (pid == 0) {
printf("client here\n");
if (read(pfd[0], directory, 256) < 0)
perror("error reading from pipe");
if (read(pfd[0], octal, sizeof(int)) < 0)
perror("error reading from pipe");
// This won't get printed:
printf("client just read from pipe\n");
// close(pfd[0]);
char command[256] = "./asd.sh ";
strcat(command, directory);
char octal_c[5];
sprintf(octal_c, " %d", *octal);
strcat(command, octal_c);
FILE *f = popen(command, "r");
while (fgets(res, 256, f) != NULL) {
printf("%s", res);
if (write(pfd[1], res, 256) < 0)
perror("Error writing res to pipe");
}
fclose(f);
close(pfd[1]);
close(pfd[0]);
fflush(stdout);
return 1;
}
read_from_file(&directory, octal);
if (write(pfd[1], directory, 256) < 0)
perror("Error writing dir to pipe");
if (write(pfd[1], octal, sizeof(int)) < 0)
perror("error writing octal to pipe");
int r;
close(pfd[1]);
while (r = read(pfd[0], res, 256)) {
if (r > 0) {
printf("%s", res);
}
}
close(pfd[0]);
while (wait(NULL) != -1 || errno != ECHILD);
}
Since the child demonstrably reaches ...
printf("client here\n");
... but seems not to reach ...
printf("client just read from pipe\n");
... we can suppose that it blocks indefinitely on one of the two read() calls between. With the right timing, that explains why the parent blocks on its own read() from the pipe. But how and why does that blocking occur?
There are at least three significant semantic errors in your program:
pipes do not work well for bidirectional communication. It is possible, for example, for a process to read back the bytes that it wrote itself and intended for a different process. If you want bidirectional communication then use two pipes. In your case, I think that would have avoided the apparent deadlock, though it would not, by itself, have made the program work correctly.
write and read do not necessarily transfer the full number of bytes requested, and short reads and writes are not considered erroneous. On success, these functions return the number of bytes transferred, and if you want to be sure to transfer a specific number of bytes then you need to run the read or write in a loop, using the return values to track progress through the buffer being transferred. Or use fread() and fwrite() instead.
Pipes convey undifferentiated streams of bytes. That is, they are not message oriented. It is not safe to assume that reads from a pipe will be paired with writes to the pipe, so that each read receives exactly the bytes written by one write. Yet your code depends on that to happen.
Here's a plausible failure scenario that could explain your observations:
The parent:
fork()s the child.
after some time performs two writes to the pipe, one from variable directory and the other from variable octal. At least the first of those is a short write.
closes its copy of the write end of the pipe.
blocks attempting to read from the pipe.
The child:
reads all the bytes written via its first read (into its copy of directory).
blocks on its second read(). It can do this despite the parent closing its copy of the write end, because the write end of the pipe is still open in the child.
You then have a deadlock. Both ends of the pipe are open in at least one process, the pipe is empty, and both processes are blocked trying to read bytes that can never arrive.
There are other possibilities that arrive at substantially the same place, too, some of them not relying on a short write.
The parent process was trying to read from the pipe before the child could have read from it and write the results to it. Using two different pipes for the two-way communication solved the problem.

Read from pipe even if the write end is closed

my teacher said that if the writing end of a pipe is closed, the child process can no longer read from the read end of the pipe and a read would generate a BROKEN _PIPE error. However, I can't get this code to generate any error while reading on the closed tube :
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
int main(void) {
int pipefd[2];
char c;
pipe(pipefd);
if (fork() == 0) {
close(pipefd[1]);
sleep(5);
// The parent has already closed pipefd[1]
while (read(pipefd[0], &c, 1)) {
printf("%c", c);
}
close(pipefd[0]);
return 0;
}
close(pipefd[0]);
char str[] = "foo";
write(pipefd[1], str, 4);
close(pipefd[1]);
return 0;
}
The output on stdout after 5 seconds is foo. So what I understand is that closing the write end just add EOF after the characters already there and DOES NOT send EOF on any forthcoming read (so the child can read all the characters already sent). Am I right ?
As you have found out, your teacher is wrong.
You do not get a broken pipe "error" (which is actually a combination of a signal, SIGPIPE, and an error EPIPE if that is ignored), when you try to read from a broken pipe, but when you attempt to write to a broken pipe.
For Linux systems, you can read more about this here, or you can take a look at the BSD man page pipe(2).

C: dup2, pipe and fork not working as expected

I'm trying to do a simple fork -> execute another program -> say "hello" to that child process -> read back something -> print what received.
The program used as child just waits for any line of input and prints something to the stdout like "hello there!"
This is my "host" program (that is not working):
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#define IN 0
#define OUT 1
#define CHILD 0
main ()
{
pid_t pid;
int pipefd[2];
FILE* output;
char buf[256];
pipe(pipefd);
pid = fork();
if (pid == CHILD)
{
printf("child\n");
dup2(pipefd[IN], IN);
dup2(pipefd[OUT], OUT);
execl("./test", "test", (char*) NULL);
}
else
{
sleep(1);
printf("parent\n");
write(pipefd[IN], "hello!", 10); // write message to the process
read(pipefd[OUT], buf, sizeof(buf));
printf("received: %s\n", buf);
}
}
I get this:
child
[.. waits 1 second ..]
parent
received:
What am I missing? Thanks!
EDIT (test.c):
By request, this is the child program:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int getln(char line[])
{
int nch = 0;
int c;
while((c = getchar()) != EOF)
{
if(c == '\n') break;
line[nch] = c;
nch++;
}
if(c == EOF && nch == 0) return EOF;
return nch;
}
main()
{
char line[20];
getln(line);
printf("hello there!", line);
fflush(stdout);
return 0;
}
You're always suppose to read from file-descriptor 0, and write to file-descriptor 1 with pipes ... you have this relationship reversed in the parent process. For what you're wanting to-do, you may end up needing two pipes for two-way communication between the parent and child that avoids situations where the parent ends up reading the contents it wrote to the pipe since process scheduling is non-deterministic (i.e., the child is not guaranteed to read what the parent wrote to the pipe if the parent is also reading from the same pipe since the parent could just end up writing and then reading with no interleaving of the child process to read what the parent wrote).
Change your code to the following:
main ()
{
pid_t pid;
int pipe_to_child[2];
int pipe_from_child[2];
FILE* output;
char buf[256];
pipe(pipe_to_child);
pipe(pipe_from_child);
pid = fork();
if (pid == CHILD)
{
printf("child\n");
//child process not using these ends of the pipe, so close them
close(pipe_to_child[1]);
close(pipe_from_child[0]);
dup2(pipe_to_child[0], fileno(stdin));
dup2(pipe_from_child[1], fileno(stdout));
execl("./test", "test", (char*) NULL);
}
else
{
sleep(1);
printf("parent\n");
write(pipe_to_child[1], "hello!\n", 10); // write message to the process
read(pipe_from_child[0], buf, sizeof(buf));
printf("received: %s\n", buf);
}
}
You need two pipes for this: one for the child process's stdin, and one for its stdout. You cannot reuse the two ends of a pipe as two pipes.
Also, this line of the parent program
write(pipefd[IN], "hello!", 10); // write message to the process
does not write a newline, so getln in the child will never return. (Furthermore, "hello!" has only six characters, but you are writing ten.)
You probably should use wait or waitpid.
It looks like you have your pipe descriptors mixed up. After calling pipe(), pipefd[0] is the read end of the pipe, and pipefd[1] is the write end of the pipe. You're writing to the read end, and reading from the write end.
Also, you're trying to use one pipe for both stdin and stdout of the child process. I don't think this is really what you want to do (you will need two pipes).
Looks like you have your IN/OUT backwards for the pipe -- pipefd[0] is the read end of the pipe, so writing to it (as the parent does) is nonsensical and will fail. Similarly pipefd[1] is the write end so reading from it (as the parent does) will also fail. You should ALWAYS check the return values of the read and write calls, to see if you're getting any errors
Others are saying that the pipe is mono-directional, which is what I thought at first. But actually that's not what my man page says:
A read from fildes[0] accesses the data written to fildes[1]
on a first-in-first-out (FIFO) basis and a read from
fildes[1] accesses the data written to fildes[0] also on a
FIFO basis.
However, this does mean that if the parent is writing to pipefd[0], then the child should read from pipefd[1], so you are associating the wrong side of the pipe with the child's stdin and stdout.
From the man page, it does seem like you can do this with one pipe. But it might be clearer code to use two.
It seems like you are thinking of each element of pipefd as a separate pipe, but that's not the case.

Resources