O FUSE + um soft-link (ou um monte de bind) é uma solução, embora eu não o considere "elegante", há muita bagagem. No * BSD você teria a opção mais simples de portalfs , com o qual você poderia resolver o problema com um link simbólico - havia uma porta para o Linux há muitos anos, mas parece ter sido descartada, presumivelmente em favor do FUSE.
Você pode facilmente injetar uma biblioteca para substituir as chamadas de open()
/ open64()
libc necessárias. por exemplo:
#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);
}
}
Compile e execute:
$ 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
Dependendo de como o aplicativo funciona exatamente (chamadas libc), talvez seja necessário manipular open()
ou fopen()/fclose()
. O acima funciona para cat
ou head
, mas não sort
, pois chama fopen()
(é fácil adicionar fopen()
/ fclose()
ao anterior também).
Você provavelmente precisará de mais tratamento de erros e verificação de integridade do que o acima (especialmente com um programa de longa duração, para evitar vazamentos). Este código não manipula corretamente as janelas simultâneas .
Como um pipe e um arquivo têm diferenças óbvias, existe o risco de um mau funcionamento do programa.
Caso contrário, supondo que você tenha o daemon e socat você pode fingir que não tem um loop infinito:
daemon -r -- socat -u EXEC:/usr/bin/uptime PIPE:/tmp/uptime
Isto tem a pequena desvantagem (que deve ser evidente aqui) do programa provedor começar a escrever e depois bloquear, assim você verá um tempo de atividade antigo, em vez de ser executado sob demanda. Seu provedor precisaria usar E / S sem bloqueio para fornecer dados just-in-time adequadamente. (Um soquete de domínio unix permitiria uma abordagem cliente / servidor mais convencional, mas isso não é o mesmo que um FIFO / named pipe que você pode simplesmente incluir).
Atualize veja também esta última pergunta que cobre o mesmo tópico, embora generalizada para processos / leitores arbitrários ao invés de um específico: Como posso fazer um arquivo especial que executa código quando lido de (note que as respostas somente fifo não serão generalizadas de forma confiável para leituras simultâneas)