Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive piping in Unix again

Tags:

c

linux

pipeline

I know this topic came up already several times, but I'm still stuck at one point. I need to write a program that emulates cmd1 | cmd2 | cmd3 ... piping.

My code is here: http://ideone.com/fedrB8

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

void pipeline( char * ar[], int pos, int in_fd);
void error_exit(const char*);
static int child = 0; /* whether it is a child process relative to main() */

int main(int argc, char * argv[]) {
    if(argc < 2){
        printf("Usage: %s option (option) ...\n", argv[0]);
        exit(1);
    }
    pipeline(argv, 1, STDIN_FILENO);
    return 0;
}

void error_exit(const char *kom){
    perror(kom);
    (child ? _exit : exit)(EXIT_FAILURE);
}

void pipeline(char *ar[], int pos, int in_fd){
    if(ar[pos+1] == NULL){ /*last command */
        if(in_fd != STDIN_FILENO){
            if(dup2(in_fd, STDIN_FILENO) != -1)
                close(in_fd); /*successfully redirected*/
            else error_exit("dup2");
        }
        execlp(ar[pos], ar[pos], NULL);
        error_exit("execlp last");
    }
    else{
        int fd[2];
        pid_t childpid;

        if ((pipe(fd) == -1) || ((childpid = fork()) == -1)) {
            error_exit("Failed to setup pipeline");
        }
        if (childpid == 0){ /* child executes current command */
            child = 1;
            close(fd[0]);
            if (dup2(in_fd, STDIN_FILENO) == -1) /*read from in_fd */
                perror("Failed to redirect stdin");
            if (dup2(fd[1], STDOUT_FILENO) == -1)   /*write to fd[1]*/
                perror("Failed to redirect stdout");
            else if ((close(fd[1]) == -1) || (close(in_fd) == - 1))
                perror("Failed to close extra pipe descriptors");
            else {
                execlp(ar[pos], ar[pos], NULL);
                error_exit("Failed to execlp");
            }
        }
        close(fd[1]);   /* parent executes the rest of commands */
        close(in_fd);
        pipeline(ar, pos+1, fd[0]);
    }
}

It works completely fine for up to 3 commands, but when it comes to 4 and more it doesnt any more and after hours of analysing, I still cant get where the problem is.

Example: 
./prog ls uniq sort head 

gives: 
sort: stat failed: -: Bad file descriptor
like image 237
Mia Avatar asked Jan 23 '14 11:01

Mia


1 Answers

Not an expert, but it seems the following line is the problem:

((close(fd[1]) == -1) || (close(in_fd) == - 1))

Try not to close in_fd there.

I think, the parent is trying to close the same fd that is closed by child.
When you use dup2() you do not need to close the files as dup2() already closes the file.

like image 69
Dipto Avatar answered Sep 30 '22 16:09

Dipto