deactivate on shutdown. To not risk hanging indefinitely on unresponsive trackers, btpd will cancel tracker requests after a while.master
@@ -34,27 +34,58 @@ static struct event m_sigint; | |||||
static struct event m_sigterm; | static struct event m_sigterm; | ||||
static unsigned m_ntorrents; | static unsigned m_ntorrents; | ||||
static struct torrent_tq m_torrents = BTPDQ_HEAD_INITIALIZER(m_torrents); | static struct torrent_tq m_torrents = BTPDQ_HEAD_INITIALIZER(m_torrents); | ||||
static unsigned m_nactive; | |||||
static int m_shutdown; | |||||
void | void | ||||
btpd_shutdown(void) | btpd_exit(int code) | ||||
{ | { | ||||
struct torrent *tp; | btpd_log(BTPD_L_BTPD, "Exiting.\n"); | ||||
exit(code); | |||||
} | |||||
void | |||||
btpd_tp_activated(struct torrent *tp) | |||||
{ | |||||
m_nactive++; | |||||
} | |||||
void | |||||
btpd_tp_deactivated(struct torrent *tp) | |||||
{ | |||||
m_nactive--; | |||||
if (m_nactive == 0 && m_shutdown) | |||||
btpd_exit(0); | |||||
} | |||||
tp = BTPDQ_FIRST(&m_torrents); | static void | ||||
while (tp != NULL) { | grace_cb(int fd, short type, void *arg) | ||||
struct torrent *next = BTPDQ_NEXT(tp, entry); | { | ||||
struct torrent *tp; | |||||
BTPDQ_FOREACH(tp, &m_torrents, entry) | |||||
torrent_deactivate(tp); | torrent_deactivate(tp); | ||||
tp = next; | } | ||||
void | |||||
btpd_shutdown(struct timeval *grace_tv) | |||||
{ | |||||
if (m_nactive == 0) | |||||
btpd_exit(0); | |||||
else { | |||||
struct torrent *tp; | |||||
m_shutdown = 1; | |||||
BTPDQ_FOREACH(tp, &m_torrents, entry) | |||||
torrent_deactivate(tp); | |||||
if (grace_tv != NULL) | |||||
event_once(-1, EV_TIMEOUT, grace_cb, NULL, grace_tv); | |||||
} | } | ||||
btpd_log(BTPD_L_BTPD, "Exiting.\n"); | |||||
exit(0); | |||||
} | } | ||||
static void | static void | ||||
signal_cb(int signal, short type, void *arg) | signal_cb(int signal, short type, void *arg) | ||||
{ | { | ||||
btpd_log(BTPD_L_BTPD, "Got signal %d.\n", signal); | btpd_log(BTPD_L_BTPD, "Got signal %d.\n", signal); | ||||
btpd_shutdown(); | btpd_shutdown((& (struct timeval) { 30, 0 })); | ||||
} | } | ||||
void | void | ||||
@@ -48,7 +48,7 @@ void btpd_err(const char *fmt, ...); | |||||
void *btpd_malloc(size_t size); | void *btpd_malloc(size_t size); | ||||
void *btpd_calloc(size_t nmemb, size_t size); | void *btpd_calloc(size_t nmemb, size_t size); | ||||
void btpd_shutdown(void); | void btpd_shutdown(struct timeval *grace_tv); | ||||
struct torrent * btpd_get_torrent(const uint8_t *hash); | struct torrent * btpd_get_torrent(const uint8_t *hash); | ||||
const struct torrent_tq *btpd_get_torrents(void); | const struct torrent_tq *btpd_get_torrents(void); | ||||
@@ -64,4 +64,7 @@ void td_release_lock(void); | |||||
void td_post(void (*fun)(void *), void *arg); | void td_post(void (*fun)(void *), void *arg); | ||||
void td_post_end(void); | void td_post_end(void); | ||||
void btpd_tp_activated(struct torrent *tp); | |||||
void btpd_tp_deactivated(struct torrent *tp); | |||||
#endif | #endif |
@@ -51,18 +51,36 @@ torrent_block_size(struct torrent *tp, uint32_t piece, uint32_t nblocks, | |||||
void | void | ||||
torrent_activate(struct torrent *tp) | torrent_activate(struct torrent *tp) | ||||
{ | { | ||||
assert(tp->state == T_INACTIVE); | if (tp->state == T_INACTIVE) { | ||||
tp->state = T_STARTING; | tp->state = T_STARTING; | ||||
cm_start(tp); | cm_start(tp); | ||||
btpd_tp_activated(tp); | |||||
} | |||||
} | } | ||||
void | void | ||||
torrent_deactivate(struct torrent *tp) | torrent_deactivate(struct torrent *tp) | ||||
{ | { | ||||
tp->state = T_STOPPING; | switch (tp->state) { | ||||
tr_stop(tp); | case T_INACTIVE: | ||||
net_del_torrent(tp); | break; | ||||
cm_stop(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); | |||||
break; | |||||
case T_STOPPING: | |||||
if (tp->tr != NULL) | |||||
tr_destroy(tp); | |||||
break; | |||||
default: | |||||
abort(); | |||||
} | |||||
} | } | ||||
int | int | ||||
@@ -110,5 +128,18 @@ 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); | ||||
tp->state = T_INACTIVE; | if (tp->tr == NULL) { | ||||
tp->state = T_INACTIVE; | |||||
btpd_tp_deactivated(tp); | |||||
} | |||||
} | |||||
void | |||||
torrent_on_tr_stopped(struct torrent *tp) | |||||
{ | |||||
assert(tp->state == T_STOPPING); | |||||
if (tp->cm == NULL) { | |||||
tp->state = T_INACTIVE; | |||||
btpd_tp_deactivated(tp); | |||||
} | |||||
} | } |
@@ -36,5 +36,6 @@ uint32_t torrent_block_size(struct torrent *tp, uint32_t piece, | |||||
void torrent_on_cm_stopped(struct torrent *tp); | void torrent_on_cm_stopped(struct torrent *tp); | ||||
void torrent_on_cm_started(struct torrent *tp); | void torrent_on_cm_started(struct torrent *tp); | ||||
void torrent_on_tr_stopped(struct torrent *tp); | |||||
#endif | #endif |
@@ -5,6 +5,7 @@ | |||||
#include "benc.h" | #include "benc.h" | ||||
#include "subr.h" | #include "subr.h" | ||||
#include "http.h" | #include "http.h" | ||||
#include "tracker_req.h" | |||||
#define REQ_TIMEOUT (& (struct timeval) { 120, 0 }) | #define REQ_TIMEOUT (& (struct timeval) { 120, 0 }) | ||||
#define RETRY_WAIT (& (struct timeval) { rand_between(35, 70), 0 }) | #define RETRY_WAIT (& (struct timeval) { rand_between(35, 70), 0 }) | ||||
@@ -128,14 +129,19 @@ http_cb(struct http *req, struct http_res *res, void *arg) | |||||
struct torrent *tp = arg; | struct torrent *tp = arg; | ||||
struct tracker *tr = tp->tr; | struct tracker *tr = tp->tr; | ||||
assert(tr->ttype == TIMER_TIMEOUT); | assert(tr->ttype == TIMER_TIMEOUT); | ||||
tr->req = NULL; | |||||
if ((http_succeeded(res) && | if ((http_succeeded(res) && | ||||
parse_reply(tp, res->content, res->length) == 0)) { | parse_reply(tp, res->content, res->length) == 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 })); | ||||
} else { | } else { | ||||
tr->nerrors++; | |||||
tr->ttype = TIMER_RETRY; | tr->ttype = TIMER_RETRY; | ||||
event_add(&tr->timer, RETRY_WAIT); | event_add(&tr->timer, RETRY_WAIT); | ||||
} | } | ||||
if (tr->event == TR_EV_STOPPED && (tr->nerrors == 0 || tr->nerrors >= 5)) | |||||
tr_destroy(tp); | |||||
} | } | ||||
static void | static void | ||||
@@ -145,8 +151,17 @@ 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: | ||||
tr->nerrors++; | |||||
if (tr->event == TR_EV_STOPPED && tr->nerrors >= 5) { | |||||
tr_destroy(tp); | |||||
break; | |||||
} | |||||
case TIMER_RETRY: | case TIMER_RETRY: | ||||
tr_send(tp, tp->tr->event); | if (tr->event == TR_EV_STOPPED) { | ||||
event_add(&tr->timer, REQ_TIMEOUT); | |||||
http_redo(&tr->req); | |||||
} else | |||||
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); | ||||
@@ -205,6 +220,18 @@ tr_start(struct torrent *tp) | |||||
return 0; | return 0; | ||||
} | } | ||||
void | |||||
tr_destroy(struct torrent *tp) | |||||
{ | |||||
struct tracker *tr = tp->tr; | |||||
tp->tr = NULL; | |||||
event_del(&tr->timer); | |||||
if (tr->req != NULL) | |||||
http_cancel(tr->req); | |||||
free(tr); | |||||
torrent_on_tr_stopped(tp); | |||||
} | |||||
void | void | ||||
tr_refresh(struct torrent *tp) | tr_refresh(struct torrent *tp) | ||||
{ | { | ||||
@@ -5,5 +5,6 @@ int 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); | |||||
#endif | #endif |