rid of the global btpd struct. Some fields in the struct got a corresponding global variable whereas some was made static and moved to a module. The bandwidht algorithm also got tweaked. It now tries to fire the event at specific times. This was to make the code simpler. It'll probably have to be tweaked again :P First step to make btpd run from a directory where it'll keep the log, socket, configuration, data on the known torrents, etc. Btpd now uses flock on the pidfile instead of connecting to the socket in order to be reasonably sure that no other btpd runs in the same directory.master
@@ -1,6 +1,8 @@ | |||
bin_PROGRAMS=btpd | |||
btpd_SOURCES=\ | |||
main.c util.c\ | |||
btpd.c btpd.h\ | |||
opts.c opts.h\ | |||
cli_if.c\ | |||
net.c net.h\ | |||
net_buf.c net_buf.h\ | |||
@@ -17,8 +17,8 @@ | |||
#include <getopt.h> | |||
#include <math.h> | |||
#include <locale.h> | |||
#include <pwd.h> | |||
#include <signal.h> | |||
#include <stdarg.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
@@ -26,146 +26,33 @@ | |||
#include <unistd.h> | |||
#include "btpd.h" | |||
#include "tracker_req.h" | |||
extern void client_connection_cb(int sd, short type, void *arg); | |||
struct btpd btpd; | |||
void * | |||
btpd_malloc(size_t size) | |||
{ | |||
void *a; | |||
if ((a = malloc(size)) == NULL) | |||
btpd_err("Failed to allocate %d bytes.\n", (int)size); | |||
return a; | |||
} | |||
void * | |||
btpd_calloc(size_t nmemb, size_t size) | |||
{ | |||
void *a; | |||
if ((a = calloc(nmemb, size)) == NULL) | |||
btpd_err("Failed to allocate %d bytes.\n", (int)(nmemb * size)); | |||
return a; | |||
} | |||
const char * | |||
logtype_str(uint32_t type) | |||
{ | |||
if (type & BTPD_L_BTPD) | |||
return "btpd"; | |||
else if (type & BTPD_L_ERROR) | |||
return "error"; | |||
else if (type & BTPD_L_CONN) | |||
return "conn"; | |||
else if (type & BTPD_L_TRACKER) | |||
return "tracker"; | |||
else if (type & BTPD_L_MSG) | |||
return "msg"; | |||
else | |||
return ""; | |||
} | |||
void | |||
btpd_err(const char *fmt, ...) | |||
{ | |||
va_list ap; | |||
va_start(ap, fmt); | |||
if (BTPD_L_ERROR & btpd.logmask) { | |||
char tbuf[20]; | |||
time_t tp = time(NULL); | |||
strftime(tbuf, 20, "%b %e %T", localtime(&tp)); | |||
printf("%s %s: ", tbuf, logtype_str(BTPD_L_ERROR)); | |||
vprintf(fmt, ap); | |||
} | |||
va_end(ap); | |||
exit(1); | |||
} | |||
void | |||
btpd_log(uint32_t type, const char *fmt, ...) | |||
{ | |||
va_list ap; | |||
va_start(ap, fmt); | |||
if (type & btpd.logmask) { | |||
char tbuf[20]; | |||
time_t tp = time(NULL); | |||
strftime(tbuf, 20, "%b %e %T", localtime(&tp)); | |||
printf("%s %s: ", tbuf, logtype_str(type)); | |||
vprintf(fmt, ap); | |||
} | |||
va_end(ap); | |||
} | |||
static void | |||
btpd_init(void) | |||
{ | |||
bcopy(BTPD_VERSION, btpd.peer_id, sizeof(BTPD_VERSION) - 1); | |||
btpd.peer_id[sizeof(BTPD_VERSION) - 1] = '|'; | |||
srandom(time(NULL)); | |||
for (int i = sizeof(BTPD_VERSION); i < 20; i++) | |||
btpd.peer_id[i] = rint(random() * 255.0 / RAND_MAX); | |||
btpd.version = BTPD_VERSION; | |||
#ifdef DEBUG | |||
btpd.logmask = BTPD_L_ALL; | |||
#else | |||
btpd.logmask = BTPD_L_BTPD | BTPD_L_ERROR; | |||
#endif | |||
BTPDQ_INIT(&btpd.kids); | |||
btpd.ntorrents = 0; | |||
BTPDQ_INIT(&btpd.cm_list); | |||
BTPDQ_INIT(&btpd.readq); | |||
BTPDQ_INIT(&btpd.writeq); | |||
BTPDQ_INIT(&btpd.unattached); | |||
btpd.port = 6881; | |||
btpd.bw_hz = 8; | |||
btpd.bwcalls = 0; | |||
for (int i = 0; i < BWCALLHISTORY; i++) | |||
btpd.bwrate[i] = 0; | |||
btpd.obwlim = 0; | |||
btpd.ibwlim = 0; | |||
btpd.obw_left = 0; | |||
btpd.ibw_left = 0; | |||
struct child { | |||
pid_t pid; | |||
void *arg; | |||
void (*cb)(pid_t, void *); | |||
BTPDQ_ENTRY(child) entry; | |||
}; | |||
btpd.npeers = 0; | |||
BTPDQ_HEAD(child_tq, child); | |||
int nfiles = getdtablesize(); | |||
if (nfiles <= 20) | |||
btpd_err("Too few open files allowed (%d). " | |||
"Check \"ulimit -n\"\n", nfiles); | |||
else if (nfiles < 64) | |||
btpd_log(BTPD_L_BTPD, | |||
"You have restricted the number of open files to %d. " | |||
"More could be beneficial to the download performance.\n", | |||
nfiles); | |||
btpd.maxpeers = nfiles - 20; | |||
static uint8_t m_peer_id[20]; | |||
static struct event m_heartbeat; | |||
static struct event m_sigint; | |||
static struct event m_sigterm; | |||
static struct event m_sigchld; | |||
static struct child_tq m_kids = BTPDQ_HEAD_INITIALIZER(m_kids); | |||
static unsigned m_ntorrents; | |||
static struct torrent_tq m_cm_list = BTPDQ_HEAD_INITIALIZER(m_cm_list); | |||
btpd.choke_msg = nb_create_choke(); | |||
nb_hold(btpd.choke_msg); | |||
btpd.unchoke_msg = nb_create_unchoke(); | |||
nb_hold(btpd.unchoke_msg); | |||
btpd.interest_msg = nb_create_interest(); | |||
nb_hold(btpd.interest_msg); | |||
btpd.uninterest_msg = nb_create_uninterest(); | |||
nb_hold(btpd.uninterest_msg); | |||
} | |||
unsigned long btpd_seconds; | |||
void | |||
btpd_shutdown(void) | |||
{ | |||
struct torrent *tp; | |||
tp = BTPDQ_FIRST(&btpd.cm_list); | |||
tp = BTPDQ_FIRST(&m_cm_list); | |||
while (tp != NULL) { | |||
struct torrent *next = BTPDQ_NEXT(tp, entry); | |||
torrent_unload(tp); | |||
@@ -182,6 +69,16 @@ signal_cb(int signal, short type, void *arg) | |||
btpd_shutdown(); | |||
} | |||
void | |||
btpd_add_child(pid_t pid, void (*cb)(pid_t, void *), void *arg) | |||
{ | |||
struct child *kid = btpd_calloc(1, sizeof(*kid)); | |||
kid->pid = pid; | |||
kid->arg = arg; | |||
kid->cb = cb; | |||
BTPDQ_INSERT_TAIL(&m_kids, kid, entry); | |||
} | |||
static void | |||
child_cb(int signal, short type, void *arg) | |||
{ | |||
@@ -190,12 +87,13 @@ child_cb(int signal, short type, void *arg) | |||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { | |||
if (WIFEXITED(status) || WIFSIGNALED(status)) { | |||
struct child *kid = BTPDQ_FIRST(&btpd.kids); | |||
struct child *kid = BTPDQ_FIRST(&m_kids); | |||
while (kid != NULL && kid->pid != pid) | |||
kid = BTPDQ_NEXT(kid, entry); | |||
assert(kid != NULL); | |||
BTPDQ_REMOVE(&btpd.kids, kid, entry); | |||
kid->child_done(kid); | |||
BTPDQ_REMOVE(&m_kids, kid, entry); | |||
kid->cb(kid->pid, kid->arg); | |||
free(kid); | |||
} | |||
} | |||
} | |||
@@ -205,224 +103,78 @@ heartbeat_cb(int sd, short type, void *arg) | |||
{ | |||
struct torrent *tp; | |||
btpd.seconds++; | |||
btpd_seconds++; | |||
net_bw_rate(); | |||
BTPDQ_FOREACH(tp, &btpd.cm_list, entry) | |||
BTPDQ_FOREACH(tp, &m_cm_list, entry) | |||
cm_by_second(tp); | |||
evtimer_add(&btpd.heartbeat, (& (struct timeval) { 1, 0 })); | |||
evtimer_add(&m_heartbeat, (& (struct timeval) { 1, 0 })); | |||
} | |||
static void | |||
usage() | |||
void | |||
btpd_add_torrent(struct torrent *tp) | |||
{ | |||
printf("Usage: btpd [options]\n" | |||
"\n" | |||
"Options:\n" | |||
"\n" | |||
"--bw-hz n\n" | |||
"\tRun the bandwidth limiter at n hz.\n" | |||
"\tDefault is 8 hz.\n" | |||
"\n" | |||
"--bw-in n\n" | |||
"\tLimit incoming BitTorrent traffic to n kB/s.\n" | |||
"\tDefault is 0 which means unlimited.\n" | |||
"\n" | |||
"--bw-out n\n" | |||
"\tLimit outgoing BitTorrent traffic to n kB/s.\n" | |||
"\tDefault is 0 which means unlimited.\n" | |||
"\n" | |||
"-d\n" | |||
"\tKeep the btpd process in the foregorund and log to std{out,err}.\n" | |||
"\tThis option is intended for debugging purposes.\n" | |||
"\n" | |||
"--ipc key\n" | |||
"\tThe same key must be used by the cli to talk to this\n" | |||
"\tbtpd instance. You shouldn't need to use this option.\n" | |||
"\n" | |||
"--logfile file\n" | |||
"\tLog to the given file. By default btpd logs to ./btpd.log.\n" | |||
"\n" | |||
"-p n, --port n\n" | |||
"\tListen at port n. Default is 6881.\n" | |||
"\n" | |||
"--help\n" | |||
"\tShow this help.\n" | |||
"\n"); | |||
exit(1); | |||
BTPDQ_INSERT_TAIL(&m_cm_list, tp, entry); | |||
m_ntorrents++; | |||
} | |||
static int longval = 0; | |||
static struct option longopts[] = { | |||
{ "port", required_argument, NULL, 'p' }, | |||
{ "bw-hz", required_argument, &longval, 6 }, | |||
{ "bw-in", required_argument, &longval, 1 }, | |||
{ "bw-out", required_argument, &longval, 2 }, | |||
{ "logfile", required_argument, &longval, 3 }, | |||
{ "ipc", required_argument, &longval, 4 }, | |||
{ "help", no_argument, &longval, 5 }, | |||
{ NULL, 0, NULL, 0 } | |||
}; | |||
int | |||
main(int argc, char **argv) | |||
void | |||
btpd_del_torrent(struct torrent *tp) | |||
{ | |||
int error, ch; | |||
char *logfile = NULL, *ipc = NULL; | |||
int d_opt = 0; | |||
setlocale(LC_ALL, ""); | |||
btpd_init(); | |||
while ((ch = getopt_long(argc, argv, "dp:", longopts, NULL)) != -1) { | |||
switch (ch) { | |||
case 'd': | |||
d_opt = 1; | |||
break; | |||
case 'p': | |||
btpd.port = atoi(optarg); | |||
break; | |||
case 0: | |||
switch (longval) { | |||
case 1: | |||
btpd.ibwlim = atoi(optarg) * 1024; | |||
break; | |||
case 2: | |||
btpd.obwlim = atoi(optarg) * 1024; | |||
break; | |||
case 3: | |||
logfile = optarg; | |||
break; | |||
case 4: | |||
ipc = optarg; | |||
for (int i = 0; i < strlen(ipc); i++) | |||
if (!isalnum(ipc[i])) | |||
btpd_err("--ipc only takes letters and digits.\n"); | |||
break; | |||
case 5: | |||
usage(); | |||
case 6: | |||
btpd.bw_hz = atoi(optarg); | |||
if (btpd.bw_hz <= 0 || btpd.bw_hz > 100) | |||
btpd_err("I will only accept bw limiter hz " | |||
"between 1 and 100.\n"); | |||
break; | |||
default: | |||
usage(); | |||
} | |||
break; | |||
case '?': | |||
default: | |||
usage(); | |||
} | |||
} | |||
argc -= optind; | |||
argv += optind; | |||
if (argc != 0) | |||
usage(); | |||
//net_init(); | |||
{ | |||
int sd; | |||
int flag = 1; | |||
struct sockaddr_in addr; | |||
addr.sin_family = AF_INET; | |||
addr.sin_addr.s_addr = htonl(INADDR_ANY); | |||
addr.sin_port = htons(btpd.port); | |||
BTPDQ_REMOVE(&m_cm_list, tp, entry); | |||
m_ntorrents--; | |||
} | |||
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) | |||
btpd_err("socket: %s\n", strerror(errno)); | |||
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); | |||
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) == -1) | |||
btpd_err("bind: %s\n", strerror(errno)); | |||
listen(sd, 10); | |||
set_nonblocking(sd); | |||
btpd.peer4_sd = sd; | |||
} | |||
const struct torrent_tq * | |||
btpd_get_torrents(void) | |||
{ | |||
return &m_cm_list; | |||
} | |||
//ipc_init(); | |||
{ | |||
int sd; | |||
struct sockaddr_un addr; | |||
size_t psiz = sizeof(addr.sun_path); | |||
unsigned | |||
btpd_get_ntorrents(void) | |||
{ | |||
return m_ntorrents; | |||
} | |||
addr.sun_family = PF_UNIX; | |||
if (ipc != NULL) { | |||
if (snprintf(addr.sun_path, psiz, "/tmp/btpd_%u_%s", | |||
geteuid(), ipc) >= psiz) | |||
btpd_err("%s is too long.\n", ipc); | |||
} else | |||
snprintf(addr.sun_path, psiz, "/tmp/btpd_%u_default", geteuid()); | |||
struct torrent * | |||
btpd_get_torrent(const uint8_t *hash) | |||
{ | |||
struct torrent *tp = BTPDQ_FIRST(&m_cm_list); | |||
while (tp != NULL && bcmp(hash, tp->meta.info_hash, 20) != 0) | |||
tp = BTPDQ_NEXT(tp, entry); | |||
return tp; | |||
} | |||
if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) | |||
btpd_err("sock: %s\n", strerror(errno)); | |||
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { | |||
if (errno == EADDRINUSE) { | |||
if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) == 0) | |||
btpd_err("btpd already running at %s.\n", addr.sun_path); | |||
else { | |||
unlink(addr.sun_path); | |||
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) | |||
btpd_err("bind: %s\n", strerror(errno)); | |||
} | |||
} else | |||
btpd_err("bind: %s\n", strerror(errno)); | |||
} | |||
if (chmod(addr.sun_path, 0600) == -1) | |||
btpd_err("chmod: %s (%s).\n", addr.sun_path, strerror(errno)); | |||
listen(sd, 4); | |||
set_nonblocking(sd); | |||
btpd.ipc_sd = sd; | |||
} | |||
const uint8_t * | |||
btpd_get_peer_id(void) | |||
{ | |||
return m_peer_id; | |||
} | |||
freopen("/dev/null", "r", stdin); | |||
if (logfile == NULL) | |||
logfile = "btpd.log"; | |||
if (!d_opt) { | |||
freopen(logfile, "w", stdout); | |||
freopen(logfile, "w", stderr); | |||
daemon(1, 1); | |||
} | |||
extern void ipc_init(void); | |||
setlinebuf(stdout); | |||
setlinebuf(stderr); | |||
void | |||
btpd_init(void) | |||
{ | |||
bcopy(BTPD_VERSION, m_peer_id, sizeof(BTPD_VERSION) - 1); | |||
m_peer_id[sizeof(BTPD_VERSION) - 1] = '|'; | |||
srandom(time(NULL)); | |||
for (int i = sizeof(BTPD_VERSION); i < 20; i++) | |||
m_peer_id[i] = rint(random() * 255.0 / RAND_MAX); | |||
event_init(); | |||
net_init(); | |||
ipc_init(); | |||
signal(SIGPIPE, SIG_IGN); | |||
signal_set(&btpd.sigint, SIGINT, signal_cb, NULL); | |||
signal_add(&btpd.sigint, NULL); | |||
signal_set(&btpd.sigterm, SIGTERM, signal_cb, NULL); | |||
signal_add(&btpd.sigterm, NULL); | |||
signal_set(&btpd.sigchld, SIGCHLD, child_cb, NULL); | |||
signal_add(&btpd.sigchld, NULL); | |||
evtimer_set(&btpd.heartbeat, heartbeat_cb, NULL); | |||
evtimer_add(&btpd.heartbeat, (& (struct timeval) { 1, 0 })); | |||
event_set(&btpd.cli, btpd.ipc_sd, EV_READ | EV_PERSIST, | |||
client_connection_cb, &btpd); | |||
event_add(&btpd.cli, NULL); | |||
event_set(&btpd.accept4, btpd.peer4_sd, EV_READ | EV_PERSIST, | |||
net_connection_cb, &btpd); | |||
event_add(&btpd.accept4, NULL); | |||
evtimer_set(&btpd.bwlim, net_bw_cb, NULL); | |||
if (btpd.obwlim > 0 || btpd.ibwlim > 0) { | |||
btpd.ibw_left = btpd.ibwlim / btpd.bw_hz; | |||
btpd.obw_left = btpd.obwlim / btpd.bw_hz; | |||
evtimer_add(&btpd.bwlim, | |||
(& (struct timeval) { 0, 1000000 / btpd.bw_hz })); | |||
} | |||
error = event_dispatch(); | |||
btpd_err("Returned from dispatch. Error = %d.\n", error); | |||
signal_set(&m_sigint, SIGINT, signal_cb, NULL); | |||
signal_add(&m_sigint, NULL); | |||
signal_set(&m_sigterm, SIGTERM, signal_cb, NULL); | |||
signal_add(&m_sigterm, NULL); | |||
signal_set(&m_sigchld, SIGCHLD, child_cb, NULL); | |||
signal_add(&m_sigchld, NULL); | |||
return error; | |||
evtimer_set(&m_heartbeat, heartbeat_cb, NULL); | |||
evtimer_add(&m_heartbeat, (& (struct timeval) { 1, 0 })); | |||
} |
@@ -25,69 +25,11 @@ | |||
#include "policy.h" | |||
#include "subr.h" | |||
#define BTPD_VERSION (PACKAGE_NAME "/" PACKAGE_VERSION) | |||
#define BWCALLHISTORY 5 | |||
struct child { | |||
pid_t pid; | |||
void *data; | |||
void (*child_done)(struct child *child); | |||
BTPDQ_ENTRY(child) entry; | |||
}; | |||
BTPDQ_HEAD(child_tq, child); | |||
struct btpd { | |||
uint8_t peer_id[20]; | |||
const char *version; | |||
uint32_t logmask; | |||
struct child_tq kids; | |||
unsigned ntorrents; | |||
struct torrent_tq cm_list; | |||
struct peer_tq readq; | |||
struct peer_tq writeq; | |||
struct peer_tq unattached; | |||
#include "opts.h" | |||
int port; | |||
int peer4_sd; | |||
int ipc_sd; | |||
unsigned bw_hz; | |||
double bw_hz_avg; | |||
unsigned bwcalls; | |||
unsigned bwrate[BWCALLHISTORY]; | |||
unsigned long obwlim, ibwlim; | |||
unsigned long ibw_left, obw_left; | |||
struct event bwlim; | |||
unsigned npeers; | |||
unsigned maxpeers; | |||
unsigned long seconds; | |||
struct event cli; | |||
struct event accept4; | |||
struct event heartbeat; | |||
struct event sigint; | |||
struct event sigterm; | |||
struct event sigchld; | |||
struct net_buf *choke_msg; | |||
struct net_buf *unchoke_msg; | |||
struct net_buf *interest_msg; | |||
struct net_buf *uninterest_msg; | |||
}; | |||
#define BTPD_VERSION (PACKAGE_NAME "/" PACKAGE_VERSION) | |||
extern struct btpd btpd; | |||
extern unsigned long btpd_seconds; | |||
#define BTPD_L_ALL 0xffffffff | |||
#define BTPD_L_ERROR 0x00000001 | |||
@@ -97,6 +39,8 @@ extern struct btpd btpd; | |||
#define BTPD_L_BTPD 0x00000010 | |||
#define BTPD_L_POL 0x00000020 | |||
void btpd_init(void); | |||
void btpd_log(uint32_t type, const char *fmt, ...); | |||
void btpd_err(const char *fmt, ...); | |||
@@ -106,4 +50,13 @@ void *btpd_calloc(size_t nmemb, size_t size); | |||
void btpd_shutdown(void); | |||
void btpd_add_child(pid_t pid, void (*cb)(pid_t, void *), void *arg); | |||
struct torrent * btpd_get_torrent(const uint8_t *hash); | |||
const struct torrent_tq *btpd_get_torrents(void); | |||
void btpd_add_torrent(struct torrent *tp); | |||
void btpd_del_torrent(struct torrent *tp); | |||
unsigned btpd_get_ntorrents(void); | |||
const uint8_t *btpd_get_peer_id(void); | |||
#endif |
@@ -1,6 +1,8 @@ | |||
#include <sys/types.h> | |||
#include <sys/socket.h> | |||
#include <sys/un.h> | |||
#include <arpa/inet.h> | |||
#include <sys/stat.h> | |||
#include <inttypes.h> | |||
#include <limits.h> | |||
@@ -17,6 +19,8 @@ | |||
#define buf_swrite(iob, s) buf_write(iob, s, sizeof(s) - 1) | |||
static struct event m_cli_incoming; | |||
static void | |||
errdie(int error) | |||
{ | |||
@@ -32,11 +36,11 @@ cmd_stat(int argc, const char *args, FILE *fp) | |||
errdie(buf_init(&iob, (1 << 14))); | |||
errdie(buf_swrite(&iob, "d")); | |||
errdie(buf_print(&iob, "6:npeersi%ue", btpd.npeers)); | |||
errdie(buf_print(&iob, "9:ntorrentsi%ue", btpd.ntorrents)); | |||
errdie(buf_print(&iob, "7:secondsi%lue", btpd.seconds)); | |||
errdie(buf_print(&iob, "6:npeersi%ue", net_npeers)); | |||
errdie(buf_print(&iob, "9:ntorrentsi%ue", btpd_get_ntorrents())); | |||
errdie(buf_print(&iob, "7:secondsi%lue", btpd_seconds)); | |||
errdie(buf_swrite(&iob, "8:torrentsl")); | |||
BTPDQ_FOREACH(tp, &btpd.cm_list, entry) { | |||
BTPDQ_FOREACH(tp, btpd_get_torrents(), entry) { | |||
uint32_t seen_npieces = 0; | |||
for (uint32_t i = 0; i < tp->meta.npieces; i++) | |||
if (tp->piece_count[i] > 0) | |||
@@ -116,7 +120,7 @@ cmd_del(int argc, const char *args, FILE *fp) | |||
return; | |||
} | |||
tp = torrent_get_by_hash(hash); | |||
tp = btpd_get_torrent(hash); | |||
if (tp != NULL) { | |||
btpd_log(BTPD_L_BTPD, "del request for %s.\n", tp->relpath); | |||
torrent_unload(tp); | |||
@@ -223,3 +227,35 @@ client_connection_cb(int sd, short type, void *arg) | |||
fclose(fp); | |||
} | |||
void | |||
ipc_init(void) | |||
{ | |||
int sd; | |||
struct sockaddr_un addr; | |||
size_t psiz = sizeof(addr.sun_path); | |||
addr.sun_family = PF_UNIX; | |||
if (snprintf(addr.sun_path, psiz, "%s/sock", btpd_dir) >= psiz) | |||
btpd_err("'%s/sock' is too long.\n", btpd_dir); | |||
if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) | |||
btpd_err("sock: %s\n", strerror(errno)); | |||
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { | |||
if (errno == EADDRINUSE) { | |||
unlink(addr.sun_path); | |||
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) | |||
btpd_err("bind: %s\n", strerror(errno)); | |||
} else | |||
btpd_err("bind: %s\n", strerror(errno)); | |||
} | |||
if (chmod(addr.sun_path, 0600) == -1) | |||
btpd_err("chmod: %s (%s).\n", addr.sun_path, strerror(errno)); | |||
listen(sd, 4); | |||
set_nonblocking(sd); | |||
event_set(&m_cli_incoming, sd, EV_READ | EV_PERSIST, | |||
client_connection_cb, NULL); | |||
event_add(&m_cli_incoming, NULL); | |||
} |
@@ -0,0 +1,182 @@ | |||
#include <sys/types.h> | |||
#include <sys/stat.h> | |||
#include <assert.h> | |||
#include <err.h> | |||
#include <errno.h> | |||
#include <fcntl.h> | |||
#include <getopt.h> | |||
#include <locale.h> | |||
#include <pwd.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <unistd.h> | |||
#include "btpd.h" | |||
static void | |||
writepid(int pidfd) | |||
{ | |||
FILE *fp = fdopen(dup(pidfd), "w"); | |||
fprintf(fp, "%d", getpid()); | |||
fclose(fp); | |||
} | |||
static char * | |||
find_homedir(void) | |||
{ | |||
char *res = getenv("BTPD_HOME"); | |||
if (res == NULL) { | |||
char *home = getenv("HOME"); | |||
if (home == NULL) { | |||
struct passwd *pwent = getpwuid(getuid()); | |||
if (pwent == NULL) | |||
errx(1, "Can't find my home directory.\n"); | |||
home = pwent->pw_dir; | |||
endpwent(); | |||
} | |||
asprintf(&res, "%s/.btpd", home); | |||
} | |||
return res; | |||
} | |||
static void | |||
setup_daemon(const char *dir) | |||
{ | |||
int pidfd; | |||
if (dir == NULL) | |||
dir = find_homedir(); | |||
btpd_dir = dir; | |||
if (mkdir(dir, 0777) == -1 && errno != EEXIST) | |||
err(1, "Couldn't create home '%s'", dir); | |||
if (chdir(dir) != 0) | |||
err(1, "Couldn't change working directory to '%s'", dir); | |||
pidfd = open("pid", O_CREAT|O_WRONLY|O_NONBLOCK|O_EXLOCK, 0666); | |||
if (pidfd == -1) | |||
err(1, "Couldn't open 'pid'"); | |||
if (btpd_daemon) { | |||
if (daemon(1, 1) != 0) | |||
err(1, "Failed to daemonize"); | |||
freopen("/dev/null", "r", stdin); | |||
if (freopen("log", "a", stdout) == NULL) | |||
err(1, "Couldn't open 'log'"); | |||
dup2(fileno(stdout), fileno(stderr)); | |||
setlinebuf(stdout); | |||
setlinebuf(stderr); | |||
} | |||
writepid(pidfd); | |||
} | |||
static void | |||
usage(void) | |||
{ | |||
printf("Usage: btpd [options]\n" | |||
"\n" | |||
"Options:\n" | |||
"\n" | |||
"--bw-hz n\n" | |||
"\tRun the bandwidth limiter at n hz.\n" | |||
"\tDefault is 8 hz.\n" | |||
"\n" | |||
"--bw-in n\n" | |||
"\tLimit incoming BitTorrent traffic to n kB/s.\n" | |||
"\tDefault is 0 which means unlimited.\n" | |||
"\n" | |||
"--bw-out n\n" | |||
"\tLimit outgoing BitTorrent traffic to n kB/s.\n" | |||
"\tDefault is 0 which means unlimited.\n" | |||
"\n" | |||
"-d\n" | |||
"\tKeep the btpd process in the foregorund and log to std{out,err}.\n" | |||
"\tThis option is intended for debugging purposes.\n" | |||
"\n" | |||
"--ipc key\n" | |||
"\tThe same key must be used by the cli to talk to this\n" | |||
"\tbtpd instance. You shouldn't need to use this option.\n" | |||
"\n" | |||
"--logfile file\n" | |||
"\tLog to the given file. By default btpd logs to ./btpd.log.\n" | |||
"\n" | |||
"-p n, --port n\n" | |||
"\tListen at port n. Default is 6881.\n" | |||
"\n" | |||
"--help\n" | |||
"\tShow this help.\n" | |||
"\n"); | |||
exit(1); | |||
} | |||
static int longval = 0; | |||
static struct option longopts[] = { | |||
{ "port", required_argument, NULL, 'p' }, | |||
{ "bw-hz", required_argument, &longval, 6 }, | |||
{ "bw-in", required_argument, &longval, 1 }, | |||
{ "bw-out", required_argument, &longval, 2 }, | |||
{ "help", no_argument, &longval, 5 }, | |||
{ NULL, 0, NULL, 0 } | |||
}; | |||
int | |||
main(int argc, char **argv) | |||
{ | |||
char *dir = NULL; | |||
setlocale(LC_ALL, ""); | |||
for (;;) { | |||
switch (getopt_long(argc, argv, "dp:", longopts, NULL)) { | |||
case -1: | |||
goto args_done; | |||
case 'd': | |||
btpd_daemon = 0; | |||
break; | |||
case 'p': | |||
net_port = atoi(optarg); | |||
break; | |||
case 0: | |||
switch (longval) { | |||
case 1: | |||
net_bw_limit_in = atoi(optarg) * 1024; | |||
break; | |||
case 2: | |||
net_bw_limit_out = atoi(optarg) * 1024; | |||
break; | |||
case 6: | |||
net_bw_hz = atoi(optarg); | |||
break; | |||
default: | |||
usage(); | |||
} | |||
break; | |||
case '?': | |||
default: | |||
usage(); | |||
} | |||
} | |||
args_done: | |||
argc -= optind; | |||
argv += optind; | |||
if (argc != 0) | |||
usage(); | |||
setup_daemon(dir); | |||
event_init(); | |||
btpd_init(); | |||
event_dispatch(); | |||
btpd_err("Unexpected exit from libevent.\n"); | |||
return 1; | |||
} |
@@ -20,6 +20,18 @@ | |||
#define min(x, y) ((x) <= (y) ? (x) : (y)) | |||
static struct event m_bw_timer; | |||
static unsigned long m_bw_bytes_in; | |||
static unsigned long m_bw_bytes_out; | |||
static struct event m_net_incoming; | |||
unsigned net_npeers; | |||
struct peer_tq net_bw_readq = BTPDQ_HEAD_INITIALIZER(net_bw_readq); | |||
struct peer_tq net_bw_writeq = BTPDQ_HEAD_INITIALIZER(net_bw_writeq); | |||
struct peer_tq net_unattached = BTPDQ_HEAD_INITIALIZER(net_unattached); | |||
void | |||
net_write32(void *buf, uint32_t num) | |||
{ | |||
@@ -89,7 +101,7 @@ net_write(struct peer *p, unsigned long wmax) | |||
peer_sent(p, nl->nb); | |||
if (nl->nb->type == NB_TORRENTDATA) { | |||
p->tp->uploaded += bufdelta; | |||
p->rate_from_me[btpd.seconds % RATEHISTORY] += bufdelta; | |||
p->rate_from_me[btpd_seconds % RATEHISTORY] += bufdelta; | |||
} | |||
bcount -= bufdelta; | |||
BTPDQ_REMOVE(&p->outq, nl, entry); | |||
@@ -100,7 +112,7 @@ net_write(struct peer *p, unsigned long wmax) | |||
} else { | |||
if (nl->nb->type == NB_TORRENTDATA) { | |||
p->tp->uploaded += bcount; | |||
p->rate_from_me[btpd.seconds % RATEHISTORY] += bcount; | |||
p->rate_from_me[btpd_seconds % RATEHISTORY] += bcount; | |||
} | |||
p->outq_off += bcount; | |||
bcount = 0; | |||
@@ -209,7 +221,7 @@ net_progress(struct peer *p, size_t length) | |||
{ | |||
if (p->net.state == BTP_MSGBODY && p->net.msg_num == MSG_PIECE) { | |||
p->tp->downloaded += length; | |||
p->rate_to_me[btpd.seconds % RATEHISTORY] += length; | |||
p->rate_to_me[btpd_seconds % RATEHISTORY] += length; | |||
} | |||
} | |||
@@ -224,7 +236,7 @@ net_state(struct peer *p, const char *buf) | |||
break; | |||
case SHAKE_INFO: | |||
if (p->flags & PF_INCOMING) { | |||
struct torrent *tp = torrent_get_by_hash(buf); | |||
struct torrent *tp = btpd_get_torrent(buf); | |||
if (tp == NULL) | |||
goto bad; | |||
p->tp = tp; | |||
@@ -235,7 +247,7 @@ net_state(struct peer *p, const char *buf) | |||
break; | |||
case SHAKE_ID: | |||
if ((torrent_has_peer(p->tp, buf) | |||
|| bcmp(buf, btpd.peer_id, 20) == 0)) | |||
|| bcmp(buf, btpd_get_peer_id(), 20) == 0)) | |||
goto bad; | |||
bcopy(buf, p->id, 20); | |||
peer_on_shake(p); | |||
@@ -378,7 +390,7 @@ net_connect(const char *ip, int port, int *sd) | |||
struct addrinfo hints, *res; | |||
char portstr[6]; | |||
assert(btpd.npeers < btpd.maxpeers); | |||
assert(net_npeers < net_max_peers); | |||
if (snprintf(portstr, sizeof(portstr), "%d", port) >= sizeof(portstr)) | |||
return EINVAL; | |||
@@ -412,8 +424,8 @@ net_connection_cb(int sd, short type, void *arg) | |||
return; | |||
} | |||
assert(btpd.npeers <= btpd.maxpeers); | |||
if (btpd.npeers == btpd.maxpeers) { | |||
assert(net_npeers <= net_max_peers); | |||
if (net_npeers == net_max_peers) { | |||
close(nsd); | |||
return; | |||
} | |||
@@ -424,17 +436,13 @@ net_connection_cb(int sd, short type, void *arg) | |||
} | |||
void | |||
net_bw_rate(void) | |||
add_bw_timer(void) | |||
{ | |||
unsigned sum = 0; | |||
for (int i = 0; i < BWCALLHISTORY - 1; i++) { | |||
btpd.bwrate[i] = btpd.bwrate[i + 1]; | |||
sum += btpd.bwrate[i]; | |||
} | |||
btpd.bwrate[BWCALLHISTORY - 1] = btpd.bwcalls; | |||
sum += btpd.bwrate[BWCALLHISTORY - 1]; | |||
btpd.bwcalls = 0; | |||
btpd.bw_hz_avg = sum / 5.0; | |||
long wait = 1000000 / net_bw_hz; | |||
struct timeval now; | |||
gettimeofday(&now, NULL); | |||
wait = wait - now.tv_usec % wait; | |||
evtimer_add(&m_bw_timer, (& (struct timeval) { 0, wait})); | |||
} | |||
void | |||
@@ -442,58 +450,50 @@ net_bw_cb(int sd, short type, void *arg) | |||
{ | |||
struct peer *p; | |||
btpd.bwcalls++; | |||
double avg_hz; | |||
if (btpd.seconds < BWCALLHISTORY) | |||
avg_hz = btpd.bw_hz; | |||
else | |||
avg_hz = btpd.bw_hz_avg; | |||
m_bw_bytes_out = net_bw_limit_out / net_bw_hz; | |||
m_bw_bytes_in = net_bw_limit_in / net_bw_hz; | |||
btpd.obw_left = btpd.obwlim / avg_hz; | |||
btpd.ibw_left = btpd.ibwlim / avg_hz; | |||
if (btpd.ibwlim > 0) { | |||
while ((p = BTPDQ_FIRST(&btpd.readq)) != NULL && btpd.ibw_left > 0) { | |||
BTPDQ_REMOVE(&btpd.readq, p, rq_entry); | |||
if (net_bw_limit_in > 0) { | |||
while ((p = BTPDQ_FIRST(&net_bw_readq)) != NULL && m_bw_bytes_in > 0) { | |||
BTPDQ_REMOVE(&net_bw_readq, p, rq_entry); | |||
p->flags &= ~PF_ON_READQ; | |||
btpd.ibw_left -= net_read(p, btpd.ibw_left); | |||
m_bw_bytes_in -= net_read(p, m_bw_bytes_in); | |||
} | |||
} else { | |||
while ((p = BTPDQ_FIRST(&btpd.readq)) != NULL) { | |||
BTPDQ_REMOVE(&btpd.readq, p, rq_entry); | |||
while ((p = BTPDQ_FIRST(&net_bw_readq)) != NULL) { | |||
BTPDQ_REMOVE(&net_bw_readq, p, rq_entry); | |||
p->flags &= ~PF_ON_READQ; | |||
net_read(p, 0); | |||
} | |||
} | |||
if (btpd.obwlim) { | |||
while ((p = BTPDQ_FIRST(&btpd.writeq)) != NULL && btpd.obw_left > 0) { | |||
BTPDQ_REMOVE(&btpd.writeq, p, wq_entry); | |||
if (net_bw_limit_out) { | |||
while ((p = BTPDQ_FIRST(&net_bw_writeq)) != NULL && m_bw_bytes_out > 0) { | |||
BTPDQ_REMOVE(&net_bw_writeq, p, wq_entry); | |||
p->flags &= ~PF_ON_WRITEQ; | |||
btpd.obw_left -= net_write(p, btpd.obw_left); | |||
m_bw_bytes_out -= net_write(p, m_bw_bytes_out); | |||
} | |||
} else { | |||
while ((p = BTPDQ_FIRST(&btpd.writeq)) != NULL) { | |||
BTPDQ_REMOVE(&btpd.writeq, p, wq_entry); | |||
while ((p = BTPDQ_FIRST(&net_bw_writeq)) != NULL) { | |||
BTPDQ_REMOVE(&net_bw_writeq, p, wq_entry); | |||
p->flags &= ~PF_ON_WRITEQ; | |||
net_write(p, 0); | |||
} | |||
} | |||
event_add(&btpd.bwlim, (& (struct timeval) { 0, 1000000 / btpd.bw_hz })); | |||
add_bw_timer(); | |||
} | |||
void | |||
net_read_cb(int sd, short type, void *arg) | |||
{ | |||
struct peer *p = (struct peer *)arg; | |||
if (btpd.ibwlim == 0) | |||
if (net_bw_limit_in == 0) | |||
net_read(p, 0); | |||
else if (btpd.ibw_left > 0) | |||
btpd.ibw_left -= net_read(p, btpd.ibw_left); | |||
else if (m_bw_bytes_in > 0) | |||
m_bw_bytes_in -= net_read(p, m_bw_bytes_in); | |||
else { | |||
p->flags |= PF_ON_READQ; | |||
BTPDQ_INSERT_TAIL(&btpd.readq, p, rq_entry); | |||
BTPDQ_INSERT_TAIL(&net_bw_readq, p, rq_entry); | |||
} | |||
} | |||
@@ -506,12 +506,53 @@ net_write_cb(int sd, short type, void *arg) | |||
peer_kill(p); | |||
return; | |||
} | |||
if (btpd.obwlim == 0) { | |||
if (net_bw_limit_out == 0) { | |||
net_write(p, 0); | |||
} else if (btpd.obw_left > 0) { | |||
btpd.obw_left -= net_write(p, btpd.obw_left); | |||
} else if (m_bw_bytes_out > 0) { | |||
m_bw_bytes_out -= net_write(p, m_bw_bytes_out); | |||
} else { | |||
p->flags |= PF_ON_WRITEQ; | |||
BTPDQ_INSERT_TAIL(&btpd.writeq, p, wq_entry); | |||
BTPDQ_INSERT_TAIL(&net_bw_writeq, p, wq_entry); | |||
} | |||
} | |||
void | |||
net_init(void) | |||
{ | |||
m_bw_bytes_out = net_bw_limit_out / net_bw_hz; | |||
m_bw_bytes_in = net_bw_limit_in / net_bw_hz; | |||
int nfiles = getdtablesize(); | |||
if (nfiles <= 20) | |||
btpd_err("Too few open files allowed (%d). " | |||
"Check \"ulimit -n\"\n", nfiles); | |||
else if (nfiles < 64) | |||
btpd_log(BTPD_L_BTPD, | |||
"You have restricted the number of open files to %d. " | |||
"More could be beneficial to the download performance.\n", | |||
nfiles); | |||
net_max_peers = nfiles - 20; | |||
int sd; | |||
int flag = 1; | |||
struct sockaddr_in addr; | |||
addr.sin_family = AF_INET; | |||
addr.sin_addr.s_addr = htonl(INADDR_ANY); | |||
addr.sin_port = htons(net_port); | |||
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) | |||
btpd_err("socket: %s\n", strerror(errno)); | |||
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); | |||
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) == -1) | |||
btpd_err("bind: %s\n", strerror(errno)); | |||
listen(sd, 10); | |||
set_nonblocking(sd); | |||
event_set(&m_net_incoming, sd, EV_READ | EV_PERSIST, | |||
net_connection_cb, NULL); | |||
event_add(&m_net_incoming, NULL); | |||
evtimer_set(&m_bw_timer, net_bw_cb, NULL); | |||
if (net_bw_limit_out > 0 || net_bw_limit_in > 0) | |||
add_bw_timer(); | |||
} |
@@ -13,7 +13,10 @@ | |||
#define WRITE_TIMEOUT (& (struct timeval) { 60, 0 }) | |||
#define SHAKE_LEN 68 | |||
extern struct peer_tq net_unattached; | |||
extern struct peer_tq net_bw_readq; | |||
extern struct peer_tq net_bw_writeq; | |||
extern unsigned net_npeers; | |||
enum net_state { | |||
SHAKE_PSTR, | |||
@@ -27,8 +30,7 @@ enum net_state { | |||
void net_set_state(struct peer *p, enum net_state state, size_t size); | |||
void net_connection_cb(int sd, short type, void *arg); | |||
void net_bw_rate(void); | |||
void net_init(void); | |||
void net_bw_cb(int sd, short type, void *arg); | |||
void net_read_cb(int sd, short type, void *arg); | |||
@@ -3,10 +3,14 @@ | |||
#include "btpd.h" | |||
static struct net_buf *m_choke; | |||
static struct net_buf *m_unchoke; | |||
static struct net_buf *m_interest; | |||
static struct net_buf *m_uninterest; | |||
static void | |||
kill_buf_no(char *buf, size_t len) | |||
{ | |||
} | |||
static void | |||
@@ -15,6 +19,12 @@ kill_buf_free(char *buf, size_t len) | |||
free(buf); | |||
} | |||
static void | |||
kill_buf_abort(char *buf, size_t len) | |||
{ | |||
abort(); | |||
} | |||
static struct net_buf * | |||
nb_create_alloc(short type, size_t len) | |||
{ | |||
@@ -47,6 +57,13 @@ nb_create_onesized(char mtype, int btype) | |||
return out; | |||
} | |||
static struct net_buf *nb_singleton(struct net_buf *nb) | |||
{ | |||
nb_hold(nb); | |||
nb->kill_buf = kill_buf_abort; | |||
return nb; | |||
} | |||
struct net_buf * | |||
nb_create_piece(uint32_t index, uint32_t begin, size_t blen) | |||
{ | |||
@@ -119,25 +136,35 @@ nb_create_multihave(struct torrent *tp) | |||
struct net_buf * | |||
nb_create_unchoke(void) | |||
{ | |||
return nb_create_onesized(MSG_UNCHOKE, NB_UNCHOKE); | |||
if (m_unchoke == NULL) | |||
m_unchoke = nb_singleton(nb_create_onesized(MSG_UNCHOKE, NB_UNCHOKE)); | |||
return m_unchoke; | |||
} | |||
struct net_buf * | |||
nb_create_choke(void) | |||
{ | |||
return nb_create_onesized(MSG_CHOKE, NB_CHOKE); | |||
if (m_choke == NULL) | |||
m_choke = nb_singleton(nb_create_onesized(MSG_CHOKE, NB_CHOKE)); | |||
return m_choke; | |||
} | |||
struct net_buf * | |||
nb_create_uninterest(void) | |||
{ | |||
return nb_create_onesized(MSG_UNINTEREST, NB_UNINTEREST); | |||
if (m_uninterest == NULL) | |||
m_uninterest = | |||
nb_singleton(nb_create_onesized(MSG_UNINTEREST, NB_UNINTEREST)); | |||
return m_uninterest; | |||
} | |||
struct net_buf * | |||
nb_create_interest(void) | |||
{ | |||
return nb_create_onesized(MSG_INTEREST, NB_INTEREST); | |||
if (m_interest == NULL) | |||
m_interest = | |||
nb_singleton(nb_create_onesized(MSG_INTEREST, NB_INTEREST)); | |||
return m_interest; | |||
} | |||
struct net_buf * | |||
@@ -166,7 +193,7 @@ nb_create_shake(struct torrent *tp) | |||
struct net_buf *out = nb_create_alloc(NB_SHAKE, 68); | |||
bcopy("\x13""BitTorrent protocol\0\0\0\0\0\0\0\0", out->buf, 28); | |||
bcopy(tp->meta.info_hash, out->buf + 28, 20); | |||
bcopy(btpd.peer_id, out->buf + 48, 20); | |||
bcopy(btpd_get_peer_id(), out->buf + 48, 20); | |||
return out; | |||
} | |||
@@ -0,0 +1,14 @@ | |||
#include <btpd.h> | |||
short btpd_daemon = 1; | |||
const char *btpd_dir; | |||
#ifdef DEBUG | |||
uint32_t btpd_logmask = BTPD_L_ALL; | |||
#else | |||
uint32_t btpd_logmask = BTPD_L_BTPD | BTPD_L_ERROR; | |||
#endif | |||
unsigned net_max_peers; | |||
unsigned net_bw_limit_in; | |||
unsigned net_bw_limit_out; | |||
short net_bw_hz = 8; | |||
int net_port = 6881; |
@@ -0,0 +1,8 @@ | |||
extern short btpd_daemon; | |||
extern const char *btpd_dir; | |||
extern uint32_t btpd_logmask; | |||
extern unsigned net_max_peers; | |||
extern unsigned net_bw_limit_in; | |||
extern unsigned net_bw_limit_out; | |||
extern short net_bw_hz; | |||
extern int net_port; |
@@ -28,11 +28,11 @@ peer_kill(struct peer *p) | |||
if (p->flags & PF_ATTACHED) | |||
cm_on_lost_peer(p); | |||
else | |||
BTPDQ_REMOVE(&btpd.unattached, p, cm_entry); | |||
BTPDQ_REMOVE(&net_unattached, p, cm_entry); | |||
if (p->flags & PF_ON_READQ) | |||
BTPDQ_REMOVE(&btpd.readq, p, rq_entry); | |||
BTPDQ_REMOVE(&net_bw_readq, p, rq_entry); | |||
if (p->flags & PF_ON_WRITEQ) | |||
BTPDQ_REMOVE(&btpd.writeq, p, wq_entry); | |||
BTPDQ_REMOVE(&net_bw_writeq, p, wq_entry); | |||
close(p->sd); | |||
event_del(&p->in_ev); | |||
@@ -51,7 +51,7 @@ peer_kill(struct peer *p) | |||
if (p->piece_field != NULL) | |||
free(p->piece_field); | |||
free(p); | |||
btpd.npeers--; | |||
net_npeers--; | |||
} | |||
void | |||
@@ -88,7 +88,7 @@ peer_unsend(struct peer *p, struct nb_link *nl) | |||
free(nl); | |||
if (BTPDQ_EMPTY(&p->outq)) { | |||
if (p->flags & PF_ON_WRITEQ) { | |||
BTPDQ_REMOVE(&btpd.writeq, p, wq_entry); | |||
BTPDQ_REMOVE(&net_bw_writeq, p, wq_entry); | |||
p->flags &= ~PF_ON_WRITEQ; | |||
} else | |||
event_del(&p->out_ev); | |||
@@ -194,7 +194,7 @@ void | |||
peer_unchoke(struct peer *p) | |||
{ | |||
p->flags &= ~PF_I_CHOKE; | |||
peer_send(p, btpd.unchoke_msg); | |||
peer_send(p, nb_create_unchoke()); | |||
} | |||
void | |||
@@ -213,7 +213,7 @@ peer_choke(struct peer *p) | |||
} | |||
p->flags |= PF_I_CHOKE; | |||
peer_send(p, btpd.choke_msg); | |||
peer_send(p, nb_create_choke()); | |||
} | |||
void | |||
@@ -228,7 +228,7 @@ peer_want(struct peer *p, uint32_t index) | |||
if (nl != NULL && nl->nb->type == NB_UNINTEREST) | |||
unsent = peer_unsend(p, nl); | |||
if (!unsent) | |||
peer_send(p, btpd.interest_msg); | |||
peer_send(p, nb_create_interest()); | |||
} | |||
p->flags |= PF_I_WANT; | |||
} | |||
@@ -242,7 +242,7 @@ peer_unwant(struct peer *p, uint32_t index) | |||
if (p->nwant == 0) { | |||
p->flags &= ~PF_I_WANT; | |||
if (p->nreqs_out == 0) | |||
peer_send(p, btpd.uninterest_msg); | |||
peer_send(p, nb_create_uninterest()); | |||
} | |||
} | |||
@@ -262,8 +262,8 @@ peer_create_common(int sd) | |||
event_set(&p->in_ev, p->sd, EV_READ, net_read_cb, p); | |||
event_add(&p->in_ev, NULL); | |||
BTPDQ_INSERT_TAIL(&btpd.unattached, p, cm_entry); | |||
btpd.npeers++; | |||
BTPDQ_INSERT_TAIL(&net_unattached, p, cm_entry); | |||
net_npeers++; | |||
return p; | |||
} | |||
@@ -312,7 +312,7 @@ void | |||
peer_on_no_reqs(struct peer *p) | |||
{ | |||
if (p->nwant == 0) | |||
peer_send(p, btpd.uninterest_msg); | |||
peer_send(p, nb_create_uninterest()); | |||
} | |||
void | |||
@@ -461,8 +461,8 @@ peer_on_request(struct peer *p, uint32_t index, uint32_t begin, | |||
peer_send(p, nb_create_torrentdata(content, length)); | |||
p->npiece_msgs++; | |||
if (p->npiece_msgs >= MAXPIECEMSGS) { | |||
peer_send(p, btpd.choke_msg); | |||
peer_send(p, btpd.unchoke_msg); | |||
peer_send(p, nb_create_choke()); | |||
peer_send(p, nb_create_unchoke()); | |||
p->flags |= PF_NO_REQUESTS; | |||
} | |||
} | |||
@@ -67,7 +67,7 @@ choke_alg(struct torrent *tp) | |||
} | |||
} | |||
tp->choke_time = btpd.seconds + 10; | |||
tp->choke_time = btpd_seconds + 10; | |||
} | |||
void | |||
@@ -86,5 +86,5 @@ next_optimistic(struct torrent *tp, struct peer *np) | |||
} | |||
assert(tp->optimistic != NULL); | |||
choke_alg(tp); | |||
tp->opt_time = btpd.seconds + 30; | |||
tp->opt_time = btpd_seconds + 30; | |||
} |
@@ -7,17 +7,17 @@ | |||
void | |||
cm_by_second(struct torrent *tp) | |||
{ | |||
if (btpd.seconds == tp->tracker_time) | |||
if (btpd_seconds == tp->tracker_time) | |||
tracker_req(tp, TR_EMPTY); | |||
if (btpd.seconds == tp->opt_time) | |||
if (btpd_seconds == tp->opt_time) | |||
next_optimistic(tp, NULL); | |||
if (btpd.seconds == tp->choke_time) | |||
if (btpd_seconds == tp->choke_time) | |||
choke_alg(tp); | |||
struct peer *p; | |||
int ri = btpd.seconds % RATEHISTORY; | |||
int ri = btpd_seconds % RATEHISTORY; | |||
BTPDQ_FOREACH(p, &tp->peers, cm_entry) { | |||
p->rate_to_me[ri] = 0; | |||
@@ -192,7 +192,7 @@ cm_on_new_peer(struct peer *p) | |||
tp->npeers++; | |||
p->flags |= PF_ATTACHED; | |||
BTPDQ_REMOVE(&btpd.unattached, p, cm_entry); | |||
BTPDQ_REMOVE(&net_unattached, p, cm_entry); | |||
if (tp->npeers == 1) { | |||
BTPDQ_INSERT_HEAD(&tp->peers, p, cm_entry); | |||
@@ -62,10 +62,9 @@ torrent_load3(const char *file, struct metainfo *mi, char *mem, size_t memsiz) | |||
tp->meta = *mi; | |||
free(mi); | |||
BTPDQ_INSERT_TAIL(&btpd.cm_list, tp, entry); | |||
btpd_add_torrent(tp); | |||
tracker_req(tp, TR_STARTED); | |||
btpd.ntorrents++; | |||
return 0; | |||
} | |||
@@ -128,7 +127,7 @@ torrent_load(const char *file) | |||
return error; | |||
} | |||
if (torrent_get_by_hash(mi->info_hash) != NULL) { | |||
if (btpd_get_torrent(mi->info_hash) != NULL) { | |||
btpd_log(BTPD_L_BTPD, "%s has same hash as an already loaded torrent.\n", file); | |||
error = EEXIST; | |||
} | |||
@@ -158,12 +157,12 @@ torrent_unload(struct torrent *tp) | |||
while (peer != NULL) { | |||
struct peer *next = BTPDQ_NEXT(peer, cm_entry); | |||
BTPDQ_REMOVE(&tp->peers, peer, cm_entry); | |||
BTPDQ_INSERT_TAIL(&btpd.unattached, peer, cm_entry); | |||
BTPDQ_INSERT_TAIL(&net_unattached, peer, cm_entry); | |||
peer->flags &= ~PF_ATTACHED; | |||
peer = next; | |||
} | |||
peer = BTPDQ_FIRST(&btpd.unattached); | |||
peer = BTPDQ_FIRST(&net_unattached); | |||
while (peer != NULL) { | |||
struct peer *next = BTPDQ_NEXT(peer, cm_entry); | |||
if (peer->tp == tp) | |||
@@ -181,9 +180,8 @@ torrent_unload(struct torrent *tp) | |||
munmap(tp->imem, tp->isiz); | |||
BTPDQ_REMOVE(&btpd.cm_list, tp, entry); | |||
btpd_del_torrent(tp); | |||
free(tp); | |||
btpd.ntorrents--; | |||
} | |||
off_t | |||
@@ -241,15 +239,6 @@ torrent_has_peer(struct torrent *tp, const uint8_t *id) | |||
return has; | |||
} | |||
struct torrent * | |||
torrent_get_by_hash(const uint8_t *hash) | |||
{ | |||
struct torrent *tp = BTPDQ_FIRST(&btpd.cm_list); | |||
while (tp != NULL && bcmp(hash, tp->meta.info_hash, 20) != 0) | |||
tp = BTPDQ_NEXT(tp, entry); | |||
return tp; | |||
} | |||
off_t | |||
torrent_piece_size(struct torrent *tp, uint32_t index) | |||
{ | |||
@@ -80,8 +80,6 @@ void torrent_unload(struct torrent *tp); | |||
int torrent_has_peer(struct torrent *tp, const uint8_t *id); | |||
struct torrent *torrent_get_by_hash(const uint8_t *hash); | |||
off_t torrent_piece_size(struct torrent *tp, uint32_t index); | |||
uint32_t torrent_block_size(struct piece *pc, uint32_t index); | |||
@@ -43,7 +43,7 @@ maybe_connect_to(struct torrent *tp, const char *pinfo) | |||
if (benc_dget_str(pinfo, "peer id", &pid, &len) != 0 || len != 20) | |||
return; | |||
if (bcmp(btpd.peer_id, pid, 20) == 0) | |||
if (bcmp(btpd_get_peer_id(), pid, 20) == 0) | |||
return; | |||
if (torrent_has_peer(tp, pid)) | |||
@@ -63,16 +63,16 @@ out: | |||
} | |||
static void | |||
tracker_done(struct child *child) | |||
tracker_done(pid_t pid, void *arg) | |||
{ | |||
struct tracker_req *req = child->data; | |||
struct tracker_req *req = arg; | |||
int failed = 0; | |||
char *buf; | |||
const char *peers; | |||
uint32_t interval; | |||
struct torrent *tp; | |||
if ((tp = torrent_get_by_hash(req->info_hash)) == NULL) | |||
if ((tp = btpd_get_torrent(req->info_hash)) == NULL) | |||
goto out; | |||
if (benc_validate(req->res->buf, req->res->buf_off) != 0 | |||
@@ -100,14 +100,14 @@ tracker_done(struct child *child) | |||
goto out; | |||
} | |||
tp->tracker_time = btpd.seconds + interval; | |||
tp->tracker_time = btpd_seconds + interval; | |||
int error = 0; | |||
size_t length; | |||
if ((error = benc_dget_lst(req->res->buf, "peers", &peers)) == 0) { | |||
for (peers = benc_first(peers); | |||
peers != NULL && btpd.npeers < btpd.maxpeers; | |||
peers != NULL && net_npeers < net_max_peers; | |||
peers = benc_next(peers)) | |||
maybe_connect_to(tp, peers); | |||
} | |||
@@ -116,7 +116,7 @@ tracker_done(struct child *child) | |||
error = benc_dget_str(req->res->buf, "peers", &peers, &length); | |||
if (error == 0 && length % 6 == 0) { | |||
size_t i; | |||
for (i = 0; i < length && btpd.npeers < btpd.maxpeers; i += 6) | |||
for (i = 0; i < length && net_npeers < net_max_peers; i += 6) | |||
peer_create_out_compact(tp, peers + i); | |||
} | |||
} | |||
@@ -134,7 +134,7 @@ out: | |||
"Start request failed for %s.\n", tp->relpath); | |||
torrent_unload(tp); | |||
} else | |||
tp->tracker_time = btpd.seconds + 10; | |||
tp->tracker_time = btpd_seconds + 10; | |||
} | |||
munmap(req->res, REQ_SIZE); | |||
free(req); | |||
@@ -162,6 +162,7 @@ static int | |||
create_url(struct tracker_req *req, struct torrent *tp, char **url) | |||
{ | |||
char e_hash[61], e_id[61]; | |||
const uint8_t *peer_id = btpd_get_peer_id(); | |||
char qc; | |||
int i; | |||
uint64_t left; | |||
@@ -175,7 +176,7 @@ create_url(struct tracker_req *req, struct torrent *tp, char **url) | |||
snprintf(e_hash + i * 3, 4, "%%%.2x", tp->meta.info_hash[i]); | |||
for (i = 0; i < 20; i++) | |||
snprintf(e_id + i * 3, 4, "%%%.2x", btpd.peer_id[i]); | |||
snprintf(e_id + i * 3, 4, "%%%.2x", peer_id[i]); | |||
left = torrent_bytes_left(tp); | |||
@@ -187,7 +188,7 @@ create_url(struct tracker_req *req, struct torrent *tp, char **url) | |||
"&left=%" PRIu64 | |||
"&compact=1" | |||
"%s%s", | |||
tp->meta.announce, qc, e_hash, e_id, btpd.port, | |||
tp->meta.announce, qc, e_hash, e_id, net_port, | |||
tp->uploaded, tp->downloaded, left, | |||
req->tr_event == TR_EMPTY ? "" : "&event=", | |||
event); | |||
@@ -231,7 +232,7 @@ http_helper(struct tracker_req *req, struct torrent *tp) | |||
err = curl_easy_setopt(handle, CURLOPT_URL, url); | |||
if (err == 0) | |||
err = curl_easy_setopt(handle, CURLOPT_USERAGENT, btpd.version); | |||
err = curl_easy_setopt(handle, CURLOPT_USERAGENT, BTPD_VERSION); | |||
if (err == 0) | |||
err = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, http_cb); | |||
if (err == 0) | |||
@@ -276,13 +277,13 @@ void | |||
tracker_req(struct torrent *tp, enum tr_event tr_event) | |||
{ | |||
struct tracker_req *req; | |||
struct child *child; | |||
pid_t pid; | |||
btpd_log(BTPD_L_TRACKER, | |||
"request for %s, event: %s.\n", | |||
tp->relpath, event2str(tr_event)); | |||
req = (struct tracker_req *)btpd_calloc(1, sizeof(*req) + sizeof(*child)); | |||
req = (struct tracker_req *)btpd_calloc(1, sizeof(*req)); | |||
req->res = mmap(NULL, REQ_SIZE, PROT_READ | PROT_WRITE, | |||
MAP_ANON | MAP_SHARED, -1, 0); | |||
@@ -299,18 +300,14 @@ tracker_req(struct torrent *tp, enum tr_event tr_event) | |||
fflush(NULL); | |||
child = (struct child *)(req + 1); | |||
child->data = req; | |||
child->child_done = tracker_done; | |||
BTPDQ_INSERT_TAIL(&btpd.kids, child, entry); | |||
child->pid = fork(); | |||
if (child->pid < 0) { | |||
pid = fork(); | |||
if (pid < 0) { | |||
btpd_err("Couldn't fork (%s).\n", strerror(errno)); | |||
} else if (child->pid == 0) { // Child | |||
} else if (pid == 0) { // Child | |||
int nfiles = getdtablesize(); | |||
for (int i = 0; i < nfiles; i++) | |||
close(i); | |||
http_helper(req, tp); | |||
} | |||
} else | |||
btpd_add_child(pid, tracker_done, req); | |||
} |
@@ -0,0 +1,70 @@ | |||
#include <stdarg.h> | |||
#include <stdio.h> | |||
#include "btpd.h" | |||
void * | |||
btpd_malloc(size_t size) | |||
{ | |||
void *a; | |||
if ((a = malloc(size)) == NULL) | |||
btpd_err("Failed to allocate %d bytes.\n", (int)size); | |||
return a; | |||
} | |||
void * | |||
btpd_calloc(size_t nmemb, size_t size) | |||
{ | |||
void *a; | |||
if ((a = calloc(nmemb, size)) == NULL) | |||
btpd_err("Failed to allocate %d bytes.\n", (int)(nmemb * size)); | |||
return a; | |||
} | |||
static const char * | |||
logtype_str(uint32_t type) | |||
{ | |||
if (type & BTPD_L_BTPD) | |||
return "btpd"; | |||
else if (type & BTPD_L_ERROR) | |||
return "error"; | |||
else if (type & BTPD_L_CONN) | |||
return "conn"; | |||
else if (type & BTPD_L_TRACKER) | |||
return "tracker"; | |||
else if (type & BTPD_L_MSG) | |||
return "msg"; | |||
else | |||
return ""; | |||
} | |||
void | |||
btpd_err(const char *fmt, ...) | |||
{ | |||
va_list ap; | |||
va_start(ap, fmt); | |||
if (BTPD_L_ERROR & btpd_logmask) { | |||
char tbuf[20]; | |||
time_t tp = time(NULL); | |||
strftime(tbuf, 20, "%b %e %T", localtime(&tp)); | |||
printf("%s %s: ", tbuf, logtype_str(BTPD_L_ERROR)); | |||
vprintf(fmt, ap); | |||
} | |||
va_end(ap); | |||
exit(1); | |||
} | |||
void | |||
btpd_log(uint32_t type, const char *fmt, ...) | |||
{ | |||
va_list ap; | |||
va_start(ap, fmt); | |||
if (type & btpd_logmask) { | |||
char tbuf[20]; | |||
time_t tp = time(NULL); | |||
strftime(tbuf, 20, "%b %e %T", localtime(&tp)); | |||
printf("%s %s: ", tbuf, logtype_str(type)); | |||
vprintf(fmt, ap); | |||
} | |||
va_end(ap); | |||
} |