views:

190

answers:

2

Hi,

I'm trying to create a simple shell in C for Unix. I've been able to do all the parsing of commands and execution, but I'm having a problem with piping. I think the problem is that I'm not hooking into the correct pipe for the input of the second command.

For example, if I type "ls | wc", it will pause after the "wc" command, which I think is because its waiting for input. I think the problem is when I use dup2(reading[i],0), and its not hooking into the correct pipe.

I know this is a bit of a broad question, but if there are any pointers I could get, I would appreciate it. Here is the code that creates new processes and tries to pipe them.

    int fileds[2];
 int reading[num_cmds];
 int writing[num_cmds];

 int p;
 for(p=0; p < num_cmds; p++)
 {
  reading[p] = -1;
  writing[p] = -1;
 }

 int j;
 for(j=0; j < num_cmds-1; j++)    //Create pipes for commands
 {
  int fileds[2];
  pipe(fileds);
  reading[j+1] = fileds[0];
  writing[j] = fileds[1];
 }

 int i = 0;
 for(i = 0; i < num_cmds;i++)
 {   
  cmd_args = parse_cmd(cmds[i],output_file,input_file,&run_bg); //Get command and args

  pid_t childpid;
  int status;
  childpid=fork();

  if (childpid >= 0) 
  {
   if (childpid == 0) 
   {    
    if(writing[i] != -1)
    {
     dup2(writing[i],1);
     close(writing[i]);
    }

    if(reading[i] != -1)
    {
     dup2(reading[i],0);
     close(reading[i]);
    }

    int h;
    for(h = 0; h < num_cmds; h++)
    {
     close(writing[h]);
     close(reading[h]);
    }

    if(execvp(cmd_args[0],cmd_args) == -1) 
    {
     perror("Problem with command");
     exit(0);
    }
   }
   else 
   {
    wait(&status);
    int m;
    for(m = 0; m < num_cmds; m++)
    {
     if( writing[m] != -1) close(writing[m]);
     if( reading[m] != -1) close(reading[m]);
    }
   }
  }
  else 
  {
    perror("fork"); 
    continue;
  }


  input_file[0] = 0;
  output_file[0] = 0;
  run_bg = 0;
 }

}



UPDATE: I was able to figure it out, thanks to Richard. It was a combination of closing the file descriptors in the wrong order and not closing some at all. Here's the working code.

int fileds[2];
 int reading[num_cmds];
 int writing[num_cmds];

 int p;
 for(p=0; p < num_cmds; p++)
 {
  reading[p] = -1;
  writing[p] = -1;
 }

 int j;
 for(j=0; j < num_cmds-1; j++)
 {
  int fileds[2];
  pipe(fileds);
  reading[j+1] = fileds[0];
  writing[j] = fileds[1];
 }

 int i = 0;
 for(i = 0; i < num_cmds;i++)
 {   
  cmd_args = parse_cmd(cmds[i],output_file,input_file,&run_bg);

  pid_t childpid;
  int status;
  childpid=fork();

  if (childpid >= 0) 
  {
   if (childpid == 0) 
   {    
    if(writing[i] != -1)
    {
     close(1);
     dup2(writing[i],1);
    }

    if(reading[i] != -1)
    {
     close(0);
     dup2(reading[i],0);
    }

    if(execvp(cmd_args[0],cmd_args) == -1) 
    {
     perror("Problem with command");
     exit(0);
    }
   }
   else 
   {

    wait(&status);
    close(writing[i]);

    if(i > 0) 
    {
     close(reading[i]);
    }
   }
  }
  else 
  {
    perror("fork");
  }


  input_file[0] = 0;
  output_file[0] = 0;
  run_bg = 0;
 }
A: 

When I type "ls | wc" wc does as expected and prints the number of words output by the ls command. Remember that when you are piping commands using "|" you don't need to create pipes in your application. The first command needs to output to stdout and the second command needs to read that output from standard in.

ssteidl
This is a project to understand how piping works, so I need to be able to set up the piping. "ls | wc" works in a normal terminal, but I need to emulate that in this program.From what I understand, I need to set the output of the first command - "ls" in this case to be the output of the pipe, and the input of the second command, to be the input of the pipe.
symmet
Well, if you are implementing a shell to provide this piping mechanism then you do need to use pipes or some similar mechanism. I think the asker knows these issues; he's asking about why it isn't working.
BobbyShaftoe
+2  A: 

I think your problem may be that you wait for each process inside the loop and then close all the file descriptors. This makes the file descriptors invalid for the next call to dup2() and results in stdin for the next process staying unchanged.

Just a guess, I haven't run the code.

Richard Pennington
I think you're right. I tried commenting out the if( reading[m] != -1) close(reading[m]); in the loop, and that is allowing me to pipe 2 commands. Thanks for pointing me in the right direction!
symmet