source: trunk/libtransmission/transmission.c @ 203

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

I will not commit without compiling first.
I will not commit without compiling first.
I will not commit without compiling first.
I will not commit without compiling first.
I will not commit without compiling first.
I will not commit without compiling first.
I will not commit without compiling first.
I will not commit without compiling first.
I will not commit without compiling first.
I will not commit without compiling first.

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