source: trunk/libtransmission/announcer.c @ 13625

Last change on this file since 13625 was 13625, checked in by jordan, 9 years ago

Follow more common whitespace style conventions in the C code (libtransmission, daemon, utils, cli, gtk).

  • Property svn:keywords set to Date Rev Author Id
File size: 51.6 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2 (b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: announcer.c 13625 2012-12-05 17:29:46Z jordan $
11 */
12
13#include <assert.h>
14#include <limits.h> /* INT_MAX */
15#include <stdio.h>
16#include <stdlib.h> /* qsort () */
17#include <string.h> /* strcmp (), memcpy () */
18
19#include <event2/buffer.h>
20#include <event2/event.h> /* evtimer */
21
22#define __LIBTRANSMISSION_ANNOUNCER_MODULE___
23
24#include "transmission.h"
25#include "announcer.h"
26#include "announcer-common.h"
27#include "crypto.h" /* tr_cryptoRandInt (), tr_cryptoWeakRandInt () */
28#include "peer-mgr.h" /* tr_peerMgrCompactToPex () */
29#include "ptrarray.h"
30#include "session.h"
31#include "torrent.h"
32#include "utils.h"
33
34struct tr_tier;
35
36static void tier_build_log_name (const struct tr_tier * tier,
37                                 char * buf, size_t buflen);
38
39#define dbgmsg(tier, ...) \
40if (tr_deepLoggingIsActive ()) do { \
41  char name[128]; \
42  tier_build_log_name (tier, name, sizeof (name)); \
43  tr_deepLog (__FILE__, __LINE__, name, __VA_ARGS__); \
44} while (0)
45
46enum
47{
48    /* unless the tracker says otherwise, rescrape this frequently */
49    DEFAULT_SCRAPE_INTERVAL_SEC = (60 * 30),
50
51    /* unless the tracker says otherwise, this is the announce interval */
52    DEFAULT_ANNOUNCE_INTERVAL_SEC = (60 * 10),
53
54    /* unless the tracker says otherwise, this is the announce min_interval */
55    DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = (60 * 2),
56
57    /* how many web tasks we allow at one time */
58    MAX_CONCURRENT_TASKS = 48,
59
60    /* the value of the 'numwant' argument passed in tracker requests. */
61    NUMWANT = 80,
62
63    UPKEEP_INTERVAL_SECS = 1,
64
65    /* this is how often to call the UDP tracker upkeep */
66    TAU_UPKEEP_INTERVAL_SECS = 5
67};
68
69/***
70****
71***/
72
73const char*
74tr_announce_event_get_string (tr_announce_event e)
75{
76    switch (e)
77    {
78        case TR_ANNOUNCE_EVENT_COMPLETED:  return "completed";
79        case TR_ANNOUNCE_EVENT_STARTED:    return "started";
80        case TR_ANNOUNCE_EVENT_STOPPED:    return "stopped";
81        default:                           return "";
82    }
83}
84
85/***
86****
87***/
88
89static int
90compareTransfer (uint64_t a_uploaded, uint64_t a_downloaded,
91                 uint64_t b_uploaded, uint64_t b_downloaded)
92{
93    /* higher upload count goes first */
94    if (a_uploaded != b_uploaded)
95        return a_uploaded > b_uploaded ? -1 : 1;
96
97    /* then higher download count goes first */
98    if (a_downloaded != b_downloaded)
99        return a_downloaded > b_downloaded ? -1 : 1;
100
101    return 0;
102}
103
104/**
105 * Comparison function for tr_announce_requests.
106 *
107 * The primary key (amount of data transferred) is used to prioritize
108 * tracker announcements of active torrents. The remaining keys are
109 * used to satisfy the uniqueness requirement of a sorted tr_ptrArray.
110 */
111static int
112compareStops (const void * va, const void * vb)
113{
114    int i;
115    const tr_announce_request * a = va;
116    const tr_announce_request * b = vb;
117
118    /* primary key: volume of data transferred. */
119    if ((i = compareTransfer (a->up, a->down, b->up, b->down)))
120        return i;
121
122    /* secondary key: the torrent's info_hash */
123    if ((i = memcmp (a->info_hash, b->info_hash, SHA_DIGEST_LENGTH)))
124        return i;
125
126    /* tertiary key: the tracker's announec url */
127    return tr_strcmp0 (a->url, b->url);
128}
129
130/***
131****
132***/
133
134/**
135 * "global" (per-tr_session) fields
136 */
137typedef struct tr_announcer
138{
139    tr_ptrArray stops; /* tr_announce_request */
140
141    tr_session * session;
142    struct event * upkeepTimer;
143    int slotsAvailable;
144    int key;
145    time_t tauUpkeepAt;
146}
147tr_announcer;
148
149bool
150tr_announcerHasBacklog (const struct tr_announcer * announcer)
151{
152    return announcer->slotsAvailable < 1;
153}
154
155static void
156onUpkeepTimer (int foo UNUSED, short bar UNUSED, void * vannouncer);
157
158void
159tr_announcerInit (tr_session * session)
160{
161    tr_announcer * a;
162
163    assert (tr_isSession (session));
164
165    a = tr_new0 (tr_announcer, 1);
166    a->stops = TR_PTR_ARRAY_INIT;
167    a->key = tr_cryptoRandInt (INT_MAX);
168    a->session = session;
169    a->slotsAvailable = MAX_CONCURRENT_TASKS;
170    a->upkeepTimer = evtimer_new (session->event_base, onUpkeepTimer, a);
171    tr_timerAdd (a->upkeepTimer, UPKEEP_INTERVAL_SECS, 0);
172
173    session->announcer = a;
174}
175
176static void flushCloseMessages (tr_announcer * announcer);
177
178void
179tr_announcerClose (tr_session * session)
180{
181    tr_announcer * announcer = session->announcer;
182
183    flushCloseMessages (announcer);
184
185    tr_tracker_udp_start_shutdown (session);
186
187    event_free (announcer->upkeepTimer);
188    announcer->upkeepTimer = NULL;
189
190    tr_ptrArrayDestruct (&announcer->stops, NULL);
191
192    session->announcer = NULL;
193    tr_free (announcer);
194}
195
196/***
197****
198***/
199
200/* a row in tr_tier's list of trackers */
201typedef struct
202{
203    char * key;
204    char * announce;
205    char * scrape;
206
207    char * tracker_id_str;
208
209    int seederCount;
210    int leecherCount;
211    int downloadCount;
212    int downloaderCount;
213
214    int consecutiveFailures;
215
216    uint32_t id;
217}
218tr_tracker;
219
220/* format: host+':'+ port */
221static char *
222getKey (const char * url)
223{
224    char * ret;
225    char * scheme = NULL;
226    char * host = NULL;
227    int port = 0;
228
229    tr_urlParse (url, -1, &scheme, &host, &port, NULL);
230    ret = tr_strdup_printf ("%s://%s:%d", (scheme?scheme:"invalid"), (host?host:"invalid"), port);
231
232    tr_free (host);
233    tr_free (scheme);
234    return ret;
235}
236
237static void
238trackerConstruct (tr_tracker * tracker, const tr_tracker_info * inf)
239{
240    memset (tracker, 0, sizeof (tr_tracker));
241    tracker->key = getKey (inf->announce);
242    tracker->announce = tr_strdup (inf->announce);
243    tracker->scrape = tr_strdup (inf->scrape);
244    tracker->id = inf->id;
245    tracker->seederCount = -1;
246    tracker->leecherCount = -1;
247    tracker->downloadCount = -1;
248}
249
250static void
251trackerDestruct (tr_tracker * tracker)
252{
253    tr_free (tracker->tracker_id_str);
254    tr_free (tracker->scrape);
255    tr_free (tracker->announce);
256    tr_free (tracker->key);
257}
258
259/***
260****
261***/
262
263struct tr_torrent_tiers;
264
265/** @brief A group of trackers in a single tier, as per the multitracker spec */
266typedef struct tr_tier
267{
268    /* number of up/down/corrupt bytes since the last time we sent an
269     * "event=stopped" message that was acknowledged by the tracker */
270    uint64_t byteCounts[3];
271
272    tr_tracker * trackers;
273    int tracker_count;
274    tr_tracker * currentTracker;
275    int currentTrackerIndex;
276
277    tr_torrent * tor;
278
279    time_t scrapeAt;
280    time_t lastScrapeStartTime;
281    time_t lastScrapeTime;
282    bool lastScrapeSucceeded;
283    bool lastScrapeTimedOut;
284
285    time_t announceAt;
286    time_t manualAnnounceAllowedAt;
287    time_t lastAnnounceStartTime;
288    time_t lastAnnounceTime;
289    bool lastAnnounceSucceeded;
290    bool lastAnnounceTimedOut;
291
292    tr_announce_event * announce_events;
293    int announce_event_count;
294    int announce_event_alloc;
295
296    /* unique lookup key */
297    int key;
298
299    int scrapeIntervalSec;
300    int announceIntervalSec;
301    int announceMinIntervalSec;
302
303    int lastAnnouncePeerCount;
304
305    bool isRunning;
306    bool isAnnouncing;
307    bool isScraping;
308    bool wasCopied;
309
310    char lastAnnounceStr[128];
311    char lastScrapeStr[128];
312}
313tr_tier;
314
315static time_t
316get_next_scrape_time (const tr_session * session, const tr_tier * tier, int interval)
317{
318    time_t ret;
319    const time_t now = tr_time ();
320
321    /* Maybe don't scrape paused torrents */
322    if (!tier->isRunning && !session->scrapePausedTorrents)
323        ret = 0;
324
325    /* Add the interval, and then increment to the nearest 10th second.
326     * The latter step is to increase the odds of several torrents coming
327     * due at the same time to improve multiscrape. */
328    else {
329        ret = now + interval;
330        while (ret % 10)
331            ++ret;
332    }
333
334    return ret;
335}
336
337static void
338tierConstruct (tr_tier * tier, tr_torrent * tor)
339{
340    static int nextKey = 1;
341
342    memset (tier, 0, sizeof (tr_tier));
343
344    tier->key = nextKey++;
345    tier->currentTrackerIndex = -1;
346    tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
347    tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
348    tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
349    tier->scrapeAt = get_next_scrape_time (tor->session, tier, tr_cryptoWeakRandInt (180));
350    tier->tor = tor;
351}
352
353static void
354tierDestruct (tr_tier * tier)
355{
356    tr_free (tier->announce_events);
357}
358
359static void
360tier_build_log_name (const tr_tier * tier, char * buf, size_t buflen)
361{
362    tr_snprintf (buf, buflen, "[%s---%s]",
363     (tier && tier->tor) ? tr_torrentName (tier->tor) : "?",
364     (tier && tier->currentTracker) ? tier->currentTracker->key : "?");
365}
366
367static void
368tierIncrementTracker (tr_tier * tier)
369{
370    /* move our index to the next tracker in the tier */
371    const int i = (tier->currentTracker == NULL)
372                ? 0
373                : (tier->currentTrackerIndex + 1) % tier->tracker_count;
374    tier->currentTrackerIndex = i;
375    tier->currentTracker = &tier->trackers[i];
376
377    /* reset some of the tier's fields */
378    tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
379    tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
380    tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
381    tier->isAnnouncing = false;
382    tier->isScraping = false;
383    tier->lastAnnounceStartTime = 0;
384    tier->lastScrapeStartTime = 0;
385}
386
387/***
388****
389***/
390
391/**
392 * @brief Opaque, per-torrent data structure for tracker announce information
393 *
394 * this opaque data structure can be found in tr_torrent.tiers
395 */
396typedef struct tr_torrent_tiers
397{
398    tr_tier * tiers;
399    int tier_count;
400
401    tr_tracker * trackers;
402    int tracker_count;
403
404    tr_tracker_callback * callback;
405    void * callbackData;
406}
407tr_torrent_tiers;
408
409static tr_torrent_tiers*
410tiersNew (void)
411{
412    return tr_new0 (tr_torrent_tiers, 1);
413}
414
415static void
416tiersDestruct (tr_torrent_tiers * tt)
417{
418    int i;
419
420    for (i=0; i<tt->tracker_count; ++i)
421        trackerDestruct (&tt->trackers[i]);
422    tr_free (tt->trackers);
423
424    for (i=0; i<tt->tier_count; ++i)
425        tierDestruct (&tt->tiers[i]);
426    tr_free (tt->tiers);
427}
428
429static void
430tiersFree (tr_torrent_tiers * tt)
431{
432    tiersDestruct (tt);
433    tr_free (tt);
434}
435
436static tr_tier*
437getTier (tr_announcer * announcer, const uint8_t * info_hash, int tierId)
438{
439    tr_tier * tier = NULL;
440
441    if (announcer != NULL)
442    {
443        tr_session * session = announcer->session;
444        tr_torrent * tor = tr_torrentFindFromHash (session, info_hash);
445
446        if (tor && tor->tiers)
447        {
448            int i;
449            tr_torrent_tiers * tt = tor->tiers;
450
451            for (i=0; !tier && i<tt->tier_count; ++i)
452                if (tt->tiers[i].key == tierId)
453                    tier = &tt->tiers[i];
454        }
455    }
456
457    return tier;
458}
459
460/***
461****  PUBLISH
462***/
463
464static const tr_tracker_event TRACKER_EVENT_INIT = { 0, 0, 0, 0, 0, 0 };
465
466static void
467publishMessage (tr_tier * tier, const char * msg, int type)
468{
469    if (tier && tier->tor && tier->tor->tiers && tier->tor->tiers->callback)
470    {
471        tr_torrent_tiers * tiers = tier->tor->tiers;
472        tr_tracker_event event = TRACKER_EVENT_INIT;
473        event.messageType = type;
474        event.text = msg;
475        if (tier->currentTracker)
476            event.tracker = tier->currentTracker->announce;
477
478        tiers->callback (tier->tor, &event, tiers->callbackData);
479    }
480}
481
482static void
483publishErrorClear (tr_tier * tier)
484{
485    publishMessage (tier, NULL, TR_TRACKER_ERROR_CLEAR);
486}
487
488static void
489publishWarning (tr_tier * tier, const char * msg)
490{
491    publishMessage (tier, msg, TR_TRACKER_WARNING);
492}
493
494static void
495publishError (tr_tier * tier, const char * msg)
496{
497    publishMessage (tier, msg, TR_TRACKER_ERROR);
498}
499
500static int8_t
501getSeedProbability (tr_tier * tier, int seeds, int leechers, int pex_count)
502{
503    /* special case optimization:
504       ocelot omits seeds from peer lists sent to seeds on private trackers.
505       so check for that case... */
506    if ((leechers == pex_count) && tr_torrentIsPrivate (tier->tor)
507                                  && tr_torrentIsSeed (tier->tor)
508                                  && (seeds + leechers < NUMWANT))
509        return 0;
510
511    if (seeds>=0 && leechers>=0 && (seeds+leechers>0))
512        return (int8_t)((100.0*seeds)/ (seeds+leechers));
513
514    return -1; /* unknown */
515}
516
517static void
518publishPeersPex (tr_tier * tier, int seeds, int leechers,
519                 const tr_pex * pex, int n)
520{
521    if (tier->tor->tiers->callback)
522    {
523        tr_tracker_event e = TRACKER_EVENT_INIT;
524        e.messageType = TR_TRACKER_PEERS;
525        e.seedProbability = getSeedProbability (tier, seeds, leechers, n);
526        e.pex = pex;
527        e.pexCount = n;
528        dbgmsg (tier, "got %d peers; seed prob %d", n, (int)e.seedProbability);
529
530        tier->tor->tiers->callback (tier->tor, &e, NULL);
531    }
532}
533
534/***
535****
536***/
537
538struct ann_tracker_info
539{
540    tr_tracker_info info;
541
542    char * scheme;
543    char * host;
544    char * path;
545    int port;
546};
547
548
549/* primary key: tier
550 * secondary key: udp comes before http */
551static int
552filter_trackers_compare_func (const void * va, const void * vb)
553{
554    const struct ann_tracker_info * a = va;
555    const struct ann_tracker_info * b = vb;
556    if (a->info.tier != b->info.tier)
557        return a->info.tier - b->info.tier;
558    return -strcmp (a->scheme, b->scheme);
559}
560
561/**
562 * Massages the incoming list of trackers into something we can use.
563 */
564static tr_tracker_info *
565filter_trackers (tr_tracker_info * input, int input_count, int * setme_count)
566{
567    int i, in;
568    int j, jn;
569    int n = 0;
570    struct tr_tracker_info * ret;
571    struct ann_tracker_info * tmp = tr_new0 (struct ann_tracker_info, input_count);
572
573    /*for (i=0, in=input_count; i<in; ++i) fprintf (stderr, "IN: [%d][%s]\n", input[i].tier, input[i].announce);*/
574
575    /* build a list of valid trackers */
576    for (i=0, in=input_count; i<in; ++i) {
577        if (tr_urlIsValidTracker (input[i].announce)) {
578            int port;
579            char * scheme;
580            char * host;
581            char * path;
582            bool is_duplicate = false;
583            tr_urlParse (input[i].announce, -1, &scheme, &host, &port, &path);
584
585            /* weed out one common source of duplicates:
586             * "http://tracker/announce" +
587             * "http://tracker:80/announce"
588             */
589            for (j=0, jn=n; !is_duplicate && j<jn; ++j)
590                is_duplicate = (tmp[j].port==port)
591                            && !strcmp (tmp[j].scheme,scheme)
592                            && !strcmp (tmp[j].host,host)
593                            && !strcmp (tmp[j].path,path);
594
595            if (is_duplicate) {
596                tr_free (path);
597                tr_free (host);
598                tr_free (scheme);
599                continue;
600            }
601            tmp[n].info = input[i];
602            tmp[n].scheme = scheme;
603            tmp[n].host = host;
604            tmp[n].port = port;
605            tmp[n].path = path;
606            n++;
607        }
608    }
609
610    /* if two announce URLs differ only by scheme, put them in the same tier.
611     * (note: this can leave gaps in the `tier' values, but since the calling
612     * function doesn't care, there's no point in removing the gaps...) */
613    for (i=0, in=n; i<in; ++i)
614        for (j=i+1, jn=n; j<jn; ++j)
615            if ((tmp[i].info.tier!=tmp[j].info.tier)
616                       && (tmp[i].port==tmp[j].port)
617                       && !tr_strcmp0 (tmp[i].host,tmp[j].host)
618                       && !tr_strcmp0 (tmp[i].path,tmp[j].path))
619                tmp[j].info.tier = tmp[i].info.tier;
620
621    /* sort them, for two reasons:
622     * (1) unjumble the tiers from the previous step
623     * (2) move the UDP trackers to the front of each tier */
624    qsort (tmp, n, sizeof (struct ann_tracker_info), filter_trackers_compare_func);
625
626    /* build the output */
627    *setme_count = n;
628    ret = tr_new0 (tr_tracker_info, n);
629    for (i=0, in=n; i<in; ++i)
630        ret[i] = tmp[i].info;
631
632    /* cleanup */
633    for (i=0, in=n; i<in; ++i) {
634        tr_free (tmp[i].path);
635        tr_free (tmp[i].host);
636        tr_free (tmp[i].scheme);
637    }
638    tr_free (tmp);
639
640    /*for (i=0, in=n; i<in; ++i) fprintf (stderr, "OUT: [%d][%s]\n", ret[i].tier, ret[i].announce);*/
641    return ret;
642}
643
644
645static void
646addTorrentToTier (tr_torrent_tiers * tt, tr_torrent * tor)
647{
648    int i, n;
649    int tier_count;
650    tr_tier * tier;
651    tr_tracker_info * infos = filter_trackers (tor->info.trackers,
652                                               tor->info.trackerCount, &n);
653
654    /* build the array of trackers */
655    tt->trackers = tr_new0 (tr_tracker, n);
656    tt->tracker_count = n;
657    for (i=0; i<n; ++i)
658        trackerConstruct (&tt->trackers[i], &infos[i]);
659
660    /* count how many tiers there are */
661    tier_count = 0;
662    for (i=0; i<n; ++i)
663        if (!i || (infos[i].tier != infos[i-1].tier))
664            ++tier_count;
665
666    /* build the array of tiers */
667    tier = NULL;
668    tt->tiers = tr_new0 (tr_tier, tier_count);
669    tt->tier_count = 0;
670    for (i=0; i<n; ++i) {
671        if (i && (infos[i].tier == infos[i-1].tier))
672            ++tier->tracker_count;
673        else {
674            tier = &tt->tiers[tt->tier_count++];
675            tierConstruct (tier, tor);
676            tier->trackers = &tt->trackers[i];
677            tier->tracker_count = 1;
678            tierIncrementTracker (tier);
679        }
680    }
681
682    /* cleanup */
683    tr_free (infos);
684}
685
686tr_torrent_tiers *
687tr_announcerAddTorrent (tr_torrent           * tor,
688                        tr_tracker_callback  * callback,
689                        void                 * callbackData)
690{
691    tr_torrent_tiers * tiers;
692
693    assert (tr_isTorrent (tor));
694
695    tiers = tiersNew ();
696    tiers->callback = callback;
697    tiers->callbackData = callbackData;
698
699    addTorrentToTier (tiers, tor);
700
701    return tiers;
702}
703
704/***
705****
706***/
707
708static bool
709tierCanManualAnnounce (const tr_tier * tier)
710{
711    return tier->manualAnnounceAllowedAt <= tr_time ();
712}
713
714bool
715tr_announcerCanManualAnnounce (const tr_torrent * tor)
716{
717    int i;
718    struct tr_torrent_tiers * tt = NULL;
719
720    assert (tr_isTorrent (tor));
721    assert (tor->tiers != NULL);
722
723    if (tor->isRunning)
724        tt = tor->tiers;
725
726    /* return true if any tier can manual announce */
727    for (i=0; tt && i<tt->tier_count; ++i)
728        if (tierCanManualAnnounce (&tt->tiers[i]))
729            return true;
730
731    return false;
732}
733
734time_t
735tr_announcerNextManualAnnounce (const tr_torrent * tor)
736{
737    int i;
738    time_t ret = ~ (time_t)0;
739    struct tr_torrent_tiers * tt = tor->tiers;
740
741    /* find the earliest manual announce time from all peers */
742    for (i=0; tt && i<tt->tier_count; ++i)
743        if (tt->tiers[i].isRunning)
744            ret = MIN (ret, tt->tiers[i].manualAnnounceAllowedAt);
745
746    return ret;
747}
748
749static void
750dbgmsg_tier_announce_queue (const tr_tier * tier)
751{
752    if (tr_deepLoggingIsActive ())
753    {
754        int i;
755        char name[128];
756        char * message;
757        struct evbuffer * buf = evbuffer_new ();
758
759        tier_build_log_name (tier, name, sizeof (name));
760        for (i=0; i<tier->announce_event_count; ++i)
761        {
762            const tr_announce_event e = tier->announce_events[i];
763            const char * str = tr_announce_event_get_string (e);
764            evbuffer_add_printf (buf, "[%d:%s]", i, str);
765        }
766
767        message = evbuffer_free_to_str (buf);
768        tr_deepLog (__FILE__, __LINE__, name, "announce queue is %s", message);
769        tr_free (message);
770    }
771}
772
773static void
774tier_announce_remove_trailing (tr_tier * tier, tr_announce_event e)
775{
776    while ((tier->announce_event_count > 0)
777        && (tier->announce_events[tier->announce_event_count-1] == e))
778        --tier->announce_event_count;
779}
780
781static void
782tier_announce_event_push (tr_tier            * tier,
783                          tr_announce_event    e,
784                          time_t               announceAt)
785{
786    int i;
787
788    assert (tier != NULL);
789
790    dbgmsg_tier_announce_queue (tier);
791    dbgmsg (tier, "queued \"%s\"", tr_announce_event_get_string (e));
792
793    if (tier->announce_event_count > 0)
794    {
795        /* special case #1: if we're adding a "stopped" event,
796         * dump everything leading up to it except "completed" */
797        if (e == TR_ANNOUNCE_EVENT_STOPPED) {
798            bool has_completed = false;
799            const tr_announce_event c = TR_ANNOUNCE_EVENT_COMPLETED;
800            for (i=0; !has_completed && i<tier->announce_event_count; ++i)
801                has_completed = c == tier->announce_events[i];
802            tier->announce_event_count = 0;
803            if (has_completed)
804                tier->announce_events[tier->announce_event_count++] = c;
805        }
806
807        /* special case #2: dump all empty strings leading up to this event */
808        tier_announce_remove_trailing (tier, TR_ANNOUNCE_EVENT_NONE);
809
810        /* special case #3: no consecutive duplicates */
811        tier_announce_remove_trailing (tier, e);
812    }
813
814    /* make room in the array for another event */
815    if (tier->announce_event_alloc <= tier->announce_event_count) {
816        tier->announce_event_alloc += 4;
817        tier->announce_events = tr_renew (tr_announce_event,
818                                          tier->announce_events,
819                                          tier->announce_event_alloc);
820    }
821
822    /* add it */
823    tier->announce_events[tier->announce_event_count++] = e;
824    tier->announceAt = announceAt;
825
826    dbgmsg_tier_announce_queue (tier);
827    dbgmsg (tier, "announcing in %d seconds", (int)difftime (announceAt,tr_time ()));
828}
829
830static tr_announce_event
831tier_announce_event_pull (tr_tier * tier)
832{
833    const tr_announce_event e = tier->announce_events[0];
834
835    tr_removeElementFromArray (tier->announce_events,
836                               0, sizeof (tr_announce_event),
837                               tier->announce_event_count--);
838
839    return e;
840}
841
842static void
843torrentAddAnnounce (tr_torrent * tor, tr_announce_event e, time_t announceAt)
844{
845    int i;
846    struct tr_torrent_tiers * tt = tor->tiers;
847
848    /* walk through each tier and tell them to announce */
849    for (i=0; i<tt->tier_count; ++i)
850        tier_announce_event_push (&tt->tiers[i], e, announceAt);
851}
852
853void
854tr_announcerTorrentStarted (tr_torrent * tor)
855{
856    torrentAddAnnounce (tor, TR_ANNOUNCE_EVENT_STARTED, tr_time ());
857}
858void
859tr_announcerManualAnnounce (tr_torrent * tor)
860{
861    torrentAddAnnounce (tor, TR_ANNOUNCE_EVENT_NONE, tr_time ());
862}
863void
864tr_announcerTorrentStopped (tr_torrent * tor)
865{
866    torrentAddAnnounce (tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time ());
867}
868void
869tr_announcerTorrentCompleted (tr_torrent * tor)
870{
871    torrentAddAnnounce (tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time ());
872}
873void
874tr_announcerChangeMyPort (tr_torrent * tor)
875{
876    tr_announcerTorrentStarted (tor);
877}
878
879/***
880****
881***/
882
883void
884tr_announcerAddBytes (tr_torrent * tor, int type, uint32_t byteCount)
885{
886    int i;
887    struct tr_torrent_tiers * tt = tor->tiers;
888
889    assert (tr_isTorrent (tor));
890    assert (type==TR_ANN_UP || type==TR_ANN_DOWN || type==TR_ANN_CORRUPT);
891
892    for (i=0; i<tt->tier_count; ++i)
893        tt->tiers[i].byteCounts[ type ] += byteCount;
894}
895
896/***
897****
898***/
899
900static tr_announce_request *
901announce_request_new (const tr_announcer  * announcer,
902                      const tr_torrent    * tor,
903                      const tr_tier       * tier,
904                      tr_announce_event     event)
905{
906    tr_announce_request * req = tr_new0 (tr_announce_request, 1);
907    req->port = tr_sessionGetPublicPeerPort (announcer->session);
908    req->url = tr_strdup (tier->currentTracker->announce);
909    req->tracker_id_str = tr_strdup (tier->currentTracker->tracker_id_str);
910    memcpy (req->info_hash, tor->info.hash, SHA_DIGEST_LENGTH);
911    memcpy (req->peer_id, tor->peer_id, PEER_ID_LEN);
912    req->up = tier->byteCounts[TR_ANN_UP];
913    req->down = tier->byteCounts[TR_ANN_DOWN];
914    req->corrupt = tier->byteCounts[TR_ANN_CORRUPT];
915    req->leftUntilComplete = tr_torrentHasMetadata (tor)
916            ? tor->info.totalSize - tr_cpHaveTotal (&tor->completion)
917            : ~ (uint64_t)0;
918    req->event = event;
919    req->numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : NUMWANT;
920    req->key = announcer->key;
921    req->partial_seed = tr_cpGetStatus (&tor->completion) == TR_PARTIAL_SEED;
922    tier_build_log_name (tier, req->log_name, sizeof (req->log_name));
923    return req;
924}
925
926static void announce_request_free (tr_announce_request * req);
927
928void
929tr_announcerRemoveTorrent (tr_announcer * announcer, tr_torrent * tor)
930{
931    struct tr_torrent_tiers * tt = tor->tiers;
932
933    if (tt != NULL)
934    {
935        int i;
936        for (i=0; i<tt->tier_count; ++i)
937        {
938            tr_tier * tier = &tt->tiers[i];
939            if (tier->isRunning)
940            {
941                const tr_announce_event e = TR_ANNOUNCE_EVENT_STOPPED;
942                tr_announce_request * req = announce_request_new (announcer, tor, tier, e);
943
944                if (tr_ptrArrayFindSorted (&announcer->stops, req, compareStops) != NULL)
945                    announce_request_free (req);
946                else
947                    tr_ptrArrayInsertSorted (&announcer->stops, req, compareStops);
948            }
949        }
950
951        tiersFree (tor->tiers);
952        tor->tiers = NULL;
953    }
954}
955
956static int
957getRetryInterval (const tr_tracker * t)
958{
959    int minutes;
960    const unsigned int jitter_seconds = tr_cryptoWeakRandInt (60);
961    switch (t->consecutiveFailures) {
962        case 0:  minutes =   1; break;
963        case 1:  minutes =   5; break;
964        case 2:  minutes =  15; break;
965        case 3:  minutes =  30; break;
966        case 4:  minutes =  60; break;
967        default: minutes = 120; break;
968    }
969    return (minutes * 60) + jitter_seconds;
970}
971
972struct announce_data
973{
974    int tierId;
975    time_t timeSent;
976    tr_announce_event event;
977    tr_session * session;
978
979    /** If the request succeeds, the value for tier's "isRunning" flag */
980    bool isRunningOnSuccess;
981};
982
983static void
984on_announce_error (tr_tier * tier, const char * err, tr_announce_event e)
985{
986    int interval;
987
988    /* increment the error count */
989    if (tier->currentTracker != NULL)
990        ++tier->currentTracker->consecutiveFailures;
991
992    /* set the error message */
993    dbgmsg (tier, "%s", err);
994    tr_torinf (tier->tor, "%s", err);
995    tr_strlcpy (tier->lastAnnounceStr, err, sizeof (tier->lastAnnounceStr));
996
997    /* switch to the next tracker */
998    tierIncrementTracker (tier);
999
1000    /* schedule a reannounce */
1001    interval = getRetryInterval (tier->currentTracker);
1002    dbgmsg (tier, "Retrying announce in %d seconds.", interval);
1003    tr_torinf (tier->tor, "Retrying announce in %d seconds.", interval);
1004    tier_announce_event_push (tier, e, tr_time () + interval);
1005}
1006
1007static void
1008on_announce_done (const tr_announce_response  * response,
1009                  void                        * vdata)
1010{
1011    struct announce_data * data = vdata;
1012    tr_announcer * announcer = data->session->announcer;
1013    tr_tier * tier = getTier (announcer, response->info_hash, data->tierId);
1014    const time_t now = tr_time ();
1015    const tr_announce_event event = data->event;
1016
1017    if (announcer)
1018        ++announcer->slotsAvailable;
1019
1020    if (tier != NULL)
1021    {
1022        tr_tracker * tracker;
1023
1024        dbgmsg (tier, "Got announce response: "
1025                      "connected:%d "
1026                      "timeout:%d "
1027                      "seeders:%d "
1028                      "leechers:%d "
1029                      "downloads:%d "
1030                      "interval:%d "
1031                      "min_interval:%d "
1032                      "tracker_id_str:%s "
1033                      "pex:%zu "
1034                      "pex6:%zu "
1035                      "err:%s "
1036                      "warn:%s",
1037                    (int)response->did_connect,
1038                    (int)response->did_timeout,
1039                      response->seeders,
1040                      response->leechers,
1041                      response->downloads,
1042                      response->interval,
1043                      response->min_interval,
1044                      response->tracker_id_str ? response->tracker_id_str : "none",
1045                      response->pex_count,
1046                      response->pex6_count,
1047                      response->errmsg ? response->errmsg : "none",
1048                      response->warning ? response->warning : "none");
1049
1050        tier->lastAnnounceTime = now;
1051        tier->lastAnnounceTimedOut = response->did_timeout;
1052        tier->lastAnnounceSucceeded = false;
1053        tier->isAnnouncing = false;
1054        tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1055
1056        if (!response->did_connect)
1057        {
1058            on_announce_error (tier, _ ("Could not connect to tracker"), event);
1059        }
1060        else if (response->did_timeout)
1061        {
1062            on_announce_error (tier, _ ("Tracker did not respond"), event);
1063        }
1064        else if (response->errmsg)
1065        {
1066            /* If the torrent's only tracker returned an error, publish it.
1067               Don't bother publishing if there are other trackers -- it's
1068               all too common for people to load up dozens of dead trackers
1069               in a torrent's metainfo... */
1070            if (tier->tor->info.trackerCount < 2)
1071                publishError (tier, response->errmsg);
1072
1073            on_announce_error (tier, response->errmsg, event);
1074        }
1075        else
1076        {
1077            int i;
1078            const char * str;
1079            int scrape_fields = 0;
1080            int seeders = 0;
1081            int leechers = 0;
1082            int downloads = 0;
1083            const bool isStopped = event == TR_ANNOUNCE_EVENT_STOPPED;
1084
1085            publishErrorClear (tier);
1086
1087            if ((tracker = tier->currentTracker))
1088            {
1089                tracker->consecutiveFailures = 0;
1090
1091                if (response->seeders >= 0)
1092                {
1093                    tracker->seederCount = seeders = response->seeders;
1094                    ++scrape_fields;
1095                }
1096
1097                if (response->leechers >= 0)
1098                {
1099                    tracker->leecherCount = leechers = response->leechers;
1100                    ++scrape_fields;
1101                }
1102                if (response->downloads >= 0)
1103                {
1104                    tracker->downloadCount = downloads = response->downloads;
1105                    ++scrape_fields;
1106                }
1107
1108                if ((str = response->tracker_id_str))
1109                {
1110                    tr_free (tracker->tracker_id_str);
1111                    tracker->tracker_id_str = tr_strdup (str);
1112                }
1113            }
1114
1115            if ((str = response->warning))
1116            {
1117                tr_strlcpy (tier->lastAnnounceStr, str,
1118                            sizeof (tier->lastAnnounceStr));
1119                dbgmsg (tier, "tracker gave \"%s\"", str);
1120                publishWarning (tier, str);
1121            }
1122            else
1123            {
1124                tr_strlcpy (tier->lastAnnounceStr, _ ("Success"),
1125                            sizeof (tier->lastAnnounceStr));
1126            }
1127
1128            if ((i = response->min_interval))
1129                tier->announceMinIntervalSec = i;
1130
1131            if ((i = response->interval))
1132                tier->announceIntervalSec = i;
1133
1134            if (response->pex_count > 0)
1135                publishPeersPex (tier, seeders, leechers,
1136                                 response->pex, response->pex_count);
1137
1138            if (response->pex6_count > 0)
1139                publishPeersPex (tier, seeders, leechers,
1140                                 response->pex6, response->pex6_count);
1141
1142            tier->isRunning = data->isRunningOnSuccess;
1143
1144            /* if the tracker included scrape fields in its announce response,
1145               then a separate scrape isn't needed */
1146            if ((scrape_fields >= 3) || (!tracker->scrape && (scrape_fields >= 1)))
1147            {
1148                tr_tordbg (tier->tor, "Announce response contained scrape info; "
1149                                      "rescheduling next scrape to %d seconds from now.",
1150                                      tier->scrapeIntervalSec);
1151                tier->scrapeAt = get_next_scrape_time (announcer->session, tier, tier->scrapeIntervalSec);
1152                tier->lastScrapeTime = now;
1153                tier->lastScrapeSucceeded = true;
1154            }
1155            else if (tier->lastScrapeTime + tier->scrapeIntervalSec <= now)
1156            {
1157                tier->scrapeAt = get_next_scrape_time (announcer->session, tier, 0);
1158            }
1159
1160            tier->lastAnnounceSucceeded = true;
1161            tier->lastAnnouncePeerCount = response->pex_count
1162                                        + response->pex6_count;
1163
1164            if (isStopped)
1165            {
1166                /* now that we've successfully stopped the torrent,
1167                 * we can reset the up/down/corrupt count we've kept
1168                 * for this tracker */
1169                tier->byteCounts[ TR_ANN_UP ] = 0;
1170                tier->byteCounts[ TR_ANN_DOWN ] = 0;
1171                tier->byteCounts[ TR_ANN_CORRUPT ] = 0;
1172            }
1173
1174            if (!isStopped && !tier->announce_event_count)
1175            {
1176                /* the queue is empty, so enqueue a perodic update */
1177                i = tier->announceIntervalSec;
1178                dbgmsg (tier, "Sending periodic reannounce in %d seconds", i);
1179                tier_announce_event_push (tier, TR_ANNOUNCE_EVENT_NONE, now + i);
1180            }
1181        }
1182    }
1183
1184    tr_free (data);
1185}
1186
1187static void
1188announce_request_free (tr_announce_request * req)
1189{
1190    tr_free (req->tracker_id_str);
1191    tr_free (req->url);
1192    tr_free (req);
1193}
1194
1195static void
1196announce_request_delegate (tr_announcer               * announcer,
1197                           tr_announce_request        * request,
1198                           tr_announce_response_func  * callback,
1199                           void                       * callback_data)
1200{
1201    tr_session * session = announcer->session;
1202
1203    if (!memcmp (request->url, "http", 4))
1204        tr_tracker_http_announce (session, request, callback, callback_data);
1205    else if (!memcmp (request->url, "udp://", 6))
1206        tr_tracker_udp_announce (session, request, callback, callback_data);
1207    else
1208        tr_err ("Unsupported url: %s", request->url);
1209
1210    announce_request_free (request);
1211}
1212
1213static void
1214tierAnnounce (tr_announcer * announcer, tr_tier * tier)
1215{
1216    tr_announce_event announce_event;
1217    tr_announce_request * req;
1218    struct announce_data * data;
1219    const tr_torrent * tor = tier->tor;
1220    const time_t now = tr_time ();
1221
1222    assert (!tier->isAnnouncing);
1223    assert (tier->announce_event_count > 0);
1224
1225    announce_event = tier_announce_event_pull (tier);
1226    req = announce_request_new (announcer, tor, tier, announce_event);
1227
1228    data = tr_new0 (struct announce_data, 1);
1229    data->session = announcer->session;
1230    data->tierId = tier->key;
1231    data->isRunningOnSuccess = tor->isRunning;
1232    data->timeSent = now;
1233    data->event = announce_event;
1234
1235    tier->isAnnouncing = true;
1236    tier->lastAnnounceStartTime = now;
1237    --announcer->slotsAvailable;
1238
1239    announce_request_delegate (announcer, req, on_announce_done, data);
1240}
1241
1242/***
1243****
1244****  SCRAPE
1245****
1246***/
1247
1248static void
1249on_scrape_error (tr_session * session, tr_tier * tier, const char * errmsg)
1250{
1251    int interval;
1252
1253    /* increment the error count */
1254    if (tier->currentTracker != NULL)
1255        ++tier->currentTracker->consecutiveFailures;
1256
1257    /* set the error message */
1258    dbgmsg (tier, "Scrape error: %s", errmsg);
1259    tr_torinf (tier->tor, "Scrape error: %s", errmsg);
1260    tr_strlcpy (tier->lastScrapeStr, errmsg, sizeof (tier->lastScrapeStr));
1261
1262    /* switch to the next tracker */
1263    tierIncrementTracker (tier);
1264
1265    /* schedule a rescrape */
1266    interval = getRetryInterval (tier->currentTracker);
1267    dbgmsg (tier, "Retrying scrape in %zu seconds.", (size_t)interval);
1268    tr_torinf (tier->tor, "Retrying scrape in %zu seconds.", (size_t)interval);
1269    tier->lastScrapeSucceeded = false;
1270    tier->scrapeAt = get_next_scrape_time (session, tier, interval);
1271}
1272
1273static tr_tier *
1274find_tier (tr_torrent * tor, const char * scrape)
1275{
1276    int i;
1277    struct tr_torrent_tiers * tt = tor->tiers;
1278
1279    for (i=0; tt && i<tt->tier_count; ++i) {
1280        const tr_tracker * const tracker = tt->tiers[i].currentTracker;
1281        if (tracker && !tr_strcmp0 (scrape, tracker->scrape))
1282            return &tt->tiers[i];
1283    }
1284
1285    return NULL;
1286}
1287
1288static void
1289on_scrape_done (const tr_scrape_response * response, void * vsession)
1290{
1291    int i;
1292    const time_t now = tr_time ();
1293    tr_session * session = vsession;
1294    tr_announcer * announcer = session->announcer;
1295
1296    for (i=0; i<response->row_count; ++i)
1297    {
1298        const struct tr_scrape_response_row * row = &response->rows[i];
1299        tr_torrent * tor = tr_torrentFindFromHash (session, row->info_hash);
1300
1301        if (tor != NULL)
1302        {
1303            tr_tier * tier = find_tier (tor, response->url);
1304
1305            if (tier != NULL)
1306            {
1307                dbgmsg (tier, "scraped url:%s -- "
1308                              "did_connect:%d "
1309                              "did_timeout:%d "
1310                              "seeders:%d "
1311                              "leechers:%d "
1312                              "downloads:%d "
1313                              "downloaders:%d "
1314                              "min_request_interval:%d "
1315                              "err:%s ",
1316                              response->url,
1317                            (int)response->did_connect,
1318                            (int)response->did_timeout,
1319                              row->seeders,
1320                              row->leechers,
1321                              row->downloads,
1322                              row->downloaders,
1323                              response->min_request_interval,
1324                              response->errmsg ? response->errmsg : "none");
1325
1326                tier->isScraping = false;
1327                tier->lastScrapeTime = now;
1328                tier->lastScrapeSucceeded = false;
1329                tier->lastScrapeTimedOut = response->did_timeout;
1330
1331                if (!response->did_connect)
1332                {
1333                    on_scrape_error (session, tier, _ ("Could not connect to tracker"));
1334                }
1335                else if (response->did_timeout)
1336                {
1337                    on_scrape_error (session, tier, _ ("Tracker did not respond"));
1338                }
1339                else if (response->errmsg)
1340                {
1341                    on_scrape_error (session, tier, response->errmsg);
1342                }
1343                else
1344                {
1345                    tr_tracker * tracker;
1346
1347                    tier->lastScrapeSucceeded = true;
1348                    tier->scrapeIntervalSec = MAX (DEFAULT_SCRAPE_INTERVAL_SEC,
1349                                                   response->min_request_interval);
1350                    tier->scrapeAt = get_next_scrape_time (session, tier, tier->scrapeIntervalSec);
1351                    tr_tordbg (tier->tor, "Scrape successful. Rescraping in %d seconds.",
1352                               tier->scrapeIntervalSec);
1353
1354                    if ((tracker = tier->currentTracker))
1355                    {
1356                        if (row->seeders >= 0)
1357                            tracker->seederCount = row->seeders;
1358                        if (row->leechers >= 0)
1359                            tracker->leecherCount = row->leechers;
1360                        if (row->downloads >= 0)
1361                            tracker->downloadCount = row->downloads;
1362                        tracker->downloaderCount = row->downloaders;
1363                        tracker->consecutiveFailures = 0;
1364                    }
1365                }
1366            }
1367        }
1368    }
1369
1370    if (announcer)
1371        ++announcer->slotsAvailable;
1372}
1373
1374static void
1375scrape_request_delegate (tr_announcer             * announcer,
1376                         const tr_scrape_request  * request,
1377                         tr_scrape_response_func  * callback,
1378                         void                     * callback_data)
1379{
1380    tr_session * session = announcer->session;
1381
1382    if (!memcmp (request->url, "http", 4))
1383        tr_tracker_http_scrape (session, request, callback, callback_data);
1384    else if (!memcmp (request->url, "udp://", 6))
1385        tr_tracker_udp_scrape (session, request, callback, callback_data);
1386    else
1387        tr_err ("Unsupported url: %s", request->url);
1388}
1389
1390static void
1391multiscrape (tr_announcer * announcer, tr_ptrArray * tiers)
1392{
1393    int i;
1394    int request_count = 0;
1395    const time_t now = tr_time ();
1396    const int tier_count = tr_ptrArraySize (tiers);
1397    const int max_request_count = MIN (announcer->slotsAvailable, tier_count);
1398    tr_scrape_request * requests = tr_new0 (tr_scrape_request, max_request_count);
1399
1400    /* batch as many info_hashes into a request as we can */
1401    for (i=0; i<tier_count; ++i)
1402    {
1403        int j;
1404        tr_tier * tier = tr_ptrArrayNth (tiers, i);
1405        char * url = tier->currentTracker->scrape;
1406        const uint8_t * hash = tier->tor->info.hash;
1407
1408        /* if there's a request with this scrape URL and a free slot, use it */
1409        for (j=0; j<request_count; ++j)
1410        {
1411            tr_scrape_request * req = &requests[j];
1412
1413            if (req->info_hash_count >= TR_MULTISCRAPE_MAX)
1414                continue;
1415            if (tr_strcmp0 (req->url, url))
1416                continue;
1417
1418            memcpy (req->info_hash[req->info_hash_count++], hash, SHA_DIGEST_LENGTH);
1419            tier->isScraping = true;
1420            tier->lastScrapeStartTime = now;
1421            break;
1422        }
1423
1424        /* otherwise, if there's room for another request, build a new one */
1425        if ((j==request_count) && (request_count < max_request_count))
1426        {
1427            tr_scrape_request * req = &requests[request_count++];
1428            req->url = url;
1429            tier_build_log_name (tier, req->log_name, sizeof (req->log_name));
1430
1431            memcpy (req->info_hash[req->info_hash_count++], hash, SHA_DIGEST_LENGTH);
1432            tier->isScraping = true;
1433            tier->lastScrapeStartTime = now;
1434        }
1435    }
1436
1437    /* send the requests we just built */
1438    for (i=0; i<request_count; ++i)
1439        scrape_request_delegate (announcer, &requests[i], on_scrape_done, announcer->session);
1440
1441    /* cleanup */
1442    tr_free (requests);
1443}
1444
1445static void
1446flushCloseMessages (tr_announcer * announcer)
1447{
1448    int i;
1449    const int n = tr_ptrArraySize (&announcer->stops);
1450
1451    for (i=0; i<n; ++i)
1452        announce_request_delegate (announcer, tr_ptrArrayNth (&announcer->stops, i), NULL, NULL);
1453
1454    tr_ptrArrayClear (&announcer->stops);
1455}
1456
1457static bool
1458tierNeedsToAnnounce (const tr_tier * tier, const time_t now)
1459{
1460    return !tier->isAnnouncing
1461        && !tier->isScraping
1462        && (tier->announceAt != 0)
1463        && (tier->announceAt <= now)
1464        && (tier->announce_event_count > 0);
1465}
1466
1467static bool
1468tierNeedsToScrape (const tr_tier * tier, const time_t now)
1469{
1470    return (!tier->isScraping)
1471        && (tier->scrapeAt != 0)
1472        && (tier->scrapeAt <= now)
1473        && (tier->currentTracker != NULL)
1474        && (tier->currentTracker->scrape != NULL);
1475}
1476
1477static int
1478compareTiers (const void * va, const void * vb)
1479{
1480    int ret;
1481    const tr_tier * a = * (const tr_tier**)va;
1482    const tr_tier * b = * (const tr_tier**)vb;
1483
1484    /* primary key: larger stats come before smaller */
1485    ret = compareTransfer (a->byteCounts[TR_ANN_UP], a->byteCounts[TR_ANN_DOWN],
1486                           b->byteCounts[TR_ANN_UP], b->byteCounts[TR_ANN_DOWN]);
1487
1488    /* secondary key: announcements that have been waiting longer go first */
1489    if (!ret && (a->announceAt != b->announceAt))
1490        ret = a->announceAt < b->announceAt ? -1 : 1;
1491
1492    return ret;
1493}
1494
1495static void
1496announceMore (tr_announcer * announcer)
1497{
1498    int i;
1499    int n;
1500    tr_torrent * tor;
1501    tr_ptrArray announceMe = TR_PTR_ARRAY_INIT;
1502    tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT;
1503    const time_t now = tr_time ();
1504
1505    dbgmsg (NULL, "announceMore: slotsAvailable is %d", announcer->slotsAvailable);
1506
1507    if (announcer->slotsAvailable < 1)
1508        return;
1509
1510    /* build a list of tiers that need to be announced */
1511    tor = NULL;
1512    while ((tor = tr_torrentNext (announcer->session, tor))) {
1513        struct tr_torrent_tiers * tt = tor->tiers;
1514        for (i=0; tt && i<tt->tier_count; ++i) {
1515            tr_tier * tier = &tt->tiers[i];
1516            if (tierNeedsToAnnounce (tier, now))
1517                tr_ptrArrayAppend (&announceMe, tier);
1518            else if (tierNeedsToScrape (tier, now))
1519                tr_ptrArrayAppend (&scrapeMe, tier);
1520        }
1521    }
1522
1523    /* if there are more tiers than slots available, prioritize */
1524    n = tr_ptrArraySize (&announceMe);
1525    if (n > announcer->slotsAvailable)
1526        qsort (tr_ptrArrayBase (&announceMe), n, sizeof (tr_tier*), compareTiers);
1527
1528    /* announce some */
1529    n = MIN (tr_ptrArraySize (&announceMe), announcer->slotsAvailable);
1530    for (i=0; i<n; ++i) {
1531        tr_tier * tier = tr_ptrArrayNth (&announceMe, i);
1532        tr_tordbg (tier->tor, "%s", "Announcing to tracker");
1533        dbgmsg (tier, "announcing tier %d of %d", i, n);
1534        tierAnnounce (announcer, tier);
1535    }
1536
1537    /* scrape some */
1538    multiscrape (announcer, &scrapeMe);
1539
1540    /* cleanup */
1541    tr_ptrArrayDestruct (&scrapeMe, NULL);
1542    tr_ptrArrayDestruct (&announceMe, NULL);
1543}
1544
1545static void
1546onUpkeepTimer (int foo UNUSED, short bar UNUSED, void * vannouncer)
1547{
1548    tr_announcer * announcer = vannouncer;
1549    tr_session * session = announcer->session;
1550    const bool is_closing = session->isClosed;
1551    const time_t now = tr_time ();
1552
1553    tr_sessionLock (session);
1554
1555    /* maybe send out some "stopped" messages for closed torrents */
1556    flushCloseMessages (announcer);
1557
1558    /* maybe send out some announcements to trackers */
1559    if (!is_closing)
1560        announceMore (announcer);
1561
1562    /* TAU upkeep */
1563    if (announcer->tauUpkeepAt <= now) {
1564        announcer->tauUpkeepAt = now + TAU_UPKEEP_INTERVAL_SECS;
1565        tr_tracker_udp_upkeep (session);
1566    }
1567
1568    /* set up the next timer */
1569    tr_timerAdd (announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0);
1570
1571    tr_sessionUnlock (session);
1572}
1573
1574/***
1575****
1576***/
1577
1578tr_tracker_stat *
1579tr_announcerStats (const tr_torrent * torrent, int * setmeTrackerCount)
1580{
1581    int i;
1582    int out = 0;
1583    tr_tracker_stat * ret;
1584    struct tr_torrent_tiers * tt;
1585    const time_t now = tr_time ();
1586
1587    assert (tr_isTorrent (torrent));
1588    assert (tr_torrentIsLocked (torrent));
1589
1590    tt = torrent->tiers;
1591
1592    /* alloc the stats */
1593    *setmeTrackerCount = tt->tracker_count;
1594    ret = tr_new0 (tr_tracker_stat, tt->tracker_count);
1595
1596    /* populate the stats */
1597    for (i=0; i<tt->tier_count; ++i)
1598    {
1599        int j;
1600        const tr_tier * const tier = &tt->tiers[i];
1601        for (j=0; j<tier->tracker_count; ++j)
1602        {
1603            const tr_tracker * const tracker = &tier->trackers[j];
1604            tr_tracker_stat * st = &ret[out++];
1605
1606            st->id = tracker->id;
1607            tr_strlcpy (st->host, tracker->key, sizeof (st->host));
1608            tr_strlcpy (st->announce, tracker->announce, sizeof (st->announce));
1609            st->tier = i;
1610            st->isBackup = tracker != tier->currentTracker;
1611            st->lastScrapeStartTime = tier->lastScrapeStartTime;
1612            if (tracker->scrape)
1613                tr_strlcpy (st->scrape, tracker->scrape, sizeof (st->scrape));
1614            else
1615                st->scrape[0] = '\0';
1616
1617            st->seederCount = tracker->seederCount;
1618            st->leecherCount = tracker->leecherCount;
1619            st->downloadCount = tracker->downloadCount;
1620
1621            if (st->isBackup)
1622            {
1623                st->scrapeState = TR_TRACKER_INACTIVE;
1624                st->announceState = TR_TRACKER_INACTIVE;
1625                st->nextScrapeTime = 0;
1626                st->nextAnnounceTime = 0;
1627            }
1628            else
1629            {
1630                if ((st->hasScraped = tier->lastScrapeTime != 0)) {
1631                    st->lastScrapeTime = tier->lastScrapeTime;
1632                    st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
1633                    st->lastScrapeTimedOut = tier->lastScrapeTimedOut;
1634                    tr_strlcpy (st->lastScrapeResult, tier->lastScrapeStr,
1635                                sizeof (st->lastScrapeResult));
1636                }
1637
1638                if (tier->isScraping)
1639                    st->scrapeState = TR_TRACKER_ACTIVE;
1640                else if (!tier->scrapeAt)
1641                    st->scrapeState = TR_TRACKER_INACTIVE;
1642                else if (tier->scrapeAt > now)
1643                {
1644                    st->scrapeState = TR_TRACKER_WAITING;
1645                    st->nextScrapeTime = tier->scrapeAt;
1646                }
1647                else
1648                    st->scrapeState = TR_TRACKER_QUEUED;
1649
1650                st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
1651
1652                if ((st->hasAnnounced = tier->lastAnnounceTime != 0)) {
1653                    st->lastAnnounceTime = tier->lastAnnounceTime;
1654                    tr_strlcpy (st->lastAnnounceResult, tier->lastAnnounceStr,
1655                                sizeof (st->lastAnnounceResult));
1656                    st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded;
1657                    st->lastAnnounceTimedOut = tier->lastAnnounceTimedOut;
1658                    st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
1659                }
1660
1661                if (tier->isAnnouncing)
1662                    st->announceState = TR_TRACKER_ACTIVE;
1663                else if (!torrent->isRunning || !tier->announceAt)
1664                    st->announceState = TR_TRACKER_INACTIVE;
1665                else if (tier->announceAt > now)
1666                {
1667                    st->announceState = TR_TRACKER_WAITING;
1668                    st->nextAnnounceTime = tier->announceAt;
1669                }
1670                else
1671                    st->announceState = TR_TRACKER_QUEUED;
1672            }
1673        }
1674    }
1675
1676    return ret;
1677}
1678
1679void
1680tr_announcerStatsFree (tr_tracker_stat * trackers,
1681                       int trackerCount UNUSED)
1682{
1683    tr_free (trackers);
1684}
1685
1686/***
1687****
1688***/
1689
1690static void
1691copy_tier_attributes_impl (struct tr_tier * tgt, int trackerIndex, const tr_tier * src)
1692{
1693    const tr_tier keep = *tgt;
1694
1695    /* sanity clause */
1696    assert (trackerIndex < tgt->tracker_count);
1697    assert (!tr_strcmp0 (tgt->trackers[trackerIndex].announce, src->currentTracker->announce));
1698
1699    /* bitwise copy will handle most of tr_tier's fields... */
1700    *tgt = *src;
1701
1702    /* ...fix the fields that can't be cleanly bitwise-copied */
1703    tgt->wasCopied = true;
1704    tgt->trackers = keep.trackers;
1705    tgt->tracker_count = keep.tracker_count;
1706    tgt->announce_events = tr_memdup (src->announce_events, sizeof (tr_announce_event) * src->announce_event_count);
1707    tgt->announce_event_count = src->announce_event_count;
1708    tgt->announce_event_alloc = src->announce_event_count;
1709    tgt->currentTrackerIndex = trackerIndex;
1710    tgt->currentTracker = &tgt->trackers[trackerIndex];
1711    tgt->currentTracker->seederCount = src->currentTracker->seederCount;
1712    tgt->currentTracker->leecherCount = src->currentTracker->leecherCount;
1713    tgt->currentTracker->downloadCount = src->currentTracker->downloadCount;
1714    tgt->currentTracker->downloaderCount = src->currentTracker->downloaderCount;
1715}
1716
1717static void
1718copy_tier_attributes (struct tr_torrent_tiers * tt, const tr_tier * src)
1719{
1720    int i, j;
1721    bool found = false;
1722
1723    /* find a tier (if any) which has a match for src->currentTracker */
1724    for (i=0; !found && i<tt->tier_count; ++i)
1725        for (j=0; !found && j<tt->tiers[i].tracker_count; ++j)
1726            if ((found = !tr_strcmp0 (src->currentTracker->announce, tt->tiers[i].trackers[j].announce)))
1727                copy_tier_attributes_impl (&tt->tiers[i], j, src);
1728}
1729
1730void
1731tr_announcerResetTorrent (tr_announcer * announcer UNUSED, tr_torrent * tor)
1732{
1733    int i;
1734    const time_t now = tr_time ();
1735    struct tr_torrent_tiers * tt = tor->tiers;
1736    tr_torrent_tiers old = *tt;
1737
1738    assert (tt != NULL);
1739
1740    /* remove the old tiers / trackers */
1741    tt->tiers = NULL;
1742    tt->trackers = NULL;
1743    tt->tier_count = 0;
1744    tt->tracker_count = 0;
1745
1746    /* create the new tiers / trackers */
1747    addTorrentToTier (tt, tor);
1748
1749    /* copy the old tiers' states into their replacements */
1750    for (i=0; i<old.tier_count; ++i)
1751        if (old.tiers[i].currentTracker != NULL)
1752            copy_tier_attributes (tt, &old.tiers[i]);
1753
1754    /* kickstart any tiers that didn't get started */
1755    if (tor->isRunning)
1756        for (i=0; i<tt->tier_count; ++i)
1757            if (!tt->tiers[i].wasCopied)
1758                tier_announce_event_push (&tt->tiers[i], TR_ANNOUNCE_EVENT_STARTED, now);
1759
1760    /* cleanup */
1761    tiersDestruct (&old);
1762}
Note: See TracBrowser for help on using the repository browser.