source: trunk/libtransmission/torrent.c @ 2149

Last change on this file since 2149 was 2149, checked in by livings124, 15 years ago

Merge file selection and torrent creation into the main branch.

The new code for these features is under a new license.

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