ocket8888 ocket8888 - 1 month ago 24
Bash Question

fork and pipe bad file descriptor error

I'm trying to write a basic shell, and in order to facilitate piping, I wrote the following function, where

tokens
is the line to be executed, and
index
is the location in
tokens
of a pipe character (for now, pipes require whitespace on either side). Every time I run a piped command in the shell, both commands run as expected, but fail to write to/read from the pipe with the error "Bad file descriptor". The reason this is perplexing is that my file redirection works perfectly, so I can't understand why it's not working with the pipe. I've read the docs and looked at some example code, and I can't think of anything I'm doing wrong. After a few hours, I'm at a loss, where did I go wrong?


int pipeit(vector<string> tokens,unsigned int index){
//separate the entire line into two delimited by the pipe
vector<string> firstline;
vector<string> secondline;
for(unsigned int i=0;i<tokens.size();i++){
if(i==index){
continue;
}
else if(i<index){
firstline.push_back(tokens[i]);
}
else{
secondline.push_back(tokens[i]);
}
}

//make sure the lines aren't empty
if(secondline.size()==0||firstline.size()==0){
fprintf(stderr, "ERROR, expected at least one command at both ends of pipe\n");
}

//open a pipe, check that it was successfully created
int pip[2];
if(pipe(pip)<0){
perror("pipe failure");
return -1;
}

//attempt to fork
int parentstatus=0;
int childstatus=0;
switch(fork()){

//fork failed, close the pipe and return -1 (failure)
case -1:
{
close(pip[0]);
close(pip[1]);
perror("fork failure");
return -1;
}

//pid of 0 indicates this is the child process
case 0:
{
//close the write end of the pipe and redirect stdin to the read end
close(pip[0]);
int stdIn=dup(0);
dup2(pip[1],0);

//once redirect is done, can close the other end of the pipe
close(pip[1]);

//parse the second line as a list of commands (not important for question)
//then restore stdin, close the old file descriptor pointing to the pipe
execute_line(secondline,builtins);
dup2(stdIn,0);
close(stdIn);
break;
}

//pid other than 0 indicates this is the parent process
default:
{
//close the read end of the pipe
close(pip[1]);

//redirect stdout to the write end of the pipe
int stdOut=dup(1);
dup2(pip[0],1);
close(pip[0]);

//execute this line (not important for question)
parentstatus=execute_line(firstline,builtins);

//restore stdout and close the temporary fd pointing to the pipe
dup2(stdOut,1);
close(stdOut);

//wait for child process to exit and store it's return value in 'status' (then childstatus)
int status;
wait(&status);
childstatus=status;
break;
}
}

//return a combination of the parent and child's return status
//(not standard, I know, just easy and irrelevant for the question)
return childstatus & parentstatus;
}


tokens
is just a space-delimited
std::vector<string>
that contains the commands to be executed (yeah you need to pad the '|' with spaces for this to work, don't worry about that just yet) and
index
is the place in the list where the '|' character was found, to make things easier. For the purposes of a runnable source file, just paste the above function definition at the bottom of:

#include <unistd.h> //pipe, dup2, fork
#include <iostream>
#include <string>

int builtins = 0xf00; //not what this actually is, but that doesn't matter atm

using namespace std;

int pipeit(vector<string> tokens, unsigned int index);

void execute_line(vector<string> cmds, int biltins){
cout << "Executing: " << endl;
for(unsigned int i = 0; i < cmds.size(); ++i){
cout<<cmds[i]<< " ";
}
cout << endl;
}

int main(){
vector<string> cmds;
cmds.push_back(new string("cmd1"));
cmds.push_back(new string("|"));
cmds.push_back(new string("cmd2"));
return pipeit(cmds, 1);
}

Answer

The problem is that I was reading from the write end of the pipe and writing to the read end. Whenever you use pipe(my_pipe), my_pipe[0] is only for reading, and my_pipe[1] is only for writing. This doesn't mean my code works for infinite piping, for anybody else looking this up, but it does single pipes now.
CTC: IRC user C--

Comments