source: trunk/libtransmission/transmission.c @ 261

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

Updated svn:keywords

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