Linux 麻煩:/dev/stdin 不適用於套接字
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 來讀取和寫入的雙向流的底層機制(不像一對管道)。
你總是可以使用一個
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()
在套接字上做,那沒有意義。在 BSD 中open("/dev/fd/x")
一點也不像。dup(x)
當文件是管道時感覺就像,但它甚至不是,它實際上與打開命名管道相同(您甚至可以以其他模式(讀取與寫入)打開它以獲取另一端管)。兩種方法都有其優點和缺點。在我看來,您的應用程序應該將 fd 數字作為參數,而不是
/dev/fd/x
首先使用這是一個 hack,例如會導致您浪費 fds。