2 /dev/fd is a BSD invention AFAICT
source | link

You can always use an LD_PRELOAD trick to for Linux to mimic the SysVBSD 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 SysVBSD 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 SysVBSD. 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.

You can always use an LD_PRELOAD trick to for Linux to mimic the SysV 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 SysV 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 SysV. 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.

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.

1
source | link

You can always use an LD_PRELOAD trick to for Linux to mimic the SysV 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 SysV 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 SysV. 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.