source: trunk/libtransmission/announcer.c @ 12297

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

(trunk libT) better multiscrape

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