source: trunk/libtransmission/transmission.c @ 67

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

Fixes a little memleak

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