2

I'm taking a programming course focusing on C, and we're going through pipes and processes at the moment. An in class activity was assigned where I have to have a parent create a child to print input that is piped from the parent. I can get the program to pipe input to the child and output it, but when I ctrl + D for EOF, I can still enter input (without output).

I tried following my logic on paper, and it seems correct, but something is wrong. I've tried to hardcode a phrase that I could write so that I can exit my child, but I get broken pipes when I try to remedy the situation.

Here are my 2 files:

newshell.c

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>

int main(void){
    pid_t child1;
    int fd[2];
    char buffer1[100];
    int bytes;

    pipe(fd);
    child1 = fork();

    if (child1 == 0){
        dup2(fd[0],fileno(stdin));
        close(fd[1]);
        printf("You are in the child.\n");
        execlp("./printdata","printdata.c",0);
    }
    else {
        dup2(fd[1],fileno(stdout));
        close(fd[0]);
        while(fgets(buffer1,sizeof(buffer1),stdin) != NULL){
            write(fd[1],buffer1,(strlen(buffer1)+1));
        }
        write(fd[1],"999",sizeof("999"));
        wait(0);
        close(fd[1]);
        close(fd[0]);

        printf("Child ended. You are in the parent.\n");
    }
    return 0;
}

printdata.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void){
    char buffer[100];
    int bytes;
    printf("You're in the new process\n");
    while(bytes = read(fileno(stdin),buffer,sizeof(buffer)) >= 0){
        printf("%s\n",buffer);
        if (strcmp(buffer,"999") == 0){
            return 0;
        }
    }
    printf("Done here\n");
    return 0;
}

The logic here is that I create a child process in the parent that waits for input from the parent. I hardcoded a 'sentinel' (as my professor calls it) since my EOF wasnt being detected when I input it. The teacher accepted what I had since most people didn't finish the assignment, but I want to know why it isn't working for my own sake.

Thank you.

totemc
  • 23
  • 6
  • `pritnf("%s\n",buffer);`? Please post code that compiles. Also note that `read()` does not terminate strings. – Andrew Henle Nov 07 '17 at 13:58
  • I fixed the typo in the code. – totemc Nov 07 '17 at 14:02
  • 1
    Keep in mind that the general pattern for using dup2 is `dup2(f1, f2); close(f1);`. That is, you almost always want to close the descriptor that you are dup'ing right away. – William Pursell Nov 07 '17 at 14:14
  • Oh wow. I had the logic for dup2 mixed up. So the point of my pipe's file descriptors are to allow communications between processes. So when I dup2(fd[x],in/out), I'm basically saying I want my in/out to point to the file descriptors of the pipe? – totemc Nov 07 '17 at 14:18

2 Answers2

2

The point is: When you press CTRL+D, you signal EOF on stdin of the parent. So the parent leaves this loop

while(fgets(buffer1,sizeof(buffer1),stdin) != NULL)

since fgets() returns NULL on EOF.

But now, you need to close fd[1] and fileno(stdout) (since you dupped it and do both before calling wait(0)), because closing the last filedescriptor refering to that pipe will signal EOF on input to the child process! It is a bit unclear to me why you dup2() fd[1] to fileno(stdout) anyway, maybe you should leave that out.

After these modifications it should work as expected.

Ctx
  • 18,090
  • 24
  • 36
  • 51
  • Your suggestion worked. Thank you. I have a question regarding my unnecessary dup2. It was my understanding that I had to dup my pipe's out to the stdout, since I'm reading from the parent, and wanting that input to be output to the child. If I remove that line of code, why would it work? – totemc Nov 07 '17 at 14:13
  • @totemc The dup2() would have the following effect: If you write something to stdout in the parent, for example with `printf()`, it will not go to the terminal, but to your pipe and through that to the child process. But since you do not do that, it seems to be useless, so you can leave it simply out. – Ctx Nov 07 '17 at 14:14
  • I understand now. I had thought that I needed my stdout to point to the pipe so that it could receive input from the parent. But if that was the case, why would I have needed my `write()` call? I got confused with the way communications are done between both the processes. Thank you so much. – totemc Nov 07 '17 at 14:23
1

The parent needs to close fd[1] before it calls wait. As long as any process has the write side of the pipe open, the read in the child is going to block. In your case, the parent is holding the write side open, waiting for the child to terminate. And the child is blocked on a read waiting for the parent to close the write side of the pipe. In other words, write:

 close(fd[1]);
 wait(0);

And, as ctx points out, you need to either close stdout in the parent, or stop the pointless dup2.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • But because of the `dup2()` there are now _two_ filedescriptors refering to the writing end of the pipe, which _both_ have to be closed – Ctx Nov 07 '17 at 14:09
  • 1
    Yikes, why on earth is that dup happening! It serves no purpose. – William Pursell Nov 07 '17 at 14:10
  • I had this before, but would get a broken pipe. I followed Ctx's advice and I got the correct results. – totemc Nov 07 '17 at 14:10