0

I want to create a UNIX based shell that supports commands with 1 pipe. For example, the command ls | wc

I have wrote the following code but when I try an input like the one above, the program actes weird and can't figure out where the fault is.

To be more specific, it appears to fork() properly, then exec() with the proper parameters but only the command before the pipe (ls) exits properly. The command after the pipe (wc) never exits actually and as a result there is no output from the program.

I 've put some printf to help me figure out where is the fault in the code. Functions fetch_input and string_tokenizer are tested and work fine.

The fault must be somewhere below the point pointed in the code. Any suggestion to help me find what is the fault would be appreciated.

Thanks in advance.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"

char * fetch_input(char * buffer);
char * trim(char * string);
char ** string_tokenizer(char * string, char c);

int main(int arc, char * argv[]){

char *input, *init_input, **commands, **com1, **com2;
pid_t fork_pid_1, fork_pid_2, ret_pid;
int stat,i, fd[2];

init_input = (char *)malloc(sizeof(char)*256);

input = init_input;
input = fetch_input(input);

while(strcmp(input,"exit")!=0){

    commands = string_tokenizer(input,'|');
    com1 = string_tokenizer(commands[0],' ');

    if(commands[1]==0){
        fork_pid_1 = fork();
        if(fork_pid_1<0){
            printf("Fork Error!\n");
            _exit(1);
        }
        else if(fork_pid_1==0){
            execvp(com1[0],com1);
            printf("Exec Error!\n");
            _exit(1);
        }
        else{
            ret_pid=wait(&stat);

        }
    }
    else{                 //the fault is probably somewhere below that point
        if(pipe(fd)<0){ _exit(1);}

        fork_pid_1 = fork();

        if(fork_pid_1<0){
            printf("Fork Error!\n");
            _exit(1);
        }
        else if(fork_pid_1==0){

            printf("First command, ready to exec...(pid:%d)\n",getpid());       

            close(fd[0]);
            dup2(fd[1],1);
            close(fd[1]);

            execvp(com1[0],com1);
            printf("Exec Error!\n");
            _exit(1);

        }
        else{
            com2 = string_tokenizer(commands[1],' ');

            fork_pid_2=fork();
            if(fork_pid_2<0){
                printf("Fork Error!\n");
                _exit(1);
            }
            else if(fork_pid_2==0){

                printf("Second command, ready to exec...(pid:%d)\n",getpid());

                close(fd[1]);
                dup2(fd[0],0);
                close(fd[0]);

                execvp(com2[0],com2);
                printf("Exec Error!\n");
                _exit(1);


            }
            else{   
                printf("Now we 're in the parent process...(p_pid:%d)\n",getpid());
                while( (ret_pid=waitpid(-1,&stat,0)) >0 ){
                    printf("Child process (%d) exited with status:%d\n",ret_pid,stat);
                }

            }

        }


    }
    input = init_input;
    input = fetch_input(input);
}

return 0;
}

char * fetch_input(char * buffer){

int i, sum;

do{
    printf(ANSI_COLOR_CYAN "$" ANSI_COLOR_RESET);
    fflush(stdin);
    fgets(buffer,256,stdin);
    if(buffer[strlen(buffer)-1]=='\n'){
        buffer[strlen(buffer)-1]='\0';
    }
    sum=0;
    for(i=0;i<strlen(buffer);i++){
        if(buffer[i]==' '){sum++;}
    }
}while(strlen(buffer)==sum);

buffer = trim(buffer);

return buffer;

}

char * trim(char * string){

    int i;

    i=strlen(string);
    while(string[i-1]==' '){
        i--;
    }
    *(string+i)='\0';
    while(isspace(*string)){string++;}

    return string;
}

char ** string_tokenizer(char * string, char c){

     int j=0,k,i,done,found,first,last;
     char ** ret, *str;

     str=string;
     if( (str==0) || (strlen(str)==0) ) return NULL;
     ret = (char **)malloc(sizeof(char*));
     do{
        done=0;
        found=0;
        i=0;
        first=-1;
        last=-1;

        while( (str[i]!='\0') && (done==0) ){

               if( (str[i]==c) && (found==0) ){
                    i++;
               }
               else if( (str[i]!=c) && (found==0) ){
                    found=1;
                    first=i;
                    i++;
               }
               else if( (str[i]!=c) && (found!=0) ){
                    i++;
               }
               else if( (str[i]==c) && (found!=0) ){
                    done=1;
                    last=i;
               }

         }


         if(done!=0){


             *(ret+j) = (char *)malloc(sizeof(char)*(last-first+1));

             for(k=first;k<last;k++){
                 *(*(ret+j)+(k-first)) = str[k];
             }
             *(*(ret+j)+(k-first)) = '\0';
             *(ret+j) = trim(*(ret+j));
             j++;
             str=str+last;
         }


     }while(done!=0);

     if( (done==0) && (found==0) ){
         *(ret+j)=NULL;
     }
     else if( (done==0) && (found!=0) ){
          *(ret+j) = (char *)malloc(sizeof(char)*(i-first+1));
          for(k=first;k<i;k++){
              *(*(ret+j)+(k-first)) = str[k];
          }
          *(*(ret+j)+(k-first)) = '\0';
      *(ret+j)=trim(*(ret+j));
          *(ret+j+1) = NULL;
     }

     return ret;
}
thomas_p
  • 97
  • 1
  • 14
  • well, your example doesn't look like a [MCVE](http://stackoverflow.com/help/mcve) – user3159253 Nov 25 '15 at 23:29
  • I know but I put the whole code in case someone would like to compile it and execute it on his computer. As I pointed above, the custom functions I use are tested and work properly. The fault is probably somewhere bellow the point I ve put a comment in the code. – thomas_p Nov 25 '15 at 23:35
  • the whole idea behind a pipe between two child processes is that you create a [pipe](http://linux.die.net/man/3/pipe) in the parent process (the shell), then after the first fork, close one of the ends of the pipe, and [dup](http://linux.die.net/man/2/dup) the other end to either `stdout` (fd=2) for the first program in the pipe or `stdin` (fd=1) for the second program in the pipe and then perform exec the correcesponding pprogram. – user3159253 Nov 25 '15 at 23:35
  • I 've followd the idea you suggest but still this is not working properly. Actually I ve achieved pipe communication between parent and child processes but between child-child processes with the same parent, following the same logic, it doesn't seem to work. – thomas_p Nov 25 '15 at 23:42
  • http://stackoverflow.com/questions/8082932/connecting-n-commands-with-pipes-in-a-shell Also I've made a mistake. stdin fd==0, stdout==1, stderr==2 – user3159253 Nov 25 '15 at 23:53
  • Well I just tried some things on the code and find the solution to the problem. All I had to do was to close the file descriptos ( fd[0],fd[1] ) after the second fork() in the parent process. Somehow, open file descriptors in the parent process caused one of the child process not to exit properly and so there was the fault. It's all clear and making sense now. Thanks for your help pal :) – thomas_p Nov 26 '15 at 00:11

0 Answers0