source: trunk/libtransmission/transmission.c @ 26

Last change on this file since 26 was 26, checked in by root, 16 years ago

Update 2006-01-11

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