source: trunk/libtransmission/announcer.c @ 12283

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

(trunk libT) announcer.c: fold the tiers and trackers into fewer mallocs/frees

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