/*
* trickle-overload.c
*
* Copyright (c) 2002, 2003 Marius Aamodt Eriksen <marius@monkey.org>
* All rights reserved.
*
* $Id: trickle-overload.c,v 1.34 2003/06/02 23:13:28 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 */
#include "bwstat.h"
#include "trickle.h"
#include "message.h"
#include "util.h"
#include "trickledu.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];
static char *argv0;
static double tsmooth;
static uint lsmooth;
static int trickled, initialized, initializing;
/* XXX initializing - volatile? */
#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));
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;
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);
GETADDR(recv);
GETADDR(recvfrom);
GETADDR(writev);
GETADDR(send);
GETADDR(sendto);
GETADDR(select);
GETADDR(poll);
GETADDR(dup);
GETADDR(dup2);
GETADDR(accept);
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");
winsz = atoi(winszstr) * 1024;
lim[TRICKLE_RECV] = atoi(recvlimstr) * 1024;
lim[TRICKLE_SEND] = atoi(sendlimstr) * 1024;
verbose = atoi(verbosestr);
// verbose = -1;
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, 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;
INIT;
sock = (*libc_socket)(domain, type, protocol);
if (sock != -1 && domain == AF_INET && type == SOCK_STREAM) {
if ((sd = calloc(1, sizeof(*sd))) == NULL)
return (-1);
if ((sd->stat = bwstat_new()) == NULL) {
free(sd);
return (-1);
}
/* All sockets are equals. */
sd->stat->pts = 1;
sd->stat->lsmooth = lsmooth;
sd->stat->tsmooth = tsmooth;
sd->sock = sock;
TAILQ_INSERT_TAIL(&sdhead, sd, next);
}
return (sock);
}
int
close(int fd)
{
struct sockdesc *sd, *next;
INIT;
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;
}
}
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 *inittv,
struct timeval **delaytv)
{
struct timeval curtv, difftv;
struct delay *d;
struct sockdesc *sd;
gettimeofday(&curtv, NULL);
timersub(&curtv, inittv, &difftv);
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, *selecttv = NULL, *timeout = NULL, _timeout,
inittv, curtv, difftv;
short which;
struct delayhead dhead;
struct delay *d, *_d;
int ret;
INIT;
TAILQ_INIT(&dhead);
/* We don't want to modify the user's timeout */
if (__timeout != NULL) {
_timeout = *__timeout;
timeout = &_timeout;
}
/*
* 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);
nfds--;
}
gettimeofday(&inittv, NULL);
curtv = inittv;
d = TAILQ_FIRST(&dhead);
delaytv = d != NULL ? &d->delaytv : NULL;
again:
timersub(&inittv, &curtv, &difftv);
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;
}
ret = (*libc_select)(nfds, rfds, wfds, efds, selecttv);
if (ret == 0 && delaytv != NULL && selecttv == delaytv) {
_d = select_shift(&dhead, &inittv, &delaytv);
while ((d = TAILQ_FIRST(&dhead)) != _d) {
FD_SET(d->sd->sock, fdsets[d->which]);
nfds++;
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);
}
return (ret);
}
#define POLL_WRMASK (POLLOUT | POLLWRNORM | POLLWRBAND)
#define POLL_RDMASK (POLLIN | /* POLLNORM | */ POLLPRI | POLLRDNORM | POLLRDBAND)
#if defined(__linux__) || (defined(__svr4__) && defined(__sun__))
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;
INIT;
if (__timeout != INFTIM) {
_timeout.tv_sec = __timeout / 1000;
_timeout.tv_usec = (__timeout % 1000) * 100;
timeout = &_timeout;
}
TAILQ_INIT(&dhead);
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;
ret = (*libc_poll)((struct pollfd *)fds, (int)nfds, (int)polltimeout);
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);
}
return (ret);
}
ssize_t
read(int fd, void *buf, size_t nbytes)
{
ssize_t ret = -1;
size_t xnbytes = nbytes;
int eagain;
INIT;
if (!(eagain = delay(fd, &xnbytes, TRICKLE_RECV) == TRICKLE_WOULDBLOCK))
ret = (*libc_read)(fd, buf, xnbytes);
update(fd, ret, TRICKLE_RECV);
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;
INIT;
for (i = 0; i < iovcnt; i++)
len += iov[i].iov_len;
if (!(eagain = delay(fd, &len, TRICKLE_RECV) == TRICKLE_WOULDBLOCK))
ret = (*libc_readv)(fd, iov, iovcnt);
update(fd, ret, TRICKLE_RECV);
if (eagain) {
errno = EAGAIN;
ret = -1;
}
return (ret);
}
ssize_t
recv(int sock, void *buf, size_t len, int flags)
{
ssize_t ret = -1;
size_t xlen = len;
int eagain;
INIT;
if (!(eagain = delay(sock, &xlen, TRICKLE_RECV) == TRICKLE_WOULDBLOCK))
ret = (*libc_recv)(sock, buf, xlen, flags);
update(sock, ret, TRICKLE_RECV);
if (eagain) {
errno = EAGAIN;
ret = -1;
}
return (ret);
}
#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;
INIT;
if (!(eagain = delay(sock, &xlen, TRICKLE_RECV) == TRICKLE_WOULDBLOCK))
ret = (*libc_recvfrom)(sock, buf, xlen, flags, from, fromlen);
update(sock, ret, TRICKLE_RECV);
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;
INIT;
if (!(eagain = delay(fd, &xlen, TRICKLE_SEND) == TRICKLE_WOULDBLOCK))
ret = (*libc_write)(fd, buf, xlen);
update(fd, ret, TRICKLE_SEND);
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;
INIT;
for (i = 0; i < iovcnt; i++)
len += iov[i].iov_len;
if (!(eagain = delay(fd, &len, TRICKLE_SEND) == TRICKLE_WOULDBLOCK))
ret = (*libc_writev)(fd, iov, iovcnt);
update(fd, ret, TRICKLE_SEND);
if (eagain) {
errno = EAGAIN;
ret = -1;
}
return (ret);
}
ssize_t
send(int sock, const void *buf, size_t len, int flags)
{
ssize_t ret = -1;
size_t xlen = len;
int eagain;
INIT;
if (!(eagain = delay(sock, &xlen, TRICKLE_SEND) == TRICKLE_WOULDBLOCK))
ret = (*libc_send)(sock, buf, xlen, flags);
update(sock, ret, TRICKLE_SEND);
if (eagain) {
errno = EAGAIN;
ret = -1;
}
return (ret);
}
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;
INIT;
if (!(eagain = delay(sock, &xlen, TRICKLE_SEND) == TRICKLE_WOULDBLOCK))
ret = (*libc_sendto)(sock, buf, xlen, flags, to, tolen);
update(sock, ret, TRICKLE_SEND);
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;
INIT;
newfd = (*libc_dup)(oldfd);
TAILQ_FOREACH(sd, &sdhead, next)
if (oldfd == sd->sock)
break;
if (sd != NULL && newfd != -1) {
if ((nsd = malloc(sizeof(*nsd))) == NULL) {
(*libc_close)(newfd);
return (-1);
}
sd->sock = newfd;
memcpy(nsd, sd, sizeof(*nsd));
TAILQ_INSERT_TAIL(&sdhead, nsd, next);
}
return (newfd);
}
int
dup2(int oldfd, int newfd)
{
struct sockdesc *sd, *nsd;
int ret;
INIT;
ret = (*libc_dup2)(oldfd, newfd);
TAILQ_FOREACH(sd, &sdhead, next)
if (oldfd == sd->sock)
break;
if (sd != NULL && ret != -1) {
if ((nsd = malloc(sizeof(*nsd))) == NULL)
return (-1);
sd->sock = newfd;
memcpy(nsd, sd, sizeof(*nsd));
TAILQ_INSERT_TAIL(&sdhead, nsd, next);
}
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;
INIT;
ret = (*libc_accept)(sock, addr, addrlen);
if (ret != -1) {
if ((sd = calloc(1, sizeof(*sd))) == NULL)
return (ret);
if ((sd->stat = bwstat_new()) == NULL) {
free(sd);
return (ret);
}
sd->sock = ret;
sd->stat->pts = 1;
sd->stat->lsmooth = lsmooth;
sd->stat->tsmooth = tsmooth;
TAILQ_INSERT_TAIL(&sdhead, sd, next);
}
return (ret);
}
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];
/* XXX check this. */
if (*len < 0)
*len = sd->data[which].lastlen;
if (trickled)
xlim = (xtv = trickled_getdelay(which, len)) != NULL ?
*len / (xtv->tv_sec + xtv->tv_usec / 1000000.0) : 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);
}
syntax highlighted by Code2HTML, v. 0.9.1