source: trunk/libtransmission/announcer.c @ 12182

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

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

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