1

Linux has this blitheringly annoying peculiarity that /dev/stdin won't work with sockets - it is hardcoded to return ENXIO. Try this:

socat TCP-OPEN:localhost:1234 EXEC:cat\ /dev/stdin,nofork

That's a perfectly reasonable command you'd expect to work, and does on basically every system, except Linux. (I'm using cat as a general example of any tool that opens a filename as the only way for you to specify a specific fd to use.)

The linux kernel is explicitly written to forbid sensible use of /dev/stdin in this way ­— see http://marc.info/?l=ast-users&m=120978595414993.

If you only need unidirectional capability, you can buffer the data in a separate process:

socat TCP-OPEN:localhost:1234 SYSTEM:'cat | thingy /dev/stdin'

It's wasteful, and worse, is useless if thingy is meant to be reading and writing to the same fd, because pipes are unidirectional in Linux.

What are we meant to do? /dev/stdin simply can't be used on Linux for building pipelines with bidirectional pipes, as far as I can tell, because sockets are the only underlying mechanism on Linux that yield a bidi stream with a single fd to read and write from (unlike a pair of pipes).

  • So what's the error, and which systems does that command work on? And, how does cat /dev/stdin usefully differ from cat in the first place, which doesn't try to re-open stdin? – Useless Nov 5 '13 at 16:32
  • I'm using cat as a general example of any tool that opens a filename, as a way for you to specify a specific fd to use. So it's not more useful than normal cat - but for lots of tools, passing a filename on the commandline is the only way to get them to use stdin. The error printed is "No such device or address", from open(2) returning ENXIO. – Nicholas Wilson Nov 5 '13 at 16:35
  • So this is specific to tools that don't take an argument like - to mean use stdin for input? – Useless Nov 5 '13 at 16:37
  • Yes - see the example in Linux's proc(5). Overloading "-" is a bit nasty, and not every application does it. It's a complete pain that you can't open() /dev/stdin on linux when it's a socket, unlike every other system. I'll may have to hack in the "-" kludge to the application if there isn't a better way to do it. – Nicholas Wilson Nov 5 '13 at 16:40
  • I think it's either that or write a wrapper to forward stdin through a pipe or fifo - that's no better than your cat| solution though. – Useless Nov 5 '13 at 17:02
4

You can always use an LD_PRELOAD trick to for Linux to mimic the BSD way. 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);
  }
}

(you may need to wrap more like freopen()).

gcc -Wall -fPIC -shared -o fddup.so fddup.c -ldl

And then:

socat TCP:localhost:22 'EXEC:env LD_PRELOAD=./ddup.so cat /dev/fd/0,nofork'

Note that Linux and BSD are fundamentally different. It's not so much that you can't open /dev/fd/0 when it's a socket, but that /dev/fd/x is a symlink to the file that is open on fd x. You can't do open() on a socket, that wouldn't make sense. open("/dev/fd/x") is not at all a dup(x) like in BSD. It feels like it when the file is a pipe, but it's not even then, it is in effect the same as as opening a named pipe (you can even open it in the other mode (read vs write) to get the other end of the pipe).

Both approaches have their pros and cons. It sounds to me your application should take fd numbers as arguments, not use /dev/fd/x which is a hack in the first place anyway and would for instance cause you to waste fds.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.