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