source: trunk/libtransmission/announcer.c @ 12507

Last change on this file since 12507 was 12507, checked in by jordan, 10 years ago

(trunk libT) modify how the "stops" sorted container is used s.t. the container's uniqueness requirements are met.

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