source: trunk/libtransmission/announcer.c @ 12253

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

(trunk libT) better scrape management on private trackers.

If a private tracker scrape says that there are no downloaders in the swarm, mark all the peers in the private swarm as seeds. This can greatly reduce unnecessary overhead on large seedboxes. We don't do this same trick on public torrents, since a public tracker won't know of all the peers.

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