
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "basic.h"

#ifndef lint
static char rcsid[]
#ifdef __GNUC__
__attribute__ ((unused))
#endif /* __GNUC__ */
     = "$Id: libcacheglibcwrapper.c,v 1.7 1999/10/21 12:49:22 stefan Exp $";
#endif /* lint */

#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
#endif
#include <stdarg.h>
#include <sys/uio.h>
#if defined(HAVE_ACCEPT) || defined(HAVE_SOCKET) || defined(HAVE_SOCKETPAIR)
# include <sys/socket.h>
#endif

#if defined(HAVE_SYSLOG) && defined(WANT_SYSLOG)
# include <syslog.h>

# define MYLOG(x) syslog ##x
#define MYLEVEL LOG_INFO
#else 
# define MYLOG(x) do { } while(0)
#undef MYLEVEL
#endif

#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define MAXFD 1024

/* glibc pointers */

#ifdef HAVE_FDATASYNC
 int     (*xxglibc_fdatasync)(int) = NULL;
#endif
#ifdef HAVE_FSYNC
 int     (*xxglibc_fsync)(int) = NULL;
#endif
#ifdef HAVE_OPEN /* hehe */
 int     (*xxglibc_open)(const char *, int, ...) = NULL;
#endif
#ifdef HAVE_READ
 ssize_t (*xxglibc_read)(int fd, void *, size_t) = NULL;
#endif
#ifdef HAVE_WRITE
 ssize_t (*xxglibc_write)(int fd, const char *, size_t) = NULL;
#endif
#ifdef HAVE_CLOSE
 int     (*xxglibc_close)(int) = NULL;
#endif
#ifdef HAVE_READV
 int     (*xxglibc_readv)(int, const struct iovec *, int) = NULL;
#endif
#ifdef HAVE_WRITEV
 int     (*xxglibc_writev)(int, const struct iovec *, int) = NULL;
#endif
#ifdef HAVE_LSEEK
 off_t   (*xxglibc_lseek)(int, off_t, int) = NULL;
#endif
#ifdef HAVE_LLSEEK
 int     (*xxglibc_llseek)(int, off_t, int) = NULL;
#endif
#ifdef HAVE_MMAP
 void *  (*xxglibc_mmap)(void *, size_t, int, int, int, off_t) = NULL;
#endif
#ifdef HAVE_MUNMAP
 int     (*xxglibc_munmap)(void *, size_t) = NULL;
#endif
#ifdef HAVE_MSYNC
 int     (*xxglibc_msync)(void *, size_t, int) = NULL;
#endif
#ifdef HAVE_PWRITE
 ssize_t        (*xxglibc_pwrite)(int, const void *, size_t, off_t) = NULL;
#endif
#ifdef HAVE_PREAD
 ssize_t        (*xxglibc_pread)(int, void *, size_t, off_t) = NULL;
#endif
#ifdef HAVE_SYNC
 int            (*xxglibc_sync)(void) = NULL;
#endif
#ifdef HAVE_DUP
 int            (*xxglibc_dup)(int) = NULL;
#endif
#ifdef HAVE_DUP2
 int            (*xxglibc_dup2)(int, int) = NULL;
#endif
#ifdef HAVE_FCNTL
 int            (*xxglibc_fcntl)(int, int, ...) = NULL;
#endif
#ifdef HAVE_SOCKET
int            (*xxglibc_socket)(int, int, int) = NULL;
#endif
#ifdef HAVE_ACCEPT
int            (*xxglibc_accept)(int, struct sockaddr *, socklen_t *) = NULL;
#endif
#ifdef HAVE_SOCKETPAIR
int           (*xxglibc_socketpair)(int d, int type, int protocol, int sv[2]) =
NULL;
#endif
#ifdef HAVE_PIPE
int           (*xxglibc_pipe)(int modus[2]) = NULL;
#endif
#ifdef HAVE_STAT
int           (*xxglibc_stat)(const char *, struct stat *) = NULL;
#endif
#ifdef HAVE_FSTAT
int           (*xxglibc_fstat)(int, struct stat *) = NULL;
#endif
#ifdef HAVE_LSTAT
int           (*xxglibc_lstat)(const char *, struct stat *) = NULL;
#endif



typedef struct {
  void *ptr;
  char *name;
  u64 cnt_in;
  u64 cnt_out;
} rTab;


typedef struct {
  char *name;
  s64 pos;        /* no, we are not really 63-bit clean, not now  */
  char must_seek; /* must perform a seek to make pos valid ? */
  unsigned ref_cnt; /* for dup, etc */
} FDlist;

static FDlist **myfd;

#define XFUNC(x) { &xxglibc_##x, #x, 0, 0 }

static rTab replTab[] = {
#ifdef HAVE_FSYNC
  XFUNC(fsync),
#endif
#ifdef HAVE_FDATASYNC
  XFUNC(fdatasync),
#endif
#ifdef HAVE_OPEN
  XFUNC(open),
#endif
#ifdef HAVE_READ
  XFUNC(read),
#endif
#ifdef HAVE_WRITE
  XFUNC(write),
#endif
#ifdef HAVE_CLOSE
  XFUNC(close),
#endif
#ifdef HAVE_READV
  XFUNC(readv),
#endif
#ifdef HAVE_WRITEV
  XFUNC(writev),
#endif
#ifdef HAVE_LSEEK
  XFUNC(lseek),
#endif
#ifdef HAVE_LLSEEK
  XFUNC(llseek),
#endif
#ifdef HAVE_MMAP
  XFUNC(mmap),
#endif
#ifdef HAVE_MUNMAP
  XFUNC(munmap),
#endif
#ifdef HAVE_MSYNC
  XFUNC(msync),
#endif
#ifdef HAVE_PWRITE
  XFUNC(pwrite),
#endif
#ifdef HAVE_PREAD
  XFUNC(pread),
#endif
#ifdef HAVE_SYNC
  XFUNC(sync),
#endif
#ifdef HAVE_DUP
  XFUNC(dup),
#endif
#ifdef HAVE_DUP2
  XFUNC(dup2),
#endif
#ifdef HAVE_FCNTL
  XFUNC(fcntl),
#endif
#ifdef HAVE_SOCKET
  XFUNC(socket),
#endif
#ifdef HAVE_ACCEPT
  XFUNC(accept),
#endif
#ifdef HAVE_SOCKETPAIR
  XFUNC(socketpair),
#endif
#ifdef HAVE_PIPE
  XFUNC(pipe),
#endif
#ifdef HAVE_STAT
  XFUNC(stat),
#endif
#ifdef HAVE_FSTAT
  XFUNC(fstat),
#endif
#ifdef HAVE_LSTAT
  XFUNC(lstat),
#endif
  { 0, 0, 0, 0 }
};

#undef XFUNC

enum { 
#ifdef HAVE_FSYNC
FSYNC_IDX,
#endif
#ifdef HAVE_FDATASYNC
FDATASYNC_IDX,
#endif
#ifdef HAVE_OPEN
OPEN_IDX,
#endif
#ifdef HAVE_READ
READ_IDX,
#endif
#ifdef HAVE_WRITE
WRITE_IDX,
#endif
#ifdef HAVE_CLOSE
CLOSE_IDX,
#endif
#ifdef HAVE_READV
READV_IDX,
#endif
#ifdef HAVE_WRITEV
WRITEV_IDX,
#endif
#ifdef HAVE_LSEEK
LSEEK_IDX,
#endif
#ifdef HAVE_LLSEEK
LLSEEK_IDX,
#endif
#ifdef HAVE_MMAP
MMAP_IDX,
#endif
#ifdef HAVE_MUNMAP
MUNMAP_IDX,
#endif
#ifdef HAVE_MSYNC
MSYNC_IDX,
#endif
#ifdef HAVE_PWRITE
PWRITE_IDX,
#endif
#ifdef HAVE_PREAD
PREAD_IDX,
#endif
#ifdef HAVE_SYNC
SYNC_IDX,
#endif
#ifdef HAVE_DUP
DUP_IDX,
#endif
#ifdef HAVE_DUP2
DUP2_IDX,
#endif
#ifdef HAVE_FCNTL
FCNTL_IDX,
#endif
#ifdef HAVE_SOCKET
SOCKET_IDX,
#endif
#ifdef HAVE_ACCEPT
ACCEPT_IDX,
#endif
#ifdef HAVE_SOCKETPAIR
SOCKETPAIR_IDX,
#endif
#ifdef HAVE_PIPE
PIPE_IDX,
#endif
#ifdef HAVE_STAT
STAT_IDX,
#endif
#ifdef HAVE_FSTAT
FSTAT_IDX,
#endif
#ifdef HAVE_LSTAT
LSTAT_IDX,
#endif
REALLY_DUMMY_JUST_BECAUSE_OF_THE_COMMA
};

/* last_called_func is a hack to find the
 * func which caused a error condition.
 * (the last function that calls mark_in)
 */
static int last_called_func = 0;

static inline void mark_in(int func_idx)
{
  replTab[func_idx].cnt_in++;
  last_called_func = func_idx;
}

static inline void mark_out(int func_idx)
{
  replTab[func_idx].cnt_out++;
}

/* check if an given fd exists and is
   in range */

static inline int bad_fd(fd)
{
  if(fd >= MAXFD || ! myfd[fd]) {
    MYLOG((MYLEVEL, "invalid file descriptor %d in func %s",
	   fd, replTab[last_called_func].name));
    return 1;
  }
  return 0;
}

/* this is for functions like pipe/open where we can
 * expect that the returned fd was not allocated before.
 * This is a "paranoid" function if we assume that this
 * source is non-broken ;-}
 */

static inline void expect_free_fd(int fd)
{
  if(fd >= MAXFD || myfd[fd]) {
    MYLOG((MYLEVEL, "unexpected inconsistency: fd=%d in func %s",
	   fd, replTab[last_called_func].name));
  }
}

/* Function entry check macro. */

#define ENTRY_CHECK(fd) if(bad_fd(fd)) { errno = EBADF; return -1; } \
                        do  { } while(0)


/* returns an error and sets errno if not set */

static inline int mark_error(int fd)
{ /* for sanity we set the file-position to -1
   * and force a seek.
   */
  ENTRY_CHECK(fd);
  myfd[fd]->pos = -1;
  myfd[fd]->must_seek = 1;
  if(!errno)
    errno = EBADF; /* this is not really good */
  return -1;
}


/* replacement functions */

#ifdef HAVE_OPEN
extern int open(const char *fn, int flag, ...)
{
  int mode;
  int rc;
  va_list a;

  mark_in(OPEN_IDX);

  MYLOG((MYLEVEL, "open \"%s\"", fn));
#if !defined(WANT_SYNC)
  flag &= ~O_SYNC;
#endif
  if(flag & O_CREAT) {
    va_start(a, flag);
    mode = va_arg(a, mode_t);
    va_end(a);
    rc = TEMP_FAILURE_RETRY(xxglibc_open(fn, flag, mode));
    mark_out(OPEN_IDX);
  } else {
    rc = TEMP_FAILURE_RETRY(xxglibc_open(fn, flag));
    mark_out(OPEN_IDX);
  }

  if(rc >= 0) {
    expect_free_fd(rc);
    if(myfd[rc] == NULL) {
      myfd[rc] = malloc(sizeof(FDlist));
      if(!myfd[rc]) {
	/* ooops */
	close(rc);
	errno = ENOMEM; /* oops, POSIX */
	return -1;
      }
    }
    memset(myfd[rc], 0, sizeof(FDlist));
    myfd[rc]->name = strdup(fn);
    myfd[rc]->ref_cnt = 1;
  }
  return rc;
}
#endif

#ifdef HAVE_READ
extern ssize_t read(int fd, void *buf, size_t size)
{
  ssize_t rc;

  mark_in(READ_IDX);
  ENTRY_CHECK(fd);

  if(myfd[fd]->must_seek) {
    rc = TEMP_FAILURE_RETRY(xxglibc_pread(fd, buf, size, myfd[fd]->pos));
    mark_out(PREAD_IDX);
    if(rc != -1) {
      myfd[fd]->pos += rc;
    } else {
      return mark_error(fd);
    }
    return rc;
  }

  rc = TEMP_FAILURE_RETRY(xxglibc_read(fd, buf, size));
  mark_out(READ_IDX);
  if(rc != -1) {
    myfd[fd]->pos += rc;
  }  else {
    return mark_error(fd);
  }
  return rc;
}
#endif

#ifdef HAVE_WRITE
extern ssize_t write(int fd, const __ptr_t buf, size_t count)
{
  ssize_t rc;

  mark_in(WRITE_IDX);
  ENTRY_CHECK(fd);

  if(myfd[fd]->must_seek) {
    rc = TEMP_FAILURE_RETRY(xxglibc_pwrite(fd, buf, count, myfd[fd]->pos));
    mark_out(PWRITE_IDX);
    if(rc != -1) {
      myfd[fd]->pos += rc;
    }  else {
      return mark_error(fd);
    }
    return rc;
  }   
  
  rc = TEMP_FAILURE_RETRY(xxglibc_write(fd, buf, count));
  mark_out(WRITE_IDX);
  if(rc != -1) {
    myfd[fd]->pos += rc;
  } else {
    return mark_error(fd);
  }
  return rc;
}
#endif

#ifdef HAVE_CLOSE
static inline void mydump_fd(int fd)
{
    MYLOG((MYLEVEL, "closing fd %d (%s)",
	   fd, (myfd[fd]->name) ? myfd[fd]->name : "unknown !"));
}

static inline void myfd_free(int fd)
{
  if(! myfd[fd])
    return;
  myfd[fd]->ref_cnt--;
  if(! myfd[fd]->ref_cnt) {
    /* destroy completely */
    if(myfd[fd]->name) {
      free(myfd[fd]->name);
      myfd[fd]->name = 0;
    }
    free(myfd[fd]);
  }
  myfd[fd] = 0;
}

extern int close(int fd)
{
  int rc;

  mark_in(CLOSE_IDX);
  ENTRY_CHECK(fd);

  rc = TEMP_FAILURE_RETRY(xxglibc_close(fd));
  mark_out(CLOSE_IDX);
  if(!rc) {
    mydump_fd(fd);
    myfd_free(fd);
  }
  return rc;
}
#endif

#ifdef HAVE_FSYNC
extern int fsync(int fd)
{
  mark_in(FSYNC_IDX);
  ENTRY_CHECK(fd);

#ifdef WANT_SYNC
  mark_out(FSYNC_IDX);
  return xxglibc_fsync(fd);
#endif

  return 0;
}
#endif

#ifdef HAVE_FDATASYNC
extern int fdatasync(int fd)
{
  mark_in(FDATASYNC_IDX);
  ENTRY_CHECK(fd);

#ifdef WANT_SYNC
  mark_out(FDATASYNC_IDX);
  return xxglibc_fdatasync(fd);
#endif
  return 0;
}
#endif

#ifdef HAVE_READV
extern int readv(int fd, const struct iovec *vector, int count)
{
  off_t rc;

  mark_in(READV_IDX);
  ENTRY_CHECK(fd);

  if(myfd[fd]->must_seek && count == 1) {
    /* handle special case: readv(count = 1) and must seek is set.
       this can be easily done without seeking (pread). */
    
    rc = TEMP_FAILURE_RETRY(xxglibc_pread(fd, vector->iov_base,
					  vector->iov_len, myfd[fd]->pos));
    mark_out(PREAD_IDX);
    if(rc >= 0) {
      myfd[fd]->pos += rc; /* must_seek is already set. */
    } else {
      return mark_error(fd);
    }
    return rc;
  }
  
  if(count == 1) { /* nonseek */
    rc = TEMP_FAILURE_RETRY(xxglibc_read(fd, vector->iov_base,
					 vector->iov_len));
    mark_out(READ_IDX);
    myfd[fd]->must_seek = 1; /* the next one must seek */
    if(rc >= 0) {
      myfd[fd]->pos += rc;
    } else {
      return mark_error(fd);
    }
    return rc;
  }
  /* count != 1 */

  if(myfd[fd]->must_seek) {
    rc = TEMP_FAILURE_RETRY(xxglibc_lseek(fd, myfd[fd]->pos, SEEK_SET));
    mark_out(LSEEK_IDX);
    if(rc == -1) {
      myfd[fd]->pos = -1; /* oops */
      return rc;
    }
  } else {
    return mark_error(fd);
  }
  rc = 0;
  if(count) {
    rc = TEMP_FAILURE_RETRY(xxglibc_readv(fd, vector, count));
    mark_out(READV_IDX);
  }
  if(rc >= 0) {
    myfd[fd]->pos += rc;
  } else {
    return mark_error(fd);
  }
  return rc;
}
#endif

#ifdef HAVE_WRITEV
extern int writev(int fd, const struct iovec *vector, int count)
{
  off_t rc;

  mark_in(WRITEV_IDX);
  ENTRY_CHECK(fd);

  if(myfd[fd]->must_seek && count == 1) {
    /* handle special case: writev(count = 1) and must seek is set.
       this can be easily done without seeking (pwrite).
       We do this fully manually here, because I'm not sure if
       our own write(...)-call is used or glibc's one */
    rc = TEMP_FAILURE_RETRY(xxglibc_pwrite(fd, vector->iov_base,
					 vector->iov_len, myfd[fd]->pos));
    mark_out(PWRITE_IDX);
    if(rc >= 0) {
      myfd[fd]->pos += rc; /* must_seek is already set. */
    } else {
      return mark_error(fd);
    }
    return rc;
  }
  
  if(count == 1) { /* nonseek */
    rc = TEMP_FAILURE_RETRY(xxglibc_write(fd, vector->iov_base,
					vector->iov_len));
    mark_out(WRITE_IDX);
    //myfd[fd]->must_seek = 1; /* the next one must seek */
    if(rc >= 0) {
      myfd[fd]->pos += rc;
    } else {
      return mark_error(fd);
    }
    return rc;
  }
  /* count != 1 */
  
  if(myfd[fd]->must_seek) {
    rc = TEMP_FAILURE_RETRY(xxglibc_lseek(fd, myfd[fd]->pos, SEEK_SET));
    mark_out(LSEEK_IDX);
    if(rc == -1) {
      return mark_error(fd);
    }
  } else {
    myfd[fd]->must_seek = 1; /* the next one must seek */
  }
  rc = 0;
  if(count) {
    rc = TEMP_FAILURE_RETRY(xxglibc_writev(fd, vector, count));
    mark_out(WRITEV_IDX);
  }
  if(rc >= 0) {
    myfd[fd]->pos += rc;
  } else {
    return mark_error(fd);
  }
  return rc;
}
#endif

#ifdef HAVE_LSEEK
extern off_t lseek(int fd, off_t offs, int whence)
{
  int rc;

  mark_in(LSEEK_IDX);
  ENTRY_CHECK(fd);

  if(whence == SEEK_SET) {
    if(myfd[fd]->pos == offs) /* avoid making dirty if seek matches current pos */
      return offs;
    myfd[fd]->pos = offs;
    myfd[fd]->must_seek = 1;
    return offs;
  }
  /* not SEEK_SET, so simply seek */
  rc = TEMP_FAILURE_RETRY(xxglibc_lseek(fd, offs, whence));
  mark_out(LSEEK_IDX);
  if(rc != (off_t) -1) {
    myfd[fd]->must_seek = 0;
    myfd[fd]->pos = rc;
  } else {
    return mark_error(fd);
  }
  return rc;
}
#endif

#ifdef HAVE_LLSEEK
extern int llseek(int fd, off_t offs, int whence)
{
  off_t rc;

  mark_in(LLSEEK_IDX);
  ENTRY_CHECK(fd);

  rc = TEMP_FAILURE_RETRY(xxglibc_llseek(fd, offs, whence));
  mark_out(LLSEEK_IDX);
  if(rc == -1) {
    return mark_error(fd);
  }

  myfd[fd]->pos = rc;
  myfd[fd]->must_seek = 0;
  
  return rc;
}
#endif

#ifdef HAVE_MMAP
extern void *mmap(void *b, size_t l, int p, int f, int fd, off_t of)
{
  void *rc;

  mark_in(MMAP_IDX);
  rc = xxglibc_mmap(b, l, p, f, fd, of);
  mark_out(MMAP_IDX);
  return rc;
}
#endif

#ifdef HAVE_MUNMAP
extern int munmap(void * b, size_t s)
{
  int rc;
  
  mark_in(MUNMAP_IDX);
  rc = xxglibc_munmap(b, s);
  mark_out(MUNMAP_IDX);
  return rc;
}
#endif

#ifdef HAVE_MYSYNC
extern int msync(void *p, size_t l, int fl)
{
  mark_in(MSYNC_IDX);
#ifdef WANT_SYNC
  mark_out(MSYNC_IDX);
  return xxglibc_msync(p, l, fl);
#endif
  return 0;
}
#endif

#ifdef HAVE_PWRITE
extern ssize_t pwrite(int fd, const void *b, size_t sz, off_t offs)
{
  ssize_t rc;

  mark_in(PWRITE_IDX);
  ENTRY_CHECK(fd);

  rc = TEMP_FAILURE_RETRY(xxglibc_pwrite(fd, b, sz, offs));
  mark_out(PWRITE_IDX);
  return rc;
}
#endif

#ifdef HAVE_PREAD
extern ssize_t pread(int fd, void *b, size_t sz, off_t offs)
{
  ssize_t rc;
  
  mark_in(PREAD_IDX);
  ENTRY_CHECK(fd);

  rc = TEMP_FAILURE_RETRY(xxglibc_pread(fd, b, sz, offs));
  mark_out(PREAD_IDX);
  return rc;
}
#endif

#ifdef HAVE_SYNC
extern int sync(void)
{
  mark_in(SYNC_IDX);
#ifdef WANT_SYNC
  mark_out(SYNC_IDX);
  return xxglibc_sync();
#endif
  return 0;
}
#endif

#ifdef HAVE_DUP
extern int dup(int oldfd)
{
  int rc = -1;

  mark_in(DUP_IDX);
  ENTRY_CHECK(oldfd);

  rc = TEMP_FAILURE_RETRY(xxglibc_dup(oldfd));
  mark_out(DUP_IDX);
  if(rc == -1) {
    return rc;
  }
  expect_free_fd(rc);
  myfd[rc] = myfd[oldfd];
  myfd[rc]->ref_cnt++;
  return rc;
}
#endif

#ifdef HAVE_DUP2
extern int dup2(int oldfd, int newfd)
{
  int rc = -1;

  mark_in(DUP_IDX);
  ENTRY_CHECK(oldfd);

  if(newfd >= MAXFD) {
    errno = EINVAL;
    return -1;
  }

  rc = TEMP_FAILURE_RETRY(xxglibc_dup2(oldfd, newfd));
  mark_out(DUP_IDX);
  if(rc == -1) {
    return rc;
  }
  if(!myfd[oldfd]) {
    MYLOG((MYLEVEL, "trouble in dup2(), oldfd %d does not exists", oldfd));
    return rc;
  }
  /* free oldfd_resources if exists */
  myfd_free(rc);
  myfd[rc] = myfd[oldfd];
  myfd[rc]->ref_cnt++;
  return rc;
}
#endif

#ifdef HAVE_FCNTL
extern int fcntl(int fd, int cmd, ...)
{
  int rc = -1;
  long arg;
  int need_arg;
  va_list a;
  
  mark_in(FCNTL_IDX);
  ENTRY_CHECK(fd);

  switch(cmd) {
  case F_GETFD:
  case F_GETFL:
  case F_GETLK:
  case F_GETOWN:
    need_arg = 0;
    break;
  case F_DUPFD:
  case F_SETFD:
  case F_SETFL:
  case F_SETLK:
  case F_SETLKW:
  case F_SETOWN:
    need_arg = 1;
    break;
  default:
    need_arg = -1;
    break;
  }

  if(need_arg == -1) {
    MYLOG((MYLEVEL, "fcntl cmd = %d: unknown command", cmd));
    errno = EINVAL;
    return -1;
  }

  if(need_arg) {
    va_start(a, cmd);
    arg = va_arg(a, long);
    va_end(a);
    rc = TEMP_FAILURE_RETRY(xxglibc_fcntl(fd, cmd, arg));
    mark_out(FCNTL_IDX);
  } else {
    rc = TEMP_FAILURE_RETRY(xxglibc_fcntl(fd, cmd));
    mark_out(FCNTL_IDX);
  }

  if(rc == -1) {
    return rc;
  }
  if(cmd == F_DUPFD) {
#if 0
    MYLOG((MYLEVEL, "duped fd = %d arg = %ld rc = %ld", fd, arg, rc));
#endif
    /* created a new fd result in rc if in range */
    if(rc >= MAXFD) {
      errno = EINVAL;
      close(rc);
      return -1;
    }
    myfd_free(rc);
    myfd[rc] = myfd[fd];
    myfd[rc]->ref_cnt++;
  }
  return rc;
}
#endif

#ifdef HAVE_SOCKET
extern int socket(int domain, int type, int protocol)
{
  int rc;
  char txt[255];

  mark_in(SOCKET_IDX);
  rc = TEMP_FAILURE_RETRY(xxglibc_socket(domain, type, protocol));
  mark_out(SOCKET_IDX);

  if(rc == -1)
    return rc;
  if(rc >= MAXFD) {
    close(rc);
    errno = EMFILE;
    return -1;
  }
  expect_free_fd(rc);
  
  sprintf(txt, "//socket dom=%d, type=%d, proto=%d",
	  domain, type, protocol);
  MYLOG((MYLEVEL, "socket \"%s\"", txt));
  if(myfd[rc] == NULL) {
    myfd[rc] = malloc(sizeof(FDlist));
    if(!myfd[rc]) {
      /* ooops */
      close(rc);
      errno = EMFILE;
      return -1;
    }
  } else {
    /* ooops, the descriptor was already allocated */
  }
  memset(myfd[rc], 0, sizeof(FDlist));
  myfd[rc]->name = strdup(txt);
  myfd[rc]->ref_cnt = 1;
  return rc;
}
#endif

#ifdef HAVE_ACCEPT
extern int accept(int s, struct sockaddr *addr, socklen_t *addrlen)
{
  int rc;
  char txt[255];
  
  mark_in(ACCEPT_IDX);
  rc = TEMP_FAILURE_RETRY(xxglibc_accept(s, addr, addrlen));
  mark_out(ACCEPT_IDX);
  if(rc == -1)
    return rc;
  if(rc >= MAXFD) {
    close(rc);
    errno = EMFILE;
    return -1;
  }
  expect_free_fd(rc);

  sprintf(txt, "//accept sock=%d, fd=%d, proto=%d",
	  s, rc);
  MYLOG((MYLEVEL, "accept \"%s\"", txt));
  if(myfd[rc] == NULL) {
    myfd[rc] = malloc(sizeof(FDlist));
    if(!myfd[rc]) {
      /* ooops */
      close(rc);
      errno = EMFILE;
      return -1;
    }
  }
  memset(myfd[rc], 0, sizeof(FDlist));
  myfd[rc]->name = strdup(txt);
  myfd[rc]->ref_cnt = 1;
  return rc;
}
#endif

#ifdef HAVE_SOCKETPAIR
extern int socketpair(int d, int type, int protocol, int sv[2])
{
  int rc;
  char txt[255];

  mark_in(SOCKETPAIR_IDX);
  rc = TEMP_FAILURE_RETRY(xxglibc_socketpair(d, type, protocol, sv));
  mark_out(SOCKETPAIR_IDX);
  if(rc == -1) {
    return rc;
  }
  if(sv[0] >= MAXFD || sv[1] >= MAXFD) {
    close(sv[0]);
    close(sv[1]);
    errno = EMFILE;
    return -1;
  }
  expect_free_fd(sv[0]);
  expect_free_fd(sv[1]);

  if(myfd[sv[0]] == NULL) {
    myfd[sv[0]] = malloc(sizeof(FDlist));
    if(!myfd[sv[0]]) {
      /* ooops */
      close(sv[0]);
      close(sv[1]);
      errno = EMFILE;
      return -1;
    }
  }
  if(myfd[sv[1]] == NULL) {
    myfd[sv[1]] = malloc(sizeof(FDlist));
    if(!myfd[sv[1]]) {
      /* ooops */
      close(sv[0]);
      close(sv[1]);
      errno = EMFILE;
      return -1;
    }
  }

  memset(myfd[sv[0]], 0, sizeof(FDlist));
  memset(myfd[sv[1]], 0, sizeof(FDlist));
  sprintf(txt, "//socketpair/0  0=%d 1=%d",
	  sv[0],sv[1]);
  myfd[sv[0]]->name = strdup(txt);
  myfd[sv[0]]->ref_cnt = 1;

  sprintf(txt, "//socketpair/1  0=%d 1=%d",
	  sv[0],sv[1]);
  myfd[sv[1]]->name = strdup(txt);
  myfd[sv[1]]->ref_cnt = 1;

  return rc;
}
#endif

#ifdef HAVE_PIPE
extern int pipe(int modus[2])
{
  int rc;
  char txt[255];

  mark_in(PIPE_IDX);
  rc = TEMP_FAILURE_RETRY(xxglibc_pipe(modus));
  mark_out(PIPE_IDX);

  if(rc == -1)
    return rc;

  expect_free_fd(modus[0]);
  expect_free_fd(modus[1]);

  if(myfd[modus[0]] == NULL) {
    myfd[modus[0]] = malloc(sizeof(FDlist));
    if(!myfd[modus[0]]) {
      /* ooops */
      close(modus[0]);
      close(modus[1]);
      errno = EMFILE;
      return -1;
    }
  }
  if(myfd[modus[1]] == NULL) {
    myfd[modus[1]] = malloc(sizeof(FDlist));
    if(!myfd[modus[1]]) {
      /* ooops */
      close(modus[0]);
      close(modus[1]);
      errno = EMFILE;
      return -1;
    }
  }

  memset(myfd[modus[0]], 0, sizeof(FDlist));
  memset(myfd[modus[1]], 0, sizeof(FDlist));
  sprintf(txt, "//pipe/0  0=%d 1=%d",
	  modus[0], modus[1]);
  MYLOG((MYLEVEL, "pipe \"%s\"", txt));
  myfd[modus[0]]->name = strdup(txt);
  myfd[modus[0]]->ref_cnt = 1;

  sprintf(txt, "//pipe/1  0=%d 1=%d",
	  modus[0],modus[1]);
  myfd[modus[1]]->name = strdup(txt);
  myfd[modus[1]]->ref_cnt = 1;

  return rc;
}
#endif

#ifdef HAVE_STAT
extern int stat(const char *file_name, struct stat *buf)
{
  int rc;

  mark_in(STAT_IDX);
  rc = TEMP_FAILURE_RETRY(xxglibc_stat(file_name, buf));
  mark_out(STAT_IDX);

  return rc;
}
#endif

#ifdef HAVE_FSTAT
extern int fstat(int filedes, struct stat *buf)
{
  int rc;

  mark_in(FSTAT_IDX);
  rc = TEMP_FAILURE_RETRY(xxglibc_fstat(filedes, buf));
  mark_out(FSTAT_IDX);

  return rc;
}
#endif

#ifdef HAVE_LSTAT
extern int lstat(const char *file_name, struct stat *buf)
{
  int rc;

  mark_in(LSTAT_IDX);
  rc = TEMP_FAILURE_RETRY(xxglibc_lstat(file_name, buf));
  mark_out(LSTAT_IDX);

  return rc;
}
#endif


/* wrapper initialisation */

static inline void * my_dl_open(void)
{
  return dlopen("/lib/libc.so.6", RTLD_LAZY | RTLD_GLOBAL);
}

static inline void * my_dl_getsym(void *hdl, void *dest, const char *name)
{
  *(unsigned long *)dest = (unsigned long) dlsym(hdl, name);
  return dest;
}
 
static void *hdl = 0;
 
static inline void init_std_fd(void)
{
  int i;

  /* the following is stupid and broken.
   * We can get all the real active handles out of 
   * /proc/self/fd/
   */ 
  const char *names[] = { "//stdin", "//stdout", "//stderr",
			  // "//dummy/2/", "//dummy/3/",
			  0 };

  for(i = 0;; i++) {
    if(!names[i])
      break;
    myfd[i] = malloc(sizeof(FDlist));
    if(!myfd[i]) {
      MYLOG((MYLEVEL, "init_std_fd: out of memory for fd=%d ...", i));
    } else {
      memset(myfd[i], 0, sizeof(FDlist)); // implicit must_seek = 0, pos = 0
      myfd[i]->name = strdup(names[i]);
      myfd[i]->ref_cnt = 1;
    }
  }
}

extern int _init(void)
{
  rTab *p;
#if defined(HAVE_OPENLOG) && defined(MYLEVEL)
  openlog("ste", LOG_PID, 0); // we want to see pids.
#endif
  MYLOG((MYLEVEL, "ste::_init() called"));

  if(hdl)
    return 0; /* only one time please */

  if((hdl = my_dl_open()) == 0) {
    MYLOG((MYLEVEL, "dl_open failed."));
    return 1;
  }

  p = replTab;
  
  while(p && p->ptr) {
    if(! my_dl_getsym(hdl, p->ptr, p->name)) {
      MYLOG((MYLEVEL, "dlsym failed for %s", p->name));
      return 2;
    }
    p->cnt_in = 0;
    p->cnt_out = 0;
    p++;
  }
  
  myfd = (FDlist **)  malloc(MAXFD * sizeof(FDlist *));
  if(!myfd) {
    MYLOG((MYLEVEL, "out of memory"));
    _exit(99);
  }
  memset(myfd, 0, MAXFD * sizeof(FDlist *));
  init_std_fd();
  return 0;
}

static void stats(void)
{
  u64 c_in = 0, c_out = 0;

  rTab *t = replTab;
  while(t && t->ptr) {
    MYLOG((MYLEVEL, "SC %10s: %10Lu,%10Lu", t->name, t->cnt_in, t->cnt_out));
    c_in += t->cnt_in;
    c_out += t->cnt_out;
    t++;
  }
  MYLOG((MYLEVEL, "Syscall summary  req: %10Lu", c_in));
  MYLOG((MYLEVEL, "Syscall summary exec: %10Lu", c_out));
}

extern int _fini(void)
{
  MYLOG((MYLEVEL, "ste::_fini() called"));
  if(hdl) {
    dlclose(hdl);
    hdl = 0;
  }
  stats();
  return 0;
}

