source: trunk/libtransmission/torrent.c @ 2185

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

add color-coded lines to the gtk debug window -- info is black, debug is grey, errors are red...

  • Property svn:keywords set to Date Rev Author Id
File size: 31.5 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 2185 2007-06-22 20:59:23Z 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 "transmission.h"
26#include "shared.h"
27#define INTERVAL_MSEC 100
28
29/***********************************************************************
30 * Local prototypes
31 **********************************************************************/
32static tr_torrent_t * torrentRealInit( tr_handle_t *, tr_torrent_t * tor,
33                                       uint8_t *, int flags, int * error );
34static void torrentReallyStop( tr_torrent_t * );
35
36static void ioInitAdd ( tr_torrent_t * );
37static int ioInitRemove ( tr_torrent_t * );
38static void downloadLoop( void * );
39
40void tr_setUseCustomUpload( tr_torrent_t * tor, int limit )
41{
42    tor->customUploadLimit = limit;
43}
44
45void tr_setUseCustomDownload( tr_torrent_t * tor, int limit )
46{
47    tor->customDownloadLimit = limit;
48}
49
50void tr_setUploadLimit( tr_torrent_t * tor, int limit )
51{
52    tr_rcSetLimit( tor->upload, limit );
53}
54
55void tr_setDownloadLimit( tr_torrent_t * tor, int limit )
56{
57    tr_rcSetLimit( tor->download, limit );
58}
59
60tr_torrent_t *
61tr_torrentInit( tr_handle_t * h, const char * path,
62                uint8_t * hash, int flags, int * error )
63{
64    tr_torrent_t * tor;
65
66    tor  = calloc( 1, sizeof *tor );
67    if( NULL == tor )
68    {
69        *error = TR_EOTHER;
70        return NULL;
71    }
72
73    /* Parse torrent file */
74    if( tr_metainfoParseFile( &tor->info, h->tag, path,
75                              TR_FLAG_SAVE & flags ) )
76    {
77        *error = TR_EINVALID;
78        free( tor );
79        return NULL;
80    }
81
82    return torrentRealInit( h, tor, hash, flags, error );
83}
84
85tr_torrent_t *
86tr_torrentInitData( tr_handle_t * h, uint8_t * data, size_t size,
87                    uint8_t * hash, int flags, int * error )
88{
89    tr_torrent_t * tor;
90
91    tor  = calloc( 1, sizeof *tor );
92    if( NULL == tor )
93    {
94        *error = TR_EOTHER;
95        return NULL;
96    }
97
98    /* Parse torrent file */
99    if( tr_metainfoParseData( &tor->info, h->tag, data, size,
100                              TR_FLAG_SAVE & flags ) )
101    {
102        *error = TR_EINVALID;
103        free( tor );
104        return NULL;
105    }
106
107    return torrentRealInit( h, tor, hash, flags, error );
108}
109
110tr_torrent_t *
111tr_torrentInitSaved( tr_handle_t * h, const char * hashStr,
112                     int flags, int * error )
113{
114    tr_torrent_t * tor;
115
116    tor  = calloc( 1, sizeof *tor );
117    if( NULL == tor )
118    {
119        *error = TR_EOTHER;
120        return NULL;
121    }
122
123    /* Parse torrent file */
124    if( tr_metainfoParseHash( &tor->info, h->tag, hashStr ) )
125    {
126        *error = TR_EINVALID;
127        free( tor );
128        return NULL;
129    }
130
131    return torrentRealInit( h, tor, NULL, ( TR_FLAG_SAVE | flags ), error );
132}
133
134/***********************************************************************
135 * tr_torrentInit
136 ***********************************************************************
137 * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
138 * to fill it.
139 **********************************************************************/
140static tr_torrent_t * torrentRealInit( tr_handle_t * h, tr_torrent_t * tor,
141                                       uint8_t * hash, int flags, int * error )
142{
143    tr_torrent_t  * tor_tmp;
144    tr_info_t     * inf;
145    int             i;
146   
147    inf         = &tor->info;
148    inf->flags |= flags;
149
150    tr_sharedLock( h->shared );
151
152    /* Make sure this torrent is not already open */
153    for( tor_tmp = h->torrentList; tor_tmp; tor_tmp = tor_tmp->next )
154    {
155        if( !memcmp( tor->info.hash, tor_tmp->info.hash,
156                     SHA_DIGEST_LENGTH ) )
157        {
158            if( NULL != hash )
159            {
160                memcpy( hash, tor->info.hash, SHA_DIGEST_LENGTH );
161            }
162            *error = TR_EDUPLICATE;
163            tr_metainfoFree( &tor->info );
164            free( tor );
165            tr_sharedUnlock( h->shared );
166            return NULL;
167        }
168    }
169
170    tor->handle   = h;
171    tor->status   = TR_STATUS_PAUSE;
172    tor->id       = h->id;
173    tor->key      = h->key;
174    tor->azId     = h->azId;
175    tor->hasChangedState = -1;
176
177    /* Escaped info hash for HTTP queries */
178    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
179    {
180        snprintf( &tor->escapedHashString[3*i],
181                  sizeof( tor->escapedHashString ) - 3 * i,
182                  "%%%02x", inf->hash[i] );
183    }
184
185    tor->pexDisabled = 0;
186
187    /* Block size: usually 16 ko, or less if we have to */
188    tor->blockSize  = MIN( inf->pieceSize, 1 << 14 );
189    tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
190                        tor->blockSize;
191    tor->completion = tr_cpInit( tor );
192
193    tor->thread = THREAD_EMPTY;
194    tr_lockInit( &tor->lock );
195    tr_condInit( &tor->cond );
196
197    tor->upload         = tr_rcInit();
198    tor->download       = tr_rcInit();
199    tor->swarmspeed     = tr_rcInit();
200 
201    /* We have a new torrent */
202    tor->publicPort = tr_sharedGetPublicPort( h->shared );
203    tor->prev       = NULL;
204    tor->next       = h->torrentList;
205    if( tor->next )
206    {
207        tor->next->prev = tor;
208    }
209    h->torrentList = tor;
210    (h->torrentCount)++;
211
212    tr_sharedUnlock( h->shared );
213
214    if( !h->isPortSet )
215    {
216        tr_setBindPort( h, TR_DEFAULT_PORT );
217    }
218
219    tr_torrentInitFilePieces( tor );
220
221    return tor;
222}
223
224tr_info_t * tr_torrentInfo( tr_torrent_t * tor )
225{
226    return &tor->info;
227}
228
229/***********************************************************************
230 * tr_torrentScrape     
231 **********************************************************************/
232int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
233{
234    return tr_trackerScrape( tor, s, l, d );
235}
236
237void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
238{
239    tor->destination = strdup( path );
240    if ( !tor->ioLoaded )
241    {
242        tr_ioLoadResume( tor );
243    }
244}
245
246char * tr_torrentGetFolder( tr_torrent_t * tor )
247{
248    return tor->destination;
249}
250
251int tr_torrentDuplicateDownload( tr_torrent_t * tor )
252{
253    tr_torrent_t * current;
254   
255    /* Check if a torrent with the same name and destination is already active */
256    for( current = tor->handle->torrentList; current; current = current->next )
257    {
258        if( current != tor && current->status != TR_STATUS_PAUSE
259            && !strcmp( tor->destination, current->destination )
260            && !strcmp( tor->info.name, current->info.name ) )
261        {
262            return 1;
263        }
264    }
265    return 0;
266}
267
268void tr_torrentStart( tr_torrent_t * tor )
269{
270    /* Join the thread first */
271    torrentReallyStop( tor );
272
273    tr_inf("Starting torrent \"%s\"", tor->info.name);
274   
275    /* Don't start if a torrent with the same name and destination is already active */
276    if( tr_torrentDuplicateDownload( tor ) )
277    {
278        tor->error = TR_ERROR_IO_DUP_DOWNLOAD;
279        snprintf( tor->errorString, sizeof( tor->errorString ),
280                    "%s", tr_errorString( TR_ERROR_IO_DUP_DOWNLOAD ) );
281        return;
282    }
283
284    tr_lockLock( &tor->lock );
285
286    tor->downloadedPrev += tor->downloadedCur;
287    tor->downloadedCur   = 0;
288    tor->uploadedPrev   += tor->uploadedCur;
289    tor->uploadedCur     = 0;
290
291    tor->status  = TR_STATUS_CHECK_WAIT;
292    tor->error   = TR_OK;
293    tor->tracker = tr_trackerInit( tor );
294
295    tor->startDate = tr_date();
296    tor->die = 0;
297    tor->thread = THREAD_EMPTY;
298
299    tr_lockUnlock( &tor->lock );
300
301    ioInitAdd ( tor );
302}
303
304static void torrentStop( tr_torrent_t * tor )
305{
306    tr_inf("Stopping torrent \"%s\"", tor->info.name);
307    tr_trackerStopped( tor->tracker );
308    tr_rcReset( tor->download );
309    tr_rcReset( tor->upload );
310    tr_rcReset( tor->swarmspeed );
311    tor->status = TR_STATUS_STOPPING;
312    tor->stopDate = tr_date();
313}
314
315void tr_torrentStop( tr_torrent_t * tor )
316{
317    tr_lockLock( &tor->lock );
318    torrentStop( tor );
319
320    /* Don't return until the files are closed, so the UI can trash
321     * them if requested */
322    if ( ioInitRemove( tor ) ) /* torrent never got started */
323        tor->status = TR_STATUS_STOPPED;
324    else
325        tr_condWait( &tor->cond, &tor->lock );
326
327    tr_lockUnlock( &tor->lock );
328}
329
330/***********************************************************************
331 * torrentReallyStop
332 ***********************************************************************
333 * Joins the download thread and frees/closes everything related to it.
334 **********************************************************************/
335static void torrentReallyStop( tr_torrent_t * tor )
336{
337    int i;
338
339    tor->die = 1;
340    tr_threadJoin( &tor->thread );
341
342    if( tor->tracker )
343    {
344       tr_trackerClose( tor->tracker );
345       tor->tracker = NULL;
346    }
347
348    tr_lockLock( &tor->lock );
349    for( i = 0; i < tor->peerCount; i++ )
350    {
351        tr_peerDestroy( tor->peers[i] );
352    }
353    tor->peerCount = 0;
354    tr_lockUnlock( &tor->lock );
355}
356
357void tr_torrentDisablePex( tr_torrent_t * tor, int disable )
358{
359    int ii;
360
361    if( TR_FLAG_PRIVATE & tor->info.flags )
362    {
363        return;
364    }
365
366    tr_lockLock( &tor->lock );
367
368    if( tor->pexDisabled == disable )
369    {
370        tr_lockUnlock( &tor->lock );
371        return;
372    }
373
374    tor->pexDisabled = disable;
375    for( ii = 0; ii < tor->peerCount; ii++ )
376    {
377        tr_peerSetPrivate( tor->peers[ii], disable );
378    }
379
380    tr_lockUnlock( &tor->lock );
381}
382
383static int tr_didStateChangeTo ( tr_torrent_t * tor, int status )
384{
385    if( tor->hasChangedState == status )
386    {
387        tor->hasChangedState = -1;
388        return 1;
389    }
390    return 0;
391}
392
393int tr_getIncomplete( tr_torrent_t * tor )
394{
395    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
396}
397int tr_getDone( tr_torrent_t * tor )
398{
399    return tr_didStateChangeTo( tor, TR_CP_DONE );
400}
401int tr_getComplete( tr_torrent_t * tor )
402{
403    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
404}
405
406void tr_manualUpdate( tr_torrent_t * tor )
407{
408    int peerCount, new;
409    uint8_t * peerCompact;
410
411    if( !( tor->status & TR_STATUS_ACTIVE ) )
412        return;
413   
414    tr_lockLock( &tor->lock );
415    tr_trackerAnnouncePulse( tor->tracker, &peerCount, &peerCompact, 1 );
416    new = 0;
417    if( peerCount > 0 )
418    {
419        new = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
420                              peerCompact, peerCount );
421        free( peerCompact );
422    }
423    tr_dbg( "got %i peers from manual announce, used %i", peerCount, new );
424    tr_lockUnlock( &tor->lock );
425}
426
427tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
428{
429    tr_stat_t * s;
430    tr_peer_t * peer;
431    tr_info_t * inf = &tor->info;
432    tr_tracker_t * tc;
433    int i;
434
435    tor->statCur = ( tor->statCur + 1 ) % 2;
436    s = &tor->stats[tor->statCur];
437
438    if( ( tor->status & TR_STATUS_STOPPED ) ||
439        ( ( tor->status & TR_STATUS_STOPPING ) &&
440          tr_date() > tor->stopDate + 60000 ) )
441    {
442        torrentReallyStop( tor );
443        tor->status = TR_STATUS_PAUSE;
444    }
445
446    tr_lockLock( &tor->lock );
447
448    s->status = tor->status;
449    s->error  = tor->error;
450    memcpy( s->errorString, tor->errorString,
451            sizeof( s->errorString ) );
452
453    tc = tor->tracker;
454    s->cannotConnect = tr_trackerCannotConnect( tc );
455    s->tracker = ( tc ? tr_trackerGet( tc ) : &inf->trackerList[0].list[0] );
456
457    s->peersTotal       = 0;
458    memset( s->peersFrom, 0, sizeof( s->peersFrom ) );
459    s->peersUploading   = 0;
460    s->peersDownloading = 0;
461   
462    for( i = 0; i < tor->peerCount; i++ )
463    {
464        peer = tor->peers[i];
465   
466        if( tr_peerIsConnected( peer ) )
467        {
468            (s->peersTotal)++;
469            (s->peersFrom[ tr_peerIsFrom( peer ) ])++;
470            if( tr_peerAmInterested( peer ) && !tr_peerIsChoking( peer ) )
471            {
472                (s->peersUploading)++;
473            }
474            if( !tr_peerAmChoking( peer ) )
475            {
476                (s->peersDownloading)++;
477            }
478        }
479    }
480
481    s->percentDone = tr_cpPercentDone( tor->completion );
482    s->percentComplete = tr_cpPercentComplete( tor->completion );
483    s->cpStatus = tr_cpGetStatus( tor->completion );
484    s->left     = tr_cpLeftUntilDone( tor->completion );
485    if( tor->status & TR_STATUS_DOWNLOAD )
486    {
487        s->rateDownload = tr_rcRate( tor->download );
488    }
489    else
490    {
491        /* tr_rcRate() doesn't make the difference between 'piece'
492           messages and other messages, which causes a non-zero
493           download rate even tough we are not downloading. So we
494           force it to zero not to confuse the user. */
495        s->rateDownload = 0.0;
496    }
497    s->rateUpload = tr_rcRate( tor->upload );
498   
499    s->seeders  = tr_trackerSeeders( tc );
500    s->leechers = tr_trackerLeechers( tc );
501    s->completedFromTracker = tr_trackerDownloaded( tc );
502
503    s->swarmspeed = tr_rcRate( tor->swarmspeed );
504   
505    s->startDate = tor->startDate;
506    s->activityDate = tor->activityDate;
507
508    if( s->rateDownload < 0.1 )
509    {
510        s->eta = -1;
511    }
512    else
513    {
514        s->eta = (float) s->left / s->rateDownload / 1024.0;
515    }
516
517    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
518    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
519    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
520   
521    if( s->downloaded == 0 && s->percentDone == 0.0 )
522    {
523        s->ratio = TR_RATIO_NA;
524    }
525    else
526    {
527        s->ratio = (float)s->uploaded
528                 / (float)MAX(s->downloaded, s->downloadedValid);
529    }
530   
531    tr_lockUnlock( &tor->lock );
532
533    return s;
534}
535
536tr_peer_stat_t * tr_torrentPeers( tr_torrent_t * tor, int * peerCount )
537{
538    tr_peer_stat_t * peers;
539
540    tr_lockLock( &tor->lock );
541
542    *peerCount = tor->peerCount;
543   
544    peers = (tr_peer_stat_t *) calloc( tor->peerCount, sizeof( tr_peer_stat_t ) );
545    if (peers != NULL)
546    {
547        tr_peer_t * peer;
548        struct in_addr * addr;
549        int i;
550        for( i = 0; i < tor->peerCount; i++ )
551        {
552            peer = tor->peers[i];
553           
554            addr = tr_peerAddress( peer );
555            if( NULL != addr )
556            {
557                tr_netNtop( addr, peers[i].addr,
558                           sizeof( peers[i].addr ) );
559            }
560           
561            peers[i].client        = tr_peerClient( peer );
562            peers[i].isConnected   = tr_peerIsConnected( peer );
563            peers[i].from          = tr_peerIsFrom( peer );
564            peers[i].progress      = tr_peerProgress( peer );
565            peers[i].port          = tr_peerPort( peer );
566           
567            if( ( peers[i].isDownloading = !tr_peerAmChoking( peer ) ) )
568            {
569                peers[i].uploadToRate = tr_peerUploadRate( peer );
570            }
571            if( ( peers[i].isUploading = ( tr_peerAmInterested( peer ) &&
572                                           !tr_peerIsChoking( peer ) ) ) )
573            {
574                peers[i].downloadFromRate = tr_peerDownloadRate( peer );
575            }
576        }
577    }
578   
579    tr_lockUnlock( &tor->lock );
580   
581    return peers;
582}
583
584void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount UNUSED )
585{
586    if (peers == NULL)
587        return;
588
589    free( peers );
590}
591
592void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
593{
594    int i, j, piece;
595    float interval;
596
597    tr_lockLock( &tor->lock );
598    interval = (float)tor->info.pieceCount / (float)size;
599    for( i = 0; i < size; i++ )
600    {
601        piece = i * interval;
602
603        if( tr_cpPieceIsComplete( tor->completion, piece ) )
604        {
605            tab[i] = -1;
606            continue;
607        }
608
609        tab[i] = 0;
610        for( j = 0; j < tor->peerCount; j++ )
611        {
612            if( tr_peerHasPiece( tor->peers[j], piece ) )
613            {
614                (tab[i])++;
615            }
616        }
617    }
618    tr_lockUnlock( &tor->lock );
619}
620
621uint64_t
622tr_torrentFileBytesCompleted ( const tr_torrent_t * tor, int fileIndex )
623{
624    const tr_file_t * file     =  &tor->info.files[fileIndex];
625    const uint64_t firstBlock       =  file->offset / tor->blockSize;
626    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
627    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
628    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
629    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
630    uint64_t haveBytes = 0;
631
632    assert( tor != NULL );
633    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
634    assert( file->offset + file->length <= tor->info.totalSize );
635    assert( (int)firstBlock < tor->blockCount );
636    assert( (int)lastBlock < tor->blockCount );
637    assert( firstBlock <= lastBlock );
638    assert( tr_blockPiece( firstBlock ) == file->firstPiece );
639    assert( tr_blockPiece( lastBlock ) == file->lastPiece );
640
641    if( firstBlock == lastBlock )
642    {
643        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
644            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
645    }
646    else
647    {
648        uint64_t i;
649
650        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
651            haveBytes += tor->blockSize - firstBlockOffset;
652
653        for( i=firstBlock+1; i<lastBlock; ++i )
654            if( tr_cpBlockIsComplete( tor->completion, i ) )
655               haveBytes += tor->blockSize;
656
657        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
658            haveBytes += lastBlockOffset + 1;
659    }
660
661    return haveBytes;
662}
663
664float
665tr_torrentFileCompletion ( const tr_torrent_t * tor, int fileIndex )
666{
667    const uint64_t c = tr_torrentFileBytesCompleted ( tor, fileIndex );
668    uint64_t length = tor->info.files[fileIndex].length;
669   
670    if( !length )
671        return 1.0;
672    return (double)c / length;
673}
674
675float*
676tr_torrentCompletion( tr_torrent_t * tor )
677{
678    int i;
679    float * f;
680
681    tr_lockLock( &tor->lock );
682    f = calloc ( tor->info.fileCount, sizeof( float ) );
683    for( i=0; i<tor->info.fileCount; ++i )
684       f[i] = tr_torrentFileCompletion ( tor, i );
685    tr_lockUnlock( &tor->lock );
686
687    return f;
688}
689
690void tr_torrentAmountFinished( tr_torrent_t * tor, float * tab, int size )
691{
692    int i, piece;
693    float interval;
694
695    tr_lockLock( &tor->lock );
696    interval = (float)tor->info.pieceCount / (float)size;
697    for( i = 0; i < size; i++ )
698    {
699        piece = i * interval;
700        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
701    }
702    tr_lockUnlock( &tor->lock );
703}
704
705void tr_torrentRemoveSaved( tr_torrent_t * tor )
706{
707    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
708}
709
710void tr_torrentRemoveFastResume( tr_torrent_t * tor )
711{
712    tr_ioRemoveResume( tor );
713}
714
715/***********************************************************************
716 * tr_torrentClose
717 ***********************************************************************
718 * Frees memory allocated by tr_torrentInit.
719 **********************************************************************/
720void tr_torrentClose( tr_torrent_t * tor )
721{
722    tr_handle_t * h = tor->handle;
723    tr_info_t * inf = &tor->info;
724
725    /* Join the thread first */
726    torrentReallyStop( tor );
727
728    tr_sharedLock( h->shared );
729
730    h->torrentCount--;
731
732    tr_lockClose( &tor->lock );
733    tr_condClose( &tor->cond );
734    tr_cpClose( tor->completion );
735
736    tr_rcClose( tor->upload );
737    tr_rcClose( tor->download );
738    tr_rcClose( tor->swarmspeed );
739
740    if( tor->destination )
741    {
742        free( tor->destination );
743    }
744
745    tr_metainfoFree( inf );
746
747    if( tor->prev )
748    {
749        tor->prev->next = tor->next;
750    }
751    else
752    {
753        h->torrentList = tor->next;
754    }
755    if( tor->next )
756    {
757        tor->next->prev = tor->prev;
758    }
759    free( tor );
760
761    tr_sharedUnlock( h->shared );
762}
763
764int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
765{
766    int i;
767    tr_peer_t * otherPeer;
768
769    if( tor->peerCount >= TR_MAX_PEER_COUNT )
770    {
771        tr_peerDestroy(  peer );
772        return 0;
773    }
774
775    /* Don't accept two connections from the same IP */
776    for( i = 0; i < tor->peerCount; i++ )
777    {
778        otherPeer = tor->peers[i];
779        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
780        {
781            tr_peerDestroy(  peer );
782            return 0;
783        }
784    }
785
786    tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE ||
787                       tor->pexDisabled );
788    tr_peerSetTorrent( peer, tor );
789    tor->peers[tor->peerCount++] = peer;
790
791    return 1;
792}
793
794int tr_torrentAddCompact( tr_torrent_t * tor, int from,
795                           uint8_t * buf, int count )
796{
797    struct in_addr addr;
798    in_port_t port;
799    int i, added;
800    tr_peer_t * peer;
801
802    added = 0;
803    for( i = 0; i < count; i++ )
804    {
805        memcpy( &addr, buf, 4 ); buf += 4;
806        memcpy( &port, buf, 2 ); buf += 2;
807
808        peer = tr_peerInit( addr, port, -1, from );
809        added += tr_torrentAttachPeer( tor, peer );
810    }
811
812    return added;
813}
814
815/***********************************************************************
816 * Push a torrent's call to tr_ioInit through a queue in a worker
817 * thread so that only one torrent can be in checkFiles() at a time.
818 **********************************************************************/
819
820struct tr_io_init_list_t
821{
822    tr_torrent_t              * tor;
823    struct tr_io_init_list_t  * next;
824};
825
826static struct tr_io_init_list_t * ioInitQueue = NULL;
827
828static int ioInitWorkerRunning = 0;
829
830static tr_thread_t ioInitThread;
831
832static tr_lock_t* getIOLock( tr_handle_t * h )
833{
834    static tr_lock_t * lock = NULL;
835
836    tr_sharedLock( h->shared );
837    if( lock == NULL )
838    {
839        lock = calloc( 1, sizeof( tr_lock_t ) );
840        tr_lockInit( lock );
841    }
842    tr_sharedUnlock( h->shared );
843
844    return lock;
845}
846
847static void ioInitWorker( void * user_data )
848{
849    tr_handle_t * h = (tr_handle_t*) user_data;
850
851    for (;;)
852    {
853        char name[32];
854
855        /* find the next torrent to process */
856        tr_torrent_t * tor = NULL;
857        tr_lock_t * lock = getIOLock( h );
858        tr_lockLock( lock );
859        if( ioInitQueue != NULL )
860        {
861            struct tr_io_init_list_t * node = ioInitQueue;
862            ioInitQueue = node->next;
863            tor = node->tor;
864            free( node );
865        }
866        tr_lockUnlock( lock );
867
868        /* if no torrents, this worker thread is done */
869        if( tor == NULL )
870        {
871          break;
872        }
873
874        /* check this torrent's files */
875        tor->status = TR_STATUS_CHECK;
876       
877        tr_dbg( "torrent %s checking files", tor->info.name );
878        tr_lockLock( &tor->lock );
879        tr_cpReset( tor->completion );
880        tor->io = tr_ioInit( tor );
881        tr_lockUnlock( &tor->lock );
882
883        snprintf( name, sizeof( name ), "torrent %p", tor );
884        tr_threadCreate( &tor->thread, downloadLoop, tor, name );
885    }
886
887    ioInitWorkerRunning = 0;
888}
889
890/* add tor to the queue of torrents waiting for an tr_ioInit */
891static void ioInitAdd( tr_torrent_t * tor )
892{
893    tr_lock_t * lock = getIOLock( tor->handle );
894    struct tr_io_init_list_t * node;
895    tr_lockLock( lock );
896
897    /* enqueue this torrent to have its io initialized */
898    node = malloc( sizeof( struct tr_io_init_list_t ) );
899    node->tor = tor;
900    node->next = NULL;
901    if( ioInitQueue == NULL )
902    {
903        ioInitQueue = node;
904    }
905    else
906    {
907        struct tr_io_init_list_t * l = ioInitQueue;
908        while( l->next != NULL )
909        {
910            l = l->next;
911        }
912        l->next = node;
913    }
914
915    /* ensure there's a worker thread to process the queue */
916    if( !ioInitWorkerRunning )
917    {
918        ioInitWorkerRunning = 1;
919        tr_threadCreate( &ioInitThread, ioInitWorker, tor->handle, "ioInit" );
920    }
921   
922    tr_lockUnlock( lock );
923}
924
925/* remove tor from the queue of torrents waiting for an tr_ioInit.
926   return nonzero if tor was found and removed */
927static int ioInitRemove( tr_torrent_t * tor )
928{
929    tr_lock_t * lock = getIOLock( tor->handle );
930    struct tr_io_init_list_t *node, *prev;
931    tr_lockLock( lock );
932
933    /* find tor's node */
934    for( prev = NULL, node = ioInitQueue;
935         node != NULL && node->tor != tor;
936         prev=node, node=node->next );
937
938    if( node != NULL )
939    {
940        if( prev == NULL )
941        {
942            ioInitQueue = node->next;
943        }
944        else
945        {
946            prev->next = node->next;
947        }
948    }
949
950    tr_lockUnlock( lock );
951    return node != NULL;
952}
953
954/***********************************************************************
955 * downloadLoop
956 **********************************************************************/
957static void downloadLoop( void * _tor )
958{
959    tr_torrent_t * tor = _tor;
960    int            i, ret;
961    int            peerCount, used;
962    cp_status_t    cpState, cpPrevState;
963    uint8_t      * peerCompact;
964    tr_peer_t    * peer;
965
966    tr_lockLock( &tor->lock );
967
968    cpState = cpPrevState = tr_cpGetStatus( tor->completion );
969    switch( cpState ) {
970        case TR_CP_COMPLETE:   tor->status = TR_STATUS_SEED; break;
971        case TR_CP_DONE:       tor->status = TR_STATUS_DONE; break;
972        case TR_CP_INCOMPLETE: tor->status = TR_STATUS_DOWNLOAD; break;
973    }
974
975    while( !tor->die )
976    {
977        tr_lockUnlock( &tor->lock );
978        tr_wait( INTERVAL_MSEC );
979        tr_lockLock( &tor->lock );
980
981        cpState = tr_cpGetStatus( tor->completion );
982
983        if( cpState != cpPrevState )
984        {
985            switch( cpState ) {
986                case TR_CP_COMPLETE:   tor->status = TR_STATUS_SEED; break;
987                case TR_CP_DONE:       tor->status = TR_STATUS_DONE; break;
988                case TR_CP_INCOMPLETE: tor->status = TR_STATUS_DOWNLOAD; break;
989            }
990
991            tor->hasChangedState = cpState;
992
993            if( cpState == TR_CP_COMPLETE )
994                tr_trackerCompleted( tor->tracker );
995
996            tr_ioSync( tor->io );
997
998            cpPrevState = cpState;
999        }
1000
1001        /* Try to get new peers or to send a message to the tracker */
1002        tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1003        if( peerCount > 0 )
1004        {
1005            used = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
1006                                         peerCompact, peerCount );
1007            free( peerCompact );
1008            tr_dbg( "got %i peers from announce, used %i", peerCount, used );
1009        }
1010        if( tor->status & TR_STATUS_STOPPED )
1011        {
1012            break;
1013        }
1014
1015        /* Stopping: make sure all files are closed and stop talking
1016           to peers */
1017        if( tor->status & TR_STATUS_STOPPING )
1018        {
1019            if( tor->io )
1020            {
1021                tr_ioClose( tor->io ); tor->io = NULL;
1022                tr_condSignal( &tor->cond );
1023            }
1024            continue;
1025        }
1026
1027        /* Shuffle peers */
1028        if( tor->peerCount > 1 )
1029        {
1030            peer = tor->peers[0];
1031            memmove( &tor->peers[0], &tor->peers[1],
1032                    ( tor->peerCount - 1 ) * sizeof( void * ) );
1033            tor->peers[tor->peerCount - 1] = peer;
1034        }
1035
1036        /* Receive/send messages */
1037        for( i = 0; i < tor->peerCount; )
1038        {
1039            peer = tor->peers[i];
1040
1041            ret = tr_peerPulse( peer );
1042            if( ret & TR_ERROR_IO_MASK )
1043            {
1044                tr_err( "Fatal error, stopping download (%d)", ret );
1045                torrentStop( tor );
1046                tor->error = ret;
1047                snprintf( tor->errorString, sizeof( tor->errorString ),
1048                          "%s", tr_errorString( ret ) );
1049                break;
1050            }
1051            if( ret )
1052            {
1053                tr_peerDestroy( peer );
1054                tor->peerCount--;
1055                memmove( &tor->peers[i], &tor->peers[i+1],
1056                         ( tor->peerCount - i ) * sizeof( void * ) );
1057                continue;
1058            }
1059            i++;
1060        }
1061    }
1062
1063    tr_lockUnlock( &tor->lock );
1064
1065    if( tor->io )
1066    {
1067        tr_ioClose( tor->io ); tor->io = NULL;
1068        tr_condSignal( &tor->cond );
1069    }
1070
1071    tor->status = TR_STATUS_STOPPED;
1072}
1073
1074/***
1075****
1076****  File prioritization
1077****
1078***/
1079
1080static int
1081getBytePiece( const tr_info_t * info, uint64_t byteOffset )
1082{
1083    assert( info != NULL );
1084    assert( info->pieceSize != 0 );
1085
1086    return byteOffset / info->pieceSize;
1087}
1088
1089static void
1090initFilePieces ( tr_info_t * info, int fileIndex )
1091{
1092    tr_file_t * file = &info->files[fileIndex];
1093    uint64_t firstByte, lastByte;
1094
1095    assert( info != NULL );
1096    assert( 0<=fileIndex && fileIndex<info->fileCount );
1097
1098    file = &info->files[fileIndex];
1099    firstByte = file->offset;
1100    lastByte = firstByte + (file->length ? file->length-1 : 0);
1101    file->firstPiece = getBytePiece( info, firstByte );
1102    file->lastPiece = getBytePiece( info, lastByte );
1103    tr_dbg( "file #%d is in pieces [%d...%d] (%s)", fileIndex, file->firstPiece, file->lastPiece, file->name );
1104}
1105
1106static tr_priority_t
1107calculatePiecePriority ( const tr_torrent_t * tor,
1108                         int                  piece )
1109{
1110    int i;
1111    tr_priority_t priority = TR_PRI_DND;
1112
1113    for( i=0; i<tor->info.fileCount; ++i )
1114    {
1115        const tr_file_t * file = &tor->info.files[i];
1116        if ( file->firstPiece <= piece
1117          && file->lastPiece  >= piece
1118          && file->priority   >  priority)
1119              priority = file->priority;
1120    }
1121
1122    return priority;
1123}
1124
1125void
1126tr_torrentInitFilePieces( tr_torrent_t * tor )
1127{
1128    int i;
1129    uint64_t offset = 0;
1130
1131    assert( tor != NULL );
1132
1133    for( i=0; i<tor->info.fileCount; ++i ) {
1134      tor->info.files[i].offset = offset;
1135      offset += tor->info.files[i].length;
1136      initFilePieces( &tor->info, i );
1137    }
1138
1139    for( i=0; i<tor->info.pieceCount; ++i )
1140      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1141}
1142
1143void
1144tr_torrentSetFilePriority( tr_torrent_t   * tor,
1145                           int              fileIndex,
1146                           tr_priority_t    priority )
1147{
1148    int i;
1149    tr_file_t * file;
1150
1151    assert( tor != NULL );
1152    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1153    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL
1154         || priority==TR_PRI_HIGH || priority==TR_PRI_DND );
1155
1156    file = &tor->info.files[fileIndex];
1157    file->priority = priority;
1158    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1159      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1160
1161    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1162             fileIndex, file->firstPiece, file->lastPiece,
1163             priority, tor->info.files[fileIndex].name );
1164}
1165
1166tr_priority_t
1167tr_torrentGetFilePriority( const tr_torrent_t *  tor, int file )
1168{
1169    assert( tor != NULL );
1170    assert( 0<=file && file<tor->info.fileCount );
1171
1172    return tor->info.files[file].priority;
1173}
1174
1175
1176void
1177tr_torrentSetFilePriorities( tr_torrent_t         * tor,
1178                             const tr_priority_t  * filePriorities )
1179{
1180    int i;
1181    for( i=0; i<tor->info.pieceCount; ++i )
1182      tr_torrentSetFilePriority( tor, i, filePriorities[i] );
1183}
1184
1185tr_priority_t*
1186tr_torrentGetFilePriorities( const tr_torrent_t * tor )
1187{
1188    int i;
1189    tr_priority_t * p = malloc( tor->info.fileCount * sizeof(tr_priority_t) );
1190    for( i=0; i<tor->info.fileCount; ++i )
1191        p[i] = tor->info.files[i].priority;
1192    return p;
1193}
Note: See TracBrowser for help on using the repository browser.