source: trunk/libtransmission/transmission.c @ 219

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

Show tracker errors again

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