popen, pclose の実装(fork,pipe,dup,execlp)

プロセス間通信,リダイレクション,パイプ処理等によりpopen,pclose 関数を実装する例題です.main 関数内での mypopen, mypclose 関数の利用例はそのまま popen, pclose でも利用可能です.プロセスへの読込み,書込みの両方の例題を含みます.

popen, pclose の実装(fork,pipe,dup,execlp)(list_78.c)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>

#define NMAXPOPEN 10

static struct fp_list {
    pid_t fils;
    FILE *flux;
} p_fplist[NMAXPOPEN];

static int fplookup(FILE *f)
{
    int i;
    for(i=0;i<NMAXPOPEN;i++){
        if(p_fplist[i].flux==f) return i;
    }
    return -1;
}

FILE *mypopen(const char *command,const char *type)
{
    int tube[2];
    FILE *flux;
    pid_t fils;
    int place,on_id,off_id;
    
    if(*type!='r' && *type!='w') return NULL;
    place = fplookup(NULL);
    if(place==-1)      return NULL;
    if(pipe(tube)==-1) return NULL;
    
    if((fils=fork())==-1) {
        close(tube[0]);
        close(tube[1]);
        return NULL;
    }

    switch(*type){
    case 'r': on_id = 0; off_id = 1; break;
    case 'w': on_id = 1; off_id = 0; break;
    }
    switch(fils) {
    case 0:
        close(tube[on_id]);
        close(off_id);
        dup(tube[off_id]);
        close(tube[off_id]);
        execlp("/bin/sh","sh","-c",command,NULL);
        perror("execlp");
        exit(EXIT_FAILURE);
    default:
        close(tube[off_id]);
        flux = fdopen(tube[on_id],type);
        break;
    }
    
    if(flux==NULL) {
        kill(fils,SIGUSR1);
    }
    p_fplist[place].fils = fils;
    p_fplist[place].flux = flux;
    return flux;
}

int mypclose(FILE *flux)
{
    int place,statut;
    
    place = fplookup(flux);
    if(place==-1) return -1;
    
    fflush(p_fplist[place].flux);
    fclose(flux);
    waitpid(p_fplist[place].fils,&statut,0);
    p_fplist[place].flux = NULL;
    
    return statut;
}

int main(int argc,char *argv[])
{
    FILE *f1,*f2;
    char cmd[256],buf[256];
    
    f1 = mypopen("file *","r");
    if(f1==NULL){
        perror("f1"); return 1;
    }
    f2 = mypopen("tr [a-z] [A-Z]","w");
    if(f2==NULL){
        mypclose(f1); perror("f2"); return 1;
    }
    
    while(fgets(buf,sizeof(buf),f1)!=NULL) {
        fputs(buf,f2);
    }
    
    mypclose(f1);
    mypclose(f2);
    
    return 0;
}

実行結果
Gami[28]% ./list78.exe
MAKEFILE:    ASCII MAKE COMMANDS TEXT
LIST_78.C:   ASCII C PROGRAM TEXT
LIST_78.EXE: MS-DOS EXECUTABLE (EXE), OS/2 OR MS WINDOWS
LIST_78.O:   80386 COFF EXECUTABLE NOT STRIPPED - VERSION 30821
Gami[29]%
inserted by FC2 system