Twiigs Twiigs - 16 days ago 5
C Question

Why do my pipes not talk to each other?

I am trying to write a simple shell in c. Right now I'm trying to get pipes to work. I have a struct

c
which I'm feeding into this function, it contains a place to store the pipe file descriptor
pipfd
and also contains information about the ending tag of each command
c->type
(this can be | || && & etc).
CommandPrev
is just tracking the last command so I can see if the command immediately before had a pipe tag.

After I finish this function, I give the child pid (the return value) to
waitpid
to wait on the command I called with
execvp


When I run commands such as
echo foo | echo bar
I get
bar
as an output exactly as I would expect and everything works great. My problem is when I try to run any command that actually uses the input from the first half of the pipe, everything gets stuck. If I run something like
echo foo | wc -c
I get no output and it just hangs forever.

I can see that this function finishes for these sort of commands because I print when it returns. What's happening is that the command that I'm calling with execvp is never happening so my waitpid waits forever.

I think that somehow my connection between the two ends of my pipe is broken. Either things are never getting written, or they're never being read, or the receiving end of the pipe never realizes that the writing side is finished and is just waiting forever. I call close immediately on all my pipes so I tend to doubt its the last one... but I'm really not sure how to go about testing any of these three scenarios.

This is my code:

pid_t start_command(command* c, pid_t pgid) {
(void) pgid;

// If its a pipe token, create a shared pipe descriptor
if (c->type == TOKEN_PIPE){
pipe(c->pipefd);
}

// Fork a child process, run the command using `execvp`
pid_t child = fork();
if (child == 0) {
// writing side of the pipe
if (c->type == TOKEN_PIPE){
dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
close(c->pipefd);
}
// receiving side of the pipe
else if (commandPrev->type == TOKEN_PIPE){
dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
close(commandPrev->pipefd);
}

// run the command
if (execvp(c->argv[0], c->argv) == -1) {
// fork failed
exit(-1);
}
}
else{
// clean up, clean up, everybody, everywhere
if (commandPrev->type == TOKEN_PIPE){
close(commandPrev->pipefd);
}
}
printf("return %i\n", getpid() );
return child;
}


Thank you!

Answer

As the other commenter says, you look like you're trying to close an array. Something like this should work better:

// writing side of the pipe
if (c->type == TOKEN_PIPE){
    close(c->pipefd[READ_SIDE]);
    dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
    close(c->pipefd[WRITE_SIDE]);
}
// receiving side of the pipe
if (commandPrev->type == TOKEN_PIPE){
    close(commandPrev->pipefd[WRITE_SIDE]);
    dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
    close(commandPrev->pipefd[READ_SIDE]);
}

Alternatively, you can close the active sides of the pipe after a waitpid call in the parent. Something like this:

waitpid(child, &status, 0);

if (commandPrev->type == TOKEN_PIPE){
    close(commandPrev->pipefd[READ_SIDE]);
}
if (c->type == TOKEN_PIPE){
    close(c->pipefd[WRITE_SIDE]);
}