source: trunk/libtransmission/torrent.c @ 2843

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

update version.sh to exclude third-party. add $Id:$ to the new files s.t. version.sh will find them.

  • Property svn:keywords set to Date Rev Author Id
File size: 38.3 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 2843 2007-08-18 17:05:51Z 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;
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
417    if( ret == TR_OK )
418        ret = infoCanAdd( h, destination, setme_info );
419
420    /* if we allocated a temporary info, free it */
421    if( !ret && ( setme_info == &tmp ) )
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;
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
468    if( ret == TR_OK )
469        ret = infoCanAdd( h, destination, setme_info );
470
471    if( setme_info == &tmp )
472        tr_metainfoFree( &tmp );
473
474    return ret;
475}
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;
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
520    if( ret == TR_OK )
521        ret = infoCanAdd( h, destination, setme_info );
522
523    if( setme_info == &tmp )
524        tr_metainfoFree( &tmp );
525
526    return ret;
527}
528
529tr_torrent_t *
530tr_torrentInitData( tr_handle_t    * h,
531                    const uint8_t  * data,
532                    size_t           size,
533                    const char     * destination,
534                    int              flags,
535                    int            * error )
536{
537    int val;
538    int tmpError;
539    tr_torrent_t * tor = NULL;
540
541    if( !error )
542         error = &tmpError;
543
544    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
545        *error = val;
546    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
547        *error = TR_EOTHER;
548    else {
549        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
550        torrentRealInit( h, tor, destination, flags );
551    }
552
553    return tor;
554}
555
556const tr_info_t *
557tr_torrentInfo( const tr_torrent_t * tor )
558{
559    return &tor->info;
560}
561
562/***
563****
564***/
565
566#if 0
567int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
568{
569    return tr_trackerScrape( tor, s, l, d );
570}
571#endif
572
573static void
574fastResumeSave( tr_torrent_t * tor )
575{
576    tr_fastResumeSave( tor );
577    tor->fastResumeDirty = FALSE;
578}
579
580void
581tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
582{
583    tr_free( tor->destination );
584    tor->destination = tr_strdup( path );
585    fastResumeSave( tor );
586}
587
588const char*
589tr_torrentGetFolder( const tr_torrent_t * tor )
590{
591    return tor->destination;
592}
593
594void
595tr_torrentChangeMyPort( tr_torrent_t * tor, int port )
596{
597    tr_torrentWriterLock( tor );
598
599    tor->publicPort = port;
600
601    if( tor->tracker )
602        tr_trackerChangeMyPort( tor->tracker );
603
604    tr_torrentWriterUnlock( tor );
605}
606
607/***********************************************************************
608 * torrentReallyStop
609 ***********************************************************************
610 * Joins the download thread and frees/closes everything related to it.
611 **********************************************************************/
612
613void tr_torrentDisablePex( tr_torrent_t * tor, int disable )
614{
615    tr_torrentWriterLock( tor );
616
617    if( ! ( TR_FLAG_PRIVATE & tor->info.flags ) )
618    {
619        if( tor->pexDisabled != disable )
620        {
621            int i;
622            tor->pexDisabled = disable;
623            for( i=0; i<tor->peerCount; ++i )
624                tr_peerSetPrivate( tor->peers[i], disable );
625        }
626    }
627
628    tr_torrentWriterUnlock( tor );
629}
630
631static int tr_didStateChangeTo ( tr_torrent_t * tor, int status )
632{
633    int ret;
634
635    tr_torrentWriterLock( tor );
636    if (( ret = tor->hasChangedState == status ))
637        tor->hasChangedState = -1;
638    tr_torrentWriterUnlock( tor );
639
640    return ret;
641}
642
643int tr_getIncomplete( tr_torrent_t * tor )
644{
645    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
646}
647int tr_getDone( tr_torrent_t * tor )
648{
649    return tr_didStateChangeTo( tor, TR_CP_DONE );
650}
651int tr_getComplete( tr_torrent_t * tor )
652{
653    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
654}
655
656void
657tr_manualUpdate( tr_torrent_t * tor )
658{
659    if( tor->runStatus == TR_RUN_RUNNING )
660        tr_trackerReannounce( tor->tracker );
661}
662int
663tr_torrentCanManualUpdate( const tr_torrent_t * tor )
664{
665    return ( tor != NULL )
666        && ( tor->runStatus == TR_RUN_RUNNING )
667        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
668}
669
670const tr_stat_t *
671tr_torrentStat( tr_torrent_t * tor )
672{
673    tr_stat_t * s;
674    struct tr_tracker_s * tc;
675    int i;
676
677    tr_torrentReaderLock( tor );
678
679    tor->statCur = ( tor->statCur + 1 ) % 2;
680    s = &tor->stats[tor->statCur];
681
682    s->error  = tor->error;
683    memcpy( s->errorString, tor->errorString,
684            sizeof( s->errorString ) );
685
686    tc = tor->tracker;
687    s->tracker = tr_trackerGetAddress( tor->tracker );
688
689    /* peers... */
690    memset( s->peersFrom, 0, sizeof( s->peersFrom ) );
691    s->peersTotal          = tor->peerCount;
692    s->peersConnected      = 0;
693    s->peersSendingToUs    = 0;
694    s->peersGettingFromUs  = 0;
695
696    for( i=0; i<tor->peerCount; ++i )
697    {
698        const tr_peer_t * peer = tor->peers[i];
699
700        if( tr_peerIsConnected( peer ) )
701        {
702            ++s->peersConnected;
703            ++s->peersFrom[tr_peerIsFrom(peer)];
704
705            if( tr_peerDownloadRate( peer ) > 0.01 )
706                ++s->peersSendingToUs;
707
708            if( tr_peerUploadRate( peer ) > 0.01 )
709                ++s->peersGettingFromUs;
710        }
711    }
712
713    s->percentComplete = tr_cpPercentComplete ( tor->completion );
714
715    s->percentDone = tr_cpPercentDone( tor->completion );
716    s->left = tr_cpLeftUntilDone( tor->completion );
717
718    if( tor->uncheckedPieces )
719        s->status = tor->runStatus==TR_RUN_CHECKING
720            ? TR_STATUS_CHECK
721            : TR_STATUS_CHECK_WAIT;
722    else switch( tor->runStatus ) {
723        case TR_RUN_STOPPING: /* fallthrough */
724        case TR_RUN_STOPPING_NET_WAIT: s->status = TR_STATUS_STOPPING; break;
725        case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break;
726        case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break;
727        case TR_RUN_RUNNING: switch( tor->cpStatus ) {
728            case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break;
729            case TR_CP_DONE: s->status = TR_STATUS_DONE; break;
730            case TR_CP_COMPLETE: s->status = TR_STATUS_SEED; break;
731        }
732    }
733
734    s->recheckProgress = (tor->uncheckedPieces == NULL)
735        ? 0.0
736        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
737
738    s->cpStatus = tor->cpStatus;
739
740    /* tr_rcRate() doesn't make the difference between 'piece'
741       messages and other messages, which causes a non-zero
742       download rate even tough we are not downloading. So we
743       force it to zero not to confuse the user. */
744    s->rateDownload = tor->runStatus==TR_RUN_RUNNING
745        ? tr_rcRate( tor->download )
746        : 0.0;
747    s->rateUpload = tr_rcRate( tor->upload );
748   
749    tr_trackerGetCounts( tc,
750                         &s->completedFromTracker,
751                         &s->leechers, 
752                         &s->seeders );
753
754    s->swarmspeed = tr_rcRate( tor->swarmspeed );
755   
756    s->startDate = tor->startDate;
757    s->activityDate = tor->activityDate;
758
759    s->eta = s->rateDownload < 0.1
760        ? -1.0f
761        : (s->left / s->rateDownload / 1024.0);
762
763    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
764    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
765    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
766   
767    s->ratio = s->downloaded || s->downloadedValid
768      ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid)
769      : TR_RATIO_NA; 
770   
771    tr_torrentReaderUnlock( tor );
772
773    return s;
774}
775
776/***
777****
778***/
779
780static uint64_t
781fileBytesCompleted ( const tr_torrent_t * tor, int fileIndex )
782{
783    const tr_file_t * file     =  &tor->info.files[fileIndex];
784    const uint64_t firstBlock       =  file->offset / tor->blockSize;
785    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
786    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
787    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
788    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
789    uint64_t haveBytes = 0;
790
791    assert( tor != NULL );
792    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
793    assert( file->offset + file->length <= tor->info.totalSize );
794    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
795    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
796    assert( firstBlock <= lastBlock );
797    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
798    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
799
800    if( firstBlock == lastBlock )
801    {
802        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
803            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
804    }
805    else
806    {
807        uint64_t i;
808
809        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
810            haveBytes += tor->blockSize - firstBlockOffset;
811
812        for( i=firstBlock+1; i<lastBlock; ++i )
813            if( tr_cpBlockIsComplete( tor->completion, i ) )
814               haveBytes += tor->blockSize;
815
816        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
817            haveBytes += lastBlockOffset + 1;
818    }
819
820    return haveBytes;
821}
822
823tr_file_stat_t *
824tr_torrentFiles( const tr_torrent_t * tor, int * fileCount )
825{
826    int i;
827    const int n = tor->info.fileCount;
828    tr_file_stat_t * files = tr_new0( tr_file_stat_t, n );
829    tr_file_stat_t * walk = files;
830
831    for( i=0; i<n; ++i, ++walk )
832    {
833        const tr_file_t * file = tor->info.files + i;
834        cp_status_t cp;
835
836        walk->bytesCompleted = fileBytesCompleted( tor, i );
837
838        walk->progress = file->length
839            ? walk->bytesCompleted / (float)file->length
840            : 1.0;
841
842        if( walk->bytesCompleted >= file->length )
843            cp = TR_CP_COMPLETE;
844        else if( tor->info.files[i].dnd )
845            cp = TR_CP_DONE;
846        else
847            cp = TR_CP_INCOMPLETE;
848
849        walk->completionStatus = cp;
850    }
851
852    *fileCount = n;
853
854    return files;
855}
856
857void
858tr_torrentFilesFree( tr_file_stat_t * files, int fileCount UNUSED )
859{
860    tr_free( files );
861}
862
863/***
864****
865***/
866
867tr_peer_stat_t *
868tr_torrentPeers( const tr_torrent_t * tor, int * peerCount )
869{
870    tr_peer_stat_t * peers;
871
872    tr_torrentReaderLock( tor );
873
874    *peerCount = tor->peerCount;
875   
876    peers = tr_new0( tr_peer_stat_t, tor->peerCount ); 
877    if (peers != NULL)
878    {
879        tr_peer_t * peer;
880        struct in_addr * addr;
881        int i;
882        for( i=0; i<tor->peerCount; ++i )
883        {
884            peer = tor->peers[i];
885           
886            addr = tr_peerAddress( peer );
887            if( NULL != addr )
888            {
889                tr_netNtop( addr, peers[i].addr,
890                           sizeof( peers[i].addr ) );
891            }
892           
893            peers[i].client           =  tr_peerClient( peer );
894            peers[i].isConnected      =  tr_peerIsConnected( peer );
895            peers[i].from             =  tr_peerIsFrom( peer );
896            peers[i].progress         =  tr_peerProgress( peer );
897            peers[i].port             =  tr_peerPort( peer );
898
899            peers[i].uploadToRate     =  tr_peerUploadRate( peer );
900            peers[i].downloadFromRate =  tr_peerDownloadRate( peer );
901           
902            peers[i].isDownloading    =  peers[i].uploadToRate > 0.01;
903            peers[i].isUploading      =  peers[i].downloadFromRate > 0.01;
904        }
905    }
906   
907    tr_torrentReaderUnlock( tor );
908   
909    return peers;
910}
911
912void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount UNUSED )
913{
914    tr_free( peers );
915}
916
917void tr_torrentAvailability( const tr_torrent_t * tor, int8_t * tab, int size )
918{
919    int i, j, piece;
920    float interval;
921
922    tr_torrentReaderLock( tor );
923
924    interval = (float)tor->info.pieceCount / (float)size;
925    for( i = 0; i < size; i++ )
926    {
927        piece = i * interval;
928
929        if( tr_cpPieceIsComplete( tor->completion, piece ) )
930        {
931            tab[i] = -1;
932            continue;
933        }
934
935        tab[i] = 0;
936        for( j = 0; j < tor->peerCount; j++ )
937        {
938            if( tr_peerHasPiece( tor->peers[j], piece ) )
939            {
940                (tab[i])++;
941            }
942        }
943    }
944
945    tr_torrentReaderUnlock( tor );
946}
947
948void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size )
949{
950    int i;
951    float interval;
952    tr_torrentReaderLock( tor );
953
954    interval = (float)tor->info.pieceCount / (float)size;
955    for( i = 0; i < size; i++ )
956    {
957        int piece = i * interval;
958        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
959    }
960
961    tr_torrentReaderUnlock( tor );
962}
963
964void
965tr_torrentResetTransferStats( tr_torrent_t * tor )
966{
967    tr_torrentWriterLock( tor );
968
969    tor->downloadedPrev += tor->downloadedCur;
970    tor->downloadedCur   = 0;
971    tor->uploadedPrev   += tor->uploadedCur;
972    tor->uploadedCur     = 0;
973
974    tr_torrentWriterUnlock( tor );
975}
976
977
978void
979tr_torrentSetHasPiece( tr_torrent_t * tor, int pieceIndex, int has )
980{
981    tr_torrentWriterLock( tor );
982
983    if( has )
984        tr_cpPieceAdd( tor->completion, pieceIndex );
985    else
986        tr_cpPieceRem( tor->completion, pieceIndex );
987
988    tr_torrentWriterUnlock( tor );
989}
990
991void tr_torrentRemoveSaved( tr_torrent_t * tor )
992{
993    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
994}
995
996void tr_torrentRecheck( tr_torrent_t * tor )
997{
998    if( !tor->uncheckedPieces )
999        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
1000    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
1001}
1002
1003
1004int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
1005{
1006    int i;
1007    tr_peer_t * otherPeer;
1008
1009    assert( tor != NULL );
1010    assert( peer != NULL );
1011
1012    if( tor->peerCount >= TR_MAX_PEER_COUNT )
1013    {
1014        tr_peerDestroy(  peer );
1015        return 0;
1016    }
1017
1018    /* Don't accept two connections from the same IP */
1019    for( i = 0; i < tor->peerCount; i++ )
1020    {
1021        otherPeer = tor->peers[i];
1022        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
1023        {
1024            tr_peerDestroy(  peer );
1025            return 0;
1026        }
1027    }
1028
1029    tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE ||
1030                       tor->pexDisabled );
1031    tr_peerSetTorrent( peer, tor );
1032    tor->peers[tor->peerCount++] = peer;
1033
1034    return 1;
1035}
1036
1037int tr_torrentAddCompact( tr_torrent_t * tor, int from,
1038                           const uint8_t * buf, int count )
1039{
1040    struct in_addr addr;
1041    tr_port_t port;
1042    int i, added;
1043    tr_peer_t * peer;
1044
1045    added = 0;
1046    for( i=0; i<count; ++i )
1047    {
1048        memcpy( &addr, buf, 4 ); buf += 4;
1049        memcpy( &port, buf, 2 ); buf += 2;
1050        peer = tr_peerInit( &addr, port, -1, from );
1051        added += tr_torrentAttachPeer( tor, peer );
1052    }
1053
1054    return added;
1055}
1056
1057/***
1058****
1059***/
1060
1061static void setRunState( tr_torrent_t * tor, run_status_t run )
1062{
1063    tr_torrentWriterLock( tor );
1064    tor->runStatus = run;
1065    tr_torrentWriterUnlock( tor );
1066}
1067
1068void tr_torrentStart( tr_torrent_t * tor )
1069{
1070    setRunState( tor, TR_RUN_RUNNING );
1071
1072    tr_trackerStart( tor->tracker );
1073}
1074
1075void tr_torrentStop( tr_torrent_t * tor )
1076{
1077    if((tor->runStatus != TR_RUN_STOPPING) && (tor->runStatus != TR_RUN_STOPPED) )
1078        setRunState( tor, TR_RUN_STOPPING );
1079}
1080
1081void tr_torrentClose( tr_torrent_t * tor )
1082{
1083    tr_torrentStop( tor );
1084    tor->dieFlag = TRUE;
1085}
1086
1087static void
1088tr_torrentFree( tr_torrent_t * tor )
1089{
1090    tr_torrent_t * t;
1091    tr_handle_t * h = tor->handle;
1092    tr_info_t * inf = &tor->info;
1093
1094    tr_sharedLock( h->shared );
1095
1096    tr_rwFree( tor->lock );
1097    tr_cpClose( tor->completion );
1098
1099    tr_rcClose( tor->upload );
1100    tr_rcClose( tor->download );
1101    tr_rcClose( tor->swarmspeed );
1102
1103    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
1104    tr_trackerFree( tor->tracker );
1105    tor->tracker = NULL;
1106
1107    tr_free( tor->destination );
1108
1109    tr_metainfoFree( inf );
1110
1111    if( tor == h->torrentList )
1112        h->torrentList = tor->next;
1113    else for( t=h->torrentList; t!=NULL; t=t->next ) {
1114        if( t->next == tor ) {
1115            t->next = tor->next;
1116            break;
1117        }
1118    }
1119
1120    h->torrentCount--;
1121
1122    tr_inf( "closing torrent %s... %d torrents left",
1123            tor->info.name, h->torrentCount );
1124
1125    tr_free( tor );
1126
1127    tr_sharedUnlock( h->shared );
1128}
1129
1130static void
1131recheckCpState( tr_torrent_t * tor )
1132{
1133    cp_status_t cpStatus;
1134
1135    tr_torrentWriterLock( tor );
1136
1137    cpStatus = tr_cpGetStatus( tor->completion );
1138    if( cpStatus != tor->cpStatus ) {
1139        tor->cpStatus = cpStatus;
1140        tor->hasChangedState = tor->cpStatus;  /* tell the client... */
1141        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1142            && tor->downloadedCur ) {        /* and it just happened */
1143            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1144        }
1145        tr_ioSync( tor->io );
1146        fastResumeSave( tor );
1147    }
1148    tr_torrentWriterUnlock( tor );
1149}
1150
1151static void
1152torrentThreadLoop ( void * _tor )
1153{
1154    static tr_lock_t * checkFilesLock = NULL;
1155    tr_torrent_t * tor = _tor;
1156
1157    /* create the check-files mutex */
1158    if( !checkFilesLock )
1159         checkFilesLock = tr_lockNew( );
1160
1161    /* loop until the torrent is being deleted */
1162    while( ! ( tor->dieFlag && (tor->runStatus == TR_RUN_STOPPED) ) )
1163    {
1164        /* sleep a little while */
1165        tr_wait( tor->runStatus == TR_RUN_STOPPED ? 1600 : 600 );
1166
1167        if( tor->fastResumeDirty )
1168        {
1169            fastResumeSave( tor );
1170            recheckCpState( tor );
1171        }
1172
1173        /* if we're stopping... */
1174        if( tor->runStatus == TR_RUN_STOPPING )
1175        {
1176            int i;
1177            tr_torrentWriterLock( tor );
1178
1179            /* close the IO */
1180            tr_ioClose( tor->io );
1181            tor->io = NULL;
1182            fastResumeSave( tor );
1183
1184            /* close the peers */
1185            for( i=0; i<tor->peerCount; ++i )
1186                tr_peerDestroy( tor->peers[i] );
1187            tor->peerCount = 0;
1188
1189            /* resest the transfer rates */
1190            tr_rcReset( tor->download );
1191            tr_rcReset( tor->upload );
1192            tr_rcReset( tor->swarmspeed );
1193
1194            /* tell the tracker we're stopping */
1195            tr_trackerStop( tor->tracker );
1196            tor->runStatus = TR_RUN_STOPPING_NET_WAIT;
1197            tor->stopDate = tr_date();
1198            tr_torrentWriterUnlock( tor );
1199        }
1200
1201        if( tor->runStatus == TR_RUN_STOPPING_NET_WAIT )
1202        {
1203#if 0
1204            tr_torrentWriterLock( tor );
1205            tor->runStatus = TR_RUN_STOPPED;
1206            tr_torrentWriterUnlock( tor );
1207#endif
1208            continue;
1209        }
1210
1211        /* do we need to check files? */
1212        if( tor->uncheckedPieces )
1213        {
1214            if( !tr_lockTryLock( checkFilesLock ) )
1215            {
1216                run_status_t realStatus;
1217
1218                tr_torrentWriterLock( tor );
1219                realStatus = tor->runStatus;
1220                tor->runStatus = TR_RUN_CHECKING;
1221                tr_torrentWriterUnlock( tor );
1222
1223                tr_ioCheckFiles( tor );
1224                setRunState( tor, realStatus );
1225
1226                tr_torrentWriterLock( tor );
1227                tor->cpStatus = tr_cpGetStatus( tor->completion );
1228                tr_torrentWriterUnlock( tor );
1229
1230                tr_lockUnlock( checkFilesLock );
1231            }
1232            continue;
1233        }
1234
1235        /* if we're paused or stopped, not much to do... */
1236        if( tor->runStatus == TR_RUN_STOPPED )
1237            continue;
1238
1239        /* ping our peers if we're running... */
1240        if( tor->runStatus == TR_RUN_RUNNING )
1241        {
1242            int i;
1243
1244            /* starting to run... */
1245            if( tor->io == NULL )
1246            {
1247                *tor->errorString = '\0';
1248                tr_torrentResetTransferStats( tor );
1249                tor->io = tr_ioNew( tor );
1250                tor->startDate = tr_date();
1251            }
1252
1253            /* refresh our completion state */
1254            recheckCpState( tor );
1255
1256            /* Shuffle peers */
1257            if ( tor->peerCount > 1 ) {
1258                tr_peer_t * tmp = tor->peers[0];
1259                memmove( tor->peers, tor->peers+1,
1260                        (tor->peerCount-1) * sizeof(void*) );
1261                tor->peers[tor->peerCount - 1] = tmp;
1262            }
1263
1264            /* receive/send messages */
1265            tr_torrentWriterLock( tor );
1266            for( i=0; i<tor->peerCount; ) {
1267                tr_peer_t * peer = tor->peers[i];
1268                int ret = tr_peerPulse( peer );
1269                if( ret & TR_ERROR_IO_MASK ) {
1270                    tr_err( "Fatal error, stopping download (%d)", ret );
1271                    tor->runStatus = TR_RUN_STOPPING;
1272                    tor->error = ret;
1273                    strlcpy( tor->errorString,
1274                             tr_errorString(ret),
1275                             sizeof(tor->errorString) );
1276                    break;
1277                }
1278                if( ret ) {
1279                    tr_peerDestroy( peer );
1280                    tor->peerCount--;
1281                    memmove( &tor->peers[i], &tor->peers[i+1],
1282                             (tor->peerCount-i)*sizeof(void*) );
1283                    continue;
1284                }
1285                i++;
1286            }
1287            tr_torrentWriterUnlock( tor );
1288        }
1289    }
1290
1291    tr_ioClose( tor->io );
1292    tr_torrentFree( tor );
1293}
1294
1295
1296/**
1297***  File priorities
1298**/
1299
1300void
1301tr_torrentSetFilePriority( tr_torrent_t   * tor,
1302                           int              fileIndex,
1303                           tr_priority_t    priority )
1304{
1305    int i;
1306    tr_file_t * file;
1307
1308    tr_torrentWriterLock( tor );
1309
1310    assert( tor != NULL );
1311    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1312    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1313
1314    file = &tor->info.files[fileIndex];
1315    file->priority = priority;
1316    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1317      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1318
1319    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1320             fileIndex, file->firstPiece, file->lastPiece,
1321             priority, tor->info.files[fileIndex].name );
1322
1323    tor->fastResumeDirty = TRUE;
1324
1325    tr_torrentWriterUnlock( tor );
1326}
1327
1328void
1329tr_torrentSetFilePriorities( tr_torrent_t        * tor,
1330                             int                 * files,
1331                             int                   fileCount,
1332                             tr_priority_t         priority )
1333{
1334    int i;
1335    for( i=0; i<fileCount; ++i )
1336        tr_torrentSetFilePriority( tor, files[i], priority );
1337}
1338
1339tr_priority_t
1340tr_torrentGetFilePriority( const tr_torrent_t *  tor, int file )
1341{
1342    tr_priority_t ret;
1343
1344    tr_torrentReaderLock( tor );
1345    assert( tor != NULL );
1346    assert( 0<=file && file<tor->info.fileCount );
1347    ret = tor->info.files[file].priority;
1348    tr_torrentReaderUnlock( tor );
1349
1350    return ret;
1351}
1352
1353
1354tr_priority_t*
1355tr_torrentGetFilePriorities( const tr_torrent_t * tor )
1356{
1357    int i;
1358    tr_priority_t * p;
1359
1360    tr_torrentReaderLock( tor );
1361    p = tr_new0( tr_priority_t, tor->info.fileCount );
1362    for( i=0; i<tor->info.fileCount; ++i )
1363        p[i] = tor->info.files[i].priority;
1364    tr_torrentReaderUnlock( tor );
1365
1366    return p;
1367}
1368
1369/**
1370***  File DND
1371**/
1372
1373int
1374tr_torrentGetFileDL( const tr_torrent_t * tor,
1375                     int                  file )
1376{
1377    int doDownload;
1378    tr_torrentReaderLock( tor );
1379
1380    assert( 0<=file && file<tor->info.fileCount );
1381    doDownload = !tor->info.files[file].dnd;
1382
1383    tr_torrentReaderUnlock( tor );
1384    return doDownload != 0;
1385}
1386
1387void
1388tr_torrentSetFileDL( tr_torrent_t  * tor,
1389                     int             fileIndex,
1390                     int             doDownload )
1391{
1392    tr_file_t * file;
1393    const int dnd = !doDownload;
1394    int firstPiece, firstPieceDND;
1395    int lastPiece, lastPieceDND;
1396    int i;
1397
1398    tr_torrentWriterLock( tor );
1399
1400    file = &tor->info.files[fileIndex];
1401    file->dnd = dnd;
1402    firstPiece = file->firstPiece;
1403    lastPiece = file->lastPiece;
1404
1405    /* can't set the first piece to DND unless
1406       every file using that piece is DND */
1407    firstPieceDND = dnd;
1408    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1409        if( tor->info.files[i].lastPiece != firstPiece )
1410            break;
1411        firstPieceDND = tor->info.files[i].dnd;
1412    }
1413
1414    /* can't set the last piece to DND unless
1415       every file using that piece is DND */
1416    lastPieceDND = dnd;
1417    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1418        if( tor->info.files[i].firstPiece != lastPiece )
1419            break;
1420        lastPieceDND = tor->info.files[i].dnd;
1421    }
1422
1423    if( firstPiece == lastPiece )
1424    {
1425        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1426    }
1427    else
1428    {
1429        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1430        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1431        for( i=firstPiece+1; i<lastPiece; ++i )
1432            tor->info.pieces[i].dnd = dnd;
1433    }
1434
1435    tr_cpInvalidateDND ( tor->completion );
1436
1437    tor->fastResumeDirty = TRUE;
1438
1439    tr_torrentWriterUnlock( tor );
1440}
1441
1442void
1443tr_torrentSetFileDLs ( tr_torrent_t   * tor,
1444                       int            * files,
1445                       int              fileCount,
1446                       int              doDownload )
1447{
1448    int i;
1449    for( i=0; i<fileCount; ++i )
1450        tr_torrentSetFileDL( tor, files[i], doDownload );
1451}
1452
1453/***
1454****
1455***/
1456
1457int _tr_block( const tr_torrent_t * tor, int index, int begin )
1458{
1459    const tr_info_t * inf = &tor->info;
1460    return index * ( inf->pieceSize / tor->blockSize ) +
1461        begin / tor->blockSize;
1462}
Note: See TracBrowser for help on using the repository browser.