/*
 * trickle-overload.c
 *
 * Copyright (c) 2002, 2003 Marius Aamodt Eriksen <marius@monkey.org>
 * All rights reserved.
 *
 * $Id: trickle-overload.c,v 1.38 2004/02/13 06:11:21 marius Exp $
 */

/* Ick.  linux sucks. */
#define _GNU_SOURCE

#include <sys/types.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/uio.h>
#include <sys/un.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif /* HAVE_SYS_TIME_H */

#include <netinet/in.h>

#ifdef HAVE_ERR_H
#include <err.h>
#endif /* HAVE_ERR_H */
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <limits.h>
#include <math.h>
#if defined(HAVE_TIME_H) && defined(TIME_WITH_SYS_TIME)
#include <time.h>
#endif /* defined(HAVE_TIME_H) && defined(TIME_WITH_SYS_TIME) */
#include <syslog.h>
#include <pwd.h>
#include <stdarg.h>
#include <string.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif /* HAVE_STDINT_H */
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
#include "bwstat.h"
#include "trickle.h"
#include "message.h"
#include "util.h"
#include "trickledu.h"
#include "schedule.h"

#ifndef INFTIM
#define INFTIM -1
#endif /* INFTIM */

#define SD_INSELECT 0x01

struct sockdesc {
	int                    sock;
	int                    flags;
	struct bwstat         *stat;
	struct {
		int     flags;
		size_t  lastlen;
		size_t  selectlen;
	}                      data[2];

	TAILQ_ENTRY(sockdesc)  next;
};

struct delay {
	struct sockdesc    *sd;
	struct timeval      delaytv;
	struct timeval      abstv;
	short               which;
	short               pollevents;
	int                 pollidx;

	TAILQ_ENTRY(delay)  next;	
};

TAILQ_HEAD(delayhead, delay);

struct _pollfd {
	struct pollfd        *pfd;
	int                   idx;

	TAILQ_ENTRY(_pollfd)  next;
};

TAILQ_HEAD(_pollfdhead, _pollfd);

static TAILQ_HEAD(sockdeschead, sockdesc) sdhead;
static uint32_t winsz;
static int verbose;
static uint lim[2][7*24*DIVS_PER_HOUR];//7 days in week * 24 hours in day * divs in hour
static char *argv0;
static double tsmooth;
static uint lsmooth/* , latency */;
static int trickled, initialized, initializing;
/* XXX initializing - volatile? */

#ifdef HAVE_PTHREAD
static pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;

static void trickle_lock()
{
	pthread_mutex_lock(&global_lock);
}

static void trickle_unlock()
{
	pthread_mutex_unlock(&global_lock);
}

#else

static void trickle_lock() {}
static void trickle_unlock() {}

#endif

#define DECLARE(name, ret, args) static ret (*libc_##name) args

DECLARE(socket, int, (int, int, int));
DECLARE(close, int, (int));
/* DECLARE(setsockopt, int, (int, int, int, const void *, socklen_t)); */

DECLARE(read, ssize_t, (int, void *, size_t));
DECLARE(recv, ssize_t, (int, void *, size_t, int));
DECLARE(readv, ssize_t, (int, const struct iovec *, int));
#ifdef __sun__
DECLARE(recvfrom, ssize_t, (int, void *, size_t, int, struct sockaddr *,
	    Psocklen_t));
#else
DECLARE(recvfrom, ssize_t, (int, void *, size_t, int, struct sockaddr *,
	    socklen_t *));
#endif /* __sun__ */

DECLARE(write, ssize_t, (int, const void *, size_t));
DECLARE(send, ssize_t, (int, const void *, size_t, int));
DECLARE(writev, ssize_t, (int, const struct iovec *, int));
DECLARE(sendto, ssize_t, (int, const void *, size_t, int,
	    const struct sockaddr *, socklen_t));

DECLARE(select, int, (int, fd_set *, fd_set *, fd_set *, struct timeval *));
DECLARE(poll, int, (struct pollfd *, int, int));

#ifdef __sun__
DECLARE(accept, int, (int, struct sockaddr *, Psocklen_t));
#else
DECLARE(accept, int, (int, struct sockaddr *, socklen_t *));
#endif /* __sun__ */
DECLARE(dup, int, (int));
DECLARE(dup2, int, (int, int));

#ifdef HAVE_SENDFILE
DECLARE(sendfile, ssize_t, (int, int, off_t *, size_t));
#endif

static int             delay(int, ssize_t *, short);
static struct timeval *getdelay(struct sockdesc *, ssize_t *, short);
static void            update(int, ssize_t, short);
static void            updatesd(struct sockdesc *, ssize_t, short);
static void            trickle_init(void);
void                   safe_printv(int, const char *, ...);

#define errx(l, fmt, arg...) do {		\
	safe_printv(0, fmt, ##arg);		\
	exit(l);				\
} while (0)

#ifdef DL_NEED_UNDERSCORE
#define UNDERSCORE "_"
#else
#define UNDERSCORE ""
#endif /* DL_NEED_UNDERSCORE */

#define INIT do {				\
	if (!initialized && !initializing)	\
		trickle_init();			\
} while (0)

#define GETADDR(x) do {							\
	if ((libc_##x = dlsym(dh, UNDERSCORE #x)) == NULL)		\
		errx(0, "[trickle] Failed to get " #x "() address");	\
} while (0)

static void
trickle_init(void)
{
	void *dh;
	char *winszstr, *verbosestr,
	    *recvlimstr, *sendlimstr, *sockname, *tsmoothstr, *lsmoothstr;
/* 	    *latencystr; */

	initializing = 1;

#ifdef NODLOPEN
	dh = (void *) -1L;
#else
 	if ((dh = dlopen(DLOPENLIBC, RTLD_LAZY)) == NULL)
		errx(1, "[trickle] Failed to open libc");
#endif /* DLOPEN */

	/*
	 * We get write first, so that we have a bigger chance of
	 * exiting gracefully with safe_printv.
	 */

	GETADDR(write);

	GETADDR(socket);
/*	GETADDR(setsockopt); */
	GETADDR(close);

	GETADDR(read);
	GETADDR(readv);
#ifndef __FreeBSD__
	GETADDR(recv);
#endif /* !__FreeBSD__ */
	GETADDR(recvfrom);

	GETADDR(writev);
#ifndef __FreeBSD__
	GETADDR(send);
#endif /* !__FreeBSD__ */
	GETADDR(sendto);

	GETADDR(select);
//	GETADDR(poll);

	GETADDR(dup);
	GETADDR(dup2);

	GETADDR(accept);

#ifdef HAVE_SENDFILE
	GETADDR(sendfile);
#endif

	/* XXX pthread test */
/*  	if ((dh = dlopen("/usr/lib/libpthread.so.1.0", RTLD_LAZY)) == NULL) */
/* 		errx(1, "[trickle] Failed to open libpthread"); */

	GETADDR(poll);

	if ((winszstr = getenv("TRICKLE_WINDOW_SIZE")) == NULL)
		errx(1, "[trickle] Failed to get window size");

	if ((recvlimstr = getenv("TRICKLE_DOWNLOAD_LIMIT")) == NULL)
		errx(1, "[trickle] Failed to get limit");

	if ((sendlimstr = getenv("TRICKLE_UPLOAD_LIMIT")) == NULL)
		errx(1, "[trickle] Failed to get limit");

	if ((verbosestr = getenv("TRICKLE_VERBOSE")) == NULL)
		errx(1, "[trickle] Failed to get verbosity level");

	if ((argv0 = getenv("TRICKLE_ARGV")) == NULL)
		errx(1, "[trickle] Failed to get argv");

	if ((sockname = getenv("TRICKLE_SOCKNAME")) == NULL)
		errx(1, "[trickle] Failed to get socket name");

	if ((tsmoothstr = getenv("TRICKLE_TSMOOTH")) == NULL)
		errx(1, "[trickle] Failed to get time smoothing parameter");

	if ((lsmoothstr = getenv("TRICKLE_LSMOOTH")) == NULL)
		errx(1, "[trickle] Failed to get length smoothing parameter");

/*
	if ((latencystr = getenv("TRICKLE_LATENCY")) == NULL)
		errx(1, "[trickle] Failed to get length latency parameter");
*/

	winsz = atoi(winszstr) * 1024;
/* 	latency = atoi(latencystr);*/
	verbose = atoi(verbosestr);
//	verbose = -1;
        schedString(recvlimstr,lim[TRICKLE_RECV],"download",safe_printv);
        schedString(sendlimstr,lim[TRICKLE_SEND],"upload",safe_printv);
	if ((tsmooth = strtod(tsmoothstr, (char **)NULL)) <= 0.0)
		errx(1, "[trickle] Invalid time smoothing parameter");
	lsmooth = atoi(lsmoothstr) * 1024;

	TAILQ_INIT(&sdhead);

	/*
	 * Open controlling socket
	 */

	trickled_configure(sockname, libc_socket, libc_read,
	    libc_write, libc_close, argv0);
	trickled_open(&trickled);

	bwstat_init(winsz);

	safe_printv(1, "[trickle] Initialized");

	initialized = 1;
}

int
socket(int domain, int type, int protocol)
{
	int sock;
	struct sockdesc *sd;
	trickle_lock();
	INIT;
	trickle_unlock();

	sock = (*libc_socket)(domain, type, protocol);

#ifdef DEBUG
	safe_printv(0, "[DEBUG] socket(%d, %d, %d) = %d",
	    domain, type, protocol, sock);
#endif /* DEBUG */

#ifdef SOCK_NONBLOCK
	type &= ~SOCK_NONBLOCK;
#endif
#ifdef SOCK_CLOEXEC
	type &= ~SOCK_CLOEXEC;
#endif

	if (sock != -1 
		&& (domain == AF_INET || domain == AF_INET6) 
		&& type & SOCK_STREAM) {
		if ((sd = calloc(1, sizeof(*sd))) == NULL)
			return (-1);
		trickle_lock();
		if ((sd->stat = bwstat_new()) == NULL) {
			trickle_unlock();
			free(sd);
			return (-1);
		}

		/* All sockets are equals. */
		sd->stat->lsmooth = lsmooth;
		sd->stat->tsmooth = tsmooth;
		sd->sock = sock;

		TAILQ_INSERT_TAIL(&sdhead, sd, next);
		trickle_unlock();
	}

	return (sock);
}

int
close(int fd)
{
	struct sockdesc *sd, *next;

	trickle_lock();
	INIT;

#ifdef DEBUG
	safe_printv(0, "[DEBUG] close(%d)", fd);
#endif /* DEBUG */

	for (sd = TAILQ_FIRST(&sdhead); sd != NULL; sd = next) {
		next = TAILQ_NEXT(sd, next);
		if (sd->sock == fd) {
			TAILQ_REMOVE(&sdhead, sd, next);
			bwstat_free(sd->stat);
			free(sd);
			break;
		}
	}

	if (fd == trickled) {
		trickled_close(&trickled);
		trickled_open(&trickled);
	}
	trickle_unlock();

	return ((*libc_close)(fd));
}

static struct delay *
select_delay(struct delayhead *dhead, struct sockdesc *sd, short which)
{
	ssize_t len = -1;
	struct timeval *delaytv;
	struct delay *d, *_d;

	updatesd(sd, 0, which);

	if ((delaytv = getdelay(sd, &len, which)) == NULL)
		return (NULL);

	safe_printv(3, "[trickle] Delaying socket (%s) %d "
	    "by %ld seconds %ld microseconds",
	    which == 0 ? "write" : "read", sd->sock,
	    delaytv->tv_sec, delaytv->tv_usec);

	if ((d = calloc(1, sizeof(*d))) == NULL)
		return (NULL);

	gettimeofday(&d->abstv, NULL);
	d->delaytv = *delaytv;
	d->which = which;
	d->sd = sd;
	sd->data[which].selectlen = len;

	if (TAILQ_EMPTY(dhead))
		TAILQ_INSERT_HEAD(dhead, d, next);
	else {
		TAILQ_FOREACH(_d, dhead, next)
			if (timercmp(&d->delaytv, &_d->delaytv, <)) {
				TAILQ_INSERT_BEFORE(_d, d, next);
				break;
			}
		if (_d == NULL)
			TAILQ_INSERT_TAIL(dhead, d, next);
	}

	return (d);
}

static struct delay *
select_shift(struct delayhead *dhead, struct timeval *difftv,
    struct timeval **delaytv)
{
	struct delay *d;
	struct sockdesc *sd;

	TAILQ_FOREACH(d, dhead, next) {
		if (timercmp(&d->delaytv, difftv, >))
			break;
		sd = d->sd;

		updatesd(sd, 0, d->which);
		SET(sd->data[d->which].flags, SD_INSELECT);
	}

	if (d != NULL)
		timersub(&d->delaytv, difftv, *delaytv);
	else 
		*delaytv = NULL;

	/* XXX this should be impossible ... */
	if (*delaytv != NULL &&
	    ((*delaytv)->tv_sec < 0 || (*delaytv)->tv_usec < 0))
		timerclear(*delaytv);

	return (d);
}

int
select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds,
    struct timeval *__timeout)
{
	struct sockdesc *sd;
	fd_set *fdsets[] = { wfds, rfds }, *fds;
	struct timeval *delaytv, _delaytv, *selecttv = NULL, *timeout = NULL,
	    _timeout, inittv, curtv, difftv;
	short which;
	struct delayhead dhead;
	struct delay *d, *_d;
	int ret;

#ifdef DEBUG
	safe_printv(0, "[DEBUG] select(%d)", nfds);
#endif /* DEBUG */

	TAILQ_INIT(&dhead);

	/* We don't want to modify the user's timeout */
	if (__timeout != NULL) {
		_timeout = *__timeout;
		timeout = &_timeout;
	} 

	trickle_lock();
	INIT;

	/*
	 * Sockets that require delaying get added to the delay list.
	 * delaytv is always assigned to the head of the list.
	 */
	for (which = 0; which < 2; which++)
		TAILQ_FOREACH(sd, &sdhead, next)
			if ((fds = fdsets[which]) != NULL && 
			    FD_ISSET(sd->sock, fds) &&
			    select_delay(&dhead, sd, which)) {
				FD_CLR(sd->sock, fds);
			}

	gettimeofday(&inittv, NULL);
	curtv = inittv;
	d = TAILQ_FIRST(&dhead);
	if (d != NULL) {
		_delaytv = d->delaytv;
		delaytv = &_delaytv;
	} else
		delaytv = NULL;
	timersub(&curtv, &inittv, &difftv);
 again:
	selecttv = NULL;

	if (delaytv != NULL)
		selecttv = delaytv;

	if (timeout != NULL) {
		timersub(timeout, &difftv, timeout);
		if (timeout->tv_sec < 0 || timeout->tv_usec < 0)
			timerclear(timeout);
		if (delaytv != NULL && timercmp(timeout, delaytv, <))
			selecttv = timeout;
		else if (delaytv == NULL)
			selecttv = timeout;
	}


#ifdef DEBUG
	safe_printv(0, "[DEBUG] IN select(%d)", nfds);
#endif /* DEBUG */
	trickle_unlock();
	ret = (*libc_select)(nfds, rfds, wfds, efds, selecttv);
	trickle_lock();

#ifdef DEBUG
	safe_printv(0, "[DEBUG] OUT select(%d) = %d", nfds, ret);
#endif /* DEBUG */

	if (ret == 0 && delaytv != NULL && selecttv == delaytv) {
		gettimeofday(&curtv, NULL);
		timersub(&curtv, &inittv, &difftv);
		_d = select_shift(&dhead, &difftv, &delaytv);
		while ((d = TAILQ_FIRST(&dhead)) != _d) {
			FD_SET(d->sd->sock, fdsets[d->which]);
			TAILQ_REMOVE(&dhead, d, next);
			free(d);
		}

		goto again;
	}

	while ((d = TAILQ_FIRST(&dhead)) != NULL) {
		CLR(d->sd->data[d->which].flags, SD_INSELECT);
		TAILQ_REMOVE(&dhead, d, next);
		free(d);
	}
	trickle_unlock();

	return (ret);
}

#define POLL_WRMASK (POLLOUT | POLLWRNORM | POLLWRBAND)
#define POLL_RDMASK (POLLIN | /* POLLNORM | */  POLLPRI | POLLRDNORM | POLLRDBAND)

#if defined(__linux__) || (defined(__svr4__) && defined(__sun__)) || defined(__OpenBSD__)
int
poll(struct pollfd *fds, nfds_t nfds, int __timeout)
#elif defined(__FreeBSD__)
int
poll(struct pollfd *fds, unsigned int nfds, int __timeout)
#else
int
poll(struct pollfd *fds, int nfds, int __timeout)
#endif /* __linux__ */
{
	struct pollfd *pfd;
	int i, polltimeout, ret;
	struct sockdesc *sd;
	struct delay *d, *_d;
	struct timeval inittv, curtv, _timeout, *timeout = NULL, *delaytv,
	    *polltv, difftv;
	struct delayhead dhead;

#if defined(DEBUG) || defined(DEBUG_POLL)
	safe_printv(0, "[DEBUG] poll(*, %d, %d)", nfds, __timeout);
#endif /* DEBUG */

	if (__timeout != INFTIM) {
		_timeout.tv_sec = __timeout / 1000;
		_timeout.tv_usec = (__timeout % 1000) * 100;
		timeout = &_timeout;
	}

	TAILQ_INIT(&dhead);

	trickle_lock();
	INIT;

	for (i = 0; i < nfds; i++) {
		pfd = &fds[i];
		TAILQ_FOREACH(sd, &sdhead, next)
			if (sd->sock == pfd->fd)
				break;
		if (sd == NULL)
			continue;

		/* For each event */
		if (pfd->events & POLL_RDMASK && 
		    (d = select_delay(&dhead, sd, TRICKLE_RECV)) != NULL) {
			d->pollevents = pfd->events & POLL_RDMASK;
			d->pollidx = i;
			pfd->events &= ~POLL_RDMASK;
		}

		if (pfd->events & POLL_WRMASK && 
		    (d = select_delay(&dhead, sd, TRICKLE_SEND)) != NULL) {
			d->pollevents = pfd->events & POLL_WRMASK;
			d->pollidx = i;
			pfd->events &= ~POLL_WRMASK;
		}
	}

	gettimeofday(&inittv, NULL);
	curtv = inittv;
	d = TAILQ_FIRST(&dhead);
	delaytv = d != NULL ? &d->delaytv : NULL;
 again:
	timersub(&inittv, &curtv, &difftv);
	polltv = NULL;

	if (delaytv != NULL)
		polltv = delaytv;

	if (timeout != NULL) {
		timersub(timeout, &difftv, timeout);
		if (delaytv != NULL && timercmp(timeout, delaytv, <))
			polltv = timeout;
		else if (delaytv == NULL)
			polltv = timeout;
	}

	/* Calculate polltimeout here based on polltv */
	if (polltv != NULL)
		polltimeout = polltv->tv_sec * 1000 +
		    polltv->tv_usec / 100;
	else
		polltimeout = INFTIM;

#if defined(DEBUG) || defined(DEBUG_POLL)
	safe_printv(0, "[DEBUG] IN poll(*, %d, %d)", nfds, polltimeout);
#endif /* DEBUG */
	trickle_unlock();
	ret = (*libc_poll)((struct pollfd *)fds, (int)nfds, (int)polltimeout);

#if defined(DEBUG) || defined(DEBUG_POLL)
	safe_printv(0, "[DEBUG] OUT poll(%d) = %d", nfds, ret);
#endif /* DEBUG */
	trickle_lock();
	if (ret == 0 && delaytv != NULL && polltv == delaytv) {
		_d = select_shift(&dhead, &inittv, &delaytv);
		while ((d = TAILQ_FIRST(&dhead)) != NULL && d != _d) {
			fds[d->pollidx].events |= d->pollevents;

			TAILQ_REMOVE(&dhead, d, next);
			free(d);
		}

		gettimeofday(&curtv, NULL);
		goto again;
	}

	while ((d = TAILQ_FIRST(&dhead)) != NULL) {
		CLR(d->sd->data[d->which].flags, SD_INSELECT);

		TAILQ_REMOVE(&dhead, d, next);
		free(d);
	}
	trickle_unlock();
	return (ret);
}

ssize_t
read(int fd, void *buf, size_t nbytes)
{
	ssize_t ret = -1;
	size_t xnbytes = nbytes;
	int eagain;

	trickle_lock();
	INIT;

	if (!(eagain = delay(fd, &xnbytes, TRICKLE_RECV) == TRICKLE_WOULDBLOCK)) {
		trickle_unlock();
		ret = (*libc_read)(fd, buf, xnbytes);
		trickle_lock();
#ifdef DEBUG
		safe_printv(0, "[DEBUG] read(%d, *, %d) = %d", fd, xnbytes, ret);
	} else {
		safe_printv(0, "[DEBUG] delaying read(%d, *, %d) = %d", fd, xnbytes, ret);
#endif /* DEBUG */
	}

	update(fd, ret, TRICKLE_RECV);
	trickle_unlock();

	if (eagain) {
		ret = -1;
		errno = EAGAIN;
	}

	return (ret);
}

/*
 * XXX defunct for smoothing ... for now
 */
ssize_t
readv(int fd, const struct iovec *iov, int iovcnt)
{
	size_t len = 0;
	ssize_t ret = -1;
	int i, eagain;

	for (i = 0; i < iovcnt; i++)
		len += iov[i].iov_len;

	trickle_lock();
	INIT;

	if (!(eagain = delay(fd, &len, TRICKLE_RECV) == TRICKLE_WOULDBLOCK)) {
		trickle_unlock();
		ret = (*libc_readv)(fd, iov, iovcnt);
		trickle_lock();
#ifdef DEBUG
		safe_printv(0, "[DEBUG] readv(%d, *, %d) = %d", fd, iovcnt, ret);
	} else {
		safe_printv(0, "[DEBUG] delaying readv(%d, *, %d)", fd, iovcnt);
#endif /* DEBUG */
	}

	update(fd, ret, TRICKLE_RECV);
	trickle_unlock();

	if (eagain) {
		errno = EAGAIN;
		ret = -1;
	}

	return (ret);
}

#ifndef __FreeBSD__ 
ssize_t
recv(int sock, void *buf, size_t len, int flags)
{
	ssize_t ret = -1;
	size_t xlen = len;
	int eagain;

	trickle_lock();
	INIT;

	if (!(eagain = delay(sock, &xlen, TRICKLE_RECV) == TRICKLE_WOULDBLOCK)) {
		trickle_unlock();
		ret = (*libc_recv)(sock, buf, xlen, flags);
		trickle_lock();
#ifdef DEBUG
		safe_printv(0, "[DEBUG] recv(%d, *, %d, %d) = %d",
		    sock, len, flags, ret);
	} else {
		safe_printv(0, "[DEBUG] delaying recv(%d, *, %d, %d)", sock, len, flags);		
#endif /* DEBUG */
	}

	update(sock, ret, TRICKLE_RECV);
	trickle_unlock();

	if (eagain) {
		errno = EAGAIN;
		ret = -1;
	}

	return (ret);
}
#endif /* !__FreeBSD__ */

#ifdef __sun__
ssize_t
recvfrom(int sock, void *buf, size_t len, int flags, struct sockaddr *from,
    Psocklen_t fromlen)
#else
ssize_t
recvfrom(int sock, void *buf, size_t len, int flags, struct sockaddr *from,
    socklen_t *fromlen)
#endif /* __sun__ */
{
	ssize_t ret = -1;
	size_t xlen = len;
	int eagain;

	trickle_lock();
	INIT;

	if (!(eagain = delay(sock, &xlen, TRICKLE_RECV) == TRICKLE_WOULDBLOCK)) {
		trickle_unlock();
		ret = (*libc_recvfrom)(sock, buf, xlen, flags, from, fromlen);
		trickle_lock();
#ifdef DEBUG
		safe_printv(0, "[DEBUG] recvfrom(%d, *, %d, %d) = %d",
		    sock, len, flags, ret);
	} else {
		safe_printv(0, "[DEBUG] delaying recvfrom(%d, *, %d, %d)", sock,
		    len, flags);		
#endif /* DEBUG */
	}

	update(sock, ret, TRICKLE_RECV);
	trickle_unlock();

	if (eagain) {
		errno = EAGAIN;
		ret = -1;
	}

	return (ret);
}

ssize_t
write(int fd, const void *buf, size_t len)
{
	ssize_t ret = -1;
	size_t xlen = len;
	int eagain;

	trickle_lock();
	INIT;

	if (!(eagain = delay(fd, &xlen, TRICKLE_SEND) == TRICKLE_WOULDBLOCK)) {
		trickle_unlock();
		ret = (*libc_write)(fd, buf, xlen);
		trickle_lock();
#ifdef DEBUG
		safe_printv(0, "[DEBUG] write(%d, *, %d) = %d", fd, len, ret);
	} else {
		safe_printv(0, "[DEBUG] delaying write(%d, *, %d)", fd, len);
#endif /* DEBUG */
	}

	update(fd, ret, TRICKLE_SEND);
	trickle_unlock();

	if (eagain) {
		errno = EAGAIN;
		ret = -1;
	}

	return (ret);
}

/*
 * XXX defunct for smoothing ... for now
 */
ssize_t
writev(int fd, const struct iovec *iov, int iovcnt)
{
	ssize_t ret = -1;
	size_t len = 0;
	int i, eagain;

	for (i = 0; i < iovcnt; i++)
		len += iov[i].iov_len;

	trickle_lock();
	INIT;

	if (!(eagain = delay(fd, &len, TRICKLE_SEND) == TRICKLE_WOULDBLOCK)) {
		trickle_unlock();
		ret = (*libc_writev)(fd, iov, iovcnt);
		trickle_lock();
#ifdef DEBUG
		safe_printv(0, "[DEBUG] writev(%d, *, %d) = %d",
		    fd, iovcnt, ret);
	} else {
		safe_printv(0, "[DEBUG] delaying writev(%d, *, %d)", fd, iovcnt);
#endif /* DEBUG */
	}

	update(fd, ret, TRICKLE_SEND);
	trickle_unlock();

	if (eagain) {
		errno = EAGAIN;
		ret = -1;
	}

	return (ret);
}

#ifndef __FreeBSD__
ssize_t
send(int sock, const void *buf, size_t len, int flags)
{
	ssize_t ret = -1;
	size_t xlen = len;
	int eagain;

	trickle_lock();
	INIT;

	if (!(eagain = delay(sock, &xlen, TRICKLE_SEND) == TRICKLE_WOULDBLOCK)) {
		trickle_unlock();
		ret = (*libc_send)(sock, buf, xlen, flags);
		trickle_lock();
#ifdef DEBUG
		safe_printv(0, "[DEBUG] send(%d, *, %d, %d) = %d",
		    sock, len, flags, ret);
	} else {
		safe_printv(0, "[DEBUG] delaying send(%d, *, %d, %d)", sock,
		    len, flags);
#endif /* DEBUG */
	}

	update(sock, ret, TRICKLE_SEND);
	trickle_unlock();

	if (eagain) {
		errno = EAGAIN;
		ret = -1;
	}

	return (ret);
}
#endif /* !__FreeBSD__ */

ssize_t
sendto(int sock, const void *buf, size_t len, int flags, const struct sockaddr *to,
    socklen_t tolen)
{
	ssize_t ret = -1;
	size_t xlen = len;
	int eagain;

	trickle_lock();
	INIT;

	if (!(eagain = delay(sock, &xlen, TRICKLE_SEND) == TRICKLE_WOULDBLOCK)) {
		trickle_unlock();
		ret = (*libc_sendto)(sock, buf, xlen, flags, to, tolen);
		trickle_lock();
#ifdef DEBUG
		safe_printv(0, "[DEBUG] sendto(%d, *, %d) = %d", sock, len, ret);
	} else {
		safe_printv(0, "[DEBUG] delaying sendto(%d, *, %d)", sock, len);
#endif /* DEBUG */
	}

	update(sock, ret, TRICKLE_SEND);
	trickle_unlock();

	if (eagain) {
		errno = EAGAIN;
		ret = -1;
	}

	return (ret);
}

#if 0
int
setsockopt(int sock, int level, int optname, const void *optval,
    socklen_t option)
{
	INIT;

	/* blocking, etc. */
	return ((*libc_setsockopt)(sock, level, optname, optval, option));
}
#endif /* 0 */

int
dup(int oldfd)
{
	int newfd;
	struct sockdesc *sd, *nsd;

	trickle_lock();
	INIT;
	trickle_unlock();

	newfd = (*libc_dup)(oldfd);

#ifdef DEBUG
	safe_printv(0, "[DEBUG] dup(%d) = %d", oldfd, newfd);
#endif /* DEBUG */

	trickle_lock();
	TAILQ_FOREACH(sd, &sdhead, next)
	        if (oldfd == sd->sock)
			break;

	if (sd != NULL && newfd != -1) {
		if ((nsd = malloc(sizeof(*nsd))) == NULL) {
			trickle_unlock();
			(*libc_close)(newfd);
			return (-1);
		}
		sd->sock = newfd;
		memcpy(nsd, sd, sizeof(*nsd));
		TAILQ_INSERT_TAIL(&sdhead, nsd, next);
	}
	trickle_unlock();

	return (newfd);
}

int
dup2(int oldfd, int newfd)
{
	struct sockdesc *sd, *nsd;
	int ret;

	trickle_lock();
	INIT;
	trickle_unlock();

	ret = (*libc_dup2)(oldfd, newfd);

#ifdef DEBUG
	safe_printv(0, "[DEBUG] dup2(%d, %d) = %d", oldfd, newfd, ret);
#endif /* DEBUG */

	trickle_lock();
	TAILQ_FOREACH(sd, &sdhead, next)
		if (oldfd == sd->sock)
			break;

	if (sd != NULL && ret != -1) {
		if ((nsd = malloc(sizeof(*nsd))) == NULL) {
			trickle_unlock();
			return (-1);
		}
		sd->sock = newfd;
		memcpy(nsd, sd, sizeof(*nsd));
		TAILQ_INSERT_TAIL(&sdhead, nsd, next);
	}
	trickle_unlock();

	return (ret);
}

#ifdef __sun__
int
accept(int sock, struct sockaddr *addr, Psocklen_t addrlen)
#else
int
accept(int sock, struct sockaddr *addr, socklen_t *addrlen)
#endif /* __sun__ */
{
	int ret;
	struct sockdesc *sd;

	trickle_lock();
	INIT;
	trickle_unlock();

	ret = (*libc_accept)(sock, addr, addrlen);

#ifdef DEBUG
	safe_printv(0, "[DEBUG] accept(%d) = %d", sock, ret);
#endif /* DEBUG */

	if (ret != -1) {
		if ((sd = calloc(1, sizeof(*sd))) == NULL)
			return (ret);
		trickle_lock();
		if ((sd->stat = bwstat_new()) == NULL) {
			trickle_unlock();
			free(sd);
			return (ret);
		}

		sd->sock = ret;
		sd->stat->lsmooth = lsmooth;
		sd->stat->tsmooth = tsmooth;
		TAILQ_INSERT_TAIL(&sdhead, sd, next);
		trickle_unlock();
	}

	return (ret);
}

#ifdef HAVE_SENDFILE
ssize_t
sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
{
	size_t inbytes = count, outbytes = count, bytes;
	ssize_t ret = 0;

	trickle_lock();
	INIT;

	/* in_fd = recv, out_fd = send */

	/* We should never get TRICKLE_WOULDBLOCK here */
	delay(in_fd, &inbytes, TRICKLE_RECV);
	delay(out_fd, &outbytes, TRICKLE_SEND);
	trickle_unlock();

	/* This is a slightly ugly hack. */
	bytes = MIN(inbytes, outbytes);
	if (bytes > 0)
		ret = (*libc_sendfile)(out_fd, in_fd, offset, bytes);

	return (ret);
}
#endif	/* HAVE_SENDFILE */

static int
delay(int sock, ssize_t *len, short which)
{
	struct sockdesc *sd;
	struct timeval *tv;
	struct timespec ts, rm;

	TAILQ_FOREACH(sd, &sdhead, next)
		if (sock == sd->sock)
			break;

	if (sd == NULL)
		return (-1);

	if (ISSET(sd->data[which].flags, SD_INSELECT)) {
		if (*len > sd->data[which].selectlen)
			*len = sd->data[which].selectlen;
		CLR(sd->data[which].flags, SD_INSELECT);
		return (0);
	}

	if ((tv = getdelay(sd, len, which)) != NULL) {
		TIMEVAL_TO_TIMESPEC(tv, &ts);

		safe_printv(2, "[trickle] Delaying %lds%ldus",
		    tv->tv_sec, tv->tv_usec);

		if (ISSET(sd->flags, TRICKLE_NONBLOCK))
			return (TRICKLE_WOULDBLOCK);

		while (nanosleep(&ts, &rm) == -1 && errno == EINTR)
			ts = rm;
	}

	return (0);
}

static struct timeval *
getdelay(struct sockdesc *sd, ssize_t *len, short which)
{
	struct timeval *xtv;

	uint xlim = lim[which][getSchedIndex()];

	/* XXX check this. */
	if (*len < 0)
		*len = sd->data[which].lastlen;

	if (trickled && (xtv = trickled_getdelay(which, len)) != NULL)
		xlim = *len / (xtv->tv_sec + xtv->tv_usec / 1000000.0);

	if (xlim == 0)
		return (NULL);

	return (bwstat_getdelay(sd->stat, len, xlim, which));
}

static void
update(int sock, ssize_t len, short which)
{
	struct sockdesc *sd;

	TAILQ_FOREACH(sd, &sdhead, next)
		if (sock == sd->sock)
			break;

	if (sd == NULL)
		return;

	updatesd(sd, len, which);
}

static void
updatesd(struct sockdesc *sd, ssize_t len, short which)
{
	struct bwstat_data *bsd;
	int ret;

	if (len < 0)
		len = 0;

	if ((ret = fcntl(sd->sock, F_GETFL, 0)) != -1) {
		if (ret & O_NONBLOCK)
			SET(sd->flags, TRICKLE_NONBLOCK);
		else
			CLR(sd->flags, TRICKLE_NONBLOCK);
	}

	if (len > 0)
		sd->data[which].lastlen = len;

	if (trickled)
		trickled_update(which, len);

	bwstat_update(sd->stat, len, which);

	bsd = &sd->stat->data[which];

	safe_printv(1, "[trickle] avg: %d.%d KB/s; win: %d.%d KB/s",
	    (bsd->rate / 1024), ((bsd->rate % 1024) * 100 / 1024),
	    (bsd->winrate / 1024), ((bsd->winrate % 1024) * 100 / 1024));
}

void
safe_printv(int level, const char *fmt, ...)
{
	va_list ap;
	char str[1024];
	int n;

	if (level > verbose)
		return;

	va_start(ap, fmt);

	if ((n = snprintf(str, sizeof(str), "%s: ", argv0)) == -1) {
		str[0] = '\0';
		n = 0;
	}

        if (fmt != NULL)
		n = vsnprintf(str + n, sizeof(str) - n, fmt, ap);

	if (n == -1)
		return;

	strlcat(str, "\n", sizeof(str));

	(*libc_write)(STDERR_FILENO, str, strlen(str));
	va_end(ap);
}

