source: trunk/libtransmission/announcer.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

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