source: trunk/libtransmission/torrent.c @ 2872

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

better fix for the leak in r2870

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