Преглед изворни кода

Rethink the tracker client code somewhat.

btpd now uses all tiers in parallel, so a torrent with two tiers will
essentially be treated by btpd as a torrent with two trackers to send
each event to. This is not quite what the multitrackes standard says,
but it's much easier to implement reasonable behaviour this way.

btpd is going to report the number of good trackers for a torrent
instead of the number of errors, but for now just report zero errors.
master
Richard Nyberg пре 15 година
родитељ
комит
7ba163fe5a
6 измењених фајлова са 320 додато и 252 уклоњено
  1. +1
    -2
      btpd/cli_if.c
  2. +71
    -55
      btpd/http_tr_if.c
  3. +12
    -20
      btpd/torrent.c
  4. +1
    -1
      btpd/torrent.h
  5. +218
    -160
      btpd/tracker_req.c
  6. +17
    -14
      btpd/tracker_req.h

+ 1
- 2
btpd/cli_if.c Прегледај датотеку

@@ -148,8 +148,7 @@ write_ans(struct iobuf *iob, struct tlib *tl, enum ipc_tval val)
(tl->tp == NULL ? 0 : tl->tp->net->uploaded)); (tl->tp == NULL ? 0 : tl->tp->net->uploaded));
return; return;
case IPC_TVAL_TRERR: case IPC_TVAL_TRERR:
iobuf_print(iob, "i%dei%ue", IPC_TYPE_NUM,
tl->tp == NULL ? 0 : tr_errors(tl->tp));
iobuf_print(iob, "i%dei%ue", IPC_TYPE_NUM, 0);
return; return;
case IPC_TVALCOUNT: case IPC_TVALCOUNT:
break; break;


+ 71
- 55
btpd/http_tr_if.c Прегледај датотеку

@@ -7,23 +7,26 @@


static const char *m_tr_events[] = { "started", "stopped", "completed", "" }; static const char *m_tr_events[] = { "started", "stopped", "completed", "" };


struct http_tr_req {
struct httptr_req {
struct torrent *tp; struct torrent *tp;
struct tr_tier *tr;
struct http_req *req; struct http_req *req;
struct iobuf buf; struct iobuf buf;
struct fdev ioev; struct fdev ioev;
struct timeout timer;
nameconn_t nc; nameconn_t nc;
int sd; int sd;
enum tr_event event; enum tr_event event;
}; };


static void static void
http_tr_free(struct http_tr_req *treq)
httptr_free(struct httptr_req *treq)
{ {
if (treq->sd != -1) { if (treq->sd != -1) {
btpd_ev_del(&treq->ioev); btpd_ev_del(&treq->ioev);
close(treq->sd); close(treq->sd);
} }
btpd_timer_del(&treq->timer);
iobuf_free(&treq->buf); iobuf_free(&treq->buf);
free(treq); free(treq);
} }
@@ -55,9 +58,9 @@ maybe_connect_to(struct torrent *tp, const char *pinfo)
free(ip); free(ip);
} }


static int
parse_reply(struct torrent *tp, const char *content, size_t size, int parse,
int *interval)
static void
parse_reply(struct torrent *tp, struct tr_response *res, const char *content,
size_t size)
{ {
const char *buf; const char *buf;
size_t len; size_t len;
@@ -67,25 +70,20 @@ parse_reply(struct torrent *tp, const char *content, size_t size, int parse,
if (benc_validate(content, size) != 0) if (benc_validate(content, size) != 0)
goto bad_data; goto bad_data;


if ((buf = benc_dget_mem(content, "failure reason", &len)) != NULL) {
btpd_log(BTPD_L_ERROR, "Tracker failure: '%.*s' for '%s'.\n",
(int)len, buf, torrent_name(tp));
return 1;
}

if (!parse) {
*interval = -1;
return 0;
if ((buf = benc_dget_any(content, "failure reason")) != NULL) {
if (!benc_isstr(buf))
goto bad_data;
res->type = TR_RES_FAIL;
res->mi_failure = buf;
return;
} }


if (!benc_dct_chk(content, 2, BE_INT, 1, "interval", BE_ANY, 1, "peers"))
goto bad_data;

*interval = benc_dget_int(content, "interval");
if (*interval < 1)
goto bad_data;
buf = benc_dget_any(content, "interval");
if (buf != NULL && benc_isint(buf))
res->interval = benc_int(buf, NULL);


peers = benc_dget_any(content, "peers");
if ((peers = benc_dget_any(content, "peers")) == NULL)
goto after_peers;


if (benc_islst(peers)) { if (benc_islst(peers)) {
for (peers = benc_first(peers); for (peers = benc_first(peers);
@@ -101,8 +99,9 @@ parse_reply(struct torrent *tp, const char *content, size_t size, int parse,
} else } else
goto bad_data; goto bad_data;


after_peers:
if (!net_ipv6) if (!net_ipv6)
return 0;
goto after_peers6;
for (int k = 0; k < 2; k++) { for (int k = 0; k < 2; k++) {
peers = benc_dget_any(content, v6key[k]); peers = benc_dget_any(content, v6key[k]);
if (peers != NULL && benc_isstr(peers)) { if (peers != NULL && benc_isstr(peers)) {
@@ -111,42 +110,44 @@ parse_reply(struct torrent *tp, const char *content, size_t size, int parse,
peer_create_out_compact(tp->net, AF_INET6, peers + i); peer_create_out_compact(tp->net, AF_INET6, peers + i);
} }
} }
return 0;
after_peers6:
res->type = TR_RES_OK;
return;


bad_data: bad_data:
btpd_log(BTPD_L_ERROR, "Bad data from tracker for '%s'.\n",
torrent_name(tp));
return 1;
res->type = TR_RES_BAD;
} }


static void static void
http_cb(struct http_req *req, struct http_response *res, void *arg) http_cb(struct http_req *req, struct http_response *res, void *arg)
{ {
int interval;
struct http_tr_req *treq = arg;
struct httptr_req *treq = arg;
struct tr_response tres = {0, NULL, -1 };
switch (res->type) { switch (res->type) {
case HTTP_T_ERR: case HTTP_T_ERR:
btpd_log(BTPD_L_ERROR, "http request failed for '%s'.\n",
torrent_name(treq->tp));
tr_result(treq->tp, TR_RES_FAIL, -1);
http_tr_free(treq);
tres.type = TR_RES_BAD;
tr_result(treq->tr, &tres);
httptr_free(treq);
break; break;
case HTTP_T_DATA: case HTTP_T_DATA:
if (treq->buf.off + res->v.data.l > MAX_DOWNLOAD) { if (treq->buf.off + res->v.data.l > MAX_DOWNLOAD) {
tr_result(treq->tp, TR_RES_FAIL, -1);
http_tr_cancel(treq);
tres.type = TR_RES_BAD;
tr_result(treq->tr, &tres);
httptr_cancel(treq);
break; break;
} }
if (!iobuf_write(&treq->buf, res->v.data.p, res->v.data.l)) if (!iobuf_write(&treq->buf, res->v.data.p, res->v.data.l))
btpd_err("Out of memory.\n"); btpd_err("Out of memory.\n");
break; break;
case HTTP_T_DONE: case HTTP_T_DONE:
if (parse_reply(treq->tp, treq->buf.buf, treq->buf.off,
treq->event != TR_EV_STOPPED, &interval) == 0)
tr_result(treq->tp, TR_RES_OK, interval);
else
tr_result(treq->tp, TR_RES_FAIL, -1);
http_tr_free(treq);
if (treq->event == TR_EV_STOPPED) {
tres.type = TR_RES_OK;
tr_result(treq->tr, &tres);
} else {
parse_reply(treq->tp, &tres, treq->buf.buf, treq->buf.off);
tr_result(treq->tr, &tres);
}
httptr_free(treq);
break; break;
default: default:
break; break;
@@ -154,9 +155,10 @@ http_cb(struct http_req *req, struct http_response *res, void *arg)
} }


static void static void
sd_io_cb(int sd, short type, void *arg)
httptr_io_cb(int sd, short type, void *arg)
{ {
struct http_tr_req *treq = arg;
struct tr_response res;
struct httptr_req *treq = arg;
switch (type) { switch (type) {
case EV_READ: case EV_READ:
if (http_read(treq->req, sd) && !http_want_read(treq->req)) if (http_read(treq->req, sd) && !http_want_read(treq->req))
@@ -166,30 +168,39 @@ sd_io_cb(int sd, short type, void *arg)
if (http_write(treq->req, sd) && !http_want_write(treq->req)) if (http_write(treq->req, sd) && !http_want_write(treq->req))
btpd_ev_disable(&treq->ioev, EV_WRITE); btpd_ev_disable(&treq->ioev, EV_WRITE);
break; break;
case EV_TIMEOUT:
res.type = TR_RES_CONN;
tr_result(treq->tr, &res);
httptr_cancel(treq);
break;
default: default:
abort(); abort();
} }
} }


static void static void
nc_cb(void *arg, int error, int sd)
httptr_nc_cb(void *arg, int error, int sd)
{ {
struct http_tr_req *treq = arg;
struct tr_response res;
struct httptr_req *treq = arg;
if (error) { if (error) {
tr_result(treq->tp, TR_RES_FAIL, -1);
res.type = TR_RES_CONN;
tr_result(treq->tr, &res);
http_cancel(treq->req); http_cancel(treq->req);
http_tr_free(treq);
httptr_free(treq);
} else { } else {
treq->sd = sd; treq->sd = sd;
uint16_t flags = uint16_t flags =
(http_want_read(treq->req) ? EV_READ : 0) | (http_want_read(treq->req) ? EV_READ : 0) |
(http_want_write(treq->req) ? EV_WRITE : 0); (http_want_write(treq->req) ? EV_WRITE : 0);
btpd_ev_new(&treq->ioev, sd, flags, sd_io_cb, treq);
btpd_ev_new(&treq->ioev, sd, flags, httptr_io_cb, treq);
btpd_timer_add(&treq->timer, (& (struct timespec) { 30, 0 }));
} }
} }


struct http_tr_req *
http_tr_req(struct torrent *tp, enum tr_event event, const char *aurl)
struct httptr_req *
httptr_req(struct torrent *tp, struct tr_tier *tr, const char *aurl,
enum tr_event event)
{ {
char e_hash[61], e_id[61], url[512], qc; char e_hash[61], e_id[61], url[512], qc;
const uint8_t *peer_id = btpd_get_peer_id(); const uint8_t *peer_id = btpd_get_peer_id();
@@ -211,28 +222,33 @@ http_tr_req(struct torrent *tp, enum tr_event event, const char *aurl)
(long long)tp->total_length - cm_content(tp), (long long)tp->total_length - cm_content(tp),
event == TR_EV_EMPTY ? "" : "&event=", m_tr_events[event]); event == TR_EV_EMPTY ? "" : "&event=", m_tr_events[event]);


struct http_tr_req *treq = btpd_calloc(1, sizeof(*treq));
struct httptr_req *treq = btpd_calloc(1, sizeof(*treq));
if (!http_get(&treq->req, url, "User-Agent: " BTPD_VERSION "\r\n", if (!http_get(&treq->req, url, "User-Agent: " BTPD_VERSION "\r\n",
http_cb, treq)) { http_cb, treq)) {
free(treq); free(treq);
return NULL; return NULL;
} }
treq->tp = tp;
treq->tr = tr;
treq->event = event;
treq->buf = iobuf_init(4096); treq->buf = iobuf_init(4096);
if (treq->buf.error) if (treq->buf.error)
btpd_err("Out of memory.\n"); btpd_err("Out of memory.\n");
treq->tp = tp;
treq->event = event;
treq->tr = tr;
treq->sd = -1; treq->sd = -1;
http_url = http_url_get(treq->req); http_url = http_url_get(treq->req);
treq->nc = btpd_name_connect(http_url->host, http_url->port, nc_cb, treq);
treq->nc = btpd_name_connect(http_url->host, http_url->port,
httptr_nc_cb, treq);
timer_init(&treq->timer, httptr_io_cb, treq);
btpd_timer_add(&treq->timer, (& (struct timespec) { 60, 0 }));
return treq; return treq;
} }


void void
http_tr_cancel(struct http_tr_req *treq)
httptr_cancel(struct httptr_req *treq)
{ {
if (treq->sd == -1) if (treq->sd == -1)
btpd_name_connect_cancel(treq->nc); btpd_name_connect_cancel(treq->nc);
http_cancel(treq->req); http_cancel(treq->req);
http_tr_free(treq);
httptr_free(treq);
} }

+ 12
- 20
btpd/torrent.c Прегледај датотеку

@@ -96,25 +96,19 @@ torrent_start(struct tlib *tl)
benc_dget_mem(benc_dget_dct(mi, "info"), "pieces", NULL) - mi; benc_dget_mem(benc_dget_dct(mi, "info"), "pieces", NULL) - mi;


btpd_log(BTPD_L_BTPD, "Starting torrent '%s'.\n", torrent_name(tp)); btpd_log(BTPD_L_BTPD, "Starting torrent '%s'.\n", torrent_name(tp));
if (tr_create(tp, mi) == 0) {
tl->tp = tp;
net_create(tp);
cm_create(tp, mi);
BTPDQ_INSERT_TAIL(&m_torrents, tp, entry);
m_ntorrents++;
cm_start(tp, 0);
free(mi);
if (m_ntorrents == 1) {
m_tsave = btpd_seconds + SAVE_INTERVAL;
m_savetp = tp;
}
return IPC_OK;
} else {
mi_free_files(tp->nfiles, tp->files);
free(tp);
free(mi);
return IPC_EBADTRACKER;
tr_create(tp, mi);
tl->tp = tp;
net_create(tp);
cm_create(tp, mi);
BTPDQ_INSERT_TAIL(&m_torrents, tp, entry);
m_ntorrents++;
cm_start(tp, 0);
free(mi);
if (m_ntorrents == 1) {
m_tsave = btpd_seconds + SAVE_INTERVAL;
m_savetp = tp;
} }
return IPC_OK;
} }


static void static void
@@ -158,8 +152,6 @@ torrent_stop(struct torrent *tp, int delete)
tlib_update_info(tp->tl, 0); tlib_update_info(tp->tl, 0);
break; break;
case T_STOPPING: case T_STOPPING:
if (tr_active(tp))
tr_stop(tp);
break; break;
} }
} }


+ 1
- 1
btpd/torrent.h Прегледај датотеку

@@ -18,7 +18,7 @@ struct torrent {
int delete; int delete;


struct content *cm; struct content *cm;
struct tracker *tr;
struct trackers *tr;
struct net *net; struct net *net;


off_t total_length; off_t total_length;


+ 218
- 160
btpd/tracker_req.c Прегледај датотеку

@@ -1,251 +1,309 @@
#include "btpd.h" #include "btpd.h"
#include "http_client.h"


#define REQ_DELAY 1 #define REQ_DELAY 1
#define STOP_ERRORS 5
#define REQ_TIMEOUT (& (struct timespec) { 120, 0 })
#define RETRY_WAIT (& (struct timespec) { rand_between(35, 70), 0 })
#define DEFAULT_INTERVAL rand_between(25 * 60, 30 * 60)
#define RETRY1_TIMEOUT (& (struct timespec) {240 + rand_between(0, 120), 0})
#define RETRY2_TIMEOUT (& (struct timespec) {900 + rand_between(0, 300), 0})


long tr_key; long tr_key;


static long m_tlast_req, m_tnext_req; static long m_tlast_req, m_tnext_req;


enum timer_type {
TIMER_NONE,
TIMER_TIMEOUT,
TIMER_INTERVAL,
TIMER_RETRY
struct tr_entry {
BTPDQ_ENTRY(tr_entry) entry;
char *url;
enum tr_type type;
}; };


struct tracker {
enum timer_type ttype;
enum tr_event event;
int interval;
unsigned nerrors;
int tier, url;
struct mi_announce *ann;
void *req;
struct timeout timer;
};

typedef struct _dummy *(*request_fun_t)(struct torrent *, enum tr_event,
const char *);
typedef void (*cancel_fun_t)(struct _dummy *);
BTPDQ_HEAD(tr_entry_tq, tr_entry);


struct tr_op {
int len;
const char *scheme;
request_fun_t request;
cancel_fun_t cancel;
struct tr_tier {
struct torrent *tp;
struct tr_entry *cur;
struct tr_entry_tq trackers;
struct timeout timer;
BTPDQ_ENTRY(tr_tier) entry;
void *req;
char *failure;
int interval;
int bad_conns;
int active;
int has_responded;
enum tr_event event;
}; };


static struct tr_op m_http_op = {
7, "http://", (request_fun_t)http_tr_req, (cancel_fun_t)http_tr_cancel
};
BTPDQ_HEAD(tr_tier_tq, tr_tier);


static struct tr_op *m_tr_ops[] = {
&m_http_op, NULL
struct trackers {
struct tr_tier_tq trackers;
}; };


static char *
get_url(struct tracker *tr)
static void *
req_send(struct tr_tier *t)
{ {
return tr->ann->tiers[tr->tier].urls[tr->url];
switch (t->cur->type) {
case TR_HTTP:
return httptr_req(t->tp, t, t->cur->url, t->event);
default:
abort();
}
} }


static void static void
good_url(struct tracker *tr)
req_cancel(struct tr_tier *t)
{ {
char *set = tr->ann->tiers[tr->tier].urls[tr->url], *hold;
for (int i = 0; i <= tr->url; i++) {
hold = tr->ann->tiers[tr->tier].urls[i];
tr->ann->tiers[tr->tier].urls[i] = set;
set = hold;
switch (t->cur->type) {
case TR_HTTP:
httptr_cancel(t->req);
break;
default:
abort();
} }
tr->tier = 0;
tr->url = 0;
t->req = NULL;
} }


static void static void
next_url(struct tracker *tr)
entry_send(struct tr_tier *t, struct tr_entry *e, enum tr_event event)
{ {
tr->url = (tr->url + 1) % tr->ann->tiers[tr->tier].nurls;
if (tr->url == 0)
tr->tier = (tr->tier + 1) % tr->ann->ntiers;
if (t->req != NULL)
req_cancel(t);
t->event = event;
t->cur = e;
if (m_tlast_req > btpd_seconds - REQ_DELAY) {
m_tnext_req = max(m_tnext_req, m_tlast_req) + REQ_DELAY;
btpd_timer_add(&t->timer,
(& (struct timespec) { m_tnext_req - btpd_seconds, 0 }));
return;
}
btpd_timer_del(&t->timer);
if ((t->req = req_send(t)) == NULL) {
asprintf(&t->failure, "failed to create tracker message to '%s' (%s).",
e->url, strerror(errno));
t->active = 0;
return;
}
m_tlast_req = btpd_seconds;
} }


struct tr_op *
get_op(struct tracker *tr)
static int
tier_active(struct tr_tier *t)
{ {
struct tr_op **opp;
char *url = get_url(tr);
for (opp = m_tr_ops; *opp != NULL; opp++)
if (strncasecmp((*opp)->scheme, url, (*opp)->len) == 0)
return *opp;
return NULL;
return t->active;
} }


static void static void
tr_cancel(struct tracker *tr)
tier_timer_cb(int fd, short type, void *arg)
{ {
struct tr_op *op = get_op(tr);
assert(op != NULL);
op->cancel(tr->req);
tr->req = NULL;
struct tr_tier *t = arg;
assert(tier_active(t));
entry_send(t, BTPDQ_FIRST(&t->trackers), t->event);
} }


static void static void
tr_set_stopped(struct torrent *tp)
tier_start(struct tr_tier *t)
{ {
struct tracker *tr = tp->tr;
btpd_timer_del(&tr->timer);
tr->ttype = TIMER_NONE;
if (tr->req != NULL)
tr_cancel(tr);
assert(!tier_active(t) || t->event == TR_EV_STOPPED);
if (t->failure != NULL) {
free(t->failure);
t->failure = NULL;
}
t->has_responded = 0;
t->bad_conns = 0;
t->active = 1;
entry_send(t, BTPDQ_FIRST(&t->trackers), TR_EV_STARTED);
} }


static void static void
tr_send(struct torrent *tp, enum tr_event event)
tier_stop(struct tr_tier *t)
{ {
struct tracker *tr = tp->tr;
struct tr_op *op = get_op(tr);
if (!tier_active(t) || t->event == TR_EV_STOPPED)
return;


tr->event = event;
if (tr->req != NULL)
tr_cancel(tr);
if (!t->has_responded && t->bad_conns > 1) {
btpd_timer_del(&t->timer);
if (t->req != NULL)
req_cancel(t);
t->active = 0;
} else
entry_send(t, BTPDQ_FIRST(&t->trackers), TR_EV_STOPPED);
}


if (m_tlast_req > btpd_seconds - REQ_DELAY) {
m_tnext_req = max(m_tnext_req, m_tlast_req) + REQ_DELAY;
tr->ttype = TIMER_RETRY;
btpd_timer_add(&tr->timer,
(& (struct timespec) { m_tnext_req - btpd_seconds, 0 }));
return;
}
static void
tier_complete(struct tr_tier *t)
{
if (tier_active(t) && t->event == TR_EV_EMPTY)
entry_send(t, BTPDQ_FIRST(&t->trackers), TR_EV_COMPLETED);
}


if ((op == NULL ||
(tr->req = op->request(tp, event, get_url(tr))) == NULL)) {
tr->nerrors++;
if (tr->event == TR_EV_STOPPED && tr->nerrors >= STOP_ERRORS) {
tr_set_stopped(tp);
return;
}
next_url(tr);
tr->ttype = TIMER_RETRY;
btpd_timer_add(&tr->timer, (& (struct timespec) { 5, 0 }));
} else {
m_tlast_req = btpd_seconds;
tr->ttype = TIMER_TIMEOUT;
btpd_timer_add(&tr->timer, REQ_TIMEOUT);
}
static void
add_tracker(struct tr_tier *t, const char *url)
{
struct tr_entry *e;
struct http_url *hu;
if ((hu = http_url_parse(url)) != NULL) {
http_url_free(hu);
e = btpd_calloc(1, sizeof(*e));
if ((e->url = strdup(url)) == NULL)
btpd_err("Out of memory.\n");
e->type = TR_HTTP;
} else
return;
BTPDQ_INSERT_TAIL(&t->trackers, e, entry);
} }


void
tr_result(struct torrent *tp, enum tr_res res, int interval)
{
struct tracker *tr = tp->tr;
tr->req = NULL;
if (tr->event == TR_EV_STOPPED &&
(res == TR_RES_OK || tr->nerrors >= STOP_ERRORS - 1))
tr_set_stopped(tp);
else if (res == TR_RES_OK) {
good_url(tr);
tr->interval = interval;
tr->nerrors = 0;
tr->ttype = TIMER_INTERVAL;
btpd_timer_add(&tr->timer, (& (struct timespec) { tr->interval, 0}));
static struct tr_tier *
tier_create(struct torrent *tp, struct mi_tier *tier)
{
struct tr_tier *t = btpd_calloc(1, sizeof(*t));
BTPDQ_INIT(&t->trackers);
for (int i = 0; i < tier->nurls; i++)
add_tracker(t, tier->urls[i]);
if (!BTPDQ_EMPTY(&t->trackers)) {
t->tp = tp;
t->interval = -1;
t->event = TR_EV_STOPPED;
timer_init(&t->timer, tier_timer_cb, t);
return t;
} else { } else {
tr->nerrors++;
tr->ttype = TIMER_RETRY;
btpd_timer_add(&tr->timer, RETRY_WAIT);
next_url(tr);
free(t);
return NULL;
} }
} }


static void static void
timer_cb(int fd, short type, void *arg)
{
struct torrent *tp = arg;
struct tracker *tr = tp->tr;
switch (tr->ttype) {
case TIMER_TIMEOUT:
btpd_log(BTPD_L_ERROR, "Tracker request timed out for '%s'.\n",
torrent_name(tp));
tr->nerrors++;
if (tr->event == TR_EV_STOPPED && tr->nerrors >= STOP_ERRORS) {
tr_set_stopped(tp);
break;
}
tr_cancel(tr);
next_url(tr);
case TIMER_RETRY:
tr_send(tp, tr->event);
break;
case TIMER_INTERVAL:
tr_send(tp, TR_EV_EMPTY);
break;
default:
abort();
tier_kill(struct tr_tier *t)
{
struct tr_entry *e, *next;
if (t->failure != NULL)
free(t->failure);
btpd_timer_del(&t->timer);
if (t->req != NULL)
req_cancel(t);
BTPDQ_FOREACH_MUTABLE(e, &t->trackers, entry , next) {
free(e->url);
free(e);
} }
free(t);
} }


int
void
tr_create(struct torrent *tp, const char *mi) tr_create(struct torrent *tp, const char *mi)
{ {
int i;
struct tr_tier *t;
struct mi_announce *ann;
tp->tr = btpd_calloc(1, sizeof(*tp->tr)); tp->tr = btpd_calloc(1, sizeof(*tp->tr));
if ((tp->tr->ann = mi_announce(mi)) == NULL)
BTPDQ_INIT(&tp->tr->trackers);
if ((ann = mi_announce(mi)) == NULL)
btpd_err("Out of memory.\n"); btpd_err("Out of memory.\n");
timer_init(&tp->tr->timer, timer_cb, tp);
return 0;
for (i = 0; i < ann->ntiers; i++)
if ((t = tier_create(tp, &ann->tiers[i])) != NULL)
BTPDQ_INSERT_TAIL(&tp->tr->trackers, t, entry);
mi_free_announce(ann);
} }


void void
tr_kill(struct torrent *tp) tr_kill(struct torrent *tp)
{ {
struct tracker *tr = tp->tr;
struct tr_tier *t, *next;
BTPDQ_FOREACH_MUTABLE(t, &tp->tr->trackers, entry, next)
tier_kill(t);
free(tp->tr);
tp->tr = NULL; tp->tr = NULL;
btpd_timer_del(&tr->timer);
if (tr->req != NULL)
tr_cancel(tr);
mi_free_announce(tr->ann);
free(tr);
} }


void void
tr_start(struct torrent *tp) tr_start(struct torrent *tp)
{ {
tr_send(tp, TR_EV_STARTED);
struct tr_tier *t;
BTPDQ_FOREACH(t, &tp->tr->trackers, entry)
tier_start(t);
} }


void void
tr_refresh(struct torrent *tp)
tr_stop(struct torrent *tp)
{ {
tr_send(tp, TR_EV_EMPTY);
struct tr_tier *t;
BTPDQ_FOREACH(t, &tp->tr->trackers, entry)
tier_stop(t);
} }


void void
tr_complete(struct torrent *tp) tr_complete(struct torrent *tp)
{ {
tr_send(tp, TR_EV_COMPLETED);
struct tr_tier *t;
BTPDQ_FOREACH(t, &tp->tr->trackers, entry)
tier_complete(t);
} }


void
tr_stop(struct torrent *tp)
int
tr_active(struct torrent *tp)
{ {
if (tp->tr->event == TR_EV_STOPPED)
tr_set_stopped(tp);
else
tr_send(tp, TR_EV_STOPPED);
struct tr_tier *t;
BTPDQ_FOREACH(t, &tp->tr->trackers, entry)
if (tier_active(t))
return 1;
return 0;
} }


int int
tr_active(struct torrent *tp)
tr_good_count(struct torrent *tp)
{ {
return tp->tr->ttype != TIMER_NONE;
int count = 0;
struct tr_tier *t;
BTPDQ_FOREACH(t, &tp->tr->trackers, entry)
if (tier_active(t) && t->bad_conns == 0)
count++;
return count;
} }


unsigned
tr_errors(struct torrent *tp)
void
tr_result(struct tr_tier *t, struct tr_response *res)
{ {
return tp->tr->nerrors;
struct tr_entry *e;
t->req = NULL;
switch (res->type) {
case TR_RES_FAIL:
t->active = 0;
t->failure = benc_str(res->mi_failure, NULL, NULL);
btpd_log(BTPD_L_ERROR, "tracker at '%s' failed (%s).\n",
t->cur->url, t->failure);
break;
case TR_RES_CONN:
if ((e = BTPDQ_NEXT(t->cur, entry)) != NULL) {
entry_send(t, e, t->event);
break;
}
t->bad_conns++;
if (t->event == TR_EV_STOPPED && t->bad_conns > 1)
t->active = 0;
else if (t->bad_conns == 1)
entry_send(t, BTPDQ_FIRST(&t->trackers), t->event);
else if (t->bad_conns == 2)
btpd_timer_add(&t->timer, RETRY1_TIMEOUT);
else
btpd_timer_add(&t->timer, RETRY2_TIMEOUT);
break;
case TR_RES_BAD:
case TR_RES_OK:
if (t->event == TR_EV_STOPPED)
t->active = 0;
else {
t->event = TR_EV_EMPTY;
if (res->interval > 0)
t->interval = res->interval;
btpd_timer_add(&t->timer, (& (struct timespec) {
t->interval > 0 ? t->interval : DEFAULT_INTERVAL, 0 }));
}
t->bad_conns = 0;
t->has_responded = 1;
BTPDQ_REMOVE(&t->trackers, t->cur, entry);
BTPDQ_INSERT_HEAD(&t->trackers, t->cur, entry);
break;
default:
abort();
}
} }


void void


+ 17
- 14
btpd/tracker_req.h Прегледај датотеку

@@ -8,28 +8,31 @@ enum tr_event {
TR_EV_EMPTY TR_EV_EMPTY
}; };


enum tr_res {
TR_RES_OK,
TR_RES_FAIL
extern long tr_key;

enum tr_type { TR_HTTP };

struct tr_response {
enum {
TR_RES_FAIL, TR_RES_CONN, TR_RES_BAD, TR_RES_OK
} type;
const char *mi_failure;
int interval;
}; };


extern long tr_key;
struct tr_tier;


int tr_create(struct torrent *tp, const char *mi);
void tr_create(struct torrent *tp, const char *mi);
void tr_kill(struct torrent *tp); void tr_kill(struct torrent *tp);
void tr_start(struct torrent *tp); void tr_start(struct torrent *tp);
void tr_stop(struct torrent *tp); void tr_stop(struct torrent *tp);
void tr_refresh(struct torrent *tp);
void tr_complete(struct torrent *tp); void tr_complete(struct torrent *tp);
unsigned tr_errors(struct torrent *tp);
int tr_active(struct torrent *tp); int tr_active(struct torrent *tp);
void tr_result(struct tr_tier *t, struct tr_response *res);
int tr_good_count(struct torrent *tp);


void tr_result(struct torrent *tp, enum tr_res res, int interval);

struct http_tr_req;

struct http_tr_req *http_tr_req(struct torrent *tp, enum tr_event event,
const char *aurl);
void http_tr_cancel(struct http_tr_req *treq);
struct httptr_req *httptr_req(struct torrent *tp, struct tr_tier *tr,
const char *url, enum tr_event event);
void httptr_cancel(struct httptr_req *req);


#endif #endif

Loading…
Откажи
Сачувај