source: trunk/libtransmission/announcer.c @ 13155

Last change on this file since 13155 was 13155, checked in by livings124, 9 years ago

#4704 req->left = tr_cpLeftUntilComplete( &tor->completion ) should end with a semicolon

  • Property svn:keywords set to Date Rev Author Id
File size: 51.9 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 13155 2012-01-11 22:31:02Z livings124 $
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        struct evbuffer * buf = evbuffer_new( );
757
758        tier_build_log_name( tier, name, sizeof( name ) );
759        for( i=0; i<tier->announce_event_count; ++i )
760        {
761            const tr_announce_event e = tier->announce_events[i];
762            const char * str = tr_announce_event_get_string( e );
763            evbuffer_add_printf( buf, "[%d:%s]", i, str );
764        }
765
766        tr_deepLog( __FILE__, __LINE__, name, "announce queue is %s", evbuffer_pullup( buf, -1 ) );
767        evbuffer_free( buf );
768    }
769}
770
771static void
772tier_announce_remove_trailing( tr_tier * tier, tr_announce_event e )
773{
774    while( ( tier->announce_event_count > 0 )
775        && ( tier->announce_events[tier->announce_event_count-1] == e ) )
776        --tier->announce_event_count;
777}
778
779static void
780tier_announce_event_push( tr_tier            * tier,
781                          tr_announce_event    e,
782                          time_t               announceAt )
783{
784    int i;
785
786    assert( tier != NULL );
787
788    dbgmsg_tier_announce_queue( tier );
789    dbgmsg( tier, "queued \"%s\"", tr_announce_event_get_string( e ) );
790
791    if( tier->announce_event_count > 0 )
792    {
793        /* special case #1: if we're adding a "stopped" event,
794         * dump everything leading up to it except "completed" */
795        if( e == TR_ANNOUNCE_EVENT_STOPPED ) {
796            bool has_completed = false;
797            const tr_announce_event c = TR_ANNOUNCE_EVENT_COMPLETED;
798            for( i=0; !has_completed && i<tier->announce_event_count; ++i )
799                has_completed = c == tier->announce_events[i];
800            tier->announce_event_count = 0;
801            if( has_completed )
802                tier->announce_events[tier->announce_event_count++] = c;
803        }
804
805        /* special case #2: dump all empty strings leading up to this event */
806        tier_announce_remove_trailing( tier, TR_ANNOUNCE_EVENT_NONE );
807
808        /* special case #3: no consecutive duplicates */
809        tier_announce_remove_trailing( tier, e );
810    }
811
812    /* make room in the array for another event */
813    if( tier->announce_event_alloc <= tier->announce_event_count ) {
814        tier->announce_event_alloc += 4;
815        tier->announce_events = tr_renew( tr_announce_event,
816                                          tier->announce_events,
817                                          tier->announce_event_alloc );
818    }
819
820    /* add it */
821    tier->announce_events[tier->announce_event_count++] = e;
822    tier->announceAt = announceAt;
823
824    dbgmsg_tier_announce_queue( tier );
825    dbgmsg( tier, "announcing in %d seconds", (int)difftime(announceAt,tr_time()) );
826}
827
828static tr_announce_event
829tier_announce_event_pull( tr_tier * tier )
830{
831    const tr_announce_event e = tier->announce_events[0];
832
833    tr_removeElementFromArray( tier->announce_events,
834                               0, sizeof( tr_announce_event ),
835                               tier->announce_event_count-- );
836
837    return e;
838}
839
840static void
841torrentAddAnnounce( tr_torrent * tor, tr_announce_event e, time_t announceAt )
842{
843    int i;
844    struct tr_torrent_tiers * tt = tor->tiers;
845
846    /* walk through each tier and tell them to announce */
847    for( i=0; i<tt->tier_count; ++i )
848        tier_announce_event_push( &tt->tiers[i], e, announceAt );
849}
850
851void
852tr_announcerTorrentStarted( tr_torrent * tor )
853{
854    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STARTED, tr_time( ) );
855}
856void
857tr_announcerManualAnnounce( tr_torrent * tor )
858{
859    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_NONE, tr_time( ) );
860}
861void
862tr_announcerTorrentStopped( tr_torrent * tor )
863{
864    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time( ) );
865}
866void
867tr_announcerTorrentCompleted( tr_torrent * tor )
868{
869    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time( ) );
870}
871void
872tr_announcerChangeMyPort( tr_torrent * tor )
873{
874    tr_announcerTorrentStarted( tor );
875}
876
877/***
878****
879***/
880
881void
882tr_announcerAddBytes( tr_torrent * tor, int type, uint32_t byteCount )
883{
884    int i;
885    struct tr_torrent_tiers * tt = tor->tiers;
886
887    assert( tr_isTorrent( tor ) );
888    assert( type==TR_ANN_UP || type==TR_ANN_DOWN || type==TR_ANN_CORRUPT );
889
890    for( i=0; i<tt->tier_count; ++i )
891        tt->tiers[i].byteCounts[ type ] += byteCount;
892}
893
894/***
895****
896***/
897
898static tr_announce_request *
899announce_request_new( const tr_announcer  * announcer,
900                      const tr_torrent    * tor,
901                      const tr_tier       * tier,
902                      tr_announce_event     event )
903{
904    tr_announce_request * req = tr_new0( tr_announce_request, 1 );
905    req->port = tr_sessionGetPublicPeerPort( announcer->session );
906    req->url = tr_strdup( tier->currentTracker->announce );
907    req->tracker_id_str = tr_strdup( tier->currentTracker->tracker_id_str );
908    memcpy( req->info_hash, tor->info.hash, SHA_DIGEST_LENGTH );
909    memcpy( req->peer_id, tor->peer_id, PEER_ID_LEN );
910    req->up = tier->byteCounts[TR_ANN_UP];
911    req->down = tier->byteCounts[TR_ANN_DOWN];
912    req->corrupt = tier->byteCounts[TR_ANN_CORRUPT];
913    req->left = tr_cpLeftUntilComplete( &tor->completion );
914    req->event = event;
915    req->numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : NUMWANT;
916    req->key = announcer->key;
917    req->partial_seed = tr_cpGetStatus( &tor->completion ) == TR_PARTIAL_SEED;
918    tier_build_log_name( tier, req->log_name, sizeof( req->log_name ) );
919    return req;
920}
921
922static void announce_request_free( tr_announce_request * req );
923
924void
925tr_announcerRemoveTorrent( tr_announcer * announcer, tr_torrent * tor )
926{
927    struct tr_torrent_tiers * tt = tor->tiers;
928
929    if( tt != NULL )
930    {
931        int i;
932        for( i=0; i<tt->tier_count; ++i )
933        {
934            tr_tier * tier = &tt->tiers[i];
935            if( tier->isRunning )
936            {
937                const tr_announce_event e = TR_ANNOUNCE_EVENT_STOPPED;
938                tr_announce_request * req = announce_request_new( announcer, tor, tier, e );
939
940                if( tr_ptrArrayFindSorted( &announcer->stops, req, compareStops ) != NULL )
941                    announce_request_free( req );
942                else
943                    tr_ptrArrayInsertSorted( &announcer->stops, req, compareStops );
944            }
945        }
946
947        tiersFree( tor->tiers );
948        tor->tiers = NULL;
949    }
950}
951
952static int
953getRetryInterval( const tr_tracker * t )
954{
955    int minutes;
956    const unsigned int jitter_seconds = tr_cryptoWeakRandInt( 60 );
957    switch( t->consecutiveFailures ) {
958        case 0:  minutes =   1; break;
959        case 1:  minutes =   5; break;
960        case 2:  minutes =  15; break;
961        case 3:  minutes =  30; break;
962        case 4:  minutes =  60; break;
963        default: minutes = 120; break;
964    }
965    return ( minutes * 60 ) + jitter_seconds;
966}
967
968struct announce_data
969{
970    int tierId;
971    time_t timeSent;
972    tr_announce_event event;
973    tr_session * session;
974
975    /** If the request succeeds, the value for tier's "isRunning" flag */
976    bool isRunningOnSuccess;
977};
978
979static void
980on_announce_error( tr_tier * tier, const char * err, tr_announce_event e )
981{
982    int interval;
983
984    /* increment the error count */
985    if( tier->currentTracker != NULL )
986        ++tier->currentTracker->consecutiveFailures;
987
988    /* set the error message */
989    dbgmsg( tier, "%s", err );
990    tr_torinf( tier->tor, "%s", err );
991    tr_strlcpy( tier->lastAnnounceStr, err, sizeof( tier->lastAnnounceStr ) );
992
993    /* switch to the next tracker */
994    tierIncrementTracker( tier );
995
996    /* schedule a reannounce */
997    interval = getRetryInterval( tier->currentTracker );
998    dbgmsg( tier, "Retrying announce in %d seconds.", interval );
999    tr_torinf( tier->tor, "Retrying announce in %d seconds.", interval );
1000    tier_announce_event_push( tier, e, tr_time( ) + interval );
1001}
1002
1003static void
1004on_announce_done( const tr_announce_response  * response,
1005                  void                        * vdata )
1006{
1007    struct announce_data * data = vdata;
1008    tr_announcer * announcer = data->session->announcer;
1009    tr_tier * tier = getTier( announcer, response->info_hash, data->tierId );
1010    const time_t now = tr_time( );
1011    const tr_announce_event event = data->event;
1012
1013    if( announcer )
1014        ++announcer->slotsAvailable;
1015
1016    if( tier != NULL )
1017    {
1018        tr_tracker * tracker;
1019
1020        dbgmsg( tier, "Got announce response: "
1021                      "connected:%d "
1022                      "timeout:%d "
1023                      "seeders:%d "
1024                      "leechers:%d "
1025                      "downloads:%d "
1026                      "interval:%d "
1027                      "min_interval:%d "
1028                      "tracker_id_str:%s "
1029                      "pex:%zu "
1030                      "pex6:%zu "
1031                      "err:%s "
1032                      "warn:%s",
1033                      (int)response->did_connect,
1034                      (int)response->did_timeout,
1035                      response->seeders,
1036                      response->leechers,
1037                      response->downloads,
1038                      response->interval,
1039                      response->min_interval,
1040                      response->tracker_id_str ? response->tracker_id_str : "none",
1041                      response->pex_count,
1042                      response->pex6_count,
1043                      response->errmsg ? response->errmsg : "none",
1044                      response->warning ? response->warning : "none" );
1045
1046        tier->lastAnnounceTime = now;
1047        tier->lastAnnounceTimedOut = response->did_timeout;
1048        tier->lastAnnounceSucceeded = false;
1049        tier->isAnnouncing = false;
1050        tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1051
1052        if( !response->did_connect )
1053        {
1054            on_announce_error( tier, _( "Could not connect to tracker" ), event );
1055        }
1056        else if( response->did_timeout )
1057        {
1058            on_announce_error( tier, _( "Tracker did not respond" ), event );
1059        }
1060        else if( response->errmsg )
1061        {
1062            /* If the torrent's only tracker returned an error, publish it.
1063               Don't bother publishing if there are other trackers -- it's
1064               all too common for people to load up dozens of dead trackers
1065               in a torrent's metainfo... */
1066            if( tier->tor->info.trackerCount < 2 )
1067                publishError( tier, response->errmsg );
1068
1069            on_announce_error( tier, response->errmsg, event );
1070        }
1071        else
1072        {
1073            int i;
1074            const char * str;
1075            int scrape_fields = 0;
1076            int seeders = 0;
1077            int leechers = 0;
1078            int downloads = 0;
1079            const bool isStopped = event == TR_ANNOUNCE_EVENT_STOPPED;
1080
1081            publishErrorClear( tier );
1082
1083            if(( tracker = tier->currentTracker ))
1084            {
1085                tracker->consecutiveFailures = 0;
1086
1087                if( response->seeders >= 0 )
1088                {
1089                    tracker->seederCount = seeders = response->seeders;
1090                    ++scrape_fields;
1091                }
1092
1093                if( response->leechers >= 0 )
1094                {
1095                    tracker->leecherCount = leechers = response->leechers;
1096                    ++scrape_fields;
1097                }
1098                if( response->downloads >= 0 )
1099                {
1100                    tracker->downloadCount = downloads = response->downloads;
1101                    ++scrape_fields;
1102                }
1103
1104                if(( str = response->tracker_id_str ))
1105                {
1106                    tr_free( tracker->tracker_id_str );
1107                    tracker->tracker_id_str = tr_strdup( str );
1108                }
1109            }
1110
1111            if(( str = response->warning ))
1112            {
1113                tr_strlcpy( tier->lastAnnounceStr, str,
1114                            sizeof( tier->lastAnnounceStr ) );
1115                dbgmsg( tier, "tracker gave \"%s\"", str );
1116                publishWarning( tier, str );
1117            }
1118            else
1119            {
1120                tr_strlcpy( tier->lastAnnounceStr, _( "Success" ),
1121                            sizeof( tier->lastAnnounceStr ) );
1122            }
1123
1124            if(( i = response->min_interval ))
1125                tier->announceMinIntervalSec = i;
1126
1127            if(( i = response->interval ))
1128                tier->announceIntervalSec = i;
1129
1130            if( response->pex_count > 0 )
1131                publishPeersPex( tier, seeders, leechers,
1132                                 response->pex, response->pex_count );
1133
1134            if( response->pex6_count > 0 )
1135                publishPeersPex( tier, seeders, leechers,
1136                                 response->pex6, response->pex6_count );
1137
1138            tier->isRunning = data->isRunningOnSuccess;
1139
1140            /* if the tracker included scrape fields in its announce response,
1141               then a separate scrape isn't needed */
1142            if( scrape_fields >= 3 )
1143            {
1144                tr_tordbg( tier->tor, "Announce response contained scrape info; "
1145                                      "rescheduling next scrape to %d seconds from now.",
1146                                      tier->scrapeIntervalSec );
1147                tier->scrapeAt = get_next_scrape_time( announcer->session, tier, tier->scrapeIntervalSec );
1148                tier->lastScrapeTime = now;
1149                tier->lastScrapeSucceeded = true;
1150            }
1151            else if( tier->lastScrapeTime + tier->scrapeIntervalSec <= now )
1152            {
1153                tier->scrapeAt = get_next_scrape_time( announcer->session, tier, 0 );
1154            }
1155
1156            tier->lastAnnounceSucceeded = true;
1157            tier->lastAnnouncePeerCount = response->pex_count
1158                                        + response->pex6_count;
1159
1160            if( isStopped )
1161            {
1162                /* now that we've successfully stopped the torrent,
1163                 * we can reset the up/down/corrupt count we've kept
1164                 * for this tracker */
1165                tier->byteCounts[ TR_ANN_UP ] = 0;
1166                tier->byteCounts[ TR_ANN_DOWN ] = 0;
1167                tier->byteCounts[ TR_ANN_CORRUPT ] = 0;
1168            }
1169
1170            if( !isStopped && !tier->announce_event_count )
1171            {
1172                /* the queue is empty, so enqueue a perodic update */
1173                i = tier->announceIntervalSec;
1174                dbgmsg( tier, "Sending periodic reannounce in %d seconds", i );
1175                tier_announce_event_push( tier, TR_ANNOUNCE_EVENT_NONE, now + i );
1176            }
1177        }
1178    }
1179
1180    tr_free( data );
1181}
1182
1183static void
1184announce_request_free( tr_announce_request * req )
1185{
1186    tr_free( req->tracker_id_str );
1187    tr_free( req->url );
1188    tr_free( req );
1189}
1190
1191static void
1192announce_request_delegate( tr_announcer               * announcer,
1193                           tr_announce_request        * request,
1194                           tr_announce_response_func  * callback,
1195                           void                       * callback_data )
1196{
1197    tr_session * session = announcer->session;
1198
1199    if( !memcmp( request->url, "http", 4 ) )
1200        tr_tracker_http_announce( session, request, callback, callback_data );
1201    else if( !memcmp( request->url, "udp://", 6 ) )
1202        tr_tracker_udp_announce( session, request, callback, callback_data );
1203    else
1204        tr_err( "Unsupported url: %s", request->url );
1205
1206    announce_request_free( request );
1207}
1208
1209static void
1210tierAnnounce( tr_announcer * announcer, tr_tier * tier )
1211{
1212    tr_announce_event announce_event;
1213    tr_announce_request * req;
1214    struct announce_data * data;
1215    const tr_torrent * tor = tier->tor;
1216    const time_t now = tr_time( );
1217
1218    assert( !tier->isAnnouncing );
1219    assert( tier->announce_event_count > 0 );
1220
1221    announce_event = tier_announce_event_pull( tier );
1222    req = announce_request_new( announcer, tor, tier, announce_event );
1223
1224    data = tr_new0( struct announce_data, 1 );
1225    data->session = announcer->session;
1226    data->tierId = tier->key;
1227    data->isRunningOnSuccess = tor->isRunning;
1228    data->timeSent = now;
1229    data->event = announce_event;
1230
1231    tier->isAnnouncing = true;
1232    tier->lastAnnounceStartTime = now;
1233    --announcer->slotsAvailable;
1234
1235    announce_request_delegate( announcer, req, on_announce_done, data );
1236}
1237
1238/***
1239****
1240****  SCRAPE
1241****
1242***/
1243
1244static void
1245on_scrape_error( tr_session * session, tr_tier * tier, const char * errmsg )
1246{
1247    int interval;
1248
1249    /* increment the error count */
1250    if( tier->currentTracker != NULL )
1251        ++tier->currentTracker->consecutiveFailures;
1252
1253    /* set the error message */
1254    dbgmsg( tier, "Scrape error: %s", errmsg );
1255    tr_torinf( tier->tor, "Scrape error: %s", errmsg );
1256    tr_strlcpy( tier->lastScrapeStr, errmsg, sizeof( tier->lastScrapeStr ) );
1257
1258    /* switch to the next tracker */
1259    tierIncrementTracker( tier );
1260
1261    /* schedule a rescrape */
1262    interval = getRetryInterval( tier->currentTracker );
1263    dbgmsg( tier, "Retrying scrape in %zu seconds.", (size_t)interval );
1264    tr_torinf( tier->tor, "Retrying scrape in %zu seconds.", (size_t)interval );
1265    tier->lastScrapeSucceeded = false;
1266    tier->scrapeAt = get_next_scrape_time( session, tier, interval );
1267}
1268
1269static tr_tier *
1270find_tier( tr_torrent * tor, const char * scrape )
1271{
1272    int i;
1273    struct tr_torrent_tiers * tt = tor->tiers;
1274
1275    for( i=0; tt && i<tt->tier_count; ++i ) {
1276        const tr_tracker * const tracker = tt->tiers[i].currentTracker;
1277        if( tracker && !tr_strcmp0( scrape, tracker->scrape ) )
1278            return &tt->tiers[i];
1279    }
1280
1281    return NULL;
1282}
1283
1284static void
1285on_scrape_done( const tr_scrape_response * response, void * vsession )
1286{
1287    int i;
1288    const time_t now = tr_time( );
1289    tr_session * session = vsession;
1290    tr_announcer * announcer = session->announcer;
1291
1292    for( i=0; i<response->row_count; ++i )
1293    {
1294        const struct tr_scrape_response_row * row = &response->rows[i];
1295        tr_torrent * tor = tr_torrentFindFromHash( session, row->info_hash );
1296
1297        if( tor != NULL )
1298        {
1299            tr_tier * tier = find_tier( tor, response->url );
1300
1301            if( tier != NULL )
1302            {
1303                dbgmsg( tier, "scraped url:%s -- "
1304                              "did_connect:%d "
1305                              "did_timeout:%d "
1306                              "seeders:%d "
1307                              "leechers:%d "
1308                              "downloads:%d "
1309                              "downloaders:%d "
1310                              "min_request_interval:%d "
1311                              "err:%s ",
1312                              response->url,
1313                              (int)response->did_connect,
1314                              (int)response->did_timeout,
1315                              row->seeders,
1316                              row->leechers,
1317                              row->downloads,
1318                              row->downloaders,
1319                              response->min_request_interval,
1320                              response->errmsg ? response->errmsg : "none" );
1321
1322                tier->isScraping = false;
1323                tier->lastScrapeTime = now;
1324                tier->lastScrapeSucceeded = false;
1325                tier->lastScrapeTimedOut = response->did_timeout;
1326
1327                if( !response->did_connect )
1328                {
1329                    on_scrape_error( session, tier, _( "Could not connect to tracker" ) );
1330                }
1331                else if( response->did_timeout )
1332                {
1333                    on_scrape_error( session, tier, _( "Tracker did not respond" ) );
1334                }
1335                else if( response->errmsg )
1336                {
1337                    on_scrape_error( session, tier, response->errmsg );
1338                }
1339                else
1340                {
1341                    tr_tracker * tracker;
1342
1343                    tier->lastScrapeSucceeded = true;
1344                    tier->scrapeIntervalSec = MAX( DEFAULT_SCRAPE_INTERVAL_SEC,
1345                                                   response->min_request_interval );
1346                    tier->scrapeAt = get_next_scrape_time( session, tier, tier->scrapeIntervalSec );
1347                    tr_tordbg( tier->tor, "Scrape successful. Rescraping in %d seconds.",
1348                               tier->scrapeIntervalSec );
1349
1350                    if(( tracker = tier->currentTracker ))
1351                    {
1352                        if( row->seeders >= 0 )
1353                            tracker->seederCount = row->seeders;
1354                        if( row->leechers >= 0 )
1355                            tracker->leecherCount = row->leechers;
1356                        if( row->downloads >= 0 )
1357                            tracker->downloadCount = row->downloads;
1358                        tracker->downloaderCount = row->downloaders;
1359                        tracker->consecutiveFailures = 0;
1360                    }
1361                }
1362            }
1363        }
1364    }
1365
1366    if( announcer )
1367        ++announcer->slotsAvailable;
1368}
1369
1370static void
1371scrape_request_delegate( tr_announcer             * announcer,
1372                         const tr_scrape_request  * request,
1373                         tr_scrape_response_func  * callback,
1374                         void                     * callback_data )
1375{
1376    tr_session * session = announcer->session;
1377
1378    if( !memcmp( request->url, "http", 4 ) )
1379        tr_tracker_http_scrape( session, request, callback, callback_data );
1380    else if( !memcmp( request->url, "udp://", 6 ) )
1381        tr_tracker_udp_scrape( session, request, callback, callback_data );
1382    else
1383        tr_err( "Unsupported url: %s", request->url );
1384}
1385
1386static void
1387multiscrape( tr_announcer * announcer, tr_ptrArray * tiers )
1388{
1389    int i;
1390    int request_count = 0;
1391    const time_t now = tr_time( );
1392    const int tier_count = tr_ptrArraySize( tiers );
1393    const int max_request_count = MIN( announcer->slotsAvailable, tier_count );
1394    tr_scrape_request * requests = tr_new0( tr_scrape_request, max_request_count );
1395
1396    /* batch as many info_hashes into a request as we can */
1397    for( i=0; i<tier_count; ++i )
1398    {
1399        int j;
1400        tr_tier * tier = tr_ptrArrayNth( tiers, i );
1401        char * url = tier->currentTracker->scrape;
1402        const uint8_t * hash = tier->tor->info.hash;
1403
1404        /* if there's a request with this scrape URL and a free slot, use it */
1405        for( j=0; j<request_count; ++j )
1406        {
1407            tr_scrape_request * req = &requests[j];
1408
1409            if( req->info_hash_count >= TR_MULTISCRAPE_MAX )
1410                continue;
1411            if( tr_strcmp0( req->url, url ) )
1412                continue;
1413
1414            memcpy( req->info_hash[req->info_hash_count++], hash, SHA_DIGEST_LENGTH );
1415            tier->isScraping = true;
1416            tier->lastScrapeStartTime = now;
1417            break;
1418        }
1419
1420        /* otherwise, if there's room for another request, build a new one */
1421        if( ( j==request_count ) && ( request_count < max_request_count ) )
1422        {
1423            tr_scrape_request * req = &requests[request_count++];
1424            req->url = url;
1425            tier_build_log_name( tier, req->log_name, sizeof( req->log_name ) );
1426
1427            memcpy( req->info_hash[req->info_hash_count++], hash, SHA_DIGEST_LENGTH );
1428            tier->isScraping = true;
1429            tier->lastScrapeStartTime = now;
1430        }
1431    }
1432
1433    /* send the requests we just built */
1434    for( i=0; i<request_count; ++i )
1435        scrape_request_delegate( announcer, &requests[i], on_scrape_done, announcer->session );
1436
1437    /* cleanup */
1438    tr_free( requests );
1439}
1440
1441static void
1442flushCloseMessages( tr_announcer * announcer )
1443{
1444    int i;
1445    const int n = tr_ptrArraySize( &announcer->stops );
1446
1447    for( i=0; i<n; ++i )
1448        announce_request_delegate( announcer, tr_ptrArrayNth( &announcer->stops, i ), NULL, NULL );
1449
1450    tr_ptrArrayClear( &announcer->stops );
1451}
1452
1453static bool
1454tierNeedsToAnnounce( const tr_tier * tier, const time_t now )
1455{
1456    return !tier->isAnnouncing
1457        && !tier->isScraping
1458        && ( tier->announceAt != 0 )
1459        && ( tier->announceAt <= now )
1460        && ( tier->announce_event_count > 0 );
1461}
1462
1463static bool
1464tierNeedsToScrape( const tr_tier * tier, const time_t now )
1465{
1466    return ( !tier->isScraping )
1467        && ( tier->scrapeAt != 0 )
1468        && ( tier->scrapeAt <= now )
1469        && ( tier->currentTracker != NULL )
1470        && ( tier->currentTracker->scrape != NULL );
1471}
1472
1473static int
1474compareTiers( const void * va, const void * vb )
1475{
1476    int ret;
1477    const tr_tier * a = *(const tr_tier**)va;
1478    const tr_tier * b = *(const tr_tier**)vb;
1479
1480    /* primary key: larger stats come before smaller */
1481    ret = compareTransfer( a->byteCounts[TR_ANN_UP], a->byteCounts[TR_ANN_DOWN],
1482                           b->byteCounts[TR_ANN_UP], b->byteCounts[TR_ANN_DOWN] );
1483
1484    /* secondary key: announcements that have been waiting longer go first */
1485    if( !ret && ( a->announceAt != b->announceAt ) )
1486        ret = a->announceAt < b->announceAt ? -1 : 1;
1487
1488    return ret;
1489}
1490
1491static void
1492announceMore( tr_announcer * announcer )
1493{
1494    int i;
1495    int n;
1496    tr_torrent * tor;
1497    tr_ptrArray announceMe = TR_PTR_ARRAY_INIT;
1498    tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT;
1499    const time_t now = tr_time( );
1500
1501    dbgmsg( NULL, "announceMore: slotsAvailable is %d", announcer->slotsAvailable );
1502
1503    if( announcer->slotsAvailable < 1 )
1504        return;
1505
1506    /* build a list of tiers that need to be announced */
1507    tor = NULL;
1508    while(( tor = tr_torrentNext( announcer->session, tor ))) {
1509        struct tr_torrent_tiers * tt = tor->tiers;
1510        for( i=0; tt && i<tt->tier_count; ++i ) {
1511            tr_tier * tier = &tt->tiers[i];
1512            if( tierNeedsToAnnounce( tier, now ) )
1513                tr_ptrArrayAppend( &announceMe, tier );
1514            else if( tierNeedsToScrape( tier, now ) )
1515                tr_ptrArrayAppend( &scrapeMe, tier );
1516        }
1517    }
1518
1519    /* if there are more tiers than slots available, prioritize */
1520    n = tr_ptrArraySize( &announceMe );
1521    if( n > announcer->slotsAvailable )
1522        qsort( tr_ptrArrayBase(&announceMe), n, sizeof(tr_tier*), compareTiers );
1523
1524    /* announce some */
1525    n = MIN( tr_ptrArraySize( &announceMe ), announcer->slotsAvailable );
1526    for( i=0; i<n; ++i ) {
1527        tr_tier * tier = tr_ptrArrayNth( &announceMe, i );
1528        tr_tordbg( tier->tor, "%s", "Announcing to tracker" );
1529        dbgmsg( tier, "announcing tier %d of %d", i, n );
1530        tierAnnounce( announcer, tier );
1531    }
1532
1533    /* scrape some */
1534    multiscrape( announcer, &scrapeMe );
1535
1536    /* cleanup */
1537    tr_ptrArrayDestruct( &scrapeMe, NULL );
1538    tr_ptrArrayDestruct( &announceMe, NULL );
1539}
1540
1541static void
1542onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer )
1543{
1544    tr_announcer * announcer = vannouncer;
1545    tr_session * session = announcer->session;
1546    const bool is_closing = session->isClosed;
1547    const time_t now = tr_time( );
1548
1549    tr_sessionLock( session );
1550
1551    /* maybe send out some "stopped" messages for closed torrents */
1552    flushCloseMessages( announcer );
1553
1554    /* maybe send out some announcements to trackers */
1555    if( !is_closing )
1556        announceMore( announcer );
1557
1558    /* TAU upkeep */
1559    if( announcer->tauUpkeepAt <= now ) {
1560        announcer->tauUpkeepAt = now + TAU_UPKEEP_INTERVAL_SECS;
1561        tr_tracker_udp_upkeep( session );
1562    }
1563
1564    /* set up the next timer */
1565    tr_timerAdd( announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
1566
1567    tr_sessionUnlock( session );
1568}
1569
1570/***
1571****
1572***/
1573
1574tr_tracker_stat *
1575tr_announcerStats( const tr_torrent * torrent, int * setmeTrackerCount )
1576{
1577    int i;
1578    int out = 0;
1579    tr_tracker_stat * ret;
1580    struct tr_torrent_tiers * tt;
1581    const time_t now = tr_time( );
1582
1583    assert( tr_isTorrent( torrent ) );
1584    assert( tr_torrentIsLocked( torrent ) );
1585
1586    tt = torrent->tiers;
1587
1588    /* alloc the stats */
1589    *setmeTrackerCount = tt->tracker_count;
1590    ret = tr_new0( tr_tracker_stat, tt->tracker_count );
1591
1592    /* populate the stats */
1593    for( i=0; i<tt->tier_count; ++i )
1594    {
1595        int j;
1596        const tr_tier * const tier = &tt->tiers[i];
1597        for( j=0; j<tier->tracker_count; ++j )
1598        {
1599            const tr_tracker * const tracker = &tier->trackers[j];
1600            tr_tracker_stat * st = &ret[out++];
1601
1602            st->id = tracker->id;
1603            tr_strlcpy( st->host, tracker->key, sizeof( st->host ) );
1604            tr_strlcpy( st->announce, tracker->announce, sizeof( st->announce ) );
1605            st->tier = i;
1606            st->isBackup = tracker != tier->currentTracker;
1607            st->lastScrapeStartTime = tier->lastScrapeStartTime;
1608            if( tracker->scrape )
1609                tr_strlcpy( st->scrape, tracker->scrape, sizeof( st->scrape ) );
1610            else
1611                st->scrape[0] = '\0';
1612
1613            st->seederCount = tracker->seederCount;
1614            st->leecherCount = tracker->leecherCount;
1615            st->downloadCount = tracker->downloadCount;
1616
1617            if( st->isBackup )
1618            {
1619                st->scrapeState = TR_TRACKER_INACTIVE;
1620                st->announceState = TR_TRACKER_INACTIVE;
1621                st->nextScrapeTime = 0;
1622                st->nextAnnounceTime = 0;
1623            }
1624            else
1625            {
1626                if(( st->hasScraped = tier->lastScrapeTime != 0 )) {
1627                    st->lastScrapeTime = tier->lastScrapeTime;
1628                    st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
1629                    st->lastScrapeTimedOut = tier->lastScrapeTimedOut;
1630                    tr_strlcpy( st->lastScrapeResult, tier->lastScrapeStr,
1631                                sizeof( st->lastScrapeResult ) );
1632                }
1633
1634                if( tier->isScraping )
1635                    st->scrapeState = TR_TRACKER_ACTIVE;
1636                else if( !tier->scrapeAt )
1637                    st->scrapeState = TR_TRACKER_INACTIVE;
1638                else if( tier->scrapeAt > now )
1639                {
1640                    st->scrapeState = TR_TRACKER_WAITING;
1641                    st->nextScrapeTime = tier->scrapeAt;
1642                }
1643                else
1644                    st->scrapeState = TR_TRACKER_QUEUED;
1645
1646                st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
1647
1648                if(( st->hasAnnounced = tier->lastAnnounceTime != 0 )) {
1649                    st->lastAnnounceTime = tier->lastAnnounceTime;
1650                    tr_strlcpy( st->lastAnnounceResult, tier->lastAnnounceStr,
1651                                sizeof( st->lastAnnounceResult ) );
1652                    st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded;
1653                    st->lastAnnounceTimedOut = tier->lastAnnounceTimedOut;
1654                    st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
1655                }
1656
1657                if( tier->isAnnouncing )
1658                    st->announceState = TR_TRACKER_ACTIVE;
1659                else if( !torrent->isRunning || !tier->announceAt )
1660                    st->announceState = TR_TRACKER_INACTIVE;
1661                else if( tier->announceAt > now )
1662                {
1663                    st->announceState = TR_TRACKER_WAITING;
1664                    st->nextAnnounceTime = tier->announceAt;
1665                }
1666                else
1667                    st->announceState = TR_TRACKER_QUEUED;
1668            }
1669        }
1670    }
1671
1672    return ret;
1673}
1674
1675void
1676tr_announcerStatsFree( tr_tracker_stat * trackers,
1677                       int trackerCount UNUSED )
1678{
1679    tr_free( trackers );
1680}
1681
1682/***
1683****
1684***/
1685
1686static void
1687copy_tier_attributes_impl( struct tr_tier * tgt, int trackerIndex, const tr_tier * src )
1688{
1689    const tr_tier keep = *tgt;
1690
1691    /* sanity clause */
1692    assert( trackerIndex < tgt->tracker_count );
1693    assert( !tr_strcmp0( tgt->trackers[trackerIndex].announce, src->currentTracker->announce ) );
1694
1695    /* bitwise copy will handle most of tr_tier's fields... */
1696    *tgt = *src;
1697
1698    /* ...fix the fields that can't be cleanly bitwise-copied */
1699    tgt->wasCopied = true;
1700    tgt->trackers = keep.trackers;
1701    tgt->tracker_count = keep.tracker_count;
1702    tgt->announce_events = tr_memdup( src->announce_events, sizeof( tr_announce_event ) * src->announce_event_count );
1703    tgt->announce_event_count = src->announce_event_count;
1704    tgt->announce_event_alloc = src->announce_event_count;
1705    tgt->currentTrackerIndex = trackerIndex;
1706    tgt->currentTracker = &tgt->trackers[trackerIndex];
1707    tgt->currentTracker->seederCount = src->currentTracker->seederCount;
1708    tgt->currentTracker->leecherCount = src->currentTracker->leecherCount;
1709    tgt->currentTracker->downloadCount = src->currentTracker->downloadCount;
1710    tgt->currentTracker->downloaderCount = src->currentTracker->downloaderCount;
1711}
1712
1713static void
1714copy_tier_attributes( struct tr_torrent_tiers * tt, const tr_tier * src )
1715{
1716    int i, j;
1717    bool found = false;
1718
1719    /* find a tier (if any) which has a match for src->currentTracker */
1720    for( i=0; !found && i<tt->tier_count; ++i )
1721        for( j=0; !found && j<tt->tiers[i].tracker_count; ++j )
1722            if(( found = !tr_strcmp0( src->currentTracker->announce, tt->tiers[i].trackers[j].announce )))
1723                copy_tier_attributes_impl( &tt->tiers[i], j, src );
1724}
1725
1726void
1727tr_announcerResetTorrent( tr_announcer * announcer UNUSED, tr_torrent * tor )
1728{
1729    int i;
1730    const time_t now = tr_time( );
1731    struct tr_torrent_tiers * tt = tor->tiers;
1732    tr_torrent_tiers old = *tt;
1733
1734    assert( tt != NULL );
1735
1736    /* remove the old tiers / trackers */
1737    tt->tiers = NULL;
1738    tt->trackers = NULL;
1739    tt->tier_count = 0;
1740    tt->tracker_count = 0;
1741
1742    /* create the new tiers / trackers */
1743    addTorrentToTier( tt, tor );
1744
1745    /* copy the old tiers' states into their replacements */
1746    for( i=0; i<old.tier_count; ++i )
1747        if( old.tiers[i].currentTracker != NULL )
1748            copy_tier_attributes( tt, &old.tiers[i] );
1749
1750    /* kickstart any tiers that didn't get started */
1751    if( tor->isRunning )
1752        for( i=0; i<tt->tier_count; ++i )
1753            if( !tt->tiers[i].wasCopied )
1754                tier_announce_event_push( &tt->tiers[i], TR_ANNOUNCE_EVENT_STARTED, now );
1755
1756    /* cleanup */
1757    tiersDestruct( &old );
1758}
Note: See TracBrowser for help on using the repository browser.