source: trunk/libtransmission/announcer.c @ 12164

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

(trunk libT) more announce.c work: better handling of incrementing the retry interval on scrape failures

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