source: trunk/libtransmission/announcer.c @ 12181

Last change on this file since 12181 was 12181, checked in by jordan, 11 years ago

(trunk libT) move tr_dhtUpkeep() out of the announcer module

During shutdown, we can stop DHT almost immediately, but need to leave the announcer running for the DHT tracker event=stopped messages. So it doesn't make sense to keep them on the same periodic timer.

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