about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRichard Nyberg <rnyberg@murmeldjur.se>2006-02-01 21:56:59 +0000
committerRichard Nyberg <rnyberg@murmeldjur.se>2006-02-01 21:56:59 +0000
commitc8f9335e6eb25b464803664abd8258869d564388 (patch)
tree39ce9f8af983bf5a1685dd337817f843672a3589
parent44165a86d05e6eebc1aa0c3665a7ade27ebd535a (diff)
downloadbtpd-c8f9335e6eb25b464803664abd8258869d564388.tar.gz
btpd-c8f9335e6eb25b464803664abd8258869d564388.zip
Torrents can now be deactivated and btpd will wait for all torrents to
deactivate on shutdown. To not risk hanging indefinitely on unresponsive
trackers, btpd will cancel tracker requests after a while.

-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