o Better code for stopping the tracker. No more need for the http_redo hack.master
@@ -159,7 +159,7 @@ add_todo(struct content *cm, struct cm_op *op) | |||
} | |||
void | |||
cm_destroy(struct torrent *tp) | |||
cm_kill(struct torrent *tp) | |||
{ | |||
struct content *cm = tp->cm; | |||
bts_close(cm->rds); | |||
@@ -168,7 +168,6 @@ cm_destroy(struct torrent *tp) | |||
free(cm->hold_field); | |||
free(cm->pos_field); | |||
tp->cm = NULL; | |||
torrent_on_cm_stopped(tp); | |||
} | |||
void | |||
@@ -215,7 +214,14 @@ cm_stop(struct torrent *tp) | |||
cm_write_done(tp); | |||
if (BTPDQ_EMPTY(&cm->todoq)) | |||
cm_destroy(tp); | |||
torrent_on_cm_stopped(tp); | |||
} | |||
int | |||
cm_active(struct torrent *tp) | |||
{ | |||
struct content *cm = tp->cm; | |||
return cm->active || !BTPDQ_EMPTY(&cm->todoq); | |||
} | |||
#define SAVE_INTERVAL (& (struct timeval) { 15, 0 }) | |||
@@ -261,14 +267,14 @@ cm_td_cb(void *arg) | |||
assert(cm->npieces_got < tp->meta.npieces); | |||
cm->npieces_got++; | |||
set_bit(cm->piece_field, op->u.test.piece); | |||
if (tp->net != NULL) | |||
if (net_active(tp)) | |||
dl_on_ok_piece(op->tp->net, op->u.test.piece); | |||
if (cm_full(tp)) | |||
cm_write_done(tp); | |||
} else { | |||
cm->ncontent_bytes -= torrent_piece_size(tp, op->u.test.piece); | |||
bzero(cm->block_field + op->u.test.piece * cm->bppbf, cm->bppbf); | |||
if (tp->net != NULL) | |||
if (net_active(tp)) | |||
dl_on_bad_piece(tp->net, op->u.test.piece); | |||
} | |||
break; | |||
@@ -281,16 +287,14 @@ cm_td_cb(void *arg) | |||
if (!BTPDQ_EMPTY(&cm->todoq)) | |||
run_todo(cm); | |||
else if (!cm->active) | |||
cm_destroy(tp); | |||
torrent_on_cm_stopped(tp); | |||
} | |||
int | |||
cm_start(struct torrent *tp) | |||
void | |||
cm_create(struct torrent *tp) | |||
{ | |||
int err; | |||
struct content *cm = btpd_calloc(1, sizeof(*cm)); | |||
size_t pfield_size = ceil(tp->meta.npieces / 8.0); | |||
cm->active = 1; | |||
struct content *cm = btpd_calloc(1, sizeof(*cm)); | |||
cm->bppbf = ceil((double)tp->meta.piece_length / (1 << 17)); | |||
cm->piece_field = btpd_calloc(pfield_size, 1); | |||
cm->hold_field = btpd_calloc(pfield_size, 1); | |||
@@ -298,18 +302,24 @@ cm_start(struct torrent *tp) | |||
cm->block_field = btpd_calloc(tp->meta.npieces * cm->bppbf, 1); | |||
BTPDQ_INIT(&cm->todoq); | |||
evtimer_set(&cm->save_timer, save_timer_cb, tp); | |||
if ((err = bts_open(&cm->rds, &tp->meta, fd_cb_rd, tp)) != 0) | |||
btpd_err("Error opening stream (%s).\n", strerror(err)); | |||
tp->cm = cm; | |||
} | |||
evtimer_set(&cm->save_timer, save_timer_cb, tp); | |||
void | |||
cm_start(struct torrent *tp) | |||
{ | |||
struct content *cm = tp->cm; | |||
if ((errno = bts_open(&cm->rds, &tp->meta, fd_cb_rd, tp)) != 0) | |||
btpd_err("Error opening stream (%s).\n", strerror(errno)); | |||
cm->active = 1; | |||
struct cm_op *op = btpd_calloc(1, sizeof(*op)); | |||
op->tp = tp; | |||
op->type = CM_START; | |||
add_todo(cm, op); | |||
return 0; | |||
} | |||
int | |||
@@ -3,9 +3,13 @@ | |||
void cm_init(void); | |||
int cm_start(struct torrent *tp); | |||
void cm_create(struct torrent *tp); | |||
void cm_kill(struct torrent *tp); | |||
void cm_start(struct torrent *tp); | |||
void cm_stop(struct torrent * tp); | |||
int cm_active(struct torrent *tp); | |||
int cm_full(struct torrent *tp); | |||
uint8_t *cm_get_piece_field(struct torrent *tp); | |||
@@ -52,7 +52,7 @@ net_torrent_has_peer(struct net *n, const uint8_t *id) | |||
} | |||
void | |||
net_add_torrent(struct torrent *tp) | |||
net_create(struct torrent *tp) | |||
{ | |||
size_t field_size = ceil(tp->meta.npieces / 8.0); | |||
size_t mem = sizeof(*(tp->net)) + field_size + | |||
@@ -62,21 +62,32 @@ net_add_torrent(struct torrent *tp) | |||
n->tp = tp; | |||
tp->net = n; | |||
n->active = 1; | |||
BTPDQ_INIT(&n->getlst); | |||
n->busy_field = (uint8_t *)(n + 1); | |||
n->piece_count = (unsigned *)(n->busy_field + field_size); | |||
} | |||
void | |||
net_kill(struct torrent *tp) | |||
{ | |||
free(tp->net); | |||
tp->net = NULL; | |||
} | |||
void | |||
net_start(struct torrent *tp) | |||
{ | |||
struct net *n = tp->net; | |||
BTPDQ_INSERT_HEAD(&m_torrents, n, entry); | |||
m_ntorrents++; | |||
n->active = 1; | |||
} | |||
void | |||
net_del_torrent(struct torrent *tp) | |||
net_stop(struct torrent *tp) | |||
{ | |||
struct net *n = tp->net; | |||
tp->net = NULL; | |||
assert(m_ntorrents > 0); | |||
m_ntorrents--; | |||
@@ -104,8 +115,12 @@ net_del_torrent(struct torrent *tp) | |||
peer_kill(p); | |||
p = next; | |||
} | |||
} | |||
free(n); | |||
int | |||
net_active(struct torrent *tp) | |||
{ | |||
return tp->net->active; | |||
} | |||
void | |||
@@ -20,8 +20,12 @@ extern unsigned net_npeers; | |||
void net_init(void); | |||
void net_add_torrent(struct torrent *tp); | |||
void net_del_torrent(struct torrent *tp); | |||
void net_create(struct torrent *tp); | |||
void net_kill(struct torrent *tp); | |||
void net_start(struct torrent *tp); | |||
void net_stop(struct torrent *tp); | |||
int net_active(struct torrent *tp); | |||
int net_torrent_has_peer(struct net *n, const uint8_t *id); | |||
@@ -126,11 +126,18 @@ torrent_start(const uint8_t *hash) | |||
bcopy(relpath, tp->relpath, RELPATH_SIZE); | |||
tp->meta = *mi; | |||
free(mi); | |||
BTPDQ_INSERT_TAIL(&m_torrents, tp, entry); | |||
m_ntorrents++; | |||
cm_start(tp); | |||
return 0; | |||
if ((error = tr_create(tp)) == 0) { | |||
net_create(tp); | |||
cm_create(tp); | |||
BTPDQ_INSERT_TAIL(&m_torrents, tp, entry); | |||
m_ntorrents++; | |||
cm_start(tp); | |||
} else { | |||
clear_metainfo(&tp->meta); | |||
free(tp); | |||
} | |||
return error; | |||
} | |||
void | |||
@@ -140,16 +147,13 @@ torrent_stop(struct torrent *tp) | |||
case T_STARTING: | |||
case T_ACTIVE: | |||
tp->state = T_STOPPING; | |||
if (tp->tr != NULL) | |||
tr_stop(tp); | |||
if (tp->net != NULL) | |||
net_del_torrent(tp); | |||
if (tp->cm != NULL) | |||
cm_stop(tp); | |||
tr_stop(tp); | |||
net_stop(tp); | |||
cm_stop(tp); | |||
break; | |||
case T_STOPPING: | |||
if (tp->tr != NULL) | |||
tr_destroy(tp); | |||
if (tr_active(tp)) | |||
tr_stop(tp); | |||
break; | |||
} | |||
} | |||
@@ -162,6 +166,9 @@ torrent_kill(struct torrent *tp) | |||
m_ntorrents--; | |||
BTPDQ_REMOVE(&m_torrents, tp, entry); | |||
clear_metainfo(&tp->meta); | |||
tr_kill(tp); | |||
net_kill(tp); | |||
cm_kill(tp); | |||
free(tp); | |||
if (m_ntorrents == 0) | |||
btpd_on_no_torrents(); | |||
@@ -171,16 +178,15 @@ void | |||
torrent_on_cm_started(struct torrent *tp) | |||
{ | |||
tp->state = T_ACTIVE; | |||
net_add_torrent(tp); | |||
if (tr_start(tp) != 0) | |||
torrent_stop(tp); | |||
net_start(tp); | |||
tr_start(tp); | |||
} | |||
void | |||
torrent_on_cm_stopped(struct torrent *tp) | |||
{ | |||
assert(tp->state == T_STOPPING); | |||
if (tp->tr == NULL) | |||
if (!tr_active(tp)) | |||
torrent_kill(tp); | |||
} | |||
@@ -188,6 +194,6 @@ void | |||
torrent_on_tr_stopped(struct torrent *tp) | |||
{ | |||
assert(tp->state == T_STOPPING); | |||
if (tp->cm == NULL) | |||
if (!cm_active(tp)) | |||
torrent_kill(tp); | |||
} |
@@ -37,7 +37,7 @@ struct tracker { | |||
static void tr_send(struct torrent *tp, enum tr_event event); | |||
void | |||
static void | |||
maybe_connect_to(struct torrent *tp, const char *pinfo) | |||
{ | |||
const char *pid; | |||
@@ -66,21 +66,25 @@ maybe_connect_to(struct torrent *tp, const char *pinfo) | |||
static int | |||
parse_reply(struct torrent *tp, const char *content, size_t size) | |||
parse_reply(struct torrent *tp, const char *content, size_t size, int parse) | |||
{ | |||
const char *buf; | |||
size_t len; | |||
const char *peers; | |||
int interval; | |||
if (benc_validate(content, size) != 0) | |||
goto bad_data; | |||
if ((buf = benc_dget_mem(content, "failure reason", &len)) != NULL) { | |||
btpd_log(BTPD_L_ERROR, "Tracker failure: %.*s.\n", (int)len, buf); | |||
return 1; | |||
} | |||
if ((benc_validate(content, size) != 0 || | |||
!benc_dct_chk(content, 2, BE_INT, 1, "interval", | |||
BE_ANY, 1, "peers"))) | |||
if (!parse) | |||
return 0; | |||
if (!benc_dct_chk(content, 2, BE_INT, 1, "interval", BE_ANY, 1, "peers")) | |||
goto bad_data; | |||
interval = benc_dget_int(content, "interval"); | |||
@@ -109,6 +113,19 @@ bad_data: | |||
return 1; | |||
} | |||
static void | |||
tr_set_stopped(struct torrent *tp) | |||
{ | |||
struct tracker *tr = tp->tr; | |||
event_del(&tr->timer); | |||
tr->ttype = TIMER_NONE; | |||
if (tr->req != NULL) { | |||
http_cancel(tr->req); | |||
tr->req = NULL; | |||
} | |||
torrent_on_tr_stopped(tp); | |||
} | |||
static void | |||
http_cb(struct http *req, struct http_res *res, void *arg) | |||
{ | |||
@@ -116,9 +133,8 @@ http_cb(struct http *req, struct http_res *res, void *arg) | |||
struct tracker *tr = tp->tr; | |||
assert(tr->ttype == TIMER_TIMEOUT); | |||
tr->req = NULL; | |||
if (res->res == HRES_OK && | |||
(tr->event == TR_EV_STOPPED | |||
|| parse_reply(tp, res->content, res->length) == 0)) { | |||
if (res->res == HRES_OK && parse_reply(tp, res->content, res->length, | |||
tr->event != TR_EV_STOPPED) == 0) { | |||
tr->nerrors = 0; | |||
tr->ttype = TIMER_INTERVAL; | |||
event_add(&tr->timer, (& (struct timeval) { tr->interval, 0 })); | |||
@@ -128,7 +144,7 @@ http_cb(struct http *req, struct http_res *res, void *arg) | |||
event_add(&tr->timer, RETRY_WAIT); | |||
} | |||
if (tr->event == TR_EV_STOPPED && (tr->nerrors == 0 || tr->nerrors >= 5)) | |||
tr_destroy(tp); | |||
tr_set_stopped(tp); | |||
} | |||
static void | |||
@@ -138,17 +154,15 @@ timer_cb(int fd, short type, void *arg) | |||
struct tracker *tr = tp->tr; | |||
switch (tr->ttype) { | |||
case TIMER_TIMEOUT: | |||
btpd_log(BTPD_L_ERROR, "Tracker request timed out for '%s'.\n", | |||
tp->meta.name); | |||
tr->nerrors++; | |||
if (tr->event == TR_EV_STOPPED && tr->nerrors >= 5) { | |||
tr_destroy(tp); | |||
tr_set_stopped(tp); | |||
break; | |||
} | |||
case TIMER_RETRY: | |||
if (tr->event == TR_EV_STOPPED) { | |||
event_add(&tr->timer, REQ_TIMEOUT); | |||
http_redo(&tr->req); | |||
} else | |||
tr_send(tp, tr->event); | |||
tr_send(tp, tr->event); | |||
break; | |||
case TIMER_INTERVAL: | |||
tr_send(tp, TR_EV_EMPTY); | |||
@@ -188,27 +202,21 @@ tr_send(struct torrent *tp, enum tr_event event) | |||
} | |||
int | |||
tr_start(struct torrent *tp) | |||
tr_create(struct torrent *tp) | |||
{ | |||
assert(tp->tr == NULL); | |||
if (strncmp(tp->meta.announce, "http://", sizeof("http://") - 1) != 0) { | |||
btpd_log(BTPD_L_ERROR, | |||
"btpd currently has no support for the protocol specified in " | |||
"'%s'.\n", tp->meta.announce); | |||
return EINVAL; | |||
} | |||
struct tracker *tr = btpd_calloc(1, sizeof(*tr)); | |||
evtimer_set(&tr->timer, timer_cb, tp); | |||
tp->tr = tr; | |||
tr_send(tp, TR_EV_STARTED); | |||
tp->tr = btpd_calloc(1, sizeof(*tp->tr)); | |||
evtimer_set(&tp->tr->timer, timer_cb, tp); | |||
return 0; | |||
} | |||
void | |||
tr_destroy(struct torrent *tp) | |||
tr_kill(struct torrent *tp) | |||
{ | |||
struct tracker *tr = tp->tr; | |||
tp->tr = NULL; | |||
@@ -216,7 +224,12 @@ tr_destroy(struct torrent *tp) | |||
if (tr->req != NULL) | |||
http_cancel(tr->req); | |||
free(tr); | |||
torrent_on_tr_stopped(tp); | |||
} | |||
void | |||
tr_start(struct torrent *tp) | |||
{ | |||
tr_send(tp, TR_EV_STARTED); | |||
} | |||
void | |||
@@ -234,7 +247,16 @@ tr_complete(struct torrent *tp) | |||
void | |||
tr_stop(struct torrent *tp) | |||
{ | |||
tr_send(tp, TR_EV_STOPPED); | |||
if (tp->tr->event == TR_EV_STOPPED) | |||
tr_set_stopped(tp); | |||
else | |||
tr_send(tp, TR_EV_STOPPED); | |||
} | |||
int | |||
tr_active(struct torrent *tp) | |||
{ | |||
return tp->tr->ttype != TIMER_NONE; | |||
} | |||
unsigned | |||
@@ -1,11 +1,13 @@ | |||
#ifndef TRACKER_REQ_H | |||
#define TRACKER_REQ_H | |||
int tr_start(struct torrent *tp); | |||
int tr_create(struct torrent *tp); | |||
void tr_kill(struct torrent *tp); | |||
void tr_start(struct torrent *tp); | |||
void tr_stop(struct torrent *tp); | |||
void tr_refresh(struct torrent *tp); | |||
void tr_complete(struct torrent *tp); | |||
void tr_destroy(struct torrent *tp); | |||
unsigned tr_errors(struct torrent *tp); | |||
int tr_active(struct torrent *tp); | |||
#endif |