동적 파일 콘텐츠 생성: "프로세스 실행"을 통해 "파일 열기" 충족

동적 파일 콘텐츠 생성: "프로세스 실행"을 통해 "파일 열기" 충족

읽기 전용 파일인 F.

읽어야 할 프로그램 P이지만 저자는 아닙니다 F.

나는 콘텐츠가 이전이 아닌 읽기를 시도할 때마다 (일반 파일로 간주되는 ) F다른 "생성기" 프로그램에서 나오길 원합니다.GPFF

나는 다음을 시도합니다 :

$ mkfifo /well-known/path/to/F    # line #1
$ G > /well-known/path/to/F       # line #2

이제 P부팅하여 읽으려고 하면 내가 원하는 대로 F생성된 출력을 읽을 수 있는 것 같습니다 . G그러나 G는 결국 한 번만 실행될 수 있기 때문에 한 번만 실행될 수 있습니다! 따라서 실행 중에 나중에 다시 읽어야 P하는 경우 F결국 FIFO를 차단하게 됩니다!

내 질문은,위의 라인 2를 일종의 무한 루프로 묶는 것 외에도위의 내용에 대한 다른 (우아한) 대안이 있습니까?

내가 원하는 것은 어떻게든 파일 열기 시스템 호출에 "후크" 프로그램을 등록하여 파일 열기로 인해 후크 프로그램이 시작되고 파일이 후크 프로그램 출력을 읽기 위해 읽히도록 하는 것입니다. 분명히 여기서는 읽기가 무작위로 보는 것이 아니라 파일의 시작부터 파일의 끝까지 순차적으로 발생한다고 가정합니다.

답변1

FUSE + 소프트 링크(또는 바인드 마운트)는 하나의 솔루션이지만 "우아하다"고 생각하지는 않지만 많은 짐이 있습니다. *BSD에는 더 쉬운 옵션이 있습니다포털 파일 시스템, 이 문제를 해결하기 위해 심볼릭 링크를 사용할 수 있습니다. 수년 전에 Linux로의 포팅이 있었지만 아마도 FUSE를 선호하여 포기된 것 같습니다.

라이브러리를 쉽게 삽입하여 필요한 open()/ open64()libc 호출을 재정의할 수 있습니다. 예를 들어:

#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <dlfcn.h>
#include <stdarg.h>

//  gcc -Wall -rdynamic -fPIC -nostartfiles -shared -ldl -Wl,-soname,open64 \
//       -o open64.so open64.c

#define DEBUG 1
#define dfprintf(fmt, ...) \
    do { if (DEBUG) fprintf(stderr, "[%14s#%04d:%8s()] " fmt, \
          __FILE__, __LINE__, __func__, __VA_ARGS__); } while (0)

typedef int open64_f(const char *pathname, int flags, ...);
typedef int close_f(int fd);
static  open64_f   *real_open64;
static  close_f    *real_close;

static FILE *mypipe=NULL;
static int mypipefd=-1;

//void __attribute__((constructor)) my_init()
void _init()
{
    char **pprog=dlsym(RTLD_NEXT, "program_invocation_name");
    dfprintf("It's alive! argv[0]=%s\n",*pprog);

    real_open64 = dlsym(RTLD_NEXT, "open64");
    dfprintf("Hook %p open64()\n",(void *)real_open64);
    if (!real_open64) printf("error: %s\n",dlerror());

    real_close = dlsym(RTLD_NEXT, "close");
    dfprintf("Hook %p close()\n",(void *)real_close);
    if (!real_close) printf("error: %s\n",dlerror());
}

int open64(const char *pathname, int flags, ...)
{
    mode_t tmpmode=0;
    va_list ap;
    va_start(ap, flags);
    if (flags & O_CREAT) tmpmode=va_arg(ap,mode_t);
    va_end(ap);

    dfprintf("open64(%s,%i,%o)\n",pathname,flags,tmpmode);

    if (!strcmp(pathname,"/etc/passwd")) {
        mypipe=popen("/usr/bin/uptime","r");
        mypipefd=fileno(mypipe);
        dfprintf("  popen()=%p fd=%i\n",mypipe,mypipefd);
        return mypipefd;
    } else {
        return real_open64(pathname,flags,tmpmode);
    }
}

int close(int fd)
{
    int rc;
    dfprintf("close(%i)\n",fd);
    if (fd==mypipefd) {
        rc=pclose(mypipe); // pclose() returns wait4() status
        mypipe=NULL; mypipefd=-1;
        return (rc==-1) ? -1 : 0;
    } else  {
        return real_close(fd);
    }
}

컴파일하고 실행합니다:

$ gcc -Wall -rdynamic -fPIC -nostartfiles -shared -ldl -Wl,-soname,open64   \
    -o open64.so open64.c 
$ LD_PRELOAD=`pwd`/open64.so cat /etc/passwd
19:55:36 up 1110 days,  9:19, 55 users,  load average: 0.53, 0.33, 0.29

open()애플리케이션이 작동하는 방식(libc 호출)에 따라 또는 를 처리해야 할 수도 있습니다 fopen()/fclose(). 위의 코드는 cator 에 대해서는 작동 head하지만 sort호출하기 때문에 작동 하지는 않습니다( / 를 직접 fopen()추가할 수도 있습니다 ).fopen()fclose()

위보다 더 많은 오류 처리 및 온전성 검사가 필요할 수 있습니다(특히 누출을 방지하기 위해 장기 실행되는 프로그램의 경우).이 코드는 동시 열기를 올바르게 처리하지 않습니다..

파이프와 파일은 크게 다르기 때문에 프로그램이 실패할 위험이 있습니다.

그렇지 않으면, 당신이 가지고 있다고 가정악마그리고소캇무한 루프가 없다고 가정할 수 있습니다.

daemon -r -- socat -u EXEC:/usr/bin/uptime PIPE:/tmp/uptime    

여기에는 약간의 단점이 있습니다(여기서 명확해야 함). 공급자가 쓰기를 시작한 다음 차단하므로 요청 시 실행되는 대신 오래된 가동 시간이 표시됩니다. 공급자는 즉각적인 데이터를 적절하게 제공하기 위해 비차단 I/O를 사용해야 합니다. (Unix 도메인 소켓은 보다 전통적인 클라이언트/서버 접근 방식을 허용하지만 이는 직접 연결할 수 있는 FIFO/명명된 파이프와는 다릅니다.)


고쳐 쓰다특정 프로세스/리더가 아닌 임의의 프로세스/리더로 일반화하더라도 동일한 주제를 다루는 이 이후 질문도 참조하세요. 읽을 때 코드를 실행하는 특수 파일을 만드는 방법 (fifo 전용 답변은 동시 읽기로 안정적으로 일반화되지 않습니다.)

답변2

filld당신이 할 수 있는 일은 데몬( 필요할 때마다 채워지기 때문에 데몬이라고 부릅니다)을 갖는 것입니다 .FP

시작하면 FIFO를 열려고 시도합니다(리더가 없기 때문에 차단됩니다). 판독기가 올 때마다 FIFO에 써야 할 내용(예: fork-ing 및 exec-ing G)을 쓰고 FIFO를 닫았다가 다시 엽니다. 성공적으로 열릴 때마다 FSIGPIPE를 받으면 모든 것을 닫고 FIFO에서 다시 차단됩니다.

답변3

무한 루프에는 적어도 두 가지 이유가 있습니다. 당신이 알아차린 것을 제외하고, 당신은 파일에 접근할 때마다 파일이 업데이트되기를 원합니다, 그렇죠? 그렇지 않으면 올바른 내용이 포함된 일반 파일을 만들면 됩니다.

open*() 후킹이 가능하지만 아마도 간단하지는 않을 것입니다.퓨즈가는 길입니다. 운이 좋으면 이 모듈이 이미 존재합니다. 그렇지 않으면 이 경로를 연결하고 나머지를 전달하기 위해 자체 모듈을 작성해야 합니다.

관련 정보