about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--btpd/btpd.c49
-rw-r--r--btpd/btpd.h5
-rw-r--r--btpd/torrent.c47
-rw-r--r--btpd/torrent.h1
-rw-r--r--btpd/tracker_req.c29
-rw-r--r--btpd/tracker_req.h1
6 files changed, 113 insertions, 19 deletions
diff --git a/btpd/btpd.c b/btpd/btpd.c
index 161c780..3a4a346 100644
--- a/btpd/btpd.c
+++ b/btpd/btpd.c
@@ -34,27 +34,58 @@ static struct event m_sigint;
 static struct event m_sigterm;
 static unsigned m_ntorrents;
 static struct torrent_tq m_torrents = BTPDQ_HEAD_INITIALIZER(m_torrents);
+static unsigned m_nactive;
+static int m_shutdown;
 
 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);
-    while (tp != NULL) {
-        struct torrent *next = BTPDQ_NEXT(tp, entry);
+static void
+grace_cb(int fd, short type, void *arg)
+{
+    struct torrent *tp;
+    BTPDQ_FOREACH(tp, &m_torrents, entry)
         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
 signal_cb(int signal, short type, void *arg)
 {
     btpd_log(BTPD_L_BTPD, "Got signal %d.\n", signal);
-    btpd_shutdown();
+    btpd_shutdown((& (struct timeval) { 30, 0 }));
 }
 
 void
diff --git a/btpd/btpd.h b/btpd/btpd.h
index 452087c..6feba67 100644
--- a/btpd/btpd.h
+++ b/btpd/btpd.h
@@ -48,7 +48,7 @@ void btpd_err(const char *fmt, ...);
 void *btpd_malloc(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);
 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_end(void);
 
+void btpd_tp_activated(struct torrent *tp);
+void btpd_tp_deactivated(struct torrent *tp);
+
 #endif
diff --git a/btpd/torrent.c b/btpd/torrent.c
index c362ad5..5c8ff89 100644
--- a/btpd/torrent.c
+++ b/btpd/torrent.c
@@ -51,18 +51,36 @@ torrent_block_size(struct torrent *tp, uint32_t piece, uint32_t nblocks,
 void
 torrent_activate(struct torrent *tp)
 {
-    assert(tp->state == T_INACTIVE);
-    tp->state = T_STARTING;
-    cm_start(tp);
+    if (tp->state == T_INACTIVE) {
+        tp->state = T_STARTING;
+        cm_start(tp);
+        btpd_tp_activated(tp);
+    }
 }
 
 void
 torrent_deactivate(struct torrent *tp)
 {
-    tp->state = T_STOPPING;
-    tr_stop(tp);
-    net_del_torrent(tp);
-    cm_stop(tp);
+    switch (tp->state) {
+    case T_INACTIVE:
+        break;
+    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
@@ -110,5 +128,18 @@ void
 torrent_on_cm_stopped(struct torrent *tp)
 {
     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);
+    }
 }
diff --git a/btpd/torrent.h b/btpd/torrent.h
index 73a26c7..c84c9be 100644
--- a/btpd/torrent.h
+++ b/btpd/torrent.h
@@ -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_started(struct torrent *tp);
+void torrent_on_tr_stopped(struct torrent *tp);
 
 #endif
diff --git a/btpd/tracker_req.c b/btpd/tracker_req.c
index ebd2fde..5325a3d 100644
--- a/btpd/tracker_req.c
+++ b/btpd/tracker_req.c
@@ -5,6 +5,7 @@
 #include "benc.h"
 #include "subr.h"
 #include "http.h"
+#include "tracker_req.h"
 
 #define REQ_TIMEOUT (& (struct timeval) { 120, 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 tracker *tr = tp->tr;
     assert(tr->ttype == TIMER_TIMEOUT);
+    tr->req = NULL;
     if ((http_succeeded(res) &&
             parse_reply(tp, res->content, res->length) == 0)) {
+        tr->nerrors = 0;
         tr->ttype = TIMER_INTERVAL;
         event_add(&tr->timer, (& (struct timeval) { tr->interval, 0 }));
     } else {
+        tr->nerrors++;
         tr->ttype = TIMER_RETRY;
         event_add(&tr->timer, RETRY_WAIT);
     }
+    if (tr->event == TR_EV_STOPPED && (tr->nerrors == 0 || tr->nerrors >= 5))
+        tr_destroy(tp);
 }
 
 static void
@@ -145,8 +151,17 @@ timer_cb(int fd, short type, void *arg)
     struct tracker *tr = tp->tr;
     switch (tr->ttype) {
     case TIMER_TIMEOUT:
+        tr->nerrors++;
+        if (tr->event == TR_EV_STOPPED && tr->nerrors >= 5) {
+            tr_destroy(tp);
+            break;
+        }
     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;
     case TIMER_INTERVAL:
         tr_send(tp, TR_EV_EMPTY);
@@ -206,6 +221,18 @@ tr_start(struct torrent *tp)
 }
 
 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
 tr_refresh(struct torrent *tp)
 {
     tr_send(tp, TR_EV_EMPTY);
diff --git a/btpd/tracker_req.h b/btpd/tracker_req.h
index 586bbb6..95cc170 100644
--- a/btpd/tracker_req.h
+++ b/btpd/tracker_req.h
@@ -5,5 +5,6 @@ int 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);
 
 #endif