Linux 有一个令人讨厌的特性,它/dev/stdin
不适用于套接字 - 它被硬编码为返回 ENXIO。尝试这个:
socat TCP-OPEN:localhost:1234 EXEC:cat\ /dev/stdin,nofork
这是一个完全合理的命令,您希望它能工作,而且基本上在除 Linux 之外的所有系统上都可以工作。 (我使用cat
任何打开文件名的工具作为一般示例,作为您指定要使用的特定 fd 的唯一方法。)
Linux 内核被明确编写为禁止/dev/stdin
以这种方式合理使用 — 请参阅http://marc.info/?l=ast-users&m=120978595414993。
如果只需要单向能力,可以在单独的进程中缓冲数据:
socat TCP-OPEN:localhost:1234 SYSTEM:'cat | thingy /dev/stdin'
这是浪费的,更糟糕的是,如果thingy
要读取和写入同一个 fd,则毫无用处,因为 Linux 中的管道是单向的。
我们要做什么?/dev/stdin
据我所知,根本不能在 Linux 上使用双向管道构建管道,因为套接字是 Linux 上唯一产生具有单个 fd 的双向流以供读取和写入的底层机制(与一对管道)。
答案1
你总是可以使用一个LD_PRELOAD
技巧来让 Linux 模仿 BSD 的方式。fddup.c
:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int whichfd(const char *pathname)
{
unsigned int fd;
if (sscanf(pathname, "/dev/fd/%u", &fd) == 1)
return fd;
else
return -1;
}
int open(const char *pathname, int flags, mode_t mode)
{
static int (*orig)(const char *, int, mode_t) = 0;
int fd = whichfd(pathname);
if (fd >= 0)
return dup(fd);
else {
if (!orig)
orig = dlsym(RTLD_NEXT,"open");
if (!orig) abort();
return orig(pathname, flags, mode);
}
}
FILE *fopen(const char *path, const char *mode)
{
static FILE *(*orig)(const char *, const char *) = 0;
int fd = whichfd(path);
if (fd >= 0)
return fdopen(dup(fd), mode);
else {
if (!orig)
orig = dlsym(RTLD_NEXT,"fopen");
if (!orig) abort();
return orig(path, mode);
}
}
(你可能需要包裹得更像freopen()
)。
gcc -Wall -fPIC -shared -o fddup.so fddup.c -ldl
进而:
socat TCP:localhost:22 'EXEC:env LD_PRELOAD=./ddup.so cat /dev/fd/0,nofork'
请注意,Linux 和 BSD 有着根本的不同。/dev/fd/0
当它是一个套接字时,并不是说你无法打开它,而是它/dev/fd/x
是一个在 fd x 上打开的文件的符号链接。你不能open()
在套接字上做,那没有意义。与 BSDopen("/dev/fd/x")
完全不同。dup(x)
当文件是管道时感觉就像这样,但事实并非如此,它实际上与打开命名管道相同(您甚至可以以其他模式(读与写)打开它以获得另一端管)。
两种方法都有其优点和缺点。在我看来,你的应用程序应该将 fd 数字作为参数,而不是使用/dev/fd/x
它,这首先是一种黑客行为,例如会导致你浪费 fd。