source: trunk/libtransmission/torrent.c @ 2454

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

put information messages in tr_inf(), not fprintf()

  • Property svn:keywords set to Date Rev Author Id
File size: 37.0 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 2454 2007-07-21 18:31:08Z charles $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29
30#include "transmission.h"
31#include "fastresume.h"
32#include "trcompat.h" /* for strlcpy */
33#include "metainfo.h"
34#include "net.h" /* tr_netNtop */
35#include "shared.h"
36
37/***
38****  LOCKS
39***/
40
41void
42tr_torrentReaderLock( const tr_torrent_t * tor )
43{
44    tr_rwReaderLock ( (tr_rwlock_t*)&tor->lock );
45}
46
47void
48tr_torrentReaderUnlock( const tr_torrent_t * tor )
49{
50    tr_rwReaderUnlock ( (tr_rwlock_t*)&tor->lock );
51}
52
53void
54tr_torrentWriterLock( tr_torrent_t * tor )
55{
56    tr_rwWriterLock ( &tor->lock );
57}
58
59void
60tr_torrentWriterUnlock( tr_torrent_t * tor )
61{
62    tr_rwWriterUnlock ( &tor->lock );
63}
64
65/***
66****  PER-TORRENT UL / DL SPEEDS
67***/
68
69void
70tr_torrentSetSpeedMode( tr_torrent_t     * tor,
71                        int                up_or_down,
72                        tr_speedlimit_t    mode )
73{
74    tr_speedlimit_t * limit = up_or_down==TR_UP
75        ? &tor->uploadLimitMode
76        : &tor->downloadLimitMode;
77    *limit = mode;
78}
79
80tr_speedlimit_t
81tr_torrentGetSpeedMode( const tr_torrent_t * tor,
82                        int                  up_or_down)
83{
84    return up_or_down==TR_UP ? tor->uploadLimitMode
85                             : tor->downloadLimitMode;
86}
87
88void
89tr_torrentSetSpeedLimit( tr_torrent_t   * tor,
90                         int              up_or_down,
91                         int              single_KiB_sec )
92{
93    tr_ratecontrol_t * rc = up_or_down==TR_UP ? tor->upload : tor->download;
94    tr_rcSetLimit( rc, single_KiB_sec );
95}
96
97int
98tr_torrentGetSpeedLimit( const tr_torrent_t  * tor,
99                         int                   up_or_down )
100{
101    tr_ratecontrol_t * rc = up_or_down==TR_UP ? tor->upload : tor->download;
102    return tr_rcGetLimit( rc );
103}
104
105/***
106****
107****  TORRENT INSTANTIATION
108****
109***/
110
111static int
112getBytePiece( const tr_info_t * info, uint64_t byteOffset )
113{
114    assert( info != NULL );
115    assert( info->pieceSize != 0 );
116
117    return byteOffset / info->pieceSize;
118}
119
120static void
121initFilePieces ( tr_info_t * info, int fileIndex )
122{
123    tr_file_t * file = &info->files[fileIndex];
124    uint64_t firstByte, lastByte;
125
126    assert( info != NULL );
127    assert( 0<=fileIndex && fileIndex<info->fileCount );
128
129    file = &info->files[fileIndex];
130    firstByte = file->offset;
131    lastByte = firstByte + (file->length ? file->length-1 : 0);
132    file->firstPiece = getBytePiece( info, firstByte );
133    file->lastPiece = getBytePiece( info, lastByte );
134    tr_dbg( "file #%d is in pieces [%d...%d] (%s)", fileIndex, file->firstPiece, file->lastPiece, file->name );
135}
136
137static tr_priority_t
138calculatePiecePriority ( const tr_torrent_t * tor,
139                         int                  piece )
140{
141    int i;
142    tr_priority_t priority = TR_PRI_NORMAL;
143
144    for( i=0; i<tor->info.fileCount; ++i )
145    {
146        const tr_file_t * file = &tor->info.files[i];
147        if ( file->firstPiece <= piece
148          && file->lastPiece  >= piece
149          && file->priority   >  priority)
150              priority = file->priority;
151    }
152
153    return priority;
154}
155
156static void
157tr_torrentInitFilePieces( tr_torrent_t * tor )
158{
159    int i;
160    uint64_t offset = 0;
161
162    assert( tor != NULL );
163
164    for( i=0; i<tor->info.fileCount; ++i ) {
165      tor->info.files[i].offset = offset;
166      offset += tor->info.files[i].length;
167      initFilePieces( &tor->info, i );
168    }
169
170    for( i=0; i<tor->info.pieceCount; ++i )
171      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
172}
173
174static void torrentThreadLoop( void * );
175
176static void
177torrentRealInit( tr_handle_t   * h,
178                 tr_torrent_t  * tor,
179                 const char    * destination,
180                 int             flags )
181{
182    int i;
183    char name[512];
184   
185    tor->info.flags |= flags;
186
187    tr_sharedLock( h->shared );
188
189    tor->destination = tr_strdup( destination );
190
191    tr_torrentInitFilePieces( tor );
192
193    tor->handle   = h;
194    tor->key      = h->key;
195    tor->azId     = h->azId;
196    tor->hasChangedState = -1;
197   
198    /* Escaped info hash for HTTP queries */
199    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
200    {
201        snprintf( &tor->escapedHashString[3*i],
202                  sizeof( tor->escapedHashString ) - 3 * i,
203                  "%%%02x", tor->info.hash[i] );
204    }
205
206    tor->pexDisabled = 0;
207
208    /* Block size: usually 16 ko, or less if we have to */
209    tor->blockSize  = MIN( tor->info.pieceSize, 1 << 14 );
210    tor->blockCount = ( tor->info.totalSize + tor->blockSize - 1 ) /
211                        tor->blockSize;
212    tor->completion = tr_cpInit( tor );
213
214    tor->thread = THREAD_EMPTY;
215    tr_rwInit( &tor->lock );
216
217    tor->upload         = tr_rcInit();
218    tor->download       = tr_rcInit();
219    tor->swarmspeed     = tr_rcInit();
220 
221    /* We have a new torrent */
222    tor->publicPort = tr_sharedGetPublicPort( h->shared );
223
224    tr_sharedUnlock( h->shared );
225
226    if( !h->isPortSet )
227        tr_setBindPort( h, TR_DEFAULT_PORT );
228
229    assert( !tor->downloadedCur );
230    assert( !tor->uploadedCur );
231
232    tor->error   = TR_OK;
233    tor->runStatus = flags & TR_FLAG_PAUSED ? TR_RUN_STOPPED : TR_RUN_RUNNING;
234    tor->recheckFlag = tr_ioCheckFiles( tor, TR_RECHECK_FAST );
235    tor->cpStatus = tr_cpGetStatus( tor->completion );
236
237    tr_sharedLock( h->shared );
238    tor->next = h->torrentList;
239    h->torrentList = tor;
240    h->torrentCount++;
241    tr_sharedUnlock( h->shared );
242
243    snprintf( name, sizeof( name ), "torrent %p (%s)", tor, tor->info.name );
244    tr_threadCreate( &tor->thread, torrentThreadLoop, tor, name );
245}
246
247static int
248pathIsInUse ( const tr_handle_t   * h,
249              const char          * destination,
250              const char          * name )
251{
252    const tr_torrent_t * tor;
253   
254    for( tor=h->torrentList; tor; tor=tor->next )
255        if( !strcmp( destination, tor->destination )
256         && !strcmp( name, tor->info.name ) )
257            return TRUE;
258
259    return FALSE;
260}
261
262static int
263hashExists( const tr_handle_t   * h,
264            const uint8_t       * hash )
265{
266    const tr_torrent_t * tor;
267
268    for( tor=h->torrentList; tor; tor=tor->next )
269        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
270            return TRUE;
271
272    return FALSE;
273}
274
275static int
276infoCanAdd( const tr_handle_t   * h,
277            const char          * destination,
278            const tr_info_t     * info )
279{
280    if( hashExists( h, info->hash ) )
281        return TR_EDUPLICATE;
282
283    if( destination && pathIsInUse( h, destination, info->name ) )
284        return TR_EDUPLICATE;
285
286    return TR_OK;
287}
288
289int
290tr_torrentParse( const tr_handle_t  * h,
291                 const char         * path,
292                 const char         * destination,
293                 tr_info_t          * setme_info )
294{
295    int ret;
296    tr_info_t tmp;
297
298    if( setme_info == NULL )
299        setme_info = &tmp;
300
301    memset( setme_info, 0, sizeof( tr_info_t ) );
302    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
303
304    if( ret == TR_OK )
305        ret = infoCanAdd( h, destination, setme_info );
306
307    if( setme_info == &tmp )
308        tr_metainfoFree( &tmp );
309
310    return ret;
311}
312 
313tr_torrent_t *
314tr_torrentInit( tr_handle_t   * h,
315                const char    * path,
316                const char    * destination,
317                int             flags,
318                int           * error )
319{
320    int val;
321    tr_torrent_t * tor = NULL;
322
323    if(( val = tr_torrentParse( h, path, destination, NULL )))
324        *error = val;
325    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
326        *error = TR_EOTHER;
327    else {
328        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
329        torrentRealInit( h, tor, destination, flags );
330    }
331
332    return tor;
333}
334
335int
336tr_torrentParseHash( const tr_handle_t  * h,
337                     const char         * hashStr,
338                     const char         * destination,
339                     tr_info_t          * setme_info )
340{
341    int ret;
342    tr_info_t tmp;
343
344    if( setme_info == NULL )
345        setme_info = &tmp;
346
347    memset( setme_info, 0, sizeof( tr_info_t ) );
348    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
349
350    if( ret == TR_OK )
351        ret = infoCanAdd( h, destination, setme_info );
352
353    if( setme_info == &tmp )
354        tr_metainfoFree( &tmp );
355
356    return ret;
357}
358
359
360tr_torrent_t *
361tr_torrentInitSaved( tr_handle_t    * h,
362                     const char     * hashStr,
363                     const char     * destination,
364                     int              flags,
365                     int            * error )
366{
367    int val;
368    tr_torrent_t * tor = NULL;
369
370    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
371        *error = val;
372    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
373        *error = TR_EOTHER;
374    else {
375        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
376        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
377    }
378
379    return tor;
380}
381
382static int
383tr_torrentParseData( const tr_handle_t  * h,
384                     const uint8_t      * data,
385                     size_t               size,
386                     const char         * destination,
387                     tr_info_t          * setme_info )
388{
389    int ret;
390    tr_info_t tmp;
391
392    if( setme_info == NULL )
393        setme_info = &tmp;
394
395    memset( setme_info, 0, sizeof( tr_info_t ) );
396    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
397
398    if( ret == TR_OK )
399        ret = infoCanAdd( h, destination, setme_info );
400
401    if( setme_info == &tmp )
402        tr_metainfoFree( &tmp );
403
404    return ret;
405}
406
407tr_torrent_t *
408tr_torrentInitData( tr_handle_t    * h,
409                    const uint8_t  * data,
410                    size_t           size,
411                    const char     * destination,
412                    int              flags,
413                    int            * error )
414{
415    int val;
416    tr_torrent_t * tor = NULL;
417
418    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
419        *error = val;
420    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
421        *error = TR_EOTHER;
422    else {
423        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
424        torrentRealInit( h, tor, destination, flags );
425    }
426
427    return tor;
428}
429
430const tr_info_t *
431tr_torrentInfo( const tr_torrent_t * tor )
432{
433    return &tor->info;
434}
435
436/***
437****
438***/
439
440#if 0
441int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
442{
443    return tr_trackerScrape( tor, s, l, d );
444}
445#endif
446
447void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
448{
449    tr_free( tor->destination );
450    tor->destination = tr_strdup( path );
451
452    if( !tor->ioLoaded )
453         tor->ioLoaded = tr_ioLoadResume( tor ) == TR_OK;
454}
455
456const char* tr_torrentGetFolder( const tr_torrent_t * tor )
457{
458    return tor->destination;
459}
460
461
462/***********************************************************************
463 * torrentReallyStop
464 ***********************************************************************
465 * Joins the download thread and frees/closes everything related to it.
466 **********************************************************************/
467
468void tr_torrentDisablePex( tr_torrent_t * tor, int disable )
469{
470    tr_torrentWriterLock( tor );
471
472    if( ! ( TR_FLAG_PRIVATE & tor->info.flags ) )
473    {
474        if( tor->pexDisabled != disable )
475        {
476            int i;
477            tor->pexDisabled = disable;
478            for( i=0; i<tor->peerCount; ++i )
479                tr_peerSetPrivate( tor->peers[i], disable );
480        }
481    }
482
483    tr_torrentWriterUnlock( tor );
484}
485
486static int tr_didStateChangeTo ( tr_torrent_t * tor, int status )
487{
488    int ret;
489
490    tr_torrentWriterLock( tor );
491    if (( ret = tor->hasChangedState == status ))
492        tor->hasChangedState = -1;
493    tr_torrentWriterUnlock( tor );
494
495    return ret;
496}
497
498int tr_getIncomplete( tr_torrent_t * tor )
499{
500    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
501}
502int tr_getDone( tr_torrent_t * tor )
503{
504    return tr_didStateChangeTo( tor, TR_CP_DONE );
505}
506int tr_getComplete( tr_torrent_t * tor )
507{
508    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
509}
510
511void tr_manualUpdate( tr_torrent_t * tor UNUSED )
512{
513#if 0
514    int peerCount, new;
515    uint8_t * peerCompact;
516
517    if( tor->status != TR_RUN_RUNNING )
518        return;
519   
520    tr_torrentWriterLock( tor );
521    tr_trackerAnnouncePulse( tor->tracker, &peerCount, &peerCompact, 1 );
522    new = 0;
523    if( peerCount > 0 )
524    {
525        new = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
526                                    peerCompact, peerCount );
527        free( peerCompact );
528    }
529    tr_dbg( "got %i peers from manual announce, used %i", peerCount, new );
530    tr_torrentWriterUnlock( tor );
531#endif
532}
533
534const tr_stat_t *
535tr_torrentStat( tr_torrent_t * tor )
536{
537    tr_stat_t * s;
538    tr_tracker_t * tc;
539    int i;
540
541    tr_torrentReaderLock( tor );
542
543    tor->statCur = ( tor->statCur + 1 ) % 2;
544    s = &tor->stats[tor->statCur];
545
546    s->error  = tor->error;
547    memcpy( s->errorString, tor->errorString,
548            sizeof( s->errorString ) );
549
550    tc = tor->tracker;
551    s->cannotConnect = tr_trackerCannotConnect( tc );
552    s->tracker = tc
553        ? tr_trackerGet( tc )
554        : &tor->info.trackerList[0].list[0];
555
556    /* peers... */
557    memset( s->peersFrom, 0, sizeof( s->peersFrom ) );
558    s->peersTotal          = tor->peerCount;
559    s->peersConnected      = 0;
560    s->peersSendingToUs    = 0;
561    s->peersGettingFromUs  = 0;
562
563    for( i=0; i<tor->peerCount; ++i )
564    {
565        const tr_peer_t * peer = tor->peers[i];
566
567        if( tr_peerIsConnected( peer ) )
568        {
569            ++s->peersConnected;
570            ++s->peersFrom[tr_peerIsFrom(peer)];
571
572            if( tr_peerDownloadRate( peer ) > 0.01 )
573                ++s->peersSendingToUs;
574
575            if( tr_peerUploadRate( peer ) > 0.01 )
576                ++s->peersGettingFromUs;
577        }
578    }
579
580    s->percentDone     = tr_cpPercentDone     ( tor->completion );
581    s->percentComplete = tr_cpPercentComplete ( tor->completion );
582    s->left            = tr_cpLeftUntilDone   ( tor->completion );
583
584
585    if( tor->recheckFlag )
586        s->status = TR_STATUS_CHECK_WAIT;
587    else switch( tor->runStatus ) {
588        case TR_RUN_STOPPING: /* fallthrough */
589        case TR_RUN_STOPPING_NET_WAIT: s->status = TR_STATUS_STOPPING; break;
590        case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break;
591        case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break;
592        case TR_RUN_RUNNING: switch( tor->cpStatus ) {
593            case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break;
594            case TR_CP_DONE: s->status = TR_STATUS_DONE; break;
595            case TR_CP_COMPLETE: s->status = TR_STATUS_SEED; break;
596        }
597    }
598
599    s->cpStatus = tor->cpStatus;
600
601    /* tr_rcRate() doesn't make the difference between 'piece'
602       messages and other messages, which causes a non-zero
603       download rate even tough we are not downloading. So we
604       force it to zero not to confuse the user. */
605    s->rateDownload = tor->runStatus==TR_RUN_RUNNING
606        ? tr_rcRate( tor->download )
607        : 0.0;
608    s->rateUpload = tr_rcRate( tor->upload );
609   
610    s->seeders  = tr_trackerSeeders( tc );
611    s->leechers = tr_trackerLeechers( tc );
612    s->completedFromTracker = tr_trackerDownloaded( tc );
613
614    s->swarmspeed = tr_rcRate( tor->swarmspeed );
615   
616    s->startDate = tor->startDate;
617    s->activityDate = tor->activityDate;
618
619    s->eta = s->rateDownload < 0.1
620        ? -1.0f
621        : (s->left / s->rateDownload / 1024.0);
622
623    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
624    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
625    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
626   
627    s->ratio = s->downloaded || s->downloadedValid
628      ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid)
629      : TR_RATIO_NA; 
630   
631    tr_torrentReaderUnlock( tor );
632
633    return s;
634}
635
636/***
637****
638***/
639
640static uint64_t
641fileBytesCompleted ( const tr_torrent_t * tor, int fileIndex )
642{
643    const tr_file_t * file     =  &tor->info.files[fileIndex];
644    const uint64_t firstBlock       =  file->offset / tor->blockSize;
645    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
646    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
647    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
648    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
649    uint64_t haveBytes = 0;
650
651    assert( tor != NULL );
652    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
653    assert( file->offset + file->length <= tor->info.totalSize );
654    assert( (int)firstBlock < tor->blockCount );
655    assert( (int)lastBlock < tor->blockCount );
656    assert( firstBlock <= lastBlock );
657    assert( tr_blockPiece( firstBlock ) == file->firstPiece );
658    assert( tr_blockPiece( lastBlock ) == file->lastPiece );
659
660    if( firstBlock == lastBlock )
661    {
662        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
663            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
664    }
665    else
666    {
667        uint64_t i;
668
669        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
670            haveBytes += tor->blockSize - firstBlockOffset;
671
672        for( i=firstBlock+1; i<lastBlock; ++i )
673            if( tr_cpBlockIsComplete( tor->completion, i ) )
674               haveBytes += tor->blockSize;
675
676        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
677            haveBytes += lastBlockOffset + 1;
678    }
679
680    return haveBytes;
681}
682
683tr_file_stat_t *
684tr_torrentFiles( const tr_torrent_t * tor, int * fileCount )
685{
686    int i;
687    const int n = tor->info.fileCount;
688    tr_file_stat_t * files = tr_new0( tr_file_stat_t, n );
689    tr_file_stat_t * walk = files;
690
691    for( i=0; i<n; ++i, ++walk )
692    {
693        const tr_file_t * file = tor->info.files + i;
694        cp_status_t cp;
695
696        walk->bytesCompleted = fileBytesCompleted( tor, i );
697
698        walk->progress = file->length
699            ? walk->bytesCompleted / (float)file->length
700            : 1.0;
701
702        if( walk->bytesCompleted >= file->length )
703            cp = TR_CP_COMPLETE;
704        else if( tor->info.files[i].dnd )
705            cp = TR_CP_DONE;
706        else
707            cp = TR_CP_INCOMPLETE;
708
709        walk->completionStatus = cp;
710    }
711
712    *fileCount = n;
713
714    return files;
715}
716
717void
718tr_torrentFilesFree( tr_file_stat_t * files, int fileCount UNUSED )
719{
720    tr_free( files );
721}
722
723/***
724****
725***/
726
727tr_peer_stat_t *
728tr_torrentPeers( const tr_torrent_t * tor, int * peerCount )
729{
730    tr_peer_stat_t * peers;
731
732    tr_torrentReaderLock( tor );
733
734    *peerCount = tor->peerCount;
735   
736    peers = tr_new0( tr_peer_stat_t, tor->peerCount ); 
737    if (peers != NULL)
738    {
739        tr_peer_t * peer;
740        struct in_addr * addr;
741        int i;
742        for( i=0; i<tor->peerCount; ++i )
743        {
744            peer = tor->peers[i];
745           
746            addr = tr_peerAddress( peer );
747            if( NULL != addr )
748            {
749                tr_netNtop( addr, peers[i].addr,
750                           sizeof( peers[i].addr ) );
751            }
752           
753            peers[i].client           =  tr_peerClient( peer );
754            peers[i].isConnected      =  tr_peerIsConnected( peer );
755            peers[i].from             =  tr_peerIsFrom( peer );
756            peers[i].progress         =  tr_peerProgress( peer );
757            peers[i].port             =  tr_peerPort( peer );
758
759            peers[i].uploadToRate     =  tr_peerUploadRate( peer );
760            peers[i].downloadFromRate =  tr_peerDownloadRate( peer );
761           
762            peers[i].isDownloading    =  peers[i].uploadToRate > 0.01;
763            peers[i].isUploading      =  peers[i].downloadFromRate > 0.01;
764        }
765    }
766   
767    tr_torrentReaderUnlock( tor );
768   
769    return peers;
770}
771
772void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount UNUSED )
773{
774    tr_free( peers );
775}
776
777void tr_torrentAvailability( const tr_torrent_t * tor, int8_t * tab, int size )
778{
779    int i, j, piece;
780    float interval;
781
782    tr_torrentReaderLock( tor );
783
784    interval = (float)tor->info.pieceCount / (float)size;
785    for( i = 0; i < size; i++ )
786    {
787        piece = i * interval;
788
789        if( tr_cpPieceIsComplete( tor->completion, piece ) )
790        {
791            tab[i] = -1;
792            continue;
793        }
794
795        tab[i] = 0;
796        for( j = 0; j < tor->peerCount; j++ )
797        {
798            if( tr_peerHasPiece( tor->peers[j], piece ) )
799            {
800                (tab[i])++;
801            }
802        }
803    }
804
805    tr_torrentReaderUnlock( tor );
806}
807
808void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size )
809{
810    int i;
811    float interval;
812    tr_torrentReaderLock( tor );
813
814    interval = (float)tor->info.pieceCount / (float)size;
815    for( i = 0; i < size; i++ )
816    {
817        int piece = i * interval;
818        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
819    }
820
821    tr_torrentReaderUnlock( tor );
822}
823
824void
825tr_torrentResetTransferStats( tr_torrent_t * tor )
826{
827    tr_torrentWriterLock( tor );
828
829    tor->downloadedPrev += tor->downloadedCur;
830    tor->downloadedCur   = 0;
831    tor->uploadedPrev   += tor->uploadedCur;
832    tor->uploadedCur     = 0;
833
834    tr_torrentWriterUnlock( tor );
835}
836
837
838void
839tr_torrentSetHasPiece( tr_torrent_t * tor, int pieceIndex, int has )
840{
841    tr_torrentWriterLock( tor );
842
843    if( has )
844        tr_cpPieceAdd( tor->completion, pieceIndex );
845    else
846        tr_cpPieceRem( tor->completion, pieceIndex );
847
848    tr_torrentWriterUnlock( tor );
849}
850
851void tr_torrentRemoveSaved( tr_torrent_t * tor )
852{
853    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
854}
855
856void tr_torrentRecheck( tr_torrent_t * tor )
857{
858    tor->recheckFlag = TRUE;
859}
860
861
862int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
863{
864    int i;
865    tr_peer_t * otherPeer;
866
867    assert( tor != NULL );
868    assert( peer != NULL );
869
870    if( tor->peerCount >= TR_MAX_PEER_COUNT )
871    {
872        tr_peerDestroy(  peer );
873        return 0;
874    }
875
876    /* Don't accept two connections from the same IP */
877    for( i = 0; i < tor->peerCount; i++ )
878    {
879        otherPeer = tor->peers[i];
880        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
881        {
882            tr_peerDestroy(  peer );
883            return 0;
884        }
885    }
886
887    tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE ||
888                       tor->pexDisabled );
889    tr_peerSetTorrent( peer, tor );
890    tor->peers[tor->peerCount++] = peer;
891
892    return 1;
893}
894
895int tr_torrentAddCompact( tr_torrent_t * tor, int from,
896                           uint8_t * buf, int count )
897{
898    struct in_addr addr;
899    in_port_t port;
900    int i, added;
901    tr_peer_t * peer;
902
903    added = 0;
904    for( i = 0; i < count; i++ )
905    {
906        memcpy( &addr, buf, 4 ); buf += 4;
907        memcpy( &port, buf, 2 ); buf += 2;
908
909        peer = tr_peerInit( &addr, port, -1, from );
910        added += tr_torrentAttachPeer( tor, peer );
911    }
912
913    return added;
914}
915
916/***
917****
918***/
919
920static void setRunState( tr_torrent_t * tor, run_status_t run )
921{
922    tr_torrentWriterLock( tor );
923    tor->runStatus = run;
924    tr_torrentWriterUnlock( tor );
925}
926
927void tr_torrentStart( tr_torrent_t * tor )
928{
929    setRunState( tor, TR_RUN_RUNNING );
930}
931
932void tr_torrentStop( tr_torrent_t * tor )
933{
934    if((tor->runStatus != TR_RUN_STOPPING) && (tor->runStatus != TR_RUN_STOPPED) )
935        setRunState( tor, TR_RUN_STOPPING );
936}
937
938void tr_torrentClose( tr_torrent_t * tor )
939{
940    tr_torrentStop( tor );
941    tor->dieFlag = TRUE;
942}
943
944static void
945tr_torrentFree( tr_torrent_t * tor )
946{
947    tr_torrent_t * t;
948    tr_handle_t * h = tor->handle;
949    tr_info_t * inf = &tor->info;
950
951    tr_sharedLock( h->shared );
952
953    tr_rwClose( &tor->lock );
954    tr_cpClose( tor->completion );
955
956    tr_rcClose( tor->upload );
957    tr_rcClose( tor->download );
958    tr_rcClose( tor->swarmspeed );
959
960    tr_free( tor->destination );
961
962    tr_metainfoFree( inf );
963
964    if( tor == h->torrentList )
965        h->torrentList = tor->next;
966    else for( t=h->torrentList; t!=NULL; t=t->next ) {
967        if( t->next == tor ) {
968            t->next = tor->next;
969            break;
970        }
971    }
972
973    h->torrentCount--;
974
975    tr_inf( "closing torrent %s... %d torrents left",
976            tor->info.name, h->torrentCount );
977
978    tr_free( tor );
979
980    tr_sharedUnlock( h->shared );
981}
982
983static void
984recheckCpState( tr_torrent_t * tor )
985{
986    cp_status_t cpStatus;
987
988    tr_torrentWriterLock( tor );
989
990    cpStatus = tr_cpGetStatus( tor->completion );
991    if( cpStatus != tor->cpStatus ) {
992        tor->cpStatus = cpStatus;
993        tor->hasChangedState = tor->cpStatus;  /* tell the client... */
994        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
995            && tor->tracker!=NULL           /* and we have a tracker */
996            && tor->downloadedCur ) {        /* and it just happened */
997            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
998        }
999        tr_ioSync( tor->io );
1000    }
1001    tr_torrentWriterUnlock( tor );
1002}
1003
1004static void
1005torrentThreadLoop ( void * _tor )
1006{
1007    static tr_lock_t checkFilesLock;
1008    static int checkFilesLockInited = FALSE;
1009    tr_torrent_t * tor = _tor;
1010
1011    /* create the check-files mutex */
1012    if( !checkFilesLockInited ) {
1013         checkFilesLockInited = TRUE;
1014         tr_lockInit( &checkFilesLock );
1015    }
1016
1017    /* loop until the torrent is being deleted */
1018    while( ! ( tor->dieFlag && (tor->runStatus == TR_RUN_STOPPED) ) )
1019    {
1020        /* sleep a little while */
1021        tr_wait( tor->runStatus == TR_RUN_STOPPED ? 1600 : 600 );
1022
1023        if( tor->fastResumeDirty )
1024        {
1025            tor->fastResumeDirty = FALSE;
1026            fastResumeSave( tor );
1027            recheckCpState( tor );
1028        }
1029
1030        /* if we're stopping... */
1031        if( tor->runStatus == TR_RUN_STOPPING )
1032        {
1033            int i;
1034            int peerCount;
1035            uint8_t * peerCompact;
1036            tr_torrentWriterLock( tor );
1037
1038            /* close the IO */
1039            tr_ioClose( tor->io );
1040            tor->io = NULL;
1041
1042            /* close the peers */
1043            for( i=0; i<tor->peerCount; ++i )
1044                tr_peerDestroy( tor->peers[i] );
1045            tor->peerCount = 0;
1046
1047            /* resest the transfer rates */
1048            tr_rcReset( tor->download );
1049            tr_rcReset( tor->upload );
1050            tr_rcReset( tor->swarmspeed );
1051
1052            /* tell the tracker we're stopping */
1053            tr_trackerStopped( tor->tracker );
1054            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1055            tor->runStatus = TR_RUN_STOPPING_NET_WAIT;
1056            tor->stopDate = tr_date();
1057            tr_torrentWriterUnlock( tor );
1058        }
1059
1060        if( tor->runStatus == TR_RUN_STOPPING_NET_WAIT )
1061        {
1062            uint64_t date;
1063            int peerCount;
1064            uint8_t * peerCompact;
1065            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1066
1067            /* have we finished telling the tracker that we're stopping? */
1068            date = tr_trackerLastResponseDate( tor->tracker );
1069            if( date > tor->stopDate )
1070            {
1071                tr_torrentWriterLock( tor );
1072                tr_trackerClose( tor->tracker );
1073                tor->tracker = NULL;
1074                tor->runStatus = TR_RUN_STOPPED;
1075                tr_torrentWriterUnlock( tor );
1076            }
1077            continue;
1078        }
1079
1080        /* do we need to check files? */
1081        if( tor->recheckFlag )
1082        {
1083            if( !tr_lockTryLock( &checkFilesLock ) )
1084            {
1085                run_status_t realStatus;
1086
1087                tr_torrentWriterLock( tor );
1088                realStatus = tor->runStatus;
1089                tor->recheckFlag = FALSE;
1090                tor->runStatus = TR_RUN_CHECKING;
1091                tr_torrentWriterUnlock( tor );
1092
1093                tr_ioCheckFiles( tor, TR_RECHECK_FORCE );
1094                setRunState( tor, realStatus );
1095
1096                tr_torrentWriterLock( tor );
1097                tor->cpStatus = tr_cpGetStatus( tor->completion );
1098                tr_torrentWriterUnlock( tor );
1099
1100                tr_lockUnlock( &checkFilesLock );
1101            }
1102            continue;
1103        }
1104
1105        /* if we're paused or stopped, not much to do... */
1106        if( tor->runStatus == TR_RUN_STOPPED )
1107            continue;
1108
1109        /* ping our peers if we're running... */
1110        if( tor->runStatus == TR_RUN_RUNNING )
1111        {
1112            int i;
1113            int peerCount;
1114            uint8_t * peerCompact;
1115
1116            /* starting to run... */
1117            if( tor->io == NULL ) {
1118                *tor->errorString = '\0';
1119                tr_torrentResetTransferStats( tor );
1120                tor->io = tr_ioInitFast( tor );
1121                if( tor->io == NULL ) {
1122                    tor->recheckFlag = TRUE;
1123                    continue;
1124                }
1125                tr_peerIdNew ( tor->peer_id, sizeof(tor->peer_id) );
1126                tor->tracker = tr_trackerInit( tor );
1127                tor->startDate = tr_date();
1128            }
1129
1130            /* refresh our completion state */
1131            recheckCpState( tor );
1132
1133            /* ping the tracker... */
1134            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1135            if( peerCount > 0 ) {
1136                int used = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
1137                                                 peerCompact, peerCount );
1138                tr_dbg( "got %i peers from announce, used %i", peerCount, used );
1139                free( peerCompact );
1140            }
1141
1142            /* Shuffle peers */
1143            if ( tor->peerCount > 1 ) {
1144                tr_peer_t * tmp = tor->peers[0];
1145                memmove( tor->peers, tor->peers+1,
1146                        (tor->peerCount-1) * sizeof(void*) );
1147                tor->peers[tor->peerCount - 1] = tmp;
1148            }
1149
1150            /* receive/send messages */
1151            tr_torrentWriterLock( tor );
1152            for( i=0; i<tor->peerCount; ) {
1153                tr_peer_t * peer = tor->peers[i];
1154                int ret = tr_peerPulse( peer );
1155                if( ret & TR_ERROR_IO_MASK ) {
1156                    tr_err( "Fatal error, stopping download (%d)", ret );
1157                    tor->runStatus = TR_RUN_STOPPING;
1158                    tor->error = ret;
1159                    strlcpy( tor->errorString,
1160                             tr_errorString(ret),
1161                             sizeof(tor->errorString) );
1162                    break;
1163                }
1164                if( ret ) {
1165                    tr_peerDestroy( peer );
1166                    tor->peerCount--;
1167                    memmove( &tor->peers[i], &tor->peers[i+1],
1168                             (tor->peerCount-i)*sizeof(void*) );
1169                    continue;
1170                }
1171                i++;
1172            }
1173            tr_torrentWriterUnlock( tor );
1174        }
1175    }
1176
1177    tr_ioClose( tor->io );
1178    tr_torrentFree( tor );
1179}
1180
1181
1182/**
1183***  File priorities
1184**/
1185
1186void
1187tr_torrentSetFilePriority( tr_torrent_t   * tor,
1188                           int              fileIndex,
1189                           tr_priority_t    priority )
1190{
1191    int i;
1192    tr_file_t * file;
1193
1194    tr_torrentWriterLock( tor );
1195
1196    assert( tor != NULL );
1197    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1198    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1199
1200    file = &tor->info.files[fileIndex];
1201    file->priority = priority;
1202    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1203      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1204
1205    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1206             fileIndex, file->firstPiece, file->lastPiece,
1207             priority, tor->info.files[fileIndex].name );
1208
1209    tor->fastResumeDirty = TRUE;
1210
1211    tr_torrentWriterUnlock( tor );
1212}
1213
1214void
1215tr_torrentSetFilePriorities( tr_torrent_t        * tor,
1216                             int                 * files,
1217                             int                   fileCount,
1218                             tr_priority_t         priority )
1219{
1220    int i;
1221    for( i=0; i<fileCount; ++i )
1222        tr_torrentSetFilePriority( tor, files[i], priority );
1223}
1224
1225tr_priority_t
1226tr_torrentGetFilePriority( const tr_torrent_t *  tor, int file )
1227{
1228    tr_priority_t ret;
1229
1230    tr_torrentReaderLock( tor );
1231    assert( tor != NULL );
1232    assert( 0<=file && file<tor->info.fileCount );
1233    ret = tor->info.files[file].priority;
1234    tr_torrentReaderUnlock( tor );
1235
1236    return ret;
1237}
1238
1239
1240tr_priority_t*
1241tr_torrentGetFilePriorities( const tr_torrent_t * tor )
1242{
1243    int i;
1244    tr_priority_t * p;
1245
1246    tr_torrentReaderLock( tor );
1247    p = tr_new0( tr_priority_t, tor->info.fileCount );
1248    for( i=0; i<tor->info.fileCount; ++i )
1249        p[i] = tor->info.files[i].priority;
1250    tr_torrentReaderUnlock( tor );
1251
1252    return p;
1253}
1254
1255/**
1256***  File DND
1257**/
1258
1259int
1260tr_torrentGetFileDL( const tr_torrent_t * tor,
1261                     int                  file )
1262{
1263    int doDownload;
1264    tr_torrentReaderLock( tor );
1265
1266    assert( 0<=file && file<tor->info.fileCount );
1267    doDownload = !tor->info.files[file].dnd;
1268
1269    tr_torrentReaderUnlock( tor );
1270    return doDownload != 0;
1271}
1272
1273void
1274tr_torrentSetFileDL( tr_torrent_t  * tor,
1275                     int             fileIndex,
1276                     int             doDownload )
1277{
1278    tr_file_t * file;
1279    const int dnd = !doDownload;
1280    int firstPiece, firstPieceDND;
1281    int lastPiece, lastPieceDND;
1282    int i;
1283
1284    tr_torrentWriterLock( tor );
1285
1286    file = &tor->info.files[fileIndex];
1287    file->dnd = dnd;
1288    firstPiece = file->firstPiece;
1289    lastPiece = file->lastPiece;
1290
1291    /* can't set the first piece to DND unless
1292       every file using that piece is DND */
1293    firstPieceDND = dnd;
1294    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1295        if( tor->info.files[i].lastPiece != firstPiece )
1296            break;
1297        firstPieceDND = tor->info.files[i].dnd;
1298    }
1299
1300    /* can't set the last piece to DND unless
1301       every file using that piece is DND */
1302    lastPieceDND = dnd;
1303    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1304        if( tor->info.files[i].firstPiece != lastPiece )
1305            break;
1306        lastPieceDND = tor->info.files[i].dnd;
1307    }
1308
1309    if( firstPiece == lastPiece )
1310    {
1311        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1312    }
1313    else
1314    {
1315        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1316        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1317        for( i=firstPiece+1; i<lastPiece; ++i )
1318            tor->info.pieces[i].dnd = dnd;
1319    }
1320
1321    tor->fastResumeDirty = TRUE;
1322
1323    tr_torrentWriterUnlock( tor );
1324}
1325
1326void
1327tr_torrentSetFileDLs ( tr_torrent_t   * tor,
1328                       int            * files,
1329                       int              fileCount,
1330                       int              doDownload )
1331{
1332    int i;
1333    for( i=0; i<fileCount; ++i )
1334        tr_torrentSetFileDL( tor, files[i], doDownload );
1335}
1336
1337/***
1338****
1339***/
1340
1341int _tr_blockPiece( const tr_torrent_t * tor, int block )
1342{
1343    const tr_info_t * inf = &tor->info;
1344    return block / ( inf->pieceSize / tor->blockSize );
1345}
1346
1347int _tr_blockSize( const tr_torrent_t * tor, int block )
1348{
1349    const tr_info_t * inf = &tor->info;
1350    int dummy;
1351
1352    if( block != tor->blockCount - 1 ||
1353        !( dummy = inf->totalSize % tor->blockSize ) )
1354    {
1355        return tor->blockSize;
1356    }
1357
1358    return dummy;
1359}
1360
1361int _tr_blockPosInPiece( const tr_torrent_t * tor, int block )
1362{
1363    const tr_info_t * inf = &tor->info;
1364    return tor->blockSize *
1365        ( block % ( inf->pieceSize / tor->blockSize ) );
1366}
1367
1368int _tr_pieceCountBlocks( const tr_torrent_t * tor, int piece )
1369{
1370    const tr_info_t * inf = &tor->info;
1371    if( piece < inf->pieceCount - 1 ||
1372        !( tor->blockCount % ( inf->pieceSize / tor->blockSize ) ) )
1373    {
1374        return inf->pieceSize / tor->blockSize;
1375    }
1376    return tor->blockCount % ( inf->pieceSize / tor->blockSize );
1377}
1378
1379int _tr_pieceStartBlock( const tr_torrent_t * tor, int piece )
1380{
1381    const tr_info_t * inf = &tor->info;
1382    return piece * ( inf->pieceSize / tor->blockSize );
1383}
1384
1385int _tr_pieceSize( const tr_torrent_t * tor, int piece )
1386{
1387    const tr_info_t * inf = &tor->info;
1388    if( piece < inf->pieceCount - 1 ||
1389        !( inf->totalSize % inf->pieceSize ) )
1390    {
1391        return inf->pieceSize;
1392    }
1393    return inf->totalSize % inf->pieceSize;
1394}
1395
1396int _tr_block( const tr_torrent_t * tor, int index, int begin )
1397{
1398    const tr_info_t * inf = &tor->info;
1399    return index * ( inf->pieceSize / tor->blockSize ) +
1400        begin / tor->blockSize;
1401}
Note: See TracBrowser for help on using the repository browser.