source: trunk/libtransmission/transmission.c @ 237

Last change on this file since 237 was 237, checked in by titer, 16 years ago

Clean up, perform all DNS resolutions in a common thread. This should
fix the remaining issues with many open torrents

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