source: branches/encryption/libtransmission/peer-mgr.c @ 3078

Last change on this file since 3078 was 3078, checked in by charles, 15 years ago

add #limits,h to peer-mgr.c

  • Property svn:keywords set to Date Rev Author Id
File size: 27.9 KB
Line 
1/*
2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
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: peer-mgr.c 3078 2007-09-15 20:49:21Z charles $
11 */
12
13#include <assert.h>
14#include <string.h> /* memcpy, memcmp */
15#include <stdlib.h> /* qsort */
16#include <stdio.h> /* printf */
17#include <limits.h> /* INT_MAX */
18
19#include "transmission.h"
20#include "clients.h"
21#include "completion.h"
22#include "handshake.h"
23#include "net.h"
24#include "peer-io.h"
25#include "peer-mgr.h"
26#include "peer-mgr-private.h"
27#include "peer-msgs.h"
28#include "ptrarray.h"
29#include "trevent.h"
30#include "utils.h"
31
32/* how frequently to change which peers are choked */
33#define RECHOKE_PERIOD_SECONDS (15 * 1000)
34
35#define REFILL_PERIOD_MSEC 1000
36
37/* how many peers to unchoke per-torrent. */
38/* FIXME: make this user-configurable? */
39#define NUM_UNCHOKED_PEERS_PER_TORRENT 8
40
41/**
42***
43**/
44
45struct tr_block
46{
47    unsigned int have          : 1;
48    unsigned int dnd           : 1;
49    unsigned int low_priority  : 1;
50    unsigned int high_priority : 1;
51    uint8_t requestCount;
52    uint8_t scarcity;
53    uint32_t block;
54};
55
56#define MAX_SCARCITY UINT8_MAX
57#define MAX_REQ_COUNT UINT8_MAX
58
59static void
60incrementReqCount( struct tr_block * block )
61{
62    assert( block != NULL );
63
64    if( block->requestCount < MAX_REQ_COUNT )
65        block->requestCount++;
66}
67
68static void
69incrementScarcity( struct tr_block * block )
70{
71    assert( block != NULL );
72
73    if( block->scarcity < MAX_SCARCITY )
74        block->scarcity++;
75}
76
77static int
78compareBlockByIndex( const void * va, const void * vb )
79{
80    const struct tr_block * a = (const struct tr_block *) va;
81    const struct tr_block * b = (const struct tr_block *) vb;
82    return tr_compareUint32( a->block, b->block );
83}
84
85static int
86compareBlockByInterest( const void * va, const void * vb )
87{
88    const struct tr_block * a = (const struct tr_block *) va;
89    const struct tr_block * b = (const struct tr_block *) vb;
90    int i;
91
92    if( a->dnd != b->dnd )
93        return a->dnd ? 1 : -1;
94
95    if( a->have != b->have )
96        return a->have ? 1 : -1;
97
98    if(( i = tr_compareUint8( a->requestCount, b->requestCount )))
99        return i;
100
101    if( a->high_priority != b->high_priority )
102        return a->high_priority ? -1 : 1;
103
104    if( a->low_priority != b->low_priority )
105        return a->low_priority ? 1 : -1;
106
107    if(( i = tr_compareUint16( a->scarcity, b->scarcity )))
108        return i;
109
110    if(( i = tr_compareUint32( a->block, b->block )))
111        return i;
112
113    return 0;
114}
115
116/**
117***
118**/
119
120typedef struct
121{
122    uint8_t hash[SHA_DIGEST_LENGTH];
123    tr_ptrArray * peers; /* tr_peer */
124    tr_timer * chokeTimer;
125    tr_timer * refillTimer;
126    tr_torrent * tor;
127
128    struct tr_block * blocks;
129    uint32_t blockCount;
130
131    unsigned int isRunning : 1;
132
133    struct tr_peerMgr * manager;
134}
135Torrent;
136
137struct tr_peerMgr
138{
139    tr_handle * handle;
140    tr_ptrArray * torrents; /* Torrent */
141    int connectionCount;
142    tr_ptrArray * handshakes; /* in-process */
143};
144
145/**
146***
147**/
148
149static int
150torrentCompare( const void * va, const void * vb )
151{
152    const Torrent * a = (const Torrent*) va;
153    const Torrent * b = (const Torrent*) vb;
154    return memcmp( a->hash, b->hash, SHA_DIGEST_LENGTH );
155}
156
157static int
158torrentCompareToHash( const void * va, const void * vb )
159{
160    const Torrent * a = (const Torrent*) va;
161    const uint8_t * b_hash = (const uint8_t*) vb;
162    return memcmp( a->hash, b_hash, SHA_DIGEST_LENGTH );
163}
164
165static Torrent*
166getExistingTorrent( tr_peerMgr * manager, const uint8_t * hash )
167{
168    return (Torrent*) tr_ptrArrayFindSorted( manager->torrents,
169                                             hash,
170                                             torrentCompareToHash );
171}
172
173static int chokePulse( void * vtorrent );
174
175static int
176peerCompare( const void * va, const void * vb )
177{
178    const tr_peer * a = (const tr_peer *) va;
179    const tr_peer * b = (const tr_peer *) vb;
180    return memcmp( &a->in_addr, &b->in_addr, sizeof(struct in_addr) );
181}
182
183static int
184peerCompareToAddr( const void * va, const void * vb )
185{
186    const tr_peer * a = (const tr_peer *) va;
187    const struct in_addr * b = (const struct in_addr *) vb;
188    return memcmp( &a->in_addr, b, sizeof(struct in_addr) );
189}
190
191static tr_peer*
192getExistingPeer( Torrent * torrent, const struct in_addr * in_addr )
193{
194    assert( torrent != NULL );
195    assert( torrent->peers != NULL );
196    assert( in_addr != NULL );
197
198    return (tr_peer*) tr_ptrArrayFindSorted( torrent->peers,
199                                             in_addr,
200                                             peerCompareToAddr );
201}
202
203static tr_peer*
204getPeer( Torrent * torrent, const struct in_addr * in_addr )
205{
206    tr_peer * peer = getExistingPeer( torrent, in_addr );
207
208    if( peer == NULL )
209    {
210        peer = tr_new0( tr_peer, 1 );
211        memcpy( &peer->in_addr, in_addr, sizeof(struct in_addr) );
212        tr_ptrArrayInsertSorted( torrent->peers, peer, peerCompare );
213fprintf( stderr, "getPeer: torrent %p now has %d peers\n", torrent, tr_ptrArraySize(torrent->peers) );
214    }
215    return peer;
216}
217
218static void
219disconnectPeer( tr_peer * peer )
220{
221    assert( peer != NULL );
222
223    tr_peerIoFree( peer->io );
224    peer->io = NULL;
225
226    if( peer->msgs != NULL )
227    {
228fprintf( stderr, "PUB unsub peer %p from msgs %p\n", peer, peer->msgs );
229        tr_peerMsgsUnsubscribe( peer->msgs, peer->msgsTag );
230        tr_peerMsgsFree( peer->msgs );
231        peer->msgs = NULL;
232    }
233
234    tr_bitfieldFree( peer->have );
235    peer->have = NULL;
236
237    tr_bitfieldFree( peer->blame );
238    peer->blame = NULL;
239
240    tr_bitfieldFree( peer->banned );
241    peer->banned = NULL;
242}
243
244static void
245freePeer( tr_peer * peer )
246{
247    disconnectPeer( peer );
248    tr_free( peer->client );
249    tr_free( peer );
250}
251
252static void
253freeTorrent( tr_peerMgr * manager, Torrent * t )
254{
255    int i, size;
256    tr_peer ** peers;
257    uint8_t hash[SHA_DIGEST_LENGTH];
258
259fprintf( stderr, "timer freeTorrent %p\n", t );
260
261    assert( manager != NULL );
262    assert( t != NULL );
263    assert( t->peers != NULL );
264    assert( getExistingTorrent( manager, t->hash ) != NULL );
265
266    memcpy( hash, t->hash, SHA_DIGEST_LENGTH );
267
268    tr_timerFree( &t->chokeTimer );
269    tr_timerFree( &t->refillTimer );
270
271    peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &size );
272    for( i=0; i<size; ++i )
273        freePeer( peers[i] );
274
275    tr_ptrArrayFree( t->peers );
276    tr_ptrArrayRemoveSorted( manager->torrents, t, torrentCompare );
277    tr_free( t->blocks );
278    tr_free( t );
279
280    assert( getExistingTorrent( manager, hash ) == NULL );
281}
282
283/**
284***
285**/
286
287tr_peerMgr*
288tr_peerMgrNew( tr_handle * handle )
289{
290    tr_peerMgr * m = tr_new0( tr_peerMgr, 1 );
291    m->handle = handle;
292    m->torrents = tr_ptrArrayNew( );
293    m->handshakes = tr_ptrArrayNew( );
294    return m;
295}
296
297void
298tr_peerMgrFree( tr_peerMgr * manager )
299{
300    int i, n;
301fprintf( stderr, "timer peerMgrFree\n" );
302
303    while( !tr_ptrArrayEmpty( manager->torrents ) )
304        freeTorrent( manager, (Torrent*)tr_ptrArrayNth( manager->torrents, 0) );
305    tr_ptrArrayFree( manager->torrents );
306
307    for( i=0, n=tr_ptrArraySize(manager->handshakes); i<n; ++i )
308        tr_handshakeAbort( (tr_handshake*) tr_ptrArrayNth( manager->handshakes, i) );
309    tr_ptrArrayFree( manager->handshakes );
310
311    tr_free( manager );
312}
313
314/**
315***
316**/
317
318static tr_peer**
319getConnectedPeers( Torrent * t, int * setmeCount )
320{
321    int i, peerCount, connectionCount;
322    tr_peer **peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
323    tr_peer **ret = tr_new( tr_peer*, peerCount );
324
325    for( i=connectionCount=0; i<peerCount; ++i )
326        if( peers[i]->msgs != NULL )
327            ret[connectionCount++] = peers[i];
328
329    *setmeCount = connectionCount;
330    return ret;
331}
332
333static void
334populateBlockArray( Torrent * t )
335{
336    uint32_t i;
337    struct tr_block * b;
338    const tr_torrent * tor = t->tor;
339
340    t->blockCount = tor->blockCount;
341    t->blocks = b = tr_new( struct tr_block, t->blockCount );
342
343    for( i=0; i<t->blockCount; ++i, ++b )
344    {
345        const int index = tr_torBlockPiece( tor, i );
346        b->have = tr_cpBlockIsComplete( tor->completion, i ) ? 1 : 0;
347        b->dnd = tor->info.pieces[index].dnd ? 1 : 0;
348        b->low_priority = tor->info.pieces[index].priority == TR_PRI_LOW;
349        b->high_priority = tor->info.pieces[index].priority == TR_PRI_HIGH;
350        b->requestCount = 0;
351        b->scarcity = 0;
352        b->block = i;
353    }
354}
355
356static int
357refillPulse( void * vtorrent )
358{
359    uint32_t i;
360    int size;
361    Torrent * t = (Torrent *) vtorrent;
362    tr_peer ** peers;
363    const int isSeeding = tr_cpGetStatus( t->tor->completion ) != TR_CP_INCOMPLETE;
364    const int wantToRefill = t->isRunning && !isSeeding;
365
366    if( !wantToRefill && t->blocks!=NULL ) /* torrent has stopped or switched to seeding */
367    {
368        tr_free( t->blocks );
369        t->blocks = NULL;
370        t->blockCount = 0;
371    }
372    else if( wantToRefill && t->blocks==NULL ) /* torrent has started or switched to leeching */
373    {
374        populateBlockArray( t );
375    }
376
377    peers = getConnectedPeers( t, &size );
378    if( wantToRefill && size>0 )
379    {
380        /* sort the blocks by interest */
381        fprintf( stderr, "sorting [%s] blocks by interest...", t->tor->info.name );
382        qsort( t->blocks, t->blockCount, sizeof(struct tr_block), compareBlockByInterest );
383        fprintf( stderr, "done\n" );
384
385        /* walk through all the most interesting blocks */
386        for( i=0; i<t->blockCount; ++i )
387        {
388            const uint32_t b = t->blocks[i].block;
389            const uint32_t index = tr_torBlockPiece( t->tor, b );
390            const uint32_t begin = ( b * t->tor->blockSize )-( index * t->tor->info.pieceSize );
391            const uint32_t length = tr_torBlockCountBytes( t->tor, (int)b );
392            int j;
393
394            if( t->blocks[i].have || t->blocks[i].dnd )
395                continue;
396
397            if( !size ) /* all peers full */
398                break;
399
400            /* find a peer who can ask for this block */
401            for( j=0; j<size; )
402            {
403                const int val = tr_peerMsgsAddRequest( peers[j]->msgs, index, begin, length );
404                switch( val )
405                {
406                    case TR_ADDREQ_FULL: 
407                    case TR_ADDREQ_CLIENT_CHOKED: 
408                        peers[j] = peers[--size];
409                        break;
410
411                    case TR_ADDREQ_MISSING: 
412                        ++j;
413                        break;
414
415                    case TR_ADDREQ_OK:
416                        fprintf( stderr, "peer %p took the request for block %d\n", peers[j]->msgs, b );
417                        incrementReqCount( &t->blocks[i] );
418                        j = size;
419                        break;
420                }
421            }
422        }
423
424        /* put the blocks back the way we found them */
425        qsort( t->blocks, t->blockCount, sizeof(struct tr_block), compareBlockByIndex );
426    }
427
428    /* cleanup */
429    tr_free( peers );
430
431    return TRUE;
432}
433
434static void
435broadcastClientHave( Torrent * t, uint32_t index )
436{
437    int i, size;
438    tr_peer ** peers = getConnectedPeers( t, &size );
439    for( i=0; i<size; ++i )
440        tr_peerMsgsHave( peers[i]->msgs, index );
441    tr_free( peers );
442}
443
444static void
445broadcastGotBlock( Torrent * t, uint32_t index, uint32_t offset, uint32_t length )
446{
447    int i, size;
448    tr_peer ** peers = getConnectedPeers( t, &size );
449    for( i=0; i<size; ++i )
450        tr_peerMsgsCancel( peers[i]->msgs, index, offset, length );
451    tr_free( peers );
452}
453
454static void
455msgsCallbackFunc( void * source UNUSED, void * vevent, void * vt )
456{
457    Torrent * t = (Torrent *) vt;
458    const tr_peermsgs_event * e = (const tr_peermsgs_event *) vevent;
459
460    switch( e->eventType )
461    {
462        case TR_PEERMSG_PEER_BITFIELD: {
463            if( t->blocks!=NULL ) {
464                int i;
465                for( i=0; i<t->tor->info.pieceCount; ++i ) {
466                    const uint32_t begin = tr_torPieceFirstBlock( t->tor, i );
467                    const uint32_t end = begin + tr_torPieceCountBlocks( t->tor, i );
468                    uint32_t j;
469                    for( j=begin; t->blocks!=NULL && j<end; ++j ) {
470                        assert( t->blocks[j].block == j );
471                        incrementScarcity( &t->blocks[j] );
472                    }
473                }
474            }
475            break;
476        }
477
478        case TR_PEERMSG_CLIENT_HAVE:
479            broadcastClientHave( t, e->pieceIndex );
480            break;
481
482        case TR_PEERMSG_PEER_HAVE: {
483            if( t->blocks != NULL ) {
484                uint32_t i;
485                const uint32_t begin = tr_torPieceFirstBlock( t->tor, e->pieceIndex );
486                const uint32_t end = begin + tr_torPieceCountBlocks( t->tor, (int)e->pieceIndex );
487                for( i=begin; i<end; ++i ) {
488                    assert( t->blocks[i].block == i );
489                    incrementScarcity( &t->blocks[i] );
490                }
491            }
492            break;
493        }
494
495        case TR_PEERMSG_CLIENT_BLOCK: {
496            if( t->blocks != NULL ) {
497                const uint32_t i = _tr_block( t->tor, e->pieceIndex, e->offset );
498                assert( t->blocks[i].block == i );
499                t->blocks[i].have = 1;
500            }
501            broadcastGotBlock( t, e->pieceIndex, e->offset, e->length );
502            break;
503        }
504
505        case TR_PEERMSG_GOT_PEX:
506            /* FIXME */
507            break;
508
509        case TR_PEERMSG_GOT_ERROR:
510            /* FIXME */
511            break;
512
513        default:
514            assert(0);
515    }
516}
517
518static void
519myHandshakeDoneCB( tr_handshake    * handshake,
520                   tr_peerIo       * io,
521                   int               isConnected,
522                   const uint8_t   * peer_id,
523                   int               peerSupportsEncryption,
524                   void            * vmanager )
525{
526    int ok = isConnected;
527    uint16_t port;
528    const struct in_addr * in_addr;
529    tr_peerMgr * manager = (tr_peerMgr*) vmanager;
530    const uint8_t * hash = NULL;
531    Torrent * t;
532    tr_handshake * ours;
533
534    assert( io != NULL );
535    assert( isConnected==0 || isConnected==1 );
536    assert( peerSupportsEncryption==0 || peerSupportsEncryption==1 );
537
538    ours = tr_ptrArrayRemoveSorted( manager->handshakes,
539                                    handshake,
540                                    tr_comparePointers );
541    assert( handshake == ours );
542
543    in_addr = tr_peerIoGetAddress( io, &port );
544
545    if( !tr_peerIoHasTorrentHash( io ) ) /* incoming connection gone wrong? */
546    {
547        tr_peerIoFree( io );
548        --manager->connectionCount;
549        return;
550    }
551
552    hash = tr_peerIoGetTorrentHash( io );
553    t = getExistingTorrent( manager, hash );
554    if( !t || !t->isRunning )
555    {
556        tr_peerIoFree( io );
557        --manager->connectionCount;
558        return;
559    }
560
561    fprintf( stderr, "peer-mgr: torrent [%s] finished a handshake; isConnected is %d\n", t->tor->info.name, isConnected );
562
563    /* if we couldn't connect or were snubbed,
564     * the peer's probably not worth remembering. */
565    if( !ok ) {
566        tr_peer * peer = getExistingPeer( t, in_addr );
567        fprintf( stderr, "peer-mgr: torrent [%s] got a bad one, and you know what? fuck them.\n", t->tor->info.name );
568        tr_peerIoFree( io );
569        --manager->connectionCount;
570        if( peer ) {
571            tr_ptrArrayRemoveSorted( t->peers, peer, peerCompare );
572            freePeer( peer );
573        }
574        return;
575    }
576
577    if( 1 ) {
578        tr_peer * peer = getPeer( t, in_addr );
579        if( peer->msgs != NULL ) { /* we alerady have this peer */
580            tr_peerIoFree( io );
581            --manager->connectionCount;
582        } else {
583            peer->port = port;
584            peer->io = io;
585            peer->msgs = tr_peerMsgsNew( t->tor, peer );
586            peer->client = peer_id ? tr_clientForId( peer_id ) : NULL;
587            peer->peerSupportsEncryption = peerSupportsEncryption ? 1 : 0;
588            fprintf( stderr, "PUB sub peer %p to msgs %p\n", peer, peer->msgs );
589            peer->msgsTag = tr_peerMsgsSubscribe( peer->msgs, msgsCallbackFunc, t );
590        }
591    }
592}
593
594static void
595initiateHandshake( tr_peerMgr * manager, tr_peerIo * io )
596{
597    tr_handshake * handshake = tr_handshakeNew( io,
598                                                HANDSHAKE_ENCRYPTION_PREFERRED,
599                                                myHandshakeDoneCB,
600                                                manager );
601    ++manager->connectionCount;
602
603    tr_ptrArrayInsertSorted( manager->handshakes, handshake, tr_comparePointers );
604}
605
606void
607tr_peerMgrAddIncoming( tr_peerMgr      * manager,
608                       struct in_addr  * addr,
609                       int               socket )
610{
611    tr_peerIo * io = tr_peerIoNewIncoming( manager->handle, addr, socket );
612    initiateHandshake( manager, io );
613}
614
615static void
616maybeConnect( tr_peerMgr * manager, Torrent * t, tr_peer * peer )
617{
618    tr_peerIo * io;
619
620    assert( manager != NULL );
621    assert( t != NULL );
622    assert( peer != NULL );
623
624    if( peer->io != NULL ) { /* already connected */
625        fprintf( stderr, "not connecting because we already have an IO for that address\n" );
626        return;
627    }
628    if( !t->isRunning ) { /* torrent's not running */
629        fprintf( stderr, "OUTGOING connection not being made because t [%s] is not running\n", t->tor->info.name );
630        return;
631    }
632
633    io = tr_peerIoNewOutgoing( manager->handle,
634                               &peer->in_addr,
635                               peer->port,
636                               t->hash );
637    initiateHandshake( manager, io );
638}
639
640void
641tr_peerMgrAddPex( tr_peerMgr     * manager,
642                  const uint8_t  * torrentHash,
643                  int              from,
644                  const tr_pex   * pex,
645                  int              pexCount )
646{
647    int i;
648    const tr_pex * walk = pex;
649    Torrent * t = getExistingTorrent( manager, torrentHash );
650    for( i=0; i<pexCount; ++i )
651    {
652        tr_peer * peer = getPeer( t, &walk->in_addr );
653        peer->port = walk->port;
654        peer->from = from;
655        maybeConnect( manager, t, peer );
656    }
657}
658
659void
660tr_peerMgrAddPeers( tr_peerMgr    * manager,
661                    const uint8_t * torrentHash,
662                    int             from,
663                    const uint8_t * peerCompact,
664                    int             peerCount )
665{
666    int i;
667    const uint8_t * walk = peerCompact;
668    Torrent * t = getExistingTorrent( manager, torrentHash );
669    for( i=0; t!=NULL && i<peerCount; ++i )
670    {
671        tr_peer * peer;
672        struct in_addr addr;
673        uint16_t port;
674        memcpy( &addr, walk, 4 ); walk += 4;
675        memcpy( &port, walk, 2 ); walk += 2;
676        peer = getPeer( t, &addr );
677        peer->port = port;
678        peer->from = from;
679        maybeConnect( manager, t, peer );
680    }
681}
682
683/**
684***
685**/
686
687int
688tr_peerMgrIsAcceptingConnections( const tr_peerMgr * manager UNUSED )
689{
690    return TRUE; /* manager->connectionCount < MAX_CONNECTED_PEERS; */
691}
692
693void
694tr_peerMgrSetBlame( tr_peerMgr     * manager UNUSED,
695                    const uint8_t  * torrentHash UNUSED,
696                    int              pieceIndex UNUSED,
697                    int              success UNUSED )
698{
699    fprintf( stderr, "FIXME: tr_peerMgrSetBlame\n" );
700}
701
702int
703tr_pexCompare( const void * va, const void * vb )
704{
705    const tr_pex * a = (const tr_pex *) va;
706    const tr_pex * b = (const tr_pex *) vb;
707    int i = memcmp( &a->in_addr, &b->in_addr, sizeof(struct in_addr) );
708    if( i ) return i;
709    if( a->port < b->port ) return -1;
710    if( a->port > b->port ) return 1;
711    return 0;
712}
713
714int tr_pexCompare( const void * a, const void * b );
715
716
717int
718tr_peerMgrGetPeers( tr_peerMgr      * manager,
719                    const uint8_t   * torrentHash,
720                    tr_pex         ** setme_pex )
721{
722    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
723    int i, peerCount;
724    const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
725    tr_pex * pex = tr_new( tr_pex, peerCount );
726    tr_pex * walk = pex;
727
728    for( i=0; i<peerCount; ++i, ++walk )
729    {
730        const tr_peer * peer = peers[i];
731
732        walk->in_addr = peer->in_addr;
733
734        walk->port = peer->port;
735
736        walk->flags = 0;
737        if( peer->peerSupportsEncryption ) walk->flags |= 1;
738        if( peer->progress >= 1.0 )        walk->flags |= 2;
739    }
740
741    assert( ( walk - pex ) == peerCount );
742    qsort( pex, peerCount, sizeof(tr_pex), tr_pexCompare );
743    *setme_pex = pex;
744    return peerCount;
745}
746
747void
748tr_peerMgrStartTorrent( tr_peerMgr     * manager,
749                        const uint8_t  * torrentHash )
750{
751    int i, peerCount;
752    Torrent * t = getExistingTorrent( manager, torrentHash );
753
754    t->isRunning = 1;
755
756    peerCount = tr_ptrArraySize( t->peers );
757    for( i=0; i<peerCount; ++i )
758        maybeConnect( manager, t, tr_ptrArrayNth( t->peers, i ) );
759}
760
761void
762tr_peerMgrStopTorrent( tr_peerMgr     * manager,
763                       const uint8_t  * torrentHash)
764{
765    int i, peerCount;
766    Torrent * t = getExistingTorrent( manager, torrentHash );
767
768    t->isRunning = 0;
769
770    peerCount = tr_ptrArraySize( t->peers );
771    for( i=0; i<peerCount; ++i )
772        disconnectPeer( tr_ptrArrayNth( t->peers, i ) );
773}
774
775void
776tr_peerMgrUpdateCompletion( tr_peerMgr     * manager,
777                            const uint8_t  * torrentHash )
778{
779    uint32_t i;
780    Torrent * t = getExistingTorrent( manager, torrentHash );
781
782    for( i=0; t->blocks!=NULL && i<t->blockCount; ++i ) {
783        assert( t->blocks[i].block == i );
784        t->blocks[i].have = tr_cpBlockIsComplete( t->tor->completion, i ) ? 1 : 0;
785    }
786}
787
788void
789tr_peerMgrAddTorrent( tr_peerMgr * manager,
790                      tr_torrent * tor )
791{
792    Torrent * t;
793
794    assert( tor != NULL );
795    assert( getExistingTorrent( manager, tor->info.hash ) == NULL );
796
797    t = tr_new0( Torrent, 1 );
798    t->manager = manager;
799    t->tor = tor;
800    t->peers = tr_ptrArrayNew( );
801    t->chokeTimer = tr_timerNew( manager->handle, chokePulse, t, RECHOKE_PERIOD_SECONDS );
802    t->refillTimer = tr_timerNew( t->manager->handle, refillPulse, t, REFILL_PERIOD_MSEC );
803
804    memcpy( t->hash, tor->info.hash, SHA_DIGEST_LENGTH );
805    tr_ptrArrayInsertSorted( manager->torrents, t, torrentCompare );
806}
807
808void
809tr_peerMgrRemoveTorrent( tr_peerMgr     * manager,
810                         const uint8_t  * torrentHash )
811{
812    Torrent * t = getExistingTorrent( manager, torrentHash );
813    assert( t != NULL );
814    tr_peerMgrStopTorrent( manager, torrentHash );
815    freeTorrent( manager, t );
816}
817
818void
819tr_peerMgrTorrentAvailability( const tr_peerMgr * manager,
820                               const uint8_t    * torrentHash,
821                               int8_t           * tab,
822                               int                tabCount )
823{
824    int i;
825    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
826    const tr_torrent * tor = t->tor;
827    const float interval = tor->info.pieceCount / (float)tabCount;
828
829    memset( tab, 0, tabCount );
830
831    for( i=0; i<tabCount; ++i )
832    {
833        const int piece = i * interval;
834
835        if( tor == NULL )
836            tab[i] = 0;
837        else if( tr_cpPieceIsComplete( tor->completion, piece ) )
838            tab[i] = -1;
839        else {
840            int j, peerCount;
841            const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
842            for( j=0; j<peerCount; ++j )
843                if( tr_bitfieldHas( peers[j]->have, i ) )
844                    ++tab[i];
845        }
846    }
847}
848
849
850void
851tr_peerMgrTorrentStats( const tr_peerMgr * manager,
852                        const uint8_t    * torrentHash,
853                        int              * setmePeersTotal,
854                        int              * setmePeersConnected,
855                        int              * setmePeersSendingToUs,
856                        int              * setmePeersGettingFromUs,
857                        int              * setmePeersFrom )
858{
859    int i, size;
860    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
861    const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size );
862
863    *setmePeersTotal          = size;
864    *setmePeersConnected      = 0;
865    *setmePeersSendingToUs    = 0;
866    *setmePeersGettingFromUs  = 0;
867
868    for( i=0; i<size; ++i )
869    {
870        const tr_peer * peer = peers[i];
871
872        if( peer->io == NULL ) /* not connected */
873            continue;
874
875        ++*setmePeersConnected;
876
877        ++setmePeersFrom[peer->from];
878
879        if( tr_peerIoGetRateToPeer( peer->io ) > 0.01 )
880            ++*setmePeersGettingFromUs;
881
882        if( tr_peerIoGetRateToClient( peer->io ) > 0.01 )
883            ++*setmePeersSendingToUs;
884    }
885}
886
887struct tr_peer_stat *
888tr_peerMgrPeerStats( const tr_peerMgr  * manager,
889                     const uint8_t     * torrentHash,
890                     int               * setmeCount UNUSED )
891{
892    int i, size;
893    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
894    const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size );
895    tr_peer_stat * ret;
896
897    ret = tr_new0( tr_peer_stat, size );
898
899    for( i=0; i<size; ++i )
900    {
901        const tr_peer * peer = peers[i];
902        const int live = peer->io != NULL;
903        tr_peer_stat * stat = ret + i;
904
905        tr_netNtop( &peer->in_addr, stat->addr, sizeof(stat->addr) );
906        stat->port             = peer->port;
907        stat->from             = peer->from;
908        stat->client           = peer->client;
909        stat->progress         = peer->progress;
910        stat->isConnected      = live ? 1 : 0;
911        stat->isEncrypted      = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
912        stat->uploadToRate     = tr_peerIoGetRateToPeer( peer->io );
913        stat->downloadFromRate = tr_peerIoGetRateToClient( peer->io );
914        stat->isDownloading    = stat->uploadToRate > 0.01;
915        stat->isUploading      = stat->downloadFromRate > 0.01;
916    }
917
918    *setmeCount = size;
919    return ret;
920}
921
922/**
923***
924**/
925
926typedef struct
927{
928    tr_peer * peer;
929    float rate;
930    int randomKey;
931    int preferred;
932    int doUnchoke;
933}
934ChokeData;
935
936static int
937compareChoke( const void * va, const void * vb )
938{
939    const ChokeData * a = ( const ChokeData * ) va;
940    const ChokeData * b = ( const ChokeData * ) vb;
941
942    if( a->preferred != b->preferred )
943        return a->preferred ? -1 : 1;
944
945    if( a->preferred )
946    {
947        if( a->rate > b->rate ) return -1;
948        if( a->rate < b->rate ) return 1;
949        return 0;
950    }
951    else
952    {
953        return a->randomKey - b->randomKey;
954    }
955}
956
957static int
958clientIsSnubbedBy( const tr_peer * peer )
959{
960    assert( peer != NULL );
961
962    return peer->peerSentDataAt < (time(NULL) - 30);
963}
964
965static void
966rechokeLeech( Torrent * t )
967{
968    int i, size, unchoked=0;
969    tr_peer ** peers = getConnectedPeers( t, &size );
970    ChokeData * choke = tr_new0( ChokeData, size );
971
972    /* sort the peers by preference and rate */
973    for( i=0; i<size; ++i ) {
974        tr_peer * peer = peers[i];
975        ChokeData * node = &choke[i];
976        node->peer = peer;
977        node->preferred = peer->peerIsInterested && !clientIsSnubbedBy(peer);
978        node->randomKey = tr_rand( INT_MAX );
979        node->rate = tr_peerIoGetRateToClient( peer->io );
980    }
981    qsort( choke, size, sizeof(ChokeData), compareChoke );
982
983    for( i=0; i<size && i<NUM_UNCHOKED_PEERS_PER_TORRENT; ++i ) {
984        choke[i].doUnchoke = 1;
985        ++unchoked;
986    }
987
988    for( ; i<size; ++i ) {
989        choke[i].doUnchoke = 1;
990        ++unchoked;
991        if( choke[i].peer->peerIsInterested )
992            break;
993    }
994
995    for( i=0; i<size; ++i )
996        tr_peerMsgsSetChoke( choke[i].peer->msgs, !choke[i].doUnchoke );
997
998    /* cleanup */
999    tr_free( choke );
1000    tr_free( peers );
1001}
1002
1003static void
1004rechokeSeed( Torrent * t )
1005{
1006    int i, size;
1007    tr_peer ** peers = getConnectedPeers( t, &size );
1008
1009    /* FIXME */
1010    for( i=0; i<size; ++i )
1011        tr_peerMsgsSetChoke( peers[i]->msgs, FALSE );
1012
1013    tr_free( peers );
1014}
1015
1016static int
1017chokePulse( void * vtorrent )
1018{
1019    Torrent * t = vtorrent;
1020    const int done = tr_cpGetStatus( t->tor->completion ) != TR_CP_INCOMPLETE;
1021    if( done )
1022        rechokeLeech( vtorrent );
1023    else
1024        rechokeSeed( vtorrent );
1025    return TRUE;
1026}
Note: See TracBrowser for help on using the repository browser.