source: trunk/libtransmission/torrent.c @ 2884

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

better saving of run/stopped state.

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