diff --git a/btpd/content.c b/btpd/content.c index df19849..10c3181 100644 --- a/btpd/content.c +++ b/btpd/content.c @@ -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 diff --git a/btpd/content.h b/btpd/content.h index ecb9467..7ac10e0 100644 --- a/btpd/content.h +++ b/btpd/content.h @@ -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); diff --git a/btpd/net.c b/btpd/net.c index 1fc25dc..ba59943 100644 --- a/btpd/net.c +++ b/btpd/net.c @@ -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 diff --git a/btpd/net.h b/btpd/net.h index 6a79b78..793386e 100644 --- a/btpd/net.h +++ b/btpd/net.h @@ -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); diff --git a/btpd/torrent.c b/btpd/torrent.c index 1202039..500c66a 100644 --- a/btpd/torrent.c +++ b/btpd/torrent.c @@ -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); } diff --git a/btpd/tracker_req.c b/btpd/tracker_req.c index 94a0960..155a03c 100644 --- a/btpd/tracker_req.c +++ b/btpd/tracker_req.c @@ -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 diff --git a/btpd/tracker_req.h b/btpd/tracker_req.h index 6106ef7..1e85163 100644 --- a/btpd/tracker_req.h +++ b/btpd/tracker_req.h @@ -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