Linux

Linux 麻煩:/dev/stdin 不適用於套接字

  • April 22, 2016

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。

引用自:https://unix.stackexchange.com/questions/98958