source: trunk/libtransmission/announcer.c @ 12346

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

(trunk libT) #4175 "very bizarre and unwieldly logfile in 2.30b2" -- fixed.

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