/*
 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
 *
 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
 */

#define __FORCE_GLIBC
#include <features.h>
#include <errno.h>
#include <syscall.h>
#include <sys/socket.h>

#ifdef __NR_socketcall
extern int __socketcall(int call, unsigned long *args) attribute_hidden;

/* Various socketcall numbers */
#define SYS_SOCKET      1
#define SYS_BIND        2
#define SYS_CONNECT     3
#define SYS_LISTEN      4
#define SYS_ACCEPT      5
#define SYS_GETSOCKNAME 6
#define SYS_GETPEERNAME 7
#define SYS_SOCKETPAIR  8
#define SYS_SEND        9
#define SYS_RECV        10
#define SYS_SENDTO      11
#define SYS_RECVFROM    12
#define SYS_SHUTDOWN    13
#define SYS_SETSOCKOPT  14
#define SYS_GETSOCKOPT  15
#define SYS_SENDMSG     16
#define SYS_RECVMSG     17
#define SYS_ACCEPT4     18
#endif

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
#include <sysdep-cancel.h>
#include <pthreadP.h>
#else
#define SINGLE_THREAD_P 1
#endif

#ifdef L_accept
extern __typeof(accept) __libc_accept;
#ifdef __NR_accept
#define __NR___sys_accept  __NR_accept
static
_syscall3(int, __sys_accept, int, call, struct sockaddr *, addr, socklen_t *,addrlen)
int __libc_accept(int s, struct sockaddr *addr, socklen_t * addrlen)
{
	if (SINGLE_THREAD_P)
		return __sys_accept(s, addr, addrlen);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __sys_accept(s, addr, addrlen);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#elif defined(__NR_socketcall)
int __libc_accept(int s, struct sockaddr *addr, socklen_t * addrlen)
{
	unsigned long args[3];

	args[0] = s;
	args[1] = (unsigned long) addr;
	args[2] = (unsigned long) addrlen;

	if (SINGLE_THREAD_P)
		return __socketcall(SYS_ACCEPT, args);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __socketcall(SYS_ACCEPT, args);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#endif
weak_alias(__libc_accept,accept)
libc_hidden_weak(accept)
#endif

#ifdef L_accept4
#ifdef __NR_accept4
# define __NR___sys_accept4  __NR_accept4
static _syscall4(int, __sys_accept4, int, fd, struct sockaddr *, addr, socklen_t *, addrlen, int, flags)
int accept4(int fd, struct sockaddr *addr, socklen_t * addrlen, int flags)
{
	if (SINGLE_THREAD_P)
		return __sys_accept4(fd, addr, addrlen, flags);
#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	else {
		int oldtype = LIBC_CANCEL_ASYNC ();
		int result = __sys_accept4(fd, addr, addrlen, flags);
		LIBC_CANCEL_RESET (oldtype);
		return result;
	}
#endif
}
#elif defined(__NR_socketcall)
int accept4(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags)
{
	unsigned long args[4];

	args[0] = fd;
	args[1] = (unsigned long) addr;
	args[2] = (unsigned long) addrlen;
	args[3] = flags;
	if (SINGLE_THREAD_P)
		return __socketcall(SYS_ACCEPT4, args);
#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	else {
		int oldtype = LIBC_CANCEL_ASYNC ();
		int result = __socketcall(SYS_ACCEPT4, args);
		LIBC_CANCEL_RESET (oldtype);
		return result;
	}
#endif
}
#endif
#endif

#ifdef L_bind
#ifdef __NR_bind
_syscall3(int, bind, int, sockfd, const struct sockaddr *, myaddr, socklen_t, addrlen)
#elif defined(__NR_socketcall)
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen)
{
	unsigned long args[3];

	args[0] = sockfd;
	args[1] = (unsigned long) myaddr;
	args[2] = addrlen;
	return __socketcall(SYS_BIND, args);
}
#endif
libc_hidden_def(bind)
#endif

#ifdef L_connect
extern __typeof(connect) __libc_connect;
#ifdef __NR_connect
#define __NR___sys_connect __NR_connect
static
_syscall3(int, __sys_connect, int, sockfd, const struct sockaddr *, saddr, socklen_t, addrlen)
int __libc_connect(int sockfd, const struct sockaddr *saddr, socklen_t addrlen)
{
	if (SINGLE_THREAD_P)
		return __sys_connect(sockfd, saddr, addrlen);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __sys_connect(sockfd, saddr, addrlen);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#elif defined(__NR_socketcall)
int __libc_connect(int sockfd, const struct sockaddr *saddr, socklen_t addrlen)
{
	unsigned long args[3];

	args[0] = sockfd;
	args[1] = (unsigned long) saddr;
	args[2] = addrlen;

	if (SINGLE_THREAD_P)
		return __socketcall(SYS_CONNECT, args);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __socketcall(SYS_CONNECT, args);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#endif
weak_alias(__libc_connect,connect)
libc_hidden_weak(connect)
#endif

#ifdef L_getpeername
#ifdef __NR_getpeername
_syscall3(int, getpeername, int, sockfd, struct sockaddr *, addr, socklen_t *,paddrlen)
#elif defined(__NR_socketcall)
int getpeername(int sockfd, struct sockaddr *addr, socklen_t * paddrlen)
{
	unsigned long args[3];

	args[0] = sockfd;
	args[1] = (unsigned long) addr;
	args[2] = (unsigned long) paddrlen;
	return __socketcall(SYS_GETPEERNAME, args);
}
#endif
#endif

#ifdef L_getsockname
#ifdef __NR_getsockname
_syscall3(int, getsockname, int, sockfd, struct sockaddr *, addr, socklen_t *,paddrlen)
#elif defined(__NR_socketcall)
int getsockname(int sockfd, struct sockaddr *addr, socklen_t * paddrlen)
{
	unsigned long args[3];

	args[0] = sockfd;
	args[1] = (unsigned long) addr;
	args[2] = (unsigned long) paddrlen;
	return __socketcall(SYS_GETSOCKNAME, args);
}
#endif
libc_hidden_def(getsockname)
#endif

#ifdef L_getsockopt
#ifdef __NR_getsockopt
_syscall5(int, getsockopt, int, fd, int, level, int, optname, __ptr_t, optval, socklen_t *, optlen)
#elif defined(__NR_socketcall)
int getsockopt(int fd, int level, int optname, __ptr_t optval,
		   socklen_t * optlen)
{
	unsigned long args[5];

	args[0] = fd;
	args[1] = level;
	args[2] = optname;
	args[3] = (unsigned long) optval;
	args[4] = (unsigned long) optlen;
	return (__socketcall(SYS_GETSOCKOPT, args));
}
#endif
#endif

#ifdef L_listen
#ifdef __NR_listen
_syscall2(int, listen, int, sockfd, int, backlog)
#elif defined(__NR_socketcall)
int listen(int sockfd, int backlog)
{
	unsigned long args[2];

	args[0] = sockfd;
	args[1] = backlog;
	return __socketcall(SYS_LISTEN, args);
}
#endif
libc_hidden_def(listen)
#endif

#ifdef L_recv
extern __typeof(recv) __libc_recv;
#ifdef __NR_recv
#define __NR___sys_recv __NR_recv
static
_syscall4(ssize_t, __sys_recv, int, sockfd, __ptr_t, buffer, size_t, len,
	int, flags)
ssize_t __libc_recv(int sockfd, __ptr_t buffer, size_t len, int flags)
{
	if (SINGLE_THREAD_P)
		return __sys_recv(sockfd, buffer, len, flags);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __sys_recv(sockfd, buffer, len, flags);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#elif defined(__NR_socketcall)
/* recv, recvfrom added by bir7@leland.stanford.edu */
ssize_t __libc_recv(int sockfd, __ptr_t buffer, size_t len, int flags)
{
	unsigned long args[4];

	args[0] = sockfd;
	args[1] = (unsigned long) buffer;
	args[2] = len;
	args[3] = flags;

	if (SINGLE_THREAD_P)
		return (__socketcall(SYS_RECV, args));

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __socketcall(SYS_RECV, args);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#elif defined(__NR_recvfrom)
ssize_t __libc_recv(int sockfd, __ptr_t buffer, size_t len, int flags)
{
	return (recvfrom(sockfd, buffer, len, flags, NULL, NULL));
}
#endif
weak_alias(__libc_recv,recv)
libc_hidden_weak(recv)
#endif

#ifdef L_recvfrom
extern __typeof(recvfrom) __libc_recvfrom;
#ifdef __NR_recvfrom
#define __NR___sys_recvfrom __NR_recvfrom
static
_syscall6(ssize_t, __sys_recvfrom, int, sockfd, __ptr_t, buffer, size_t, len,
	int, flags, struct sockaddr *, to, socklen_t *, tolen)
ssize_t __libc_recvfrom(int sockfd, __ptr_t buffer, size_t len, int flags,
		 struct sockaddr *to, socklen_t * tolen)
{
	if (SINGLE_THREAD_P)
		return __sys_recvfrom(sockfd, buffer, len, flags, to, tolen);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __sys_recvfrom(sockfd, buffer, len, flags, to, tolen);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#elif defined(__NR_socketcall)
/* recv, recvfrom added by bir7@leland.stanford.edu */
ssize_t __libc_recvfrom(int sockfd, __ptr_t buffer, size_t len, int flags,
		 struct sockaddr *to, socklen_t * tolen)
{
	unsigned long args[6];

	args[0] = sockfd;
	args[1] = (unsigned long) buffer;
	args[2] = len;
	args[3] = flags;
	args[4] = (unsigned long) to;
	args[5] = (unsigned long) tolen;

	if (SINGLE_THREAD_P)
		return (__socketcall(SYS_RECVFROM, args));

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __socketcall(SYS_RECVFROM, args);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#endif
weak_alias(__libc_recvfrom,recvfrom)
libc_hidden_weak(recvfrom)
#endif

#ifdef L_recvmsg
extern __typeof(recvmsg) __libc_recvmsg;
#ifdef __NR_recvmsg
#define __NR___sys_recvmsg __NR_recvmsg
static
_syscall3(ssize_t, __sys_recvmsg, int, sockfd, struct msghdr *, msg, int, flags)
ssize_t __libc_recvmsg(int sockfd, struct msghdr *msg, int flags)
{
	if (SINGLE_THREAD_P)
		return __sys_recvmsg(sockfd, msg, flags);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __sys_recvmsg(sockfd, msg, flags);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#elif defined(__NR_socketcall)
ssize_t __libc_recvmsg(int sockfd, struct msghdr *msg, int flags)
{
	unsigned long args[3];

	args[0] = sockfd;
	args[1] = (unsigned long) msg;
	args[2] = flags;

	if (SINGLE_THREAD_P)
		return (__socketcall(SYS_RECVMSG, args));

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __socketcall(SYS_RECVMSG, args);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#endif
weak_alias(__libc_recvmsg,recvmsg)
libc_hidden_weak(recvmsg)
#endif

#ifdef L_send
extern __typeof(send) __libc_send;
#ifdef __NR_send
#define __NR___sys_send    __NR_send
static
_syscall4(ssize_t, __sys_send, int, sockfd, const void *, buffer, size_t, len, int, flags)
ssize_t __libc_send(int sockfd, const void *buffer, size_t len, int flags)
{
	if (SINGLE_THREAD_P)
		return __sys_send(sockfd, buffer, len, flags);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __sys_send(sockfd, buffer, len, flags);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#elif defined(__NR_socketcall)
/* send, sendto added by bir7@leland.stanford.edu */
ssize_t __libc_send(int sockfd, const void *buffer, size_t len, int flags)
{
	unsigned long args[4];

	args[0] = sockfd;
	args[1] = (unsigned long) buffer;
	args[2] = len;
	args[3] = flags;

	if (SINGLE_THREAD_P)
		return (__socketcall(SYS_SEND, args));

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __socketcall(SYS_SEND, args);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}

#elif defined(__NR_sendto)
ssize_t __libc_send(int sockfd, const void *buffer, size_t len, int flags)
{
	return (sendto(sockfd, buffer, len, flags, NULL, 0));
}
#endif
weak_alias(__libc_send,send)
libc_hidden_weak(send)
#endif

#ifdef L_sendmsg
extern __typeof(sendmsg) __libc_sendmsg;
#ifdef __NR_sendmsg
#define __NR___sys_sendmsg __NR_sendmsg
static
_syscall3(ssize_t, __sys_sendmsg, int, sockfd, const struct msghdr *, msg, int, flags)
ssize_t __libc_sendmsg(int sockfd, const struct msghdr *msg, int flags)
{
	if (SINGLE_THREAD_P)
		return __sys_sendmsg(sockfd, msg, flags);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __sys_sendmsg(sockfd, msg, flags);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#elif defined(__NR_socketcall)
ssize_t __libc_sendmsg(int sockfd, const struct msghdr *msg, int flags)
{
	unsigned long args[3];

	args[0] = sockfd;
	args[1] = (unsigned long) msg;
	args[2] = flags;

	if (SINGLE_THREAD_P)
		return (__socketcall(SYS_SENDMSG, args));

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __socketcall(SYS_SENDMSG, args);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#endif
weak_alias(__libc_sendmsg,sendmsg)
libc_hidden_weak(sendmsg)
#endif

#ifdef L_sendto
extern __typeof(sendto) __libc_sendto;
#ifdef __NR_sendto
#define __NR___sys_sendto  __NR_sendto
static
_syscall6(ssize_t, __sys_sendto, int, sockfd, const void *, buffer,
	size_t, len, int, flags, const struct sockaddr *, to, socklen_t, tolen)
ssize_t __libc_sendto(int sockfd, const void *buffer, size_t len, int flags,const struct sockaddr *to, socklen_t tolen)
{
	if (SINGLE_THREAD_P)
		return __sys_sendto(sockfd, buffer, len, flags, to, tolen);

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __sys_sendto(sockfd, buffer, len, flags, to, tolen);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#elif defined(__NR_socketcall)
/* send, sendto added by bir7@leland.stanford.edu */
ssize_t __libc_sendto(int sockfd, const void *buffer, size_t len, int flags,
	   const struct sockaddr *to, socklen_t tolen)
{
	unsigned long args[6];

	args[0] = sockfd;
	args[1] = (unsigned long) buffer;
	args[2] = len;
	args[3] = flags;
	args[4] = (unsigned long) to;
	args[5] = tolen;

	if (SINGLE_THREAD_P)
		return (__socketcall(SYS_SENDTO, args));

#ifdef __UCLIBC_HAS_THREADS_NATIVE__
	int oldtype = LIBC_CANCEL_ASYNC ();
	int result = __socketcall(SYS_SENDTO, args);
	LIBC_CANCEL_RESET (oldtype);
	return result;
#endif
}
#endif
weak_alias(__libc_sendto,sendto)
libc_hidden_weak(sendto)
#endif

#ifdef L_setsockopt
#ifdef __NR_setsockopt
_syscall5(int, setsockopt, int, fd, int, level, int, optname, const void *, optval, socklen_t, optlen)
#elif defined(__NR_socketcall)
/* [sg]etsockoptions by bir7@leland.stanford.edu */
int setsockopt(int fd, int level, int optname, const void *optval,
		   socklen_t optlen)
{
	unsigned long args[5];

	args[0] = fd;
	args[1] = level;
	args[2] = optname;
	args[3] = (unsigned long) optval;
	args[4] = optlen;
	return (__socketcall(SYS_SETSOCKOPT, args));
}
#endif
libc_hidden_def(setsockopt)
#endif

#ifdef L_shutdown
#ifdef __NR_shutdown
_syscall2(int, shutdown, int, sockfd, int, how)
#elif defined(__NR_socketcall)
/* shutdown by bir7@leland.stanford.edu */
int shutdown(int sockfd, int how)
{
	unsigned long args[2];

	args[0] = sockfd;
	args[1] = how;
	return (__socketcall(SYS_SHUTDOWN, args));
}
#endif
#endif

#ifdef L_socket
#ifdef __NR_socket
_syscall3(int, socket, int, family, int, type, int, protocol)
#elif defined(__NR_socketcall)
int socket(int family, int type, int protocol)
{
	unsigned long args[3];

	args[0] = family;
	args[1] = type;
	args[2] = (unsigned long) protocol;
	return __socketcall(SYS_SOCKET, args);
}
#endif
libc_hidden_def(socket)
#endif

#ifdef L_socketpair
#ifdef __NR_socketpair
_syscall4(int, socketpair, int, family, int, type, int, protocol, int *, sockvec)
#elif defined(__NR_socketcall)
int socketpair(int family, int type, int protocol, int sockvec[2])
{
	unsigned long args[4];

	args[0] = family;
	args[1] = type;
	args[2] = protocol;
	args[3] = (unsigned long) sockvec;
	return __socketcall(SYS_SOCKETPAIR, args);
}
#endif
#endif

