2014-01-23 9 views
7

Ich weiß, dass dieses Thema schon mehrere Male aufkam, aber ich bin immer noch an einem Punkt fest. Ich muss ein Programm schreiben, das cmd1 | cmd2 | cmd3 ... Piping emuliert.Rekursive Rohrleitungen in Unix wieder

Mein Code ist hier: 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]); 
    } 
} 

Es ist für bis zu 3 Befehle ganz gut funktioniert, aber wenn es um 4 kommt und es nicht mehr tut und nach Stunden zu analysieren, ich kann immer noch nicht bekommen, wo das Problem ist.

Example: 
./prog ls uniq sort head 

gives: 
sort: stat failed: -: Bad file descriptor 
+0

Was passiert in Ihrer letzten 'if'-Klausel (' childpid == 0') im "wahren" Fall? Sollten die letzten drei Zeilen nach dieser Klausel nicht in einem else-Block stehen? – krlmlr

+0

krlmlr: war meine Idee vorher, nachdem ich es im else Block genommen habe ändert nichts nichts – Mia

Antwort

5

kein Experte, aber es scheint die folgende Zeile ist das Problem:

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

Versuchen Sie nicht in_fd es zu schließen.

Ich denke, der Elternteil versucht, die gleiche fd zu schließen, die von Kind geschlossen wird.
Wenn Sie dup2() verwenden, müssen Sie die Dateien nicht schließen, da dup2() die Datei bereits schließt.

+0

Ich konnte @ Mias Problem kopieren und diesen Vorschlag mit Erfolg anwenden. – mah

+0

wow, danke! so einfach war es. – Mia

+0

Kommt hier von einer [Folgefrage] (http://stackoverflow.com/questions/21332970/pipe-with-fork-with-recursion-file-descriptors-handling/21333381?noredirect=1#comment32160059_21333381), ich verstehe eines nicht: Ich kann nicht finden, wo es auf der 'man'-Seite steht, dass' dup2() 'das * erste * Argument schließt; es versucht und schließt die zweite, die 'STDOUT_FILENO' oder' STDIN_FILENO' ist. 'fd [1]' und 'in_fd' sollten immer noch geöffnet sein. Man-Seitenbereich: "int dup2 (int oldfd, int newfd); [...] dup2() macht newfd zur Kopie von oldfd, schließt nefdf wenn nötig ..." – Evert