Commit 287c7b63 authored by “李磊”'s avatar “李磊”
Browse files

Initial commit

parents
# Autogenerated files
/autom4te.cache
/Makefile.in
/configure
/aclocal.m4
*.loT
*.lo
*.o
*.so
/config.h
/config.log
/config.status
/libtool
/stamp-h1
/trickle
/tricklectl
/trickled
/config.guess
/config.sub
/install-sh
/missing
/mkinstalldirs
/ltmain.sh
/compile
/m4/libtool.m4
/m4/ltoptions.m4
/m4/ltsugar.m4
/m4/ltversion.m4
/m4/lt~obsolete.m4
# Editor backups
*~
*.sh
Copyright (c) 2002-2004 Marius Aamodt Eriksen <marius@monkey.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. The names of the copyright holders may not be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED ``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 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.
# Makefile for building dynamic libraries for Android NDK and x86
# Compiler settings
NDK_PATH = /usr/local/android-ndk-r19c
NDK_TOOLCHAIN = $(NDK_PATH)/toolchains/llvm/prebuilt/linux-x86_64/bin
NDK_CC = $(NDK_TOOLCHAIN)/armv7a-linux-androideabi28-clang
GCC = gcc
# Compiler flags
CFLAGS = -shared -fPIC -g -O2 -Wall -I./
COMM_FLAGS = -DHAVE_CONFIG_H
NDK_FLAGS = -DHAVE_ANDROID_H
X86_FLAGS = -DDEBUG -DNODLOPEN
# Source files
SOURCES = bwstat.c atomicio.c strlcpy.c strlcat.c trickle-overload.c
# Output files
NDK = ndk
X86 = x86
NDK_OUT = trickle-overload-ndk.so
X86_OUT = trickle-overload-x86.so
# Default target
all: $(NDK) $(X86)
# Rule for NDK dynamic library
$(NDK): $(SOURCES)
$(NDK_CC) $(CFLAGS) $^ -o $(NDK_OUT) $(COMM_FLAGS) $(NDK_FLAGS)
# Rule for x86 dynamic library
$(X86): $(SOURCES)
$(GCC) $(CFLAGS) $^ -o $(X86_OUT) $(COMM_FLAGS) $(X86_FLAGS)
# Clean up
clean:
rm -f $(NDK_OUT) $(X86_OUT)
.PHONY: all clean
# linkfog-trickle
基于开源项目trickle裁剪 https://github.com/mariusae/trickle
commit: 09a1d955c6554eb7e625c99bf96b2d99ec7db3dc
## 使用前配置环境变量
```bash
# 滑动窗口大小 单位KB
export TRICKLE_WINDOW_SIZE=
# 接收速率限制大小 单位KB
export TRICKLE_DOWNLOAD_LIMIT=
# 发送速率限制大小 单位KB
export TRICKLE_UPLOAD_LIMIT=
# 日志打印级别
export TRICKLE_VERBOSE=
# 调用的程序名
export TRICKLE_ARGV=
# 平滑时间 单位秒
export TRICKLE_TSMOOTH=
# 平滑长度 单位KB
export TRICKLE_LSMOOTH=
````
#undef socklen_t
#undef u_int16_t
#undef u_int32_t
#undef u_int64_t
#undef u_int8_t
#undef in_addr_t
#undef SYSCONFDIR
#undef LIBDIR
#undef SPT_TYPE
#undef HAVE___PROGNAME
#undef DL_NEED_UNDERSCORE
#undef NODLOPEN
#undef DLOPENLIBC
@BOTTOM@
/* Prototypes for missing functions */
#ifndef HAVE_STRLCAT
size_t strlcat(char *, const char *, size_t);
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char *, const char *, size_t);
#endif
#ifndef HAVE_SETENV
int setenv(register const char *name, register const char *value, int rewrite);
#endif /* !HAVE_SETENV */
#ifndef HAVE_STRSEP
char *strsep(char **, const char *);
#endif /* HAVE_STRSEP */
#ifndef HAVE_ERR
void err(int, const char *, ...);
void warn(const char *, ...);
void errx(int , const char *, ...);
void warnx(const char *, ...);
#endif
#ifndef HAVE_DAEMON
int daemon(int, int);
#endif /* HAVE_DAEMON */
/*
* Copyright (c) 2002 Marius Aamodt Eriksen <marius@monkey.org>
* Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*/
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <unistd.h>
#include <errno.h>
/*
* ensure all of data on socket comes through. f==read || f==write
*/
ssize_t
atomicio(f, fd, _s, n)
ssize_t (*f) ();
int fd;
void *_s;
size_t n;
{
char *s = _s;
ssize_t res, pos = 0;
while (n > pos) {
res = (f) (fd, s + pos, n - pos);
switch (res) {
case -1:
if (errno == EINTR || errno == EAGAIN)
continue;
case 0:
if (pos != 0)
return (pos);
return (res);
default:
pos += res;
}
}
return (pos);
}
/*
* bwstat.c
*
* Copyright (c) 2003 Marius Aamodt Eriksen <marius@monkey.org>
* All rights reserved.
*
* $Id: bwstat.c,v 1.15 2003/07/29 05:58:58 marius Exp $
*/
#include <sys/types.h>
#include <sys/queue.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif /* HAVE_SYS_TIME_H */
#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif /* HAVE_STDINT_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 <string.h>
#include "util.h"
#include "bwstat.h"
static TAILQ_HEAD(bwstathead, bwstat) statq;
static uint winsz;
static double difftv(struct timeval *, struct timeval *);
static void _bwstat_update(struct bwstat_data *, size_t);
#define INITTV(tv, curtv) do { \
if (!timerisset(&(tv))) \
(tv) = (curtv); \
} while (0)
/*
* XXX should winsz be per-update?
*/
int
bwstat_init(uint xwinsz)
{
struct bwstat *bs;
winsz = xwinsz;
TAILQ_INIT(&statq);
/* First entry is the totals. */
if ((bs = bwstat_new()) == NULL)
return (-1);
return (0);
}
struct bwstat *
bwstat_new(void)
{
struct bwstat *bs;
if ((bs = calloc(1, sizeof(*bs))) == NULL)
return (NULL);
bs->pts = 1;
TAILQ_INSERT_TAIL(&statq, bs, next);
return (bs);
}
struct bwstat *
bwstat_free(struct bwstat *bs)
{
TAILQ_REMOVE(&statq, bs, next);
return (NULL);
}
struct bwstat *
bwstat_gettot(void)
{
return (TAILQ_FIRST(&statq));
}
void
bwstat_update(struct bwstat *bs, size_t len, short which)
{
struct bwstat *bstot = TAILQ_FIRST(&statq);
if (bs != NULL)
_bwstat_update(&bs->data[which], len);
_bwstat_update(&bstot->data[which], len);
}
static void
_bwstat_update(struct bwstat_data *bsd, size_t len)
{
struct timeval curtv;
double elap, elapwin;
gettimeofday(&curtv, NULL);
INITTV(bsd->tv, curtv);
INITTV(bsd->wintv, curtv);
elap = difftv(&curtv, &bsd->tv);
elapwin = difftv(&curtv, &bsd->wintv);
/* XXX Window follows. */
if (bsd->winbytes == 0 && bsd->winrate > 0)
bsd->winbytes = (1.0 * bsd->winrate) * elapwin;
bsd->bytes += len;
bsd->winbytes += len;
if (elap == 0.0 || elapwin == 0.0)
return;
bsd->rate = (1.0 * bsd->bytes) / elap;
bsd->winrate = (1.0 * bsd->winbytes) / elapwin;
/*
* Reset window; XXX make this a sliding window wrt a
* timeline; keep average, but reduce #bytes?
*/
if (bsd->winbytes >= winsz) {
gettimeofday(&bsd->wintv, NULL);
bsd->winbytes = 0;
}
}
/*
* Return required delay for bs for direction which.
*/
struct timeval *
bwstat_getdelay(struct bwstat *bs, size_t *len, uint lim, short which)
{
uint rate = 0, npts = 0, ent;
int ncli = 0, pool = 0, xent;
double delay;
static struct timeval tv;
struct bwstathead poolq;
struct bwstat *xbs, *bstot = TAILQ_FIRST(&statq);
//uint initent;
size_t xlen = *len;
if (*len == 0)
return (NULL);
memset(&tv, 0, sizeof(tv));
TAILQ_INIT(&poolq);
rate = bstot->data[which].winrate;
if (rate <= lim)
return (NULL);
xbs = bstot;
while ((xbs = TAILQ_NEXT(xbs, next)) != NULL) {
ncli++;
npts += xbs->pts;
TAILQ_INSERT_TAIL(&poolq, xbs, qnext);
}
if (ncli == 0)
return (NULL);
/* Entitlement per point */
//initent = ent = lim / npts;
ent = lim / npts;
if (ent == 0)
; /*
* XXX we have surpassed our
* granularity.
*/
/*
* Sprinkle some bandwidth: increase the value of a point
* until everyone is satisfied.
*/
do {
/* Take from the poor ... */
TAILQ_FOREACH(xbs, &poolq, qnext)
if (xbs->data[which].winrate < ent * xbs->pts) {
pool += ent * xbs->pts -
xbs->data[which].winrate;
ncli--;
npts -= xbs->pts;
TAILQ_REMOVE(&poolq, xbs, qnext);
}
/* And give to the rich ... */
if (ncli > 0) {
xent = pool / npts;
if (xent <= 0)
break;
TAILQ_FOREACH(xbs, &poolq, qnext)
if (xbs->data[which].winrate > ent * xbs->pts)
pool -= xent * xbs->pts;
ent += xent;
}
} while (pool > 0 && ncli > 0);
/*
* This is the case of a client that is not using its limit.
* We reset ent in this case. The rest will adjust itself
* over time.
*/
if (ent * bs->pts > lim)
ent = lim / bs->pts;
if (bs->data[which].winrate > ent * bs->pts)
delay = (1.0 * *len) / (1.0 * ent * bs->pts);
else
delay = 0.0;
/* if (delay > bs->tsmooth) { */
if ((*len = ent * bs->pts * bs->tsmooth) == 0) {
*len = bs->lsmooth;
delay = (1.0 * *len) / (1.0 * ent * bs->pts);
} else
delay = bs->tsmooth;
/* } */
if (*len > xlen) {
*len = xlen;
delay = (1.0 * *len) / (1.0 * ent * bs->pts);
}
/* XXX */
if (delay < 0.0)
return (NULL);
tv.tv_sec = delay;
tv.tv_usec = (delay - tv.tv_sec) * 1000000L;
return (&tv);
}
static double
difftv(struct timeval *tv0, struct timeval *tv1)
{
struct timeval diff_tv;
timersub(tv0, tv1, &diff_tv);
return (diff_tv.tv_sec + (diff_tv.tv_usec / 1000000.0));
}
/*
* bwstat.h
*
* Copyright (c) 2003 Marius Aamodt Eriksen <marius@monkey.org>
* All rights reserved.
*
* $Id: bwstat.h,v 1.5 2003/04/06 00:29:37 marius Exp $
*/
#ifndef TRICKLE_BWSTAT
#define TRICKLE_BWSTAT
#define BWSTAT_SEND 0
#define BWSTAT_RECV 1
struct bwstat_data {
uint32_t bytes;
uint32_t rate;
struct timeval tv;
uint32_t winbytes;
uint32_t winrate;
struct timeval wintv;
};
struct bwstat {
struct bwstat_data data[2];
uint pts;
uint lsmooth;
double tsmooth;
TAILQ_ENTRY(bwstat) next, qnext;
};
int bwstat_init(uint);
struct bwstat *bwstat_new(void);
struct bwstat *bwstat_free(struct bwstat *);
void bwstat_update(struct bwstat *, size_t, short);
struct timeval *bwstat_getdelay(struct bwstat *, size_t *, uint, short);
struct bwstat *bwstat_gettot(void);
#endif /* TRICKLE_BWSTAT */
/*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char *rcsid = "$OpenBSD: getopt.c,v 1.2 1996/08/19 08:33:32 tholo Exp $";
#endif /* LIBC_SCCS and not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int opterr = 1, /* if error message should be printed */
optind = 1, /* index into parent argv vector */
optopt, /* character checked for validity */
optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
getopt(nargc, nargv, ostr)
int nargc;
char * const *nargv;
const char *ostr;
{
extern char *__progname;
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-') { /* found "--" */
++optind;
place = EMSG;
return (-1);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-')
return (-1);
if (!*place)
++optind;
if (opterr && *ostr != ':')
(void)fprintf(stderr,
"%s: illegal option -- %c\n", __progname, optopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
}
else { /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if (*ostr == ':')
return (BADARG);
if (opterr)
(void)fprintf(stderr,
"%s: option requires an argument -- %c\n",
__progname, optopt);
return (BADCH);
}
else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return (optopt); /* dump back option letter */
}
/* $OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* 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. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``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 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.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $";
#endif /* LIBC_SCCS and not lint */
#if defined(__sun__)
#include <sys/socket.h>
#endif /* __sun__ */
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <string.h>
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(dst, src, siz)
char *dst;
const char *src;
size_t siz;
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* 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. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``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 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.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $";
#endif /* LIBC_SCCS and not lint */
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <string.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(dst, src, siz)
char *dst;
const char *src;
size_t siz;
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
/*
* 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 "util.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/* , latency */;
static int 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)
__attribute__((constructor)) void init() {
trickle_lock();
INIT;
trickle_unlock();
}
__attribute__((destructor)) void fini() {
}
static void
trickle_init(void)
{
void *dh;
char *winszstr, *verbosestr,
*recvlimstr, *sendlimstr, *tsmoothstr, *lsmoothstr;
/* *latencystr; */
initializing = 1;
#ifdef NODLOPEN
dh = (void *) -1L;
#else
//if ((dh = dlopen(DLOPENLIBC, RTLD_LAZY)) == NULL)
#ifdef HAVE_ANDROID_H
if ((dh = dlopen("/system/lib/libc.so", RTLD_LAZY)) == NULL)
#else
if ((dh = dlopen("/lib/x86_64-linux-gnu/libc.so.6", RTLD_LAZY)) == NULL)
#endif
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 ((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");
//printf("winsz:%s, recvlim:%s, sendlim:%s, verbose:%s, argv0:%s, sockname:%s, tsmooth:%s, lsmooth:%s",
//winszstr, recvlimstr, sendlimstr, verbosestr, argv0, sockname, tsmoothstr, lsmoothstr);
/*
if ((latencystr = getenv("TRICKLE_LATENCY")) == NULL)
errx(1, "[trickle] Failed to get length latency parameter");
*/
winsz = atoi(winszstr) * 1024;
lim[TRICKLE_RECV] = atoi(recvlimstr) * 1024;
lim[TRICKLE_SEND] = atoi(sendlimstr) * 1024;
/* latency = atoi(latencystr);*/
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);
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();
printf("before socket\n");
sock = (*libc_socket)(domain, type, protocol);
printf("after socket\n");
#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 && type == SOCK_STREAM) {
if (sock != -1 && domain == AF_INET && type == SOCK_DGRAM) {
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();
#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;
}
}
trickle_unlock();
if (libc_close == NULL) {
libc_close = dlsym(RTLD_NEXT, "close");
if (libc_close == NULL) {
return -1;
}
}
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 = 0;
trickle_lock();
if (libc_read != NULL) {
if (!(eagain = delay(fd, (ssize_t *)&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();
} else {
trickle_unlock();
libc_read = dlsym(RTLD_NEXT, "read");
if (libc_read != NULL) {
ret = (*libc_read)(fd, buf, xnbytes);
}
}
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, (ssize_t *)&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, (ssize_t *)&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, (ssize_t *)&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, (ssize_t *)&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, (ssize_t *)&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, (ssize_t *)&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, (ssize_t *)&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, (ssize_t *)&inbytes, TRICKLE_RECV);
delay(out_fd, (ssize_t *)&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)
{
uint xlim = lim[which];
/* XXX check this. */
if (*len < 0)
*len = sd->data[which].lastlen;
if (xlim == 0)
return (NULL);
return (bwstat_getdelay(sd->stat, (size_t *)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;
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);
}
/*
* trickle.c
*
* Copyright (c) 2002, 2003 Marius Aamodt Eriksen <marius@monkey.org>
* All rights reserved.
*
* $Id: trickle.c,v 1.18 2004/02/13 06:13:05 marius Exp $
*/
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <sys/param.h>
#include <sys/stat.h>
#ifdef HAVE_ERR_H
#include <err.h>
#endif /* HAVE_ERR_H */
#include <errno.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "util.h"
size_t strlcat(char *, const char *, size_t);
void usage(void);
#ifdef HAVE___PROGNAME
extern char *__progname;
#else
char *__progname;
#endif
#define LIBNAME "trickle-overload.so"
int
main(int argc, char **argv)
{
char *winsz = "200", verbosestr[16],
*uplim = "10", *downlim = "10", *tsmooth = "3.0", *lsmooth = "20",
*latency = "0";
int opt, verbose = 0, standalone = 0;
char buf[MAXPATHLEN], sockname[MAXPATHLEN], *path, **pathp;
struct stat sb;
char *trypaths[] = {
LIBNAME,
LIBDIR "/" LIBNAME,
NULL
};
__progname = get_progname(argv[0]);
sockname[0] = '\0';
while ((opt = getopt(argc, argv, "hvVsw:n:u:d:t:l:L:")) != -1)
switch (opt) {
case 'v':
verbose++;
break;
case 'w':
winsz = optarg;
break;
case 'u':
uplim = optarg;
break;
case 'd':
downlim = optarg;
break;
case 'V':
errx(1, "version " VERSION);
break;
case 'n':
strlcpy(sockname, optarg, sizeof(sockname));
break;
case 't':
tsmooth = optarg;
break;
case 'l':
lsmooth = optarg;
break;
case 's':
standalone = 1;
break;
case 'L':
latency = optarg;
break;
case 'h':
default:
usage();
}
argc -= optind;
argv += optind;
if (argc == 0)
usage();
for (pathp = trypaths; *pathp != NULL; pathp++)
if (lstat(*pathp, &sb) == 0)
break;
path = *pathp;
if (path == NULL)
errx(1, "Could not find overload object");
if (path[0] != '/') {
if (getcwd(buf, sizeof(buf)) == NULL)
errx(1, "getcwd");
strlcat(buf, "/", sizeof(buf));
strlcat(buf, path, sizeof(buf));
path = buf;
}
if (!standalone) {
if (sockname[0] == '\0')
strlcpy(sockname, "/tmp/.trickled.sock",
sizeof(sockname));
if (stat(sockname, &sb) == -1 &&
(errno == EACCES || errno == ENOENT))
warn("Could not reach trickled, working independently");
} else
strlcpy(sockname, "", sizeof(sockname));
snprintf(verbosestr, sizeof(verbosestr), "%d", verbose);
setenv("TRICKLE_DOWNLOAD_LIMIT", downlim, 1);
setenv("TRICKLE_UPLOAD_LIMIT", uplim, 1);
setenv("TRICKLE_VERBOSE", verbosestr, 1);
setenv("TRICKLE_WINDOW_SIZE", winsz, 1);
setenv("TRICKLE_ARGV", argv[0], 1);
setenv("TRICKLE_SOCKNAME", sockname, 1);
setenv("TRICKLE_TSMOOTH", tsmooth, 1);
setenv("TRICKLE_LSMOOTH", lsmooth, 1);
/* setenv("TRICKLE_LATENCY", latency, 1); */
setenv("LD_PRELOAD", path, 1);
execvp(argv[0], argv);
err(1, "exec()");
/* NOTREACHED */
return (1);
}
void
usage(void)
{
fprintf(stderr,
"Usage: %s [-hvVs] [-d <rate>] [-u <rate>] [-w <length>] "
"[-t <seconds>]\n"
" %*c [-l <length>] [-n <path>] command ...\n"
"\t-h Help (this)\n"
"\t-v Increase verbosity level\n"
"\t-V Print %s version\n"
"\t-s Run trickle in standalone mode independent of trickled\n"
"\t-d <rate> Set maximum cumulative download rate to <rate> KB/s\n"
"\t-u <rate> Set maximum cumulative upload rate to <rate> KB/s\n"
"\t-w <length> Set window length to <length> KB \n"
"\t-t <seconds> Set default smoothing time to <seconds> s\n"
"\t-l <length> Set default smoothing length to <length> KB\n"
"\t-n <path> Use trickled socket name <path>\n"
"\t-L <ms> Set latency to <ms> milliseconds\n",
__progname, (int)strlen(__progname), ' ', __progname);
exit(1);
}
/*
* trickle.h
*
* Copyright (c) 2003 Marius Aamodt Eriksen <marius@monkey.org>
* All rights reserved.
*
* $Id: trickle.h,v 1.3 2003/04/06 00:29:37 marius Exp $
*/
#ifndef TRICKLE_H
#define TRICKLE_H
#define TRICKLE_SEND 0
#define TRICKLE_RECV 1
#define TRICKLE_NONBLOCK 0x1
#define TRICKLE_WOULDBLOCK 1
#endif /* !TRICKLE_H */
/*
* util.c
*
* Copyright (c) 2001, 2002 Marius Aamodt Eriksen <marius@monkey.org>
*
* $Id: util.c,v 1.3 2003/03/06 05:49:36 marius Exp $
*/
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <string.h>
/* From OpenSSH */
char *
get_progname(char *argv0)
{
#ifdef HAVE___PROGNAME
extern char *__progname;
return __progname;
#else
char *p;
if (argv0 == NULL)
return "unknown"; /* XXX */
p = strrchr(argv0, '/');
if (p == NULL)
p = argv0;
else
p++;
return p;
#endif
}
/*
* trickledu.h
*
* Copyright (c) 2003 Marius Aamodt Eriksen <marius@monkey.org>
* All rights reserved.
*
* $Id: util.h,v 1.3 2003/03/07 09:35:18 marius Exp $
*/
#ifndef TRICKLE_UTIL_H
#define TRICKLE_UTIL_H
#ifndef TIMEVAL_TO_TIMESPEC
#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
(ts)->tv_sec = (tv)->tv_sec; \
(ts)->tv_nsec = (tv)->tv_usec * 1000; \
}
#endif /* !TIMEVAL_TO_TIMESPEC */
#ifndef timersub
#define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
#endif
#undef SET
#undef CLR
#undef ISSET
#define SET(t, f) ((t) |= (f))
#define CLR(t, f) ((t) &= ~(f))
#define ISSET(t, f) ((t) & (f))
#undef MAX
#undef MIN
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
ssize_t atomicio(ssize_t (*)(), int, void *, size_t);
char *get_progname(char *);
#endif /* TRICKLE_UTIL_H */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment