source: trunk/libtransmission/torrent.c @ 1537

Last change on this file since 1537 was 1537, checked in by joshe, 15 years ago

Don't clobber the private flag during torrent initialization.
Display if the private flag is set in transmissioncli -i

  • Property svn:keywords set to Date Rev Author Id
File size: 20.3 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 1537 2007-03-06 01:58:14Z joshe $
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
28/***********************************************************************
29 * Local prototypes
30 **********************************************************************/
31static tr_torrent_t * torrentRealInit( tr_handle_t *, tr_torrent_t * tor,
32                                       int flags, int * error );
33static void torrentReallyStop( tr_torrent_t * );
34static void downloadLoop( void * );
35
36void tr_setUseCustomUpload( tr_torrent_t * tor, int limit )
37{
38    tor->customUploadLimit = limit;
39}
40
41void tr_setUseCustomDownload( tr_torrent_t * tor, int limit )
42{
43    tor->customDownloadLimit = limit;
44}
45
46void tr_setUploadLimit( tr_torrent_t * tor, int limit )
47{
48    tr_rcSetLimit( tor->upload, limit );
49}
50
51void tr_setDownloadLimit( tr_torrent_t * tor, int limit )
52{
53    tr_rcSetLimit( tor->download, limit );
54}
55
56tr_torrent_t * tr_torrentInit( tr_handle_t * h, const char * path,
57                               int flags, int * error )
58{
59    tr_torrent_t  * tor = calloc( sizeof( tr_torrent_t ), 1 );
60    int             saveCopy = ( TR_FLAG_SAVE & flags );
61
62    /* Parse torrent file */
63    if( tr_metainfoParse( &tor->info, path, NULL, saveCopy ) )
64    {
65        *error = TR_EINVALID;
66        free( tor );
67        return NULL;
68    }
69
70    return torrentRealInit( h, tor, flags, error );
71}
72
73tr_torrent_t * tr_torrentInitSaved( tr_handle_t * h, const char * hashStr,
74                                    int flags, int * error )
75{
76    tr_torrent_t  * tor = calloc( sizeof( tr_torrent_t ), 1 );
77
78    /* Parse torrent file */
79    if( tr_metainfoParse( &tor->info, NULL, hashStr, 0 ) )
80    {
81        *error = TR_EINVALID;
82        free( tor );
83        return NULL;
84    }
85
86    return torrentRealInit( h, tor, ( TR_FLAG_SAVE | flags ), error );
87}
88
89/***********************************************************************
90 * tr_torrentInit
91 ***********************************************************************
92 * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
93 * to fill it.
94 **********************************************************************/
95static tr_torrent_t * torrentRealInit( tr_handle_t * h, tr_torrent_t * tor,
96                                       int flags, int * error )
97{
98    tr_torrent_t  * tor_tmp;
99    tr_info_t     * inf;
100    int             i;
101   
102    inf         = &tor->info;
103    inf->flags |= flags;
104
105    tr_sharedLock( h->shared );
106
107    /* Make sure this torrent is not already open */
108    for( tor_tmp = h->torrentList; tor_tmp; tor_tmp = tor_tmp->next )
109    {
110        if( !memcmp( tor->info.hash, tor_tmp->info.hash,
111                     SHA_DIGEST_LENGTH ) )
112        {
113            *error = TR_EDUPLICATE;
114            tr_metainfoFree( &tor->info );
115            free( tor );
116            tr_sharedUnlock( h->shared );
117            return NULL;
118        }
119    }
120
121    tor->handle   = h;
122    tor->status   = TR_STATUS_PAUSE;
123    tor->id       = h->id;
124    tor->key      = h->key;
125    tor->finished = 0;
126
127    /* Escaped info hash for HTTP queries */
128    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
129    {
130        sprintf( &tor->escapedHashString[3*i], "%%%02x", inf->hash[i] );
131    }
132
133    /* Block size: usually 16 ko, or less if we have to */
134    tor->blockSize  = MIN( inf->pieceSize, 1 << 14 );
135    tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
136                        tor->blockSize;
137    tor->completion = tr_cpInit( tor );
138
139    tr_lockInit( &tor->lock );
140    tr_condInit( &tor->cond );
141
142    tor->upload         = tr_rcInit();
143    tor->download       = tr_rcInit();
144    tor->swarmspeed     = tr_rcInit();
145 
146    /* We have a new torrent */
147    tor->publicPort = tr_sharedGetPublicPort( h->shared );
148    tor->prev       = NULL;
149    tor->next       = h->torrentList;
150    if( tor->next )
151    {
152        tor->next->prev = tor;
153    }
154    h->torrentList = tor;
155    (h->torrentCount)++;
156
157    tr_sharedUnlock( h->shared );
158
159    if( !h->isPortSet )
160    {
161        tr_setBindPort( h, TR_DEFAULT_PORT );
162    }
163
164    return tor;
165}
166
167tr_info_t * tr_torrentInfo( tr_torrent_t * tor )
168{
169    return &tor->info;
170}
171
172/***********************************************************************
173 * tr_torrentScrape     
174 **********************************************************************/
175int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
176{
177    return tr_trackerScrape( tor, s, l, d );
178}
179
180void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
181{
182    tor->destination = strdup( path );
183    tr_ioLoadResume( tor );
184}
185
186char * tr_torrentGetFolder( tr_torrent_t * tor )
187{
188    return tor->destination;
189}
190
191void tr_torrentStart( tr_torrent_t * tor )
192{
193    char name[32];
194
195    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
196    {
197        /* Join the thread first */
198        torrentReallyStop( tor );
199    }
200
201    tr_lockLock( &tor->lock );
202
203    tor->downloadedPrev += tor->downloadedCur;
204    tor->downloadedCur   = 0;
205    tor->uploadedPrev   += tor->uploadedCur;
206    tor->uploadedCur     = 0;
207
208    tor->status  = TR_STATUS_CHECK;
209    tor->error   = TR_OK;
210    tor->tracker = tr_trackerInit( tor );
211
212    tor->date = tr_date();
213    tor->die = 0;
214    snprintf( name, sizeof( name ), "torrent %p", tor );
215
216    tr_lockUnlock( &tor->lock );
217
218    tr_threadCreate( &tor->thread, downloadLoop, tor, name );
219}
220
221static void torrentStop( tr_torrent_t * tor )
222{
223    tr_trackerStopped( tor->tracker );
224    tr_rcReset( tor->download );
225    tr_rcReset( tor->upload );
226    tr_rcReset( tor->swarmspeed );
227    tor->status = TR_STATUS_STOPPING;
228    tor->stopDate = tr_date();
229}
230
231void tr_torrentStop( tr_torrent_t * tor )
232{
233    tr_lockLock( &tor->lock );
234    torrentStop( tor );
235
236    /* Don't return until the files are closed, so the UI can trash
237     * them if requested */
238    tr_condWait( &tor->cond, &tor->lock );
239    tr_lockUnlock( &tor->lock );
240}
241
242/***********************************************************************
243 * torrentReallyStop
244 ***********************************************************************
245 * Joins the download thread and frees/closes everything related to it.
246 **********************************************************************/
247static void torrentReallyStop( tr_torrent_t * tor )
248{
249    int i;
250
251    tor->die = 1;
252    tr_threadJoin( &tor->thread );
253
254    tr_trackerClose( tor->tracker );
255    tor->tracker = NULL;
256
257    tr_lockLock( &tor->lock );
258    for( i = 0; i < tor->peerCount; i++ )
259    {
260        tr_peerDestroy( tor->peers[i] );
261    }
262    tor->peerCount = 0;
263    tr_lockUnlock( &tor->lock );
264}
265
266int tr_getFinished( tr_torrent_t * tor )
267{
268    if( tor->finished )
269    {
270        tor->finished = 0;
271        return 1;
272    }
273    return 0;
274}
275
276void tr_manualUpdate( tr_torrent_t * tor )
277{
278    int peerCount;
279    uint8_t * peerCompact;
280
281    if( !( tor->status & TR_STATUS_ACTIVE ) )
282        return;
283   
284    tr_lockLock( &tor->lock );
285    tr_trackerAnnouncePulse( tor->tracker, &peerCount, &peerCompact, 1 );
286    if( peerCount > 0 )
287    {
288        tr_torrentAddCompact( tor, peerCompact, peerCount );
289        free( peerCompact );
290    }
291    tr_lockUnlock( &tor->lock );
292}
293
294tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
295{
296    tr_stat_t * s;
297    tr_peer_t * peer;
298    tr_info_t * inf = &tor->info;
299    tr_tracker_t * tc;
300    int i;
301
302    tor->statCur = ( tor->statCur + 1 ) % 2;
303    s = &tor->stats[tor->statCur];
304
305    if( ( tor->status & TR_STATUS_STOPPED ) ||
306        ( ( tor->status & TR_STATUS_STOPPING ) &&
307          tr_date() > tor->stopDate + 60000 ) )
308    {
309        torrentReallyStop( tor );
310        tor->status = TR_STATUS_PAUSE;
311    }
312
313    tr_lockLock( &tor->lock );
314
315    s->status = tor->status;
316    s->error  = tor->error;
317    memcpy( s->errorString, tor->errorString,
318            sizeof( s->errorString ) );
319
320    tc = tor->tracker;
321    s->cannotConnect = tr_trackerCannotConnect( tc );
322    s->tracker = ( tc ? tr_trackerGet( tc ) : &inf->trackerList[0].list[0] );
323
324    s->peersTotal       = 0;
325    s->peersIncoming    = 0;
326    s->peersUploading   = 0;
327    s->peersDownloading = 0;
328   
329    for( i = 0; i < tor->peerCount; i++ )
330    {
331        peer = tor->peers[i];
332   
333        if( tr_peerIsConnected( peer ) )
334        {
335            (s->peersTotal)++;
336           
337            if( tr_peerIsIncoming( peer ) )
338            {
339                (s->peersIncoming)++;
340            }
341            if( tr_peerAmInterested( peer ) && !tr_peerIsChoking( peer ) )
342            {
343                (s->peersUploading)++;
344            }
345            if( !tr_peerAmChoking( peer ) )
346            {
347                (s->peersDownloading)++;
348            }
349        }
350    }
351
352    s->progress = tr_cpCompletionAsFloat( tor->completion );
353    if( tor->status & TR_STATUS_DOWNLOAD )
354    {
355        s->rateDownload = tr_rcRate( tor->download );
356    }
357    else
358    {
359        /* tr_rcRate() doesn't make the difference between 'piece'
360           messages and other messages, which causes a non-zero
361           download rate even tough we are not downloading. So we
362           force it to zero not to confuse the user. */
363        s->rateDownload = 0.0;
364    }
365    s->rateUpload = tr_rcRate( tor->upload );
366   
367    s->seeders  = tr_trackerSeeders( tc );
368    s->leechers = tr_trackerLeechers( tc );
369    s->completedFromTracker = tr_trackerDownloaded( tc );
370
371    s->swarmspeed = tr_rcRate( tor->swarmspeed );
372
373    if( s->rateDownload < 0.1 )
374    {
375        s->eta = -1;
376    }
377    else
378    {
379        s->eta = ( 1.0 - s->progress ) *
380            (float) inf->totalSize / s->rateDownload / 1024.0;
381    }
382
383    s->downloaded = tor->downloadedCur + tor->downloadedPrev;
384    s->uploaded   = tor->uploadedCur   + tor->uploadedPrev;
385   
386    if( s->downloaded == 0 )
387    {
388        s->ratio = s->uploaded == 0 ? TR_RATIO_NA : TR_RATIO_INF;
389    }
390    else
391    {
392        s->ratio = (float)s->uploaded / (float)s->downloaded;
393    }
394   
395    tr_lockUnlock( &tor->lock );
396
397    return s;
398}
399
400tr_peer_stat_t * tr_torrentPeers( tr_torrent_t * tor, int * peerCount )
401{
402    tr_peer_stat_t * peers;
403
404    tr_lockLock( &tor->lock );
405
406    *peerCount = tor->peerCount;
407   
408    peers = (tr_peer_stat_t *) calloc( tor->peerCount, sizeof( tr_peer_stat_t ) );
409    if (peers != NULL)
410    {
411        tr_peer_t * peer;
412        struct in_addr * addr;
413        int i;
414        for( i = 0; i < tor->peerCount; i++ )
415        {
416            peer = tor->peers[i];
417           
418            addr = tr_peerAddress( peer );
419            if( NULL != addr )
420            {
421                tr_netNtop( addr, peers[i].addr,
422                           sizeof( peers[i].addr ) );
423            }
424           
425            peers[i].client = tr_clientForId(tr_peerId(peer));
426           
427            peers[i].isConnected   = tr_peerIsConnected( peer );
428            peers[i].isIncoming    = tr_peerIsIncoming( peer );
429            peers[i].progress      = tr_peerProgress( peer );
430            peers[i].port          = tr_peerPort( peer );
431           
432            if( ( peers[i].isDownloading = !tr_peerAmChoking( peer ) ) )
433            {
434                peers[i].uploadToRate = tr_peerUploadRate( peer );
435            }
436            if( ( peers[i].isUploading = ( tr_peerAmInterested( peer ) &&
437                                           !tr_peerIsChoking( peer ) ) ) )
438            {
439                peers[i].downloadFromRate = tr_peerDownloadRate( peer );
440            }
441        }
442    }
443   
444    tr_lockUnlock( &tor->lock );
445   
446    return peers;
447}
448
449void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount )
450{
451    int i;
452
453    if (peers == NULL)
454        return;
455
456    for (i = 0; i < peerCount; i++)
457        free( peers[i].client );
458
459    free( peers );
460}
461
462void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
463{
464    int i, j, piece;
465    float interval;
466
467    tr_lockLock( &tor->lock );
468    interval = (float)tor->info.pieceCount / (float)size;
469    for( i = 0; i < size; i++ )
470    {
471        piece = i * interval;
472
473        if( tr_cpPieceIsComplete( tor->completion, piece ) )
474        {
475            tab[i] = -1;
476            continue;
477        }
478
479        tab[i] = 0;
480        for( j = 0; j < tor->peerCount; j++ )
481        {
482            if( tr_peerBitfield( tor->peers[j] ) &&
483                tr_bitfieldHas( tr_peerBitfield( tor->peers[j] ), piece ) )
484            {
485                (tab[i])++;
486            }
487        }
488    }
489    tr_lockUnlock( &tor->lock );
490}
491
492float * tr_torrentCompletion( tr_torrent_t * tor )
493{
494    tr_info_t * inf = &tor->info;
495    int         piece, file;
496    float     * ret, prog, weight;
497    uint64_t    piecemax, piecesize;
498    uint64_t    filestart, fileoff, filelen, blockend, blockused;
499
500    tr_lockLock( &tor->lock );
501
502    ret       = calloc( inf->fileCount, sizeof( float ) );
503    file      = 0;
504    piecemax  = inf->pieceSize;
505    filestart = 0;
506    fileoff   = 0;
507    piece     = 0;
508    while( inf->pieceCount > piece )
509    {
510        assert( file < inf->fileCount );
511        assert( filestart + fileoff < inf->totalSize );
512        filelen    = inf->files[file].length;
513        piecesize  = tr_pieceSize( piece );
514        blockend   = MIN( filestart + filelen, piecemax * piece + piecesize );
515        blockused  = blockend - ( filestart + fileoff );
516        weight     = ( filelen ? ( float )blockused / ( float )filelen : 1.0 );
517        prog       = tr_cpPercentBlocksInPiece( tor->completion, piece );
518        ret[file] += prog * weight;
519        fileoff   += blockused;
520        assert( -0.1 < prog   && 1.1 > prog );
521        assert( -0.1 < weight && 1.1 > weight );
522        if( fileoff == filelen )
523        {
524            ret[file] = MIN( 1.0, ret[file] );
525            ret[file] = MAX( 0.0, ret[file] );
526            filestart += fileoff;
527            fileoff    = 0;
528            file++;
529        }
530        if( filestart + fileoff >= piecemax * piece + piecesize )
531        {
532            piece++;
533        }
534    }
535
536    tr_lockUnlock( &tor->lock );
537
538    return ret;
539}
540
541void tr_torrentAmountFinished( tr_torrent_t * tor, float * tab, int size )
542{
543    int i, piece;
544    float interval;
545
546    tr_lockLock( &tor->lock );
547    interval = (float)tor->info.pieceCount / (float)size;
548    for( i = 0; i < size; i++ )
549    {
550        piece = i * interval;
551        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
552    }
553    tr_lockUnlock( &tor->lock );
554}
555
556void tr_torrentRemoveSaved( tr_torrent_t * tor )
557{
558    tr_metainfoRemoveSaved( tor->info.hashString );
559}
560
561/***********************************************************************
562 * tr_torrentClose
563 ***********************************************************************
564 * Frees memory allocated by tr_torrentInit.
565 **********************************************************************/
566void tr_torrentClose( tr_handle_t * h, tr_torrent_t * tor )
567{
568    tr_info_t * inf = &tor->info;
569
570    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
571    {
572        /* Join the thread first */
573        torrentReallyStop( tor );
574    }
575
576    tr_sharedLock( h->shared );
577
578    h->torrentCount--;
579
580    tr_lockClose( &tor->lock );
581    tr_condClose( &tor->cond );
582    tr_cpClose( tor->completion );
583
584    tr_rcClose( tor->upload );
585    tr_rcClose( tor->download );
586    tr_rcClose( tor->swarmspeed );
587
588    if( tor->destination )
589    {
590        free( tor->destination );
591    }
592
593    tr_metainfoFree( inf );
594
595    if( tor->prev )
596    {
597        tor->prev->next = tor->next;
598    }
599    else
600    {
601        h->torrentList = tor->next;
602    }
603    if( tor->next )
604    {
605        tor->next->prev = tor->prev;
606    }
607    free( tor );
608
609    tr_sharedUnlock( h->shared );
610}
611
612void tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
613{
614    int i;
615    tr_peer_t * otherPeer;
616
617    if( tor->peerCount >= TR_MAX_PEER_COUNT )
618    {
619        tr_peerDestroy(  peer );
620        return;
621    }
622
623    /* Don't accept two connections from the same IP */
624    for( i = 0; i < tor->peerCount; i++ )
625    {
626        otherPeer = tor->peers[i];
627        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
628        {
629            tr_peerDestroy(  peer );
630            return;
631        }
632    }
633
634    tr_peerSetTorrent( peer, tor );
635    tor->peers[tor->peerCount++] = peer;
636}
637
638void tr_torrentAddCompact( tr_torrent_t * tor, uint8_t * buf, int count )
639{
640    struct in_addr addr;
641    in_port_t port;
642    int i;
643    tr_peer_t * peer;
644
645    for( i = 0; i < count; i++ )
646    {
647        memcpy( &addr, buf, 4 ); buf += 4;
648        memcpy( &port, buf, 2 ); buf += 2;
649
650        peer = tr_peerInit( addr, port, -1 );
651        tr_torrentAttachPeer( tor, peer );
652    }
653}
654
655/***********************************************************************
656 * downloadLoop
657 **********************************************************************/
658static void downloadLoop( void * _tor )
659{
660    tr_torrent_t * tor = _tor;
661    int            i, ret;
662    int            peerCount;
663    uint8_t      * peerCompact;
664    tr_peer_t    * peer;
665
666    tr_lockLock( &tor->lock );
667
668    tr_cpReset( tor->completion );
669    tor->io     = tr_ioInit( tor );
670    tor->status = tr_cpIsSeeding( tor->completion ) ?
671                      TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
672
673    while( !tor->die )
674    {
675        tr_lockUnlock( &tor->lock );
676        tr_wait( 20 );
677        tr_lockLock( &tor->lock );
678
679        /* Are we finished ? */
680        if( ( tor->status & TR_STATUS_DOWNLOAD ) &&
681            tr_cpIsSeeding( tor->completion ) )
682        {
683            /* Done */
684            tor->status = TR_STATUS_SEED;
685                        tor->finished = 1;
686            tr_trackerCompleted( tor->tracker );
687            tr_ioSync( tor->io );
688        }
689
690        /* Try to get new peers or to send a message to the tracker */
691        tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
692        if( peerCount > 0 )
693        {
694            tr_torrentAddCompact( tor, peerCompact, peerCount );
695            free( peerCompact );
696        }
697        if( tor->status & TR_STATUS_STOPPED )
698        {
699            break;
700        }
701
702        /* Stopping: make sure all files are closed and stop talking
703           to peers */
704        if( tor->status & TR_STATUS_STOPPING )
705        {
706            if( tor->io )
707            {
708                tr_ioClose( tor->io ); tor->io = NULL;
709                tr_condSignal( &tor->cond );
710            }
711            continue;
712        }
713
714        /* Shuffle peers */
715        if( tor->peerCount > 1 )
716        {
717            peer = tor->peers[0];
718            memmove( &tor->peers[0], &tor->peers[1],
719                    ( tor->peerCount - 1 ) * sizeof( void * ) );
720            tor->peers[tor->peerCount - 1] = peer;
721        }
722
723        /* Receive/send messages */
724        for( i = 0; i < tor->peerCount; )
725        {
726            peer = tor->peers[i];
727
728            ret = tr_peerPulse( peer );
729            if( ret & TR_ERROR_IO_MASK )
730            {
731                tr_err( "Fatal error, stopping download (%d)", ret );
732                torrentStop( tor );
733                tor->error = ret;
734                snprintf( tor->errorString, sizeof( tor->errorString ),
735                          "%s", tr_errorString( ret ) );
736                break;
737            }
738            if( ret )
739            {
740                tr_peerDestroy( peer );
741                tor->peerCount--;
742                memmove( &tor->peers[i], &tor->peers[i+1],
743                         ( tor->peerCount - i ) * sizeof( void * ) );
744                continue;
745            }
746            i++;
747        }
748    }
749
750    tr_lockUnlock( &tor->lock );
751
752    if( tor->io )
753    {
754        tr_ioClose( tor->io ); tor->io = NULL;
755        tr_condSignal( &tor->cond );
756    }
757
758    tor->status = TR_STATUS_STOPPED;
759}
760
Note: See TracBrowser for help on using the repository browser.