
読み取り専用ファイルですF
。
読むべきプログラムですP
が、著者ではありませんF
。
私は、コンテンツが以前ではなく読み取りを試みるたびに(通常のファイルと見なされる)F
他の「ジェネレータ」プログラムから来たいと思います。G
P
F
F
私は以下を試してみます:
$ 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()
。上記のコードはcat
or に対しては動作しますhead
がsort
呼び出すので動作しません ( / を直接fopen()
追加することもできます )。fopen()
fclose()
上記よりも多くのエラー処理と完全性チェックが必要になる場合があります(特に漏れを防ぐために長時間実行されるプログラムの場合)。このコードは同時オープンを正しく処理しません。。
パイプとファイルは大きく異なるため、プログラムが失敗する危険があります。
そうでなければ、あなたが持っていると仮定悪魔そしてソカット無限ループがないと仮定できます。
daemon -r -- socat -u EXEC:/usr/bin/uptime PIPE:/tmp/uptime
これにはいくつかの欠点があります(ここでは明確にする必要があります)。プロバイダが書き込みを開始してブロックするので、要求に応じて実行されるのではなく、古い稼働時間が表示されます。プロバイダは、即時データを適切に提供するために非ブロックI / Oを使用する必要があります。 (Unixドメインソケットはより伝統的なクライアント/サーバーアプローチを可能にしますが、これは直接接続できるFIFO /名前付きパイプとは異なります。)
修正する特定のプロセス/リーダーではなくランダムなプロセス/リーダーに一般化しても、同じトピックを扱うこの後の質問も参照してください。 読み込み時にコードを実行する特別なファイルを作成する方法 (fifo専用の回答は同時読み取りで安定して一般化されません。)
答え2
filld
あなたができることは、デーモン(必要に応じていっぱいになるのでデーモンと呼ばれます)を持つことです。F
P
起動すると、FIFOを開こうとします(リーダーがないためブロックされます)。リーダーが来るたびにFIFOに書き込む必要がある内容(fork
-ingやexec
-ingなどG
)を書き、FIFOを閉じてからもう一度開きます。正常に開くたびにF
SIGPIPEを受け取ると、すべてが閉じてFIFOから再びブロックされます。
答え3
無限ループには少なくとも2つの理由があります。あなたが気づいたことを除いて、あなたがファイルにアクセスするたびにファイルが更新されることを望みます、そうですか?それ以外の場合は、正しい内容を含む一般的なファイルを作成します。
open*() フックは可能ですが、おそらく簡単ではありません。ヒューズ行く道です。運が良ければ、このモジュールはすでに存在します。それ以外の場合は、このパスを接続して残りを渡すために独自のモジュールを作成する必要があります。