1

I am trying to write code that forks a subprocess and communicates with it using pipes. I am using two pipes - one for writing to, and the other for reading from the standard streams of the subprocess. Here's what I have so far:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

void read_move(int fd)
{
    FILE *stream = fdopen(fd, "r");
    char c;
    setvbuf(stream, NULL, _IONBF, BUFSIZ);  
    while ((c = fgetc(stream)) != EOF)
    {
        putchar(c);
    }
    fclose(stream);
}

void write_move(int fd, const char *move)
{
    FILE *stream = fdopen(fd, "w");
    setvbuf(stream, NULL, _IONBF, BUFSIZ);
    fprintf(stream, "%s", move);
    fclose(stream);
}

int main() {
    pid_t pid;
    int wpipe[2];
    int rpipe[2];
    if (pipe(wpipe) || pipe(rpipe))
    {
        fprintf(stderr, "Pipe creation failed.\n");
        return EXIT_FAILURE;
    }
    pid = fork();
    if (pid == 0)
    {
        /* gnuchess process */
        setvbuf(stdin, NULL, _IONBF, BUFSIZ);
        setvbuf(stdout, NULL, _IONBF, BUFSIZ);
        setvbuf(stderr, NULL, _IONBF, BUFSIZ);
        dup2(wpipe[0], STDIN_FILENO);
        dup2(rpipe[1], STDOUT_FILENO);
        dup2(rpipe[1], STDERR_FILENO);
        close(wpipe[0]);
        close(wpipe[1]);
        close(rpipe[0]);
        close(rpipe[1]);
        if (execl("/usr/games/gnuchess", "gnuchess", "-x", NULL) == -1)
        {
            fprintf(stderr, "Exec failed.\n");
            return EXIT_FAILURE;
        }
        return EXIT_SUCCESS;
    }

    /* parent process */

    printf("Sending move to GNU Chess... \n");
    close(wpipe[0]); /* Close other end */
    write_move(wpipe[1], "c3\n");   

    printf("Reading response... \n");
    close(rpipe[1]); /* Close other end */
    read_move(rpipe[0]);

    /* clean up */  
    close(wpipe[1]);
    close(rpipe[0]);    

    /* kill gnuchess */
    kill(pid, SIGTERM);

    return EXIT_SUCCESS;
}

The output of the above program is

Sending move to GNU Chess... 
Reading response... 

It gets stuck at the getline call in the read_move function, waiting for input. But I don't understand how that's possible, since I have closed the other end.

What am I doing wrong?

EDIT: I changed the read_move method and closed the pipe ends in the child process after the dup2 calls.

1 Answer 1

2

You're not closing wpipe in the child process. So you're actually passing 7 file descriptors to gnu chess when you start it - the dup'ed stdin, stdout and stderr; the 2 descriptors in wpipe, and the 2 descriptors in rpipe.

If you're on linux, find out the process id of gnu chess while your program is running, and do a ls /proc//fd to see all its file descriptors.

Once you add

close(wpipe[0]); close(wpipe[1]); close(rpipe[0]); close(rpipe[1]);

between the dup2()s and the execl(), you should be ok.

(This still omits error handling, like the case that one of the dup2()s fails, or, even worse, your program has closed one of the fds [0, 1, 2] before calling pipe() so one of the standard descriptors gets replaced with a pipe accidentially. But i guess that's not the point here.)

3
  • Thanks a lot for pointing that out. It did have 7 file descriptors. However, after I closed the 4 pipe ends, ls /proc/PID/fd still gave me 4 FDs - 0 1 2 3. FD 3 was in cyan, the others in red. What does this mean?
    – prvnsmpth
    Commented Dec 13, 2013 at 3:22
  • Also, closing the pipe ends does not solve the proble completely - the program goes into an infinite loop. The child process keeps reading the same thing over and over from its end of the pipe - although I only write it once.
    – prvnsmpth
    Commented Dec 13, 2013 at 3:40
  • This is probably because gnuchess doesn't handle EOF well. Try cat | gnuchess, then press ^D - gnuchess just ignores the EOF instead of exiting. If you do an ls -l to /proc/PID/fd, you'll get more information about which fd is connected to what. Commented Dec 13, 2013 at 7:50

Not the answer you're looking for? Browse other questions tagged or ask your own question.