c++ - Imitate unix shell pipe -


educational task: want imitate pipe symbol (command, method) "|" work. program gets command in unix shell stdin:

command1 | command2 | command3 | .... 

and should execute redirecting stdin|stdout pipe each command. final output redirects result.out file. should use execlp , fork.

1st variant: works fine 1-2 commands, freezes 3 or more. i'm doing wrong: seems close pipe descriptors?

now in 2nd variant execute_line simplified, problem: mess in output. how correctly pass pipes between commands?

3rd variant: close correct, added more debug info. question: how correctly connect middle children?

4th variant, fixed logic, correct: works fine 1, 3 or more commands, start fail 2 (which worked correct previously) - weird :)

#include <stdio.h> #include <unistd.h> #include <iostream> #include <fstream> #include <algorithm> #include <cstring> #include <vector> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>  using namespace std;  void split(const string& str, vector<string> &tokens,            const string &delimiters = " ") {     // skip delimiters @ beginning.     string::size_type lastpos = str.find_first_not_of(delimiters, 0);     // find first "non-delimiter".     string::size_type pos     = str.find_first_of(delimiters, lastpos);      while (string::npos != pos || string::npos != lastpos)     {         // found token, add vector.         tokens.push_back(str.substr(lastpos, pos - lastpos));         // skip delimiters.  note "not_of"         lastpos = str.find_first_not_of(delimiters, pos);         // find next "non-delimiter"         pos = str.find_first_of(delimiters, lastpos);     } }  inline string trim(string &str) {     const string whitespaces(" \t\f\v\n\r");      string::size_type pos = str.find_first_not_of(whitespaces);     if(pos != string::npos)         str.erase(0, pos);   // prefixing spaces      pos = str.find_last_not_of(whitespaces);     if(pos != string::npos)         str.erase(pos + 1);    // surfixing spaces      return str; }  void parse_command(string &command, string &name, string &argc) {     command = trim(command);      string::size_type pos = command.find_first_of(' ');     if(pos != string::npos) {         name = command.substr(0, pos);         argc = command.substr(pos + 1, command.length() - pos - 1);     } else {         name = command;         argc = "";     } }  void exec_command(uint n, vector<string> &commands) {     string name, args;     parse_command(commands[n], name, args);     if(args.length() > 0)         execlp(name.c_str(), name.c_str(), args.c_str(), null);     else         execlp(name.c_str(), name.c_str(), null); }  // ----(stdout)---> pfd[1] --- pfd[0] ----(stdin)---> wc -l void execute_line(vector<string> &commands, uint i, int *parent_pfd = 0) {     int pfd[2];     pipe(pfd);     if(i > 0 && !fork()) {         // child         printf("child, i: %d\n", i);         if(i > 1) {             execute_line(commands, i-1, pfd);             close(pfd[1]);             close(pfd[0]);         } else {             printf("deeper child %d: %s, parent_pfd[0]=%d, parent_pfd[1]=%d, "                    "pfd[0]=%d, pfd[1]=%d\n",                    getpid(), trim(commands[i-1]).c_str(),                    parent_pfd[0], parent_pfd[1], pfd[0], pfd[1]);              close(stdout_fileno);  //            if(parent_pfd) //                dup2(parent_pfd[1], stdout_fileno); // copy stdout parent pipe out //            else                 dup2(pfd[1], stdout_fileno);        // copy stdout pipe out              close(pfd[1]);             close(pfd[0]);              exec_command(i - 1, commands);         }     } else {         if(parent_pfd) {             printf("middle child, i: %d\n", i);             printf("middle child %d: %s, parent_pfd[0]=%d, parent_pfd[1]=%d, "                    "pfd[0]=%d, pfd[1]=%d\n",                    getpid(), trim(commands[i]).c_str(), parent_pfd[0], parent_pfd[1],                    pfd[0], pfd[1]);              close(stdin_fileno);             dup2(pfd[0], stdin_fileno);         // copy stdin pipe in              close(stdout_fileno);             dup2(parent_pfd[1], stdout_fileno); // copy stdout parent pipe out              close(pfd[1]);             close(pfd[0]);              exec_command(i, commands);         } else {             printf("final, i: %d\n", i);             printf("final %d: %s, pfd=%p, parent_pfd=%p, pfd[0]=%d, pfd[1]=file\n",                    getpid(), trim(commands[i]).c_str(), pfd, parent_pfd, pfd[0]);             int fd = open("result.out", o_rdwr | o_creat | o_trunc, s_irusr | s_iwusr);             dup2(fd, stdout_fileno);            // copy stdout file             dup2(pfd[0], stdin_fileno);         // copy stdin pipe in             close(pfd[0]);  // close redirected             close(pfd[1]);  // close write not necessary here             close(fd);              exec_command(i, commands);         }     } }  int main() {     char buffer[1024];     ssize_t size = read(stdin_fileno, buffer, 1024);      if(size > 0) {         buffer[size] = '\0';         string command = buffer;         vector<string> commands;         split(command, commands, "|");         execute_line(commands, commands.size() - 1);     }     return 0; } 

the logic use connect pipes standard input , output looks faulty.

int pfd[2]; pipe(pfd); 

you start creating pipe, presumably connect 1 process's standard output process's standard input. that's fine.

now, let's @ 1 of section of code going execute 1 of processes:

close(stdin_fileno); close(stdout_fileno); dup2(pfd[0], stdin_fileno);     // copy stdin pipe in dup2(pfd[1], stdout_fileno);    // copy stdout pipe out close(pfd[0]);  // close redirected close(pfd[1]);  // close redirected exec_command(i, commands); 

now, don't need explain this. can read own comments here, try explain why attaching both ends of same pipe same process's standard input , output? makes no sense. pipe should attaching 1 process's standard input process's standard output. in kind of situation, makes no sense execute process , attach standard input standard output. that's head scratcher me.

that's 1 of issues here, there few others here too, apparent after closer look.

the overall approach seems complicated me, here. recursive function, sets pipeline, should have single decision point: last command in pipeline. if so, 1 thing. if not, else involves recursion, set rest of pipeline.

it looks me there either 3 or 4 decision points here, if overall logic here over-complicated, not wrong, should simplified. shouldn't have special coding "middle" part of pipeline, comments describe. either you're dealing last command in pipeline, or not. that's it. try rewrite function in manner. should simpler, , work better.


Comments

Popular posts from this blog

sublimetext3 - what keyboard shortcut is to comment/uncomment for this script tag in sublime -

java - No use of nillable="0" in SOAP Webservice -

ubuntu - Laravel 5.2 quickstart guide gives Not Found Error -