/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright (c) 2005-2008 Jelmer Vernooij <jelmer@samba.org>
 * Copyright (C) 2006-2014 Stefan Metzmacher <metze@samba.org>
 * Copyright (C) 2013-2014 Andreas Schneider <asn@samba.org>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the author nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

/*
   Socket wrapper library. Passes all socket communication over
   unix domain sockets if the environment variable SOCKET_WRAPPER_DIR
   is set.
*/

#include <signal.h>
#include <dlfcn.h>

#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <pthread.h>

#include <vcl/ldp_socket_wrapper.h>


enum swrap_dbglvl_e
{
  SWRAP_LOG_ERROR = 0,
  SWRAP_LOG_WARN,
  SWRAP_LOG_DEBUG,
  SWRAP_LOG_TRACE
};


/* Macros for accessing mutexes */
#define SWRAP_LOCK(m) do { \
        pthread_mutex_lock(&(m ## _mutex)); \
} while(0)

#define SWRAP_UNLOCK(m) do { \
        pthread_mutex_unlock(&(m ## _mutex)); \
} while(0)

/* Add new global locks here please */
#define SWRAP_LOCK_ALL \
        SWRAP_LOCK(libc_symbol_binding); \

#define SWRAP_UNLOCK_ALL \
        SWRAP_UNLOCK(libc_symbol_binding); \



/* The mutex for accessing the global libc.symbols */
static pthread_mutex_t libc_symbol_binding_mutex = PTHREAD_MUTEX_INITIALIZER;

/* Function prototypes */

#ifdef NDEBUG
#define SWRAP_LOG(...)
#else
static unsigned int swrap_log_lvl = SWRAP_LOG_WARN;

static void
swrap_log (enum swrap_dbglvl_e dbglvl, const char *func,
	   const char *format, ...)
PRINTF_ATTRIBUTE (3, 4);
#define SWRAP_LOG(dbglvl, ...) swrap_log((dbglvl), __func__, __VA_ARGS__)

     static void
       swrap_log (enum swrap_dbglvl_e dbglvl,
		  const char *func, const char *format, ...)
{
  char buffer[1024];
  va_list va;

  va_start (va, format);
  vsnprintf (buffer, sizeof (buffer), format, va);
  va_end (va);

  if (dbglvl <= swrap_log_lvl)
    {
      switch (dbglvl)
	{
	case SWRAP_LOG_ERROR:
	  fprintf (stderr,
		   "SWRAP_ERROR(%d) - %s: %s\n",
		   (int) getpid (), func, buffer);
	  break;
	case SWRAP_LOG_WARN:
	  fprintf (stderr,
		   "SWRAP_WARN(%d) - %s: %s\n",
		   (int) getpid (), func, buffer);
	  break;
	case SWRAP_LOG_DEBUG:
	  fprintf (stderr,
		   "SWRAP_DEBUG(%d) - %s: %s\n",
		   (int) getpid (), func, buffer);
	  break;
	case SWRAP_LOG_TRACE:
	  fprintf (stderr,
		   "SWRAP_TRACE(%d) - %s: %s\n",
		   (int) getpid (), func, buffer);
	  break;
	}
    }
}
#endif


/*********************************************************
 * SWRAP LOADING LIBC FUNCTIONS
 *********************************************************/

typedef int (*__libc_accept4) (int sockfd,
			       struct sockaddr * addr,
			       socklen_t * addrlen, int flags);
typedef int (*__libc_accept) (int sockfd,
			      struct sockaddr * addr, socklen_t * addrlen);
typedef int (*__libc_bind) (int sockfd,
			    const struct sockaddr * addr, socklen_t addrlen);
typedef int (*__libc_close) (int fd);
typedef int (*__libc_connect) (int sockfd,
			       const struct sockaddr * addr,
			       socklen_t addrlen);

#if 0
/* TBD: dup and dup2 to be implemented later */
typedef int (*__libc_dup) (int fd);
typedef int (*__libc_dup2) (int oldfd, int newfd);
#endif

typedef int (*__libc_fcntl) (int fd, int cmd, ...);
typedef FILE *(*__libc_fopen) (const char *name, const char *mode);
#ifdef HAVE_FOPEN64
typedef FILE *(*__libc_fopen64) (const char *name, const char *mode);
#endif
#ifdef HAVE_EVENTFD
typedef int (*__libc_eventfd) (int count, int flags);
#endif
typedef int (*__libc_getpeername) (int sockfd,
				   struct sockaddr * addr,
				   socklen_t * addrlen);
typedef int (*__libc_getsockname) (int sockfd,
				   struct sockaddr * addr,
				   socklen_t * addrlen);
typedef int (*__libc_getsockopt) (int sockfd,
				  int level,
				  int optname,
				  void *optval, socklen_t * optlen);
typedef int (*__libc_ioctl) (int d, unsigned long int request, ...);
typedef int (*__libc_listen) (int sockfd, int backlog);
typedef int (*__libc_open) (const char *pathname, int flags, mode_t mode);
#ifdef HAVE_OPEN64
typedef int (*__libc_open64) (const char *pathname, int flags, mode_t mode);
#endif /* HAVE_OPEN64 */
typedef int (*__libc_openat) (int dirfd, const char *path, int flags, ...);
typedef int (*__libc_pipe) (int pipefd[2]);
typedef int (*__libc_read) (int fd, void *buf, size_t count);
typedef ssize_t (*__libc_readv) (int fd, const struct iovec * iov,
				 int iovcnt);
typedef int (*__libc_recv) (int sockfd, void *buf, size_t len, int flags);
typedef int (*__libc_recvfrom) (int sockfd,
				void *buf,
				size_t len,
				int flags,
				struct sockaddr * src_addr,
				socklen_t * addrlen);
typedef int (*__libc_recvmsg) (int sockfd, const struct msghdr * msg,
			       int flags);
typedef int (*__libc_send) (int sockfd, const void *buf, size_t len,
			    int flags);
typedef ssize_t (*__libc_sendfile) (int out_fd, int in_fd, off_t * offset,
				    size_t len);
typedef int (*__libc_sendmsg) (int sockfd, const struct msghdr * msg,
			       int flags);
typedef int (*__libc_sendto) (int sockfd, const void *buf, size_t len,
			      int flags, const struct sockaddr * dst_addr,
			      socklen_t addrlen);
typedef int (*__libc_setsockopt) (int sockfd, int level, int optname,
				  const void *optval, socklen_t optlen);
#ifdef HAVE_SIGNALFD
typedef int (*__libc_signalfd) (int fd, const sigset_t * mask, int flags);
#endif
typedef int (*__libc_socket) (int domain, int type, int protocol);
typedef int (*__libc_socketpair) (int domain, int type, int protocol,
				  int sv[2]);
#ifdef HAVE_TIMERFD_CREATE
typedef int (*__libc_timerfd_create) (int clockid, int flags);
#endif
typedef ssize_t (*__libc_write) (int fd, const void *buf, size_t count);
typedef ssize_t (*__libc_writev) (int fd, const struct iovec * iov,
				  int iovcnt);

typedef int (*__libc_shutdown) (int fd, int how);

typedef int (*__libc_select) (int __nfds, fd_set * __restrict __readfds,
			      fd_set * __restrict __writefds,
			      fd_set * __restrict __exceptfds,
			      struct timeval * __restrict __timeout);

#ifdef __USE_XOPEN2K
typedef int (*__libc_pselect) (int __nfds, fd_set * __restrict __readfds,
			       fd_set * __restrict __writefds,
			       fd_set * __restrict __exceptfds,
			       const struct timespec * __restrict __timeout,
			       const __sigset_t * __restrict __sigmask);
#endif

typedef int (*__libc_epoll_create) (int __size);

typedef int (*__libc_epoll_create1) (int __flags);

typedef int (*__libc_epoll_ctl) (int __epfd, int __op, int __fd,
				 struct epoll_event * __event);

typedef int (*__libc_epoll_wait) (int __epfd, struct epoll_event * __events,
				  int __maxevents, int __timeout);

typedef int (*__libc_epoll_pwait) (int __epfd, struct epoll_event * __events,
				   int __maxevents, int __timeout,
				   const __sigset_t * __ss);

typedef int (*__libc_poll) (struct pollfd * __fds, nfds_t __nfds,
			    int __timeout);

#ifdef __USE_GNU
typedef int (*__libc_ppoll) (struct pollfd * __fds, nfds_t __nfds,
			     const struct timespec * __timeout,
			     const __sigset_t * __ss);
#endif


#define SWRAP_SYMBOL_ENTRY(i) \
        union { \
                __libc_##i f; \
                void *obj; \
        } _libc_##i

struct swrap_libc_symbols
{
  SWRAP_SYMBOL_ENTRY (accept4);
  SWRAP_SYMBOL_ENTRY (accept);
  SWRAP_SYMBOL_ENTRY (bind);
  SWRAP_SYMBOL_ENTRY (close);
  SWRAP_SYMBOL_ENTRY (connect);
#if 0
  /* TBD: dup and dup2 to be implemented later */
  SWRAP_SYMBOL_ENTRY (dup);
  SWRAP_SYMBOL_ENTRY (dup2);
#endif
  SWRAP_SYMBOL_ENTRY (fcntl);
  SWRAP_SYMBOL_ENTRY (fopen);
#ifdef HAVE_FOPEN64
  SWRAP_SYMBOL_ENTRY (fopen64);
#endif
#ifdef HAVE_EVENTFD
  SWRAP_SYMBOL_ENTRY (eventfd);
#endif
  SWRAP_SYMBOL_ENTRY (getpeername);
  SWRAP_SYMBOL_ENTRY (getsockname);
  SWRAP_SYMBOL_ENTRY (getsockopt);
  SWRAP_SYMBOL_ENTRY (ioctl);
  SWRAP_SYMBOL_ENTRY (listen);
  SWRAP_SYMBOL_ENTRY (open);
#ifdef HAVE_OPEN64
  SWRAP_SYMBOL_ENTRY (open64);
#endif
  SWRAP_SYMBOL_ENTRY (openat);
  SWRAP_SYMBOL_ENTRY (pipe);
  SWRAP_SYMBOL_ENTRY (read);
  SWRAP_SYMBOL_ENTRY (readv);
  SWRAP_SYMBOL_ENTRY (recv);
  SWRAP_SYMBOL_ENTRY (recvfrom);
  SWRAP_SYMBOL_ENTRY (recvmsg);
  SWRAP_SYMBOL_ENTRY (send);
  SWRAP_SYMBOL_ENTRY (sendfile);
  SWRAP_SYMBOL_ENTRY (sendmsg);
  SWRAP_SYMBOL_ENTRY (sendto);
  SWRAP_SYMBOL_ENTRY (setsockopt);
#ifdef HAVE_SIGNALFD
  SWRAP_SYMBOL_ENTRY (signalfd);
#endif
  SWRAP_SYMBOL_ENTRY (socket);
  SWRAP_SYMBOL_ENTRY (socketpair);
#ifdef HAVE_TIMERFD_CREATE
  SWRAP_SYMBOL_ENTRY (timerfd_create);
#endif
  SWRAP_SYMBOL_ENTRY (write);
  SWRAP_SYMBOL_ENTRY (writev);

  SWRAP_SYMBOL_ENTRY (shutdown);
  SWRAP_SYMBOL_ENTRY (select);
#ifdef __USE_XOPEN2K
  SWRAP_SYMBOL_ENTRY (pselect);
#endif
  SWRAP_SYMBOL_ENTRY (epoll_create);
  SWRAP_SYMBOL_ENTRY (epoll_create1);
  SWRAP_SYMBOL_ENTRY (epoll_ctl);
  SWRAP_SYMBOL_ENTRY (epoll_wait);
  SWRAP_SYMBOL_ENTRY (epoll_pwait);
  SWRAP_SYMBOL_ENTRY (poll);
#ifdef __USE_GNU
  SWRAP_SYMBOL_ENTRY (ppoll);
#endif
};

struct swrap
{
  struct
  {
    void *handle;
    void *socket_handle;
    struct swrap_libc_symbols symbols;
  } libc;
};

static struct swrap swrap;

#define LIBC_NAME "libc.so"

enum swrap_lib
{
  SWRAP_LIBC,
};

#ifndef NDEBUG
static const char *
swrap_str_lib (enum swrap_lib lib)
{
  switch (lib)
    {
    case SWRAP_LIBC:
      return "libc";
    }

  /* Compiler would warn us about unhandled enum value if we get here */
  return "unknown";
}
#endif

static void *
swrap_load_lib_handle (enum swrap_lib lib)
{
  int flags = RTLD_LAZY;
  void *handle = NULL;
  int i;

#ifdef RTLD_DEEPBIND
  flags |= RTLD_DEEPBIND;
#endif

  switch (lib)
    {
    case SWRAP_LIBC:
      handle = swrap.libc.handle;
#ifdef LIBC_SO
      if (handle == NULL)
	{
	  handle = dlopen (LIBC_SO, flags);

	  swrap.libc.handle = handle;
	}
#endif
      if (handle == NULL)
	{
	  for (i = 10; i >= 0; i--)
	    {
	      char soname[256] = { 0 };

	      snprintf (soname, sizeof (soname), "libc.so.%d", i);
	      handle = dlopen (soname, flags);
	      if (handle != NULL)
		{
		  break;
		}
	    }

	  swrap.libc.handle = handle;
	}
      break;
    }

  if (handle == NULL)
    {
      SWRAP_LOG (SWRAP_LOG_ERROR,
		 "Failed to dlopen library: %s\n", dlerror ());
      exit (-1);
    }

  return handle;
}

static void *
_swrap_bind_symbol (enum swrap_lib lib, const char *fn_name)
{
  void *handle;
  void *func;

  handle = swrap_load_lib_handle (lib);

  func = dlsym (handle, fn_name);
  if (func == NULL)
    {
      SWRAP_LOG (SWRAP_LOG_ERROR,
		 "Failed to find %s: %s\n", fn_name, dlerror ());
      exit (-1);
    }

  SWRAP_LOG (SWRAP_LOG_TRACE,
	     "Loaded %s from %s", fn_name, swrap_str_lib (lib));

  return func;
}

#define swrap_bind_symbol_libc(sym_name) \
        SWRAP_LOCK(libc_symbol_binding); \
        if (swrap.libc.symbols._libc_##sym_name.obj == NULL) { \
                swrap.libc.symbols._libc_##sym_name.obj = \
                        _swrap_bind_symbol(SWRAP_LIBC, #sym_name); \
        } \
        SWRAP_UNLOCK(libc_symbol_binding)

/*
 * IMPORTANT
 *
 * Functions especially from libc need to be loaded individually, you can't load
 * all at once or gdb will segfault at startup. The same applies to valgrind and
 * has probably something todo with with the linker.
 * So we need load each function at the point it is called the first time.
 */
int
libc_accept4 (int sockfd,
	      struct sockaddr *addr, socklen_t * addrlen, int flags)
{
  swrap_bind_symbol_libc (accept4);

  return swrap.libc.symbols._libc_accept4.f (sockfd, addr, addrlen, flags);
}

int
libc_accept (int sockfd, struct sockaddr *addr, socklen_t * addrlen)
{
  swrap_bind_symbol_libc (accept);

  return swrap.libc.symbols._libc_accept.f (sockfd, addr, addrlen);
}

int
libc_bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
  swrap_bind_symbol_libc (bind);

  return swrap.libc.symbols._libc_bind.f (sockfd, addr, addrlen);
}

int
libc_close (int fd)
{
  swrap_bind_symbol_libc (close);

  return swrap.libc.symbols._libc_close.f (fd);
}

int
libc_connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
  swrap_bind_symbol_libc (connect);

  return swrap.libc.symbols._libc_connect.f (sockfd, addr, addrlen);
}

#if 0
/* TBD: dup and dup2 to be implemented later */
int
libc_dup (int fd)
{
  swrap_bind_symbol_libc (dup);

  return swrap.libc.symbols._libc_dup.f (fd);
}

int
libc_dup2 (int oldfd, int newfd)
{
  swrap_bind_symbol_libc (dup2);

  return swrap.libc.symbols._libc_dup2.f (oldfd, newfd);
}
#endif

#ifdef HAVE_EVENTFD
int
libc_eventfd (int count, int flags)
{
  swrap_bind_symbol_libc (eventfd);

  return swrap.libc.symbols._libc_eventfd.f (count, flags);
}
#endif

DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE int
libc_vfcntl (int fd, int cmd, va_list ap)
{
  long int args[4];
  int rc;
  int i;

  swrap_bind_symbol_libc (fcntl);

  for (i = 0; i < 4; i++)
    {
      args[i] = va_arg (ap, long int);
    }

  rc = swrap.libc.symbols._libc_fcntl.f (fd,
					 cmd,
					 args[0], args[1], args[2], args[3]);

  return rc;
}

DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE int
libc_vioctl (int fd, int cmd, va_list ap)
{
  long int args[4];
  int rc;
  int i;

  swrap_bind_symbol_libc (ioctl);

  for (i = 0; i < 4; i++)
    {
      args[i] = va_arg (ap, long int);
    }

  rc = swrap.libc.symbols._libc_ioctl.f (fd,
					 cmd,
					 args[0], args[1], args[2], args[3]);

  return rc;
}

int
libc_getpeername (int sockfd, struct sockaddr *addr, socklen_t * addrlen)
{
  swrap_bind_symbol_libc (getpeername);

  return swrap.libc.symbols._libc_getpeername.f (sockfd, addr, addrlen);
}

int
libc_getsockname (int sockfd, struct sockaddr *addr, socklen_t * addrlen)
{
  swrap_bind_symbol_libc (getsockname);

  return swrap.libc.symbols._libc_getsockname.f (sockfd, addr, addrlen);
}

int
libc_getsockopt (int sockfd,
		 int level, int optname, void *optval, socklen_t * optlen)
{
  swrap_bind_symbol_libc (getsockopt);

  return swrap.libc.symbols._libc_getsockopt.f (sockfd,
						level,
						optname, optval, optlen);
}

int
libc_listen (int sockfd, int backlog)
{
  swrap_bind_symbol_libc (listen);

  return swrap.libc.symbols._libc_listen.f (sockfd, backlog);
}

/* TBD: libc_read() should return ssize_t not an int */
int
libc_read (int fd, void *buf, size_t count)
{
  swrap_bind_symbol_libc (read);

  return swrap.libc.symbols._libc_read.f (fd, buf, count);
}

ssize_t
libc_readv (int fd, const struct iovec * iov, int iovcnt)
{
  swrap_bind_symbol_libc (readv);

  return swrap.libc.symbols._libc_readv.f (fd, iov, iovcnt);
}

int
libc_recv (int sockfd, void *buf, size_t len, int flags)
{
  swrap_bind_symbol_libc (recv);

  return swrap.libc.symbols._libc_recv.f (sockfd, buf, len, flags);
}

int
libc_recvfrom (int sockfd,
	       void *buf,
	       size_t len,
	       int flags, struct sockaddr *src_addr, socklen_t * addrlen)
{
  swrap_bind_symbol_libc (recvfrom);

  return swrap.libc.symbols._libc_recvfrom.f (sockfd,
					      buf,
					      len, flags, src_addr, addrlen);
}

int
libc_recvmsg (int sockfd, struct msghdr *msg, int flags)
{
  swrap_bind_symbol_libc (recvmsg);

  return swrap.libc.symbols._libc_recvmsg.f (sockfd, msg, flags);
}

int
libc_send (int sockfd, const void *buf, size_t len, int flags)
{
  swrap_bind_symbol_libc (send);

  return swrap.libc.symbols._libc_send.f (sockfd, buf, len, flags);
}

ssize_t
libc_sendfile (int out_fd, int in_fd, off_t * offset, size_t len)
{
  swrap_bind_symbol_libc (sendfile);

  return swrap.libc.symbols._libc_sendfile.f (out_fd, in_fd, offset, len);
}

int
libc_sendmsg (int sockfd, const struct msghdr *msg, int flags)
{
  swrap_bind_symbol_libc (sendmsg);

  return swrap.libc.symbols._libc_sendmsg.f (sockfd, msg, flags);
}

int
libc_sendto (int sockfd,
	     const void *buf,
	     size_t len,
	     int flags, const struct sockaddr *dst_addr, socklen_t addrlen)
{
  swrap_bind_symbol_libc (sendto);

  return swrap.libc.symbols._libc_sendto.f (sockfd,
					    buf,
					    len, flags, dst_addr, addrlen);
}

int
libc_setsockopt (int sockfd,
		 int level, int optname, const void *optval, socklen_t optlen)
{
  swrap_bind_symbol_libc (setsockopt);

  return swrap.libc.symbols._libc_setsockopt.f (sockfd,
						level,
						optname, optval, optlen);
}

int
libc_socket (int domain, int type, int protocol)
{
  swrap_bind_symbol_libc (socket);

  return swrap.libc.symbols._libc_socket.f (domain, type, protocol);
}

int
libc_socketpair (int domain, int type, int protocol, int sv[2])
{
  swrap_bind_symbol_libc (socketpair);

  return swrap.libc.symbols._libc_socketpair.f (domain, type, protocol, sv);
}

ssize_t
libc_write (int fd, const void *buf, size_t count)
{
  swrap_bind_symbol_libc (write);

  return swrap.libc.symbols._libc_write.f (fd, buf, count);
}

ssize_t
libc_writev (int fd, const struct iovec * iov, int iovcnt)
{
  swrap_bind_symbol_libc (writev);

  return swrap.libc.symbols._libc_writev.f (fd, iov, iovcnt);
}

int
libc_shutdown (int fd, int how)
{
  swrap_bind_symbol_libc (shutdown);

  return swrap.libc.symbols._libc_shutdown.f (fd, how);
}

int
libc_select (int __nfds, fd_set * __restrict __readfds,
	     fd_set * __restrict __writefds,
	     fd_set * __restrict __exceptfds,
	     struct timeval *__restrict __timeout)
{
  swrap_bind_symbol_libc (select);

  return swrap.libc.symbols._libc_select.f (__nfds, __readfds,
					    __writefds,
					    __exceptfds, __timeout);
}

#ifdef __USE_XOPEN2K
int
libc_pselect (int __nfds, fd_set * __restrict __readfds,
	      fd_set * __restrict __writefds,
	      fd_set * __restrict __exceptfds,
	      const struct timespec *__restrict __timeout,
	      const __sigset_t * __restrict __sigmask)
{
  swrap_bind_symbol_libc (pselect);

  return swrap.libc.symbols._libc_pselect.f (__nfds, __readfds,
					     __writefds,
					     __exceptfds,
					     __timeout, __sigmask);
}
#endif

int
libc_epoll_create (int __size)
{
  swrap_bind_symbol_libc (epoll_create);

  return swrap.libc.symbols._libc_epoll_create.f (__size);
}

int
libc_epoll_create1 (int __flags)
{
  swrap_bind_symbol_libc (epoll_create1);

  return swrap.libc.symbols._libc_epoll_create1.f (__flags);
}

int
libc_epoll_ctl (int __epfd, int __op, int __fd, struct epoll_event *__event)
{
  swrap_bind_symbol_libc (epoll_ctl);

  return swrap.libc.symbols._libc_epoll_ctl.f (__epfd, __op, __fd, __event);
}

int
libc_epoll_wait (int __epfd, struct epoll_event *__events,
		 int __maxevents, int __timeout)
{
  swrap_bind_symbol_libc (epoll_wait);

  return swrap.libc.symbols._libc_epoll_wait.f (__epfd, __events,
						__maxevents, __timeout);
}

int
libc_epoll_pwait (int __epfd, struct epoll_event *__events,
		  int __maxevents, int __timeout, const __sigset_t * __ss)
{
  swrap_bind_symbol_libc (epoll_pwait);

  return swrap.libc.symbols._libc_epoll_pwait.f (__epfd, __events,
						 __maxevents, __timeout,
						 __ss);
}

int
libc_poll (struct pollfd *__fds, nfds_t __nfds, int __timeout)
{
  swrap_bind_symbol_libc (poll);

  return swrap.libc.symbols._libc_poll.f (__fds, __nfds, __timeout);
}

#ifdef __USE_GNU
int
libc_ppoll (struct pollfd *__fds, nfds_t __nfds,
	    const struct timespec *__timeout, const __sigset_t * __ss)
{
  swrap_bind_symbol_libc (ppoll);

  return swrap.libc.symbols._libc_ppoll.f (__fds, __nfds, __timeout, __ss);
}
#endif

static void
swrap_thread_prepare (void)
{
  SWRAP_LOCK_ALL;
}

static void
swrap_thread_parent (void)
{
  SWRAP_UNLOCK_ALL;
}

static void
swrap_thread_child (void)
{
  SWRAP_UNLOCK_ALL;
}

/****************************
 * CONSTRUCTOR
 ***************************/
void
swrap_constructor (void)
{
  /*
   * If we hold a lock and the application forks, then the child
   * is not able to unlock the mutex and we are in a deadlock.
   * This should prevent such deadlocks.
   */
  pthread_atfork (&swrap_thread_prepare,
		  &swrap_thread_parent, &swrap_thread_child);
}

/****************************
 * DESTRUCTOR
 ***************************/

/*
 * This function is called when the library is unloaded and makes sure that
 * sockets get closed and the unix file for the socket are unlinked.
 */
void
swrap_destructor (void)
{
  if (swrap.libc.handle != NULL)
    {
      dlclose (swrap.libc.handle);
    }
  if (swrap.libc.socket_handle)
    {
      dlclose (swrap.libc.socket_handle);
    }
}

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */