動的ファイルコンテンツの作成:「プロセスの実行」を通じて「ファイルを開く」を満たす

動的ファイルコンテンツの作成:「プロセスの実行」を通じて「ファイルを開く」を満たす

読み取り専用ファイルです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 +ソフトリンク(またはバインドマウント)は1つの解決策ですが、「エレガント」とは思えませんが、多くの荷物があります。 *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 に対しては動作しますheadsort呼び出すので動作しません ( / を直接fopen()追加することもできます )。fopen()fclose()

上記よりも多くのエラー処理と完全性チェックが必要になる場合があります(特に漏れを防ぐために長時間実行されるプログラムの場合)。このコードは同時オープンを正しく処理しません。

パイプとファイルは大きく異なるため、プログラムが失敗する危険があります。

そうでなければ、あなたが持っていると仮定悪魔そしてソカット無限ループがないと仮定できます。

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

これにはいくつかの欠点があります(ここでは明確にする必要があります)。プロバイダが書き込みを開始してブロックするので、要求に応じて実行されるのではなく、古い稼働時間が表示されます。プロバイダは、即時データを適切に提供するために非ブロックI / Oを使用する必要があります。 (Unixドメインソケットはより伝統的なクライアント/サーバーアプローチを可能にしますが、これは直接接続できるFIFO /名前付きパイプとは異なります。)


修正する特定のプロセス/リーダーではなくランダムなプロセス/リーダーに一般化しても、同じトピックを扱うこの後の質問も参照してください。 読み込み時にコードを実行する特別なファイルを作成する方法 (fifo専用の回答は同時読み取りで安定して一般化されません。)

おすすめ記事