source: trunk/libtransmission/transmission.c @ 60

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

Rewrote rate control, background work for the download limit, the dl/ul
limits per torrent and for the choking algorithm

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