2014-12-05 11 views
7

ich eine Funktion haben, die einen Prozess Gabeln, Duplikate Filedeskriptoren für Eingangs- und Ausgangspuffer, und läuft dann execl auf einem Befehl über einen String übergeben in cmd genannt:Capturing Ausgang Statuscode Kindprozess

static pid_t 
c2b_popen4(const char* cmd, int pin[2], int pout[2], int perr[2], int flags) 
{ 
    pid_t ret = fork(); 

    if (ret < 0) { 
     fprintf(stderr, "fork() failed!\n"); 
     return ret; 
    } 
    else if (ret == 0) { 
     /*                                                                             
      Assign file descriptors to child pipes (not shown)...                                                                        
     */ 
     execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 
     fprintf(stderr, "execl() failed!\n"); 
     exit(EXIT_FAILURE); 
    } 
    else { 
     /*                                                                             
      Close parent read and write pipes (not shown)...                                                                        
     */ 
     return ret; 
    } 
    return ret; 
} 

Jede der cmd Instanzen verarbeitet meine Daten korrekt, solange meine Testeingaben korrekt sind.

Wenn schlechte Daten zu einem Kind-Prozess übergeben wird, meine Eltern Programm wird mit einem nicht-Fehlerstatuscode von 0.

bis zur Fertigstellung und Ausfahrt läuft Wenn ich absichtlich in schlechten Eingang setzen - gezielt versuchen, zu erhalten eine der cmd Instanzen in einer erwarteten Weise fehlschlagen - Ich würde gerne wissen, wie Sie den Exit-Status dieser cmd erfassen, so dass ich den richtigen Fehlerstatuscode aus dem übergeordneten Programm vor der Beendigung ausgeben kann.

Wie wird das generell gemacht?

Antwort

11

Sie können über das erste Argument von wait() oder dem zweiten Argumente von waitpid(), und dann mit dem Makros WIFEXITED und WEXITSTATUS damit den Exit-Status des Kindes bekommen.

Zum Beispiel:

pid_t ret = c2b_popen4("myprog", pin, pout, perr, 0); 

if (ret > 0) { 
    int status; 

    if (waitpid(ret, &status, 0) == -1) { 
     perror("waitpid() failed"); 
     exit(EXIT_FAILURE); 
    } 

    if (WIFEXITED(status)) { 
     int es = WEXITSTATUS(status); 
     printf("Exit status was %d\n", es); 
    } 
} 

Ein vereinfachtes Ausführungsbeispiel:

failprog.c:

int main(void) { 
    return 53; 
} 

shellex.c:

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

int main(void) 
{ 
    pid_t p = fork(); 
    if (p == -1) { 
     perror("fork failed"); 
     return EXIT_FAILURE; 
    } 
    else if (p == 0) { 
     execl("/bin/sh", "bin/sh", "-c", "./failprog", "NULL"); 
     return EXIT_FAILURE; 
    } 

    int status; 
    if (waitpid(p, &status, 0) == -1) { 
     perror("waitpid failed"); 
     return EXIT_FAILURE; 
    } 

    if (WIFEXITED(status)) { 
     const int es = WEXITSTATUS(status); 
     printf("exit status was %d\n", es); 
    } 

    return EXIT_SUCCESS; 
} 

Output :

[email protected]:~/src/sandbox$ ./shellex 
exit status was 53 
[email protected]:~/src/sandbox$ 

waitpid() wird mit den mitgelieferten Prozess-ID-Exits, bis der Prozess blockieren. Da Sie Ihre Funktion mit einem popen() Namen aufrufen und Pipes an sie übergeben, wird der untergeordnete Prozess vermutlich nicht schnell beendet, so dass dies wahrscheinlich nicht der richtige Ort für die Überprüfung ist, wenn der Aufruf erfolgreich war. Sie können WNOHANG als dritten Parameter an waitpid() übergeben, um zu überprüfen, ob der Prozess beendet wurde, und 0 zurückgeben, wenn das Kind noch nicht beendet wurde, aber Sie müssen vorsichtig sein, wenn Sie dies tun, da Sie keine Garantien über erhalten welcher Prozess wann ausgeführt wird. Wenn Sie waitpid() mit WNOHANG rufen Sie uns sofort, nachdem sie von c2b_popen4() Rückkehr kann es 0 zurückkehren, bevor Ihr Kind Prozess eine Chance, auszuführen und zu beenden mit einem Fehlercode gehabt hat, und lassen es so aussehen, als ob die Ausführung erfolgreich war, wenn es nur um nicht zu sein erfolgreich.

Wenn der Prozess sofort stirbt, werden Sie Probleme von und Schreiben auf Ihre Rohre zu lesen, so würde eine Option sein waitpid() zu überprüfen, ob Sie einen Fehler aus dem ersten Versuch erhalten, das zu tun, wenn die read() zu überprüfen oder write() schlägt fehl, weil Ihr Kindprozess gestorben ist. Wenn das der Fall ist, können Sie den Exit-Status abrufen und dann Ihr Gesamtprogramm beenden.

Es gibt andere mögliche Strategien, einschließlich der SIGCHLD Signal zu kontrollieren, da das wird erhöht werden, wenn einer Ihrer Kindprozesse stirbt. Es wäre in Ordnung sein, zum Beispiel, _exit() direkt von Ihrem Signal-Handler aufrufen, nachdem für das Kind Prozess wartet (waitpid() in einem Signal-Handler Aufruf ist auch sicher) und dessen Exit-Status zu bekommen.

+1

Wenn ich Ihren ersten Code-Block (das „zum Beispiel“ Beispiel) ausgeführt wird, hängt der Prozess auf dem 'waitpid()' Anruf. –

+1

@AlexReynolds: Das ist, wie es standardmäßig funktioniert, siehe Update zu beantworten. Sie werden eine Art von Strategie müssen zum Nachweis und dann mit der Möglichkeit der Child-Prozesse zu tun endet früh. –