source: trunk/libtransmission/transmission.c @ 216

Last change on this file since 216 was 216, checked in by joshe, 16 years ago

Fixed bug where incorrect stats were being reported to the tracker.
This was introduced when download and upload totals became persistent.

File size: 20.0 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005-2006 Transmission authors and contributors
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23#include "transmission.h"
24
25/***********************************************************************
26 * Local prototypes
27 **********************************************************************/
28static void torrentReallyStop( tr_torrent_t * );
29static void  downloadLoop( void * );
30static void  acceptLoop( void * );
31static void acceptStop( tr_handle_t * h );
32
33/***********************************************************************
34 * tr_init
35 ***********************************************************************
36 * Allocates a tr_handle_t structure and initializes a few things
37 **********************************************************************/
38tr_handle_t * tr_init()
39{
40    tr_handle_t * h;
41    int           i, r;
42
43    h = calloc( sizeof( tr_handle_t ), 1 );
44
45    /* Generate a peer id : "-TRxxyy-" + 12 random alphanumeric
46       characters, where xx is the major version number and yy the
47       minor version number (Azureus-style) */
48    sprintf( h->id, "-TR%02d%02d-", VERSION_MAJOR, VERSION_MINOR );
49    for( i = 8; i < 20; i++ )
50    {
51        r        = tr_rand( 36 );
52        h->id[i] = ( r < 26 ) ? ( 'a' + r ) : ( '0' + r - 26 ) ;
53    }
54
55    /* Random key */
56    for( i = 0; i < 20; i++ )
57    {
58        r         = tr_rand( 36 );
59        h->key[i] = ( r < 26 ) ? ( 'a' + r ) : ( '0' + r - 26 ) ;
60    }
61
62    /* Don't exit when writing on a broken socket */
63    signal( SIGPIPE, SIG_IGN );
64
65    /* Initialize rate and file descripts controls */
66    h->upload   = tr_rcInit();
67    h->download = tr_rcInit();
68    h->fdlimit  = tr_fdInit();
69    h->choking  = tr_chokingInit( h );
70
71    h->bindPort = -1;
72    h->bindSocket = -1;
73
74    h->acceptDie = 0;
75    tr_lockInit( &h->acceptLock );
76    tr_threadCreate( &h->acceptThread, acceptLoop, h );
77
78    return h;
79}
80
81/***********************************************************************
82 * tr_setBindPort
83 ***********************************************************************
84 *
85 **********************************************************************/
86void tr_setBindPort( tr_handle_t * h, int port )
87{
88    int sock = -1;
89    tr_torrent_t * tor;
90
91    if( h->bindPort == port )
92      return;
93
94#ifndef BEOS_NETSERVER
95    /* BeOS net_server seems to be unable to set incoming connections to
96       non-blocking. Too bad. */
97    if( !tr_fdSocketWillCreate( h->fdlimit, 0 ) )
98    {
99        /* XXX should handle failure here in a better way */
100        sock = tr_netBind( port );
101    }
102#else
103    return;
104#endif
105
106    tr_lockLock( &h->acceptLock );
107
108    h->bindPort = port;
109
110    for( tor = h->torrentList; tor; tor = tor->next )
111    {
112        tr_lockLock( &tor->lock );
113        if( NULL != tor->tracker )
114        {
115            tr_trackerChangePort( tor->tracker, port );
116        }
117        tr_lockUnlock( &tor->lock );
118    }
119
120    if( h->bindSocket > -1 )
121    {
122        tr_netClose( h->bindSocket );
123        tr_fdSocketClosed( h->fdlimit, 0 );
124    }
125
126    h->bindSocket = sock;
127
128    tr_lockUnlock( &h->acceptLock );
129}
130
131/***********************************************************************
132 * tr_setUploadLimit
133 ***********************************************************************
134 *
135 **********************************************************************/
136void tr_setUploadLimit( tr_handle_t * h, int limit )
137{
138    tr_rcSetLimit( h->upload, limit );
139    tr_chokingSetLimit( h->choking, limit );
140}
141
142/***********************************************************************
143 * tr_setDownloadLimit
144 ***********************************************************************
145 *
146 **********************************************************************/
147void tr_setDownloadLimit( tr_handle_t * h, int limit )
148{
149    tr_rcSetLimit( h->download, limit );
150}
151
152/***********************************************************************
153 * tr_torrentRates
154 ***********************************************************************
155 *
156 **********************************************************************/
157void tr_torrentRates( tr_handle_t * h, float * dl, float * ul )
158{
159    tr_torrent_t * tor;
160
161    *dl = 0.0;
162    *ul = 0.0;
163    for( tor = h->torrentList; tor; tor = tor->next )
164    {
165        tr_lockLock( &tor->lock );
166        if( tor->status & TR_STATUS_DOWNLOAD )
167            *dl += tr_rcRate( tor->download );
168        *ul += tr_rcRate( tor->upload );
169        tr_lockUnlock( &tor->lock );
170    }
171}
172
173/***********************************************************************
174 * tr_torrentInit
175 ***********************************************************************
176 * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
177 * to fill it.
178 **********************************************************************/
179tr_torrent_t * tr_torrentInit( tr_handle_t * h, const char * path,
180                               int * error )
181{
182    tr_torrent_t  * tor, * tor_tmp;
183    tr_info_t     * inf;
184    int             i;
185    char          * s1, * s2;
186
187    tor = calloc( sizeof( tr_torrent_t ), 1 );
188    inf = &tor->info;
189
190    /* Parse torrent file */
191    if( tr_metainfoParse( inf, path ) )
192    {
193        *error = TR_EINVALID;
194        free( tor );
195        return NULL;
196    }
197
198    /* Make sure this torrent is not already open */
199    for( tor_tmp = h->torrentList; tor_tmp; tor_tmp = tor_tmp->next )
200    {
201        if( !memcmp( tor->info.hash, tor_tmp->info.hash,
202                     SHA_DIGEST_LENGTH ) )
203        {
204            *error = TR_EDUPLICATE;
205            free( tor );
206            return NULL;
207        }
208    }
209
210    tor->status = TR_STATUS_PAUSE;
211    tor->id     = h->id;
212    tor->key    = h->key;
213    tor->bindPort = &h->bindPort;
214        tor->finished = 0;
215
216
217    /* Guess scrape URL */
218    s1 = strchr( inf->trackerAnnounce, '/' );
219    while( ( s2 = strchr( s1 + 1, '/' ) ) )
220    {
221        s1 = s2;
222    }
223    s1++;
224    if( !strncmp( s1, "announce", 8 ) )
225    {
226        int pre  = (long) s1 - (long) inf->trackerAnnounce;
227        int post = strlen( inf->trackerAnnounce ) - pre - 8;
228        memcpy( tor->scrape, inf->trackerAnnounce, pre );
229        sprintf( &tor->scrape[pre], "scrape" );
230        memcpy( &tor->scrape[pre+6], &inf->trackerAnnounce[pre+8], post );
231    }
232
233    /* Escaped info hash for HTTP queries */
234    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
235    {
236        sprintf( &tor->hashString[3*i], "%%%02x", inf->hash[i] );
237    }
238
239    /* Block size: usually 16 ko, or less if we have to */
240    tor->blockSize  = MIN( inf->pieceSize, 1 << 14 );
241    tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
242                        tor->blockSize;
243    tor->completion = tr_cpInit( tor );
244
245    tr_lockInit( &tor->lock );
246
247    tor->globalUpload   = h->upload;
248    tor->globalDownload = h->download;
249    tor->fdlimit        = h->fdlimit;
250    tor->upload         = tr_rcInit();
251    tor->download       = tr_rcInit();
252 
253    /* We have a new torrent */
254    tr_lockLock( &h->acceptLock );
255    tor->prev = NULL;
256    tor->next = h->torrentList;
257    if( tor->next )
258    {
259        tor->next->prev = tor;
260    }
261    h->torrentList = tor;
262    (h->torrentCount)++;
263    tr_lockUnlock( &h->acceptLock );
264
265    if( 0 > h->bindPort )
266    {
267        tr_setBindPort( h, TR_DEFAULT_PORT );
268    }
269
270    return tor;
271}
272
273tr_info_t * tr_torrentInfo( tr_torrent_t * tor )
274{
275    return &tor->info;
276}
277
278/***********************************************************************
279 * tr_torrentScrape
280 **********************************************************************/
281int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l )
282{
283    return tr_trackerScrape( tor, s, l );
284}
285
286void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
287{
288    tor->destination = strdup( path );
289}
290
291char * tr_torrentGetFolder( tr_torrent_t * tor )
292{
293    return tor->destination;
294}
295
296void tr_torrentStart( tr_torrent_t * tor )
297{
298    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
299    {
300        /* Join the thread first */
301        torrentReallyStop( tor );
302    }
303
304    tor->status  = TR_STATUS_CHECK;
305
306    tor->date = tr_date();
307    tor->die = 0;
308    tr_threadCreate( &tor->thread, downloadLoop, tor );
309}
310
311void tr_torrentStop( tr_torrent_t * tor )
312{
313    tr_lockLock( &tor->lock );
314    tr_trackerStopped( tor->tracker );
315    tr_rcReset( tor->download );
316    tr_rcReset( tor->upload );
317    tor->status = TR_STATUS_STOPPING;
318    tor->stopDate = tr_date();
319    tr_lockUnlock( &tor->lock );
320}
321
322/***********************************************************************
323 * torrentReallyStop
324 ***********************************************************************
325 * Joins the download thread and frees/closes everything related to it.
326 **********************************************************************/
327static void torrentReallyStop( tr_torrent_t * tor )
328{
329    tor->die = 1;
330    tr_threadJoin( &tor->thread );
331    tr_dbg( "Thread joined" );
332
333    tr_trackerClose( tor->tracker );
334
335    while( tor->peerCount > 0 )
336    {
337        tr_peerRem( tor, 0 );
338    }
339}
340
341/***********************************************************************
342 * tr_torrentCount
343 ***********************************************************************
344 *
345 **********************************************************************/
346int tr_torrentCount( tr_handle_t * h )
347{
348    return h->torrentCount;
349}
350
351void tr_torrentIterate( tr_handle_t * h, tr_callback_t func, void * d )
352{
353    tr_torrent_t * tor;
354
355    for( tor = h->torrentList; tor; tor = tor->next )
356    {
357        func( tor, d );
358    }
359}
360
361int tr_getFinished( tr_torrent_t * tor )
362{
363    if( tor->finished )
364    {
365        tor->finished = 0;
366        return 1;
367    }
368    return 0;
369}
370
371tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
372{
373    tr_stat_t * s;
374    tr_info_t * inf = &tor->info;
375    int i;
376
377    tor->statCur = ( tor->statCur + 1 ) % 2;
378    s = &tor->stats[tor->statCur];
379
380    if( ( tor->status & TR_STATUS_STOPPED ) ||
381        ( ( tor->status & TR_STATUS_STOPPING ) &&
382          tr_date() > tor->stopDate + 60000 ) )
383    {
384        torrentReallyStop( tor );
385        tor->status = TR_STATUS_PAUSE;
386    }
387
388    tr_lockLock( &tor->lock );
389
390    s->status = tor->status;
391    memcpy( s->trackerError, tor->trackerError,
392            sizeof( s->trackerError ) );
393
394    s->peersTotal       = 0;
395    s->peersUploading   = 0;
396    s->peersDownloading = 0;
397
398    for( i = 0; i < tor->peerCount; i++ )
399    {
400        if( tr_peerIsConnected( tor->peers[i] ) )
401        {
402            (s->peersTotal)++;
403            if( tr_peerIsUploading( tor->peers[i] ) )
404            {
405                (s->peersUploading)++;
406            }
407            if( tr_peerIsDownloading( tor->peers[i] ) )
408            {
409                (s->peersDownloading)++;
410            }
411        }
412    }
413
414    s->progress = tr_cpCompletionAsFloat( tor->completion );
415    if( tor->status & TR_STATUS_DOWNLOAD )
416        s->rateDownload = tr_rcRate( tor->download );
417    else
418        /* tr_rcRate() doesn't make the difference between 'piece'
419           messages and other messages, which causes a non-zero
420           download rate even tough we are not downloading. So we
421           force it to zero not to confuse the user. */
422        s->rateDownload = 0.0;
423    s->rateUpload = tr_rcRate( tor->upload );
424   
425    s->seeders    = tr_trackerSeeders(tor);
426        s->leechers       = tr_trackerLeechers(tor);
427
428    if( s->rateDownload < 0.1 )
429    {
430        s->eta = -1;
431    }
432    else
433    {
434        s->eta = (float) ( 1.0 - s->progress ) *
435            (float) inf->totalSize / s->rateDownload / 1024.0;
436        if( s->eta > 99 * 3600 + 59 * 60 + 59 )
437        {
438            s->eta = -1;
439        }
440    }
441
442    s->downloaded = tor->downloaded;
443    s->uploaded   = tor->uploaded;
444
445    tr_lockUnlock( &tor->lock );
446
447    return s;
448}
449
450void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
451{
452    int i, j, piece;
453
454    tr_lockLock( &tor->lock );
455    for( i = 0; i < size; i++ )
456    {
457        piece = i * tor->info.pieceCount / size;
458
459        if( tr_cpPieceIsComplete( tor->completion, piece ) )
460        {
461            tab[i] = -1;
462            continue;
463        }
464
465        tab[i] = 0;
466        for( j = 0; j < tor->peerCount; j++ )
467        {
468            if( tr_peerBitfield( tor->peers[j] ) &&
469                tr_bitfieldHas( tr_peerBitfield( tor->peers[j] ), piece ) )
470            {
471                (tab[i])++;
472            }
473        }
474    }
475    tr_lockUnlock( &tor->lock );
476}
477
478/***********************************************************************
479 * tr_torrentClose
480 ***********************************************************************
481 * Frees memory allocated by tr_torrentInit.
482 **********************************************************************/
483void tr_torrentClose( tr_handle_t * h, tr_torrent_t * tor )
484{
485    tr_info_t * inf = &tor->info;
486
487    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
488    {
489        /* Join the thread first */
490        torrentReallyStop( tor );
491    }
492
493    tr_lockLock( &h->acceptLock );
494
495    h->torrentCount--;
496
497    tr_lockClose( &tor->lock );
498    tr_cpClose( tor->completion );
499
500    tr_rcClose( tor->upload );
501    tr_rcClose( tor->download );
502
503    if( tor->destination )
504    {
505        free( tor->destination );
506    }
507    free( inf->pieces );
508    free( inf->files );
509
510    if( tor->prev )
511    {
512        tor->prev->next = tor->next;
513    }
514    else
515    {
516        h->torrentList = tor->next;
517    }
518    if( tor->next )
519    {
520        tor->next->prev = tor->prev;
521    }
522    free( tor );
523
524    tr_lockUnlock( &h->acceptLock );
525}
526
527void tr_close( tr_handle_t * h )
528{
529    acceptStop( h );
530    tr_chokingClose( h->choking );
531    tr_fdClose( h->fdlimit );
532    tr_rcClose( h->upload );
533    tr_rcClose( h->download );
534    free( h );
535}
536
537/***********************************************************************
538 * downloadLoop
539 **********************************************************************/
540static void downloadLoop( void * _tor )
541{
542    tr_torrent_t * tor = _tor;
543    uint64_t       date1, date2;
544
545    tr_dbg( "Thread started" );
546
547#ifdef SYS_BEOS
548    /* This is required because on BeOS, SIGINT is sent to each thread,
549       which kills them not nicely */
550    signal( SIGINT, SIG_IGN );
551#endif
552
553    tr_lockLock( &tor->lock );
554
555    tr_cpReset( tor->completion );
556    tor->io     = tr_ioInit( tor );
557    tor->status = tr_cpIsSeeding( tor->completion ) ?
558                      TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
559    tor->tracker = tr_trackerInit( tor );
560
561    while( !tor->die )
562    {
563        date1 = tr_date();
564
565        /* Are we finished ? */
566        if( ( tor->status & TR_STATUS_DOWNLOAD ) &&
567            tr_cpIsSeeding( tor->completion ) )
568        {
569            /* Done */
570            tor->status = TR_STATUS_SEED;
571                        tor->finished = 1;
572            tr_trackerCompleted( tor->tracker );
573            tr_ioSaveResume( tor->io );
574        }
575
576        /* Receive/send messages */
577        tr_peerPulse( tor );
578
579        /* Try to get new peers or to send a message to the tracker */
580        tr_trackerPulse( tor->tracker );
581
582        if( tor->status & TR_STATUS_STOPPED )
583        {
584            break;
585        }
586
587        /* Wait up to 20 ms */
588        date2 = tr_date();
589        if( date2 < date1 + 20 )
590        {
591            tr_lockUnlock( &tor->lock );
592            tr_wait( date1 + 20 - date2 );
593            tr_lockLock( &tor->lock );
594        }
595    }
596
597    tr_lockUnlock( &tor->lock );
598
599    tr_ioClose( tor->io );
600
601    tor->status = TR_STATUS_STOPPED;
602
603    tr_dbg( "Thread exited" );
604}
605
606/***********************************************************************
607 * acceptLoop
608 **********************************************************************/
609static void acceptLoop( void * _h )
610{
611    tr_handle_t * h = _h;
612    uint64_t      date1, date2, lastchoke = 0;
613    int           ii;
614    uint8_t     * hash;
615    tr_torrent_t * tor;
616
617    tr_dbg( "Accept thread started" );
618
619#ifdef SYS_BEOS
620    /* This is required because on BeOS, SIGINT is sent to each thread,
621       which kills them not nicely */
622    signal( SIGINT, SIG_IGN );
623#endif
624
625    tr_lockLock( &h->acceptLock );
626
627    while( !h->acceptDie )
628    {
629        date1 = tr_date();
630
631        /* Check for incoming connections */
632        if( h->bindSocket > -1 &&
633            h->acceptPeerCount < TR_MAX_PEER_COUNT &&
634            !tr_fdSocketWillCreate( h->fdlimit, 0 ) )
635        {
636            int            s;
637            struct in_addr addr;
638            in_port_t      port;
639            s = tr_netAccept( h->bindSocket, &addr, &port );
640            if( s > -1 )
641            {
642                h->acceptPeers[h->acceptPeerCount++] = tr_peerInit( addr, port, s );
643            }
644            else
645            {
646                tr_fdSocketClosed( h->fdlimit, 0 );
647            }
648        }
649
650        for( ii = 0; ii < h->acceptPeerCount; )
651        {
652            if( tr_peerRead( NULL, h->acceptPeers[ii] ) )
653            {
654                tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
655                goto removePeer;
656            }
657            if( NULL != ( hash = tr_peerHash( h->acceptPeers[ii] ) ) )
658            {
659                for( tor = h->torrentList; tor; tor = tor->next )
660                {
661                    tr_lockLock( &tor->lock );
662                    if( 0 == memcmp( tor->info.hash, hash,
663                                     SHA_DIGEST_LENGTH ) )
664                    {
665                      tr_peerAttach( tor, h->acceptPeers[ii] );
666                      tr_lockUnlock( &tor->lock );
667                      goto removePeer;
668                    }
669                    tr_lockUnlock( &tor->lock );
670                }
671                tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
672                goto removePeer;
673            }
674            if( date1 > tr_peerDate( h->acceptPeers[ii] ) + 10000 )
675            {
676                /* Give them 10 seconds to send the handshake */
677                tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
678                goto removePeer;
679            }
680            ii++;
681            continue;
682           removePeer:
683            h->acceptPeerCount--;
684            memmove( &h->acceptPeers[ii], &h->acceptPeers[ii+1],
685                     ( h->acceptPeerCount - ii ) * sizeof( tr_peer_t * ) );
686        }
687
688        if( date1 > lastchoke + 2000 )
689        {
690            tr_chokingPulse( h->choking );
691            lastchoke = date1;
692        }
693
694        /* Wait up to 20 ms */
695        date2 = tr_date();
696        if( date2 < date1 + 20 )
697        {
698            tr_lockUnlock( &h->acceptLock );
699            tr_wait( date1 + 20 - date2 );
700            tr_lockLock( &h->acceptLock );
701        }
702    }
703
704    tr_lockUnlock( &h->acceptLock );
705
706    tr_dbg( "Accept thread exited" );
707}
708
709/***********************************************************************
710 * acceptStop
711 ***********************************************************************
712 * Joins the accept thread and frees/closes everything related to it.
713 **********************************************************************/
714static void acceptStop( tr_handle_t * h )
715{
716    int ii;
717
718    h->acceptDie = 1;
719    tr_threadJoin( &h->acceptThread );
720    tr_lockClose( &h->acceptLock );
721    tr_dbg( "Accept thread joined" );
722
723    for( ii = 0; ii < h->acceptPeerCount; ii++ )
724    {
725        tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
726    }
727
728    if( h->bindSocket > -1 )
729    {
730        tr_netClose( h->bindSocket );
731        tr_fdSocketClosed( h->fdlimit, 0 );
732    }
733}
Note: See TracBrowser for help on using the repository browser.