source: trunk/libtransmission/transmission.c @ 6

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

Update 2005-11-21

File size: 16.1 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  downloadLoop( void * );
29static float rateDownload( tr_torrent_t * );
30static float rateUpload( tr_torrent_t * );
31
32/***********************************************************************
33 * tr_init
34 ***********************************************************************
35 * Allocates a tr_handle_t structure and initializes a few things
36 **********************************************************************/
37tr_handle_t * tr_init()
38{
39    tr_handle_t * h;
40    int           i, r;
41
42    h = calloc( sizeof( tr_handle_t ), 1 );
43
44    /* Generate a peer id : "-TRxxyy-" + 12 random alphanumeric
45       characters, where xx is the major version number and yy the
46       minor version number (Azureus-style) */
47    sprintf( h->id, "-TR%02d%02d-", VERSION_MAJOR, VERSION_MINOR );
48    for( i = 8; i < 20; i++ )
49    {
50        r        = tr_rand( 36 );
51        h->id[i] = ( r < 26 ) ? ( 'a' + r ) : ( '0' + r - 26 ) ;
52    }
53
54    /* Random key */
55    for( i = 0; i < 20; i++ )
56    {
57        r         = tr_rand( 36 );
58        h->key[i] = ( r < 26 ) ? ( 'a' + r ) : ( '0' + r - 26 ) ;
59    }
60
61    /* Don't exit when writing on a broken socket */
62    signal( SIGPIPE, SIG_IGN );
63
64    /* Initialize rate and file descripts controls */
65    h->upload  = tr_uploadInit();
66    h->fdlimit = tr_fdInit();
67
68    h->bindPort = 9090;
69
70    snprintf( h->prefsDirectory, sizeof( h->prefsDirectory ),
71              "%s/.transmission", getenv( "HOME" ) );
72    mkdir( h->prefsDirectory, 0755 );
73   
74    return h;
75}
76
77/***********************************************************************
78 * tr_getPrefsDirectory
79 ***********************************************************************
80 *
81 **********************************************************************/
82char * tr_getPrefsDirectory( tr_handle_t * h )
83{
84    return (char *) h->prefsDirectory;
85}
86
87/***********************************************************************
88 * tr_setBindPort
89 ***********************************************************************
90 *
91 **********************************************************************/
92void tr_setBindPort( tr_handle_t * h, int port )
93{
94    /* FIXME multithread safety */
95    h->bindPort = port;
96}
97
98/***********************************************************************
99 * tr_setUploadLimit
100 ***********************************************************************
101 *
102 **********************************************************************/
103void tr_setUploadLimit( tr_handle_t * h, int limit )
104{
105    tr_uploadSetLimit( h->upload, limit );
106}
107
108/***********************************************************************
109 * tr_torrentRates
110 ***********************************************************************
111 *
112 **********************************************************************/
113void tr_torrentRates( tr_handle_t * h, float * dl, float * ul )
114{
115    int            i;
116    tr_torrent_t * tor;
117
118    *dl = 0.0;
119    *ul = 0.0;
120
121    for( i = 0; i < h->torrentCount; i++ )
122    {
123        tor = h->torrents[i];
124        tr_lockLock( tor->lock );
125        *dl += rateDownload( tor );
126        *ul += rateUpload( tor );
127        tr_lockUnlock( tor->lock );
128    }
129}
130
131/***********************************************************************
132 * tr_torrentInit
133 ***********************************************************************
134 * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
135 * to fill it.
136 **********************************************************************/
137int tr_torrentInit( tr_handle_t * h, const char * path )
138{
139    tr_torrent_t  * tor;
140    tr_info_t     * inf;
141    int             i;
142    char          * s1, * s2;
143
144    if( h->torrentCount >= TR_MAX_TORRENT_COUNT )
145    {
146        tr_err( "Maximum number of torrents reached" );
147        return 1;
148    }
149
150    tor = calloc( sizeof( tr_torrent_t ), 1 );
151    inf = &tor->info;
152
153    /* Parse torrent file */
154    if( tr_metainfoParse( inf, path ) )
155    {
156        free( tor );
157        return 1;
158    }
159
160    /* Make sure this torrent is not already open */
161    for( i = 0; i < h->torrentCount; i++ )
162    {
163        if( !memcmp( tor->info.hash, h->torrents[i]->info.hash,
164                     SHA_DIGEST_LENGTH ) )
165        {
166            tr_err( "Torrent already open" );
167            free( tor );
168            return 1;
169        }
170    }
171
172    tor->status = TR_STATUS_PAUSE;
173    tor->id     = h->id;
174    tor->key    = h->key;
175        tor->finished = 0;
176
177
178    /* Guess scrape URL */
179    s1 = strchr( inf->trackerAnnounce, '/' );
180    while( ( s2 = strchr( s1 + 1, '/' ) ) )
181    {
182        s1 = s2;
183    }
184    s1++;
185    if( !strncmp( s1, "announce", 8 ) )
186    {
187        int pre  = (long) s1 - (long) inf->trackerAnnounce;
188        int post = strlen( inf->trackerAnnounce ) - pre - 8;
189        memcpy( tor->scrape, inf->trackerAnnounce, pre );
190        sprintf( &tor->scrape[pre], "scrape" );
191        memcpy( &tor->scrape[pre+6], &inf->trackerAnnounce[pre+8], post );
192    }
193
194    /* Escaped info hash for HTTP queries */
195    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
196    {
197        sprintf( &tor->hashString[3*i], "%%%02x", inf->hash[i] );
198    }
199
200    /* Block size: usually 16 ko, or less if we have to */
201    tor->blockSize  = MIN( inf->pieceSize, 1 << 14 );
202    tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
203                        tor->blockSize;
204    tor->completion = tr_cpInit( tor );
205
206    tr_lockInit( &tor->lock );
207
208    tor->upload  = h->upload;
209    tor->fdlimit = h->fdlimit;
210    tor->prefsDirectory = (char *) h->prefsDirectory;
211 
212    /* We have a new torrent */
213    h->torrents[h->torrentCount] = tor;
214    (h->torrentCount)++;
215
216    return 0;
217}
218
219/***********************************************************************
220 * tr_torrentScrape
221 ***********************************************************************
222 * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
223 * to fill it.
224 **********************************************************************/
225int tr_torrentScrape( tr_handle_t * h, int t, int * s, int * l )
226{
227    return tr_trackerScrape( h->torrents[t], s, l );
228}
229
230void tr_torrentSetFolder( tr_handle_t * h, int t, const char * path )
231{
232    tr_torrent_t * tor = h->torrents[t];
233
234    tor->destination = strdup( path );
235}
236
237char * tr_torrentGetFolder( tr_handle_t * h, int t )
238{
239    tr_torrent_t * tor = h->torrents[t];
240
241    return tor->destination;
242}
243
244void tr_torrentStart( tr_handle_t * h, int t )
245{
246    tr_torrent_t * tor = h->torrents[t];
247    uint64_t       now;
248    int            i;
249
250    tor->status   = TR_STATUS_CHECK;
251    tor->tracker  = tr_trackerInit( h, tor );
252    tor->bindPort = h->bindPort;
253#ifndef BEOS_NETSERVER
254    /* BeOS net_server seems to be unable to set incoming connections to
255       non-blocking. Too bad. */
256    if( !tr_fdSocketWillCreate( h->fdlimit, 0 ) )
257    {
258        tor->bindSocket = tr_netBind( &tor->bindPort );
259    }
260#endif
261
262    now = tr_date();
263    for( i = 0; i < 10; i++ )
264    {
265        tor->dates[i] = now;
266    }
267
268    tor->die = 0;
269    tr_threadCreate( &tor->thread, downloadLoop, tor );
270}
271
272void tr_torrentStop( tr_handle_t * h, int t )
273{
274    tr_torrent_t * tor = h->torrents[t];
275
276    tr_lockLock( tor->lock );
277    tr_trackerStopped( tor->tracker );
278    tor->status = TR_STATUS_STOPPING;
279    tor->stopDate = tr_date();
280    tr_lockUnlock( tor->lock );
281}
282
283/***********************************************************************
284 * torrentReallyStop
285 ***********************************************************************
286 * Joins the download thread and frees/closes everything related to it.
287 **********************************************************************/
288static void torrentReallyStop( tr_handle_t * h, int t )
289{
290    tr_torrent_t * tor = h->torrents[t];
291
292    tor->die = 1;
293    tr_threadJoin( tor->thread );
294    tr_dbg( "Thread joined" );
295
296    tr_trackerClose( tor->tracker );
297
298    while( tor->peerCount > 0 )
299    {
300        tr_peerRem( tor, 0 );
301    }
302    if( tor->bindSocket > -1 )
303    {
304        tr_netClose( tor->bindSocket );
305        tr_fdSocketClosed( h->fdlimit, 0 );
306    }
307
308    memset( tor->downloaded, 0, sizeof( tor->downloaded ) );
309    memset( tor->uploaded,   0, sizeof( tor->uploaded ) );
310}
311
312/***********************************************************************
313 * tr_torrentCount
314 ***********************************************************************
315 *
316 **********************************************************************/
317int tr_torrentCount( tr_handle_t * h )
318{
319    return h->torrentCount;
320}
321
322int tr_getFinished( tr_handle_t * h, int i)
323{
324        return h->torrents[i]->finished;
325}
326void tr_setFinished( tr_handle_t * h, int i, int val)
327{
328        h->torrents[i]->finished = val;
329}
330
331int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat )
332{
333    tr_stat_t * s;
334    tr_torrent_t * tor;
335    tr_info_t * inf;
336    int i, j, k, piece;
337
338    if( h->torrentCount < 1 )
339    {
340        *stat = NULL;
341        return 0;
342    }
343
344    s = malloc( h->torrentCount * sizeof( tr_stat_t ) );
345
346    for( i = 0; i < h->torrentCount; i++ )
347    {
348        tor = h->torrents[i];
349        inf = &tor->info;
350
351        if( ( tor->status & TR_STATUS_STOPPED ) ||
352            ( ( tor->status & TR_STATUS_STOPPING ) &&
353              tr_date() > tor->stopDate + 60000 ) )
354        {
355            torrentReallyStop( h, i );
356            tor->status = TR_STATUS_PAUSE;
357        }
358
359        tr_lockLock( tor->lock );
360
361        memcpy( &s[i].info, &tor->info, sizeof( tr_info_t ) );
362        s[i].status = tor->status;
363        memcpy( s[i].error, tor->error, sizeof( s[i].error ) );
364
365        s[i].peersTotal       = 0;
366        s[i].peersUploading   = 0;
367        s[i].peersDownloading = 0;
368
369        for( j = 0; j < tor->peerCount; j++ )
370        {
371            if( tr_peerIsConnected( tor->peers[j] ) )
372            {
373                (s[i].peersTotal)++;
374                if( tr_peerIsUploading( tor->peers[j] ) )
375                {
376                    (s[i].peersUploading)++;
377                }
378                if( tr_peerIsDownloading( tor->peers[j] ) )
379                {
380                    (s[i].peersDownloading)++;
381                }
382            }
383        }
384
385        s[i].progress     = tr_cpCompletionAsFloat( tor->completion );
386        s[i].rateDownload = rateDownload( tor );
387        s[i].rateUpload   = rateUpload( tor );
388       
389        s[i].seeders      = tr_trackerSeeders(tor);
390                s[i].leechers     = tr_trackerLeechers(tor);
391
392        if( s[i].rateDownload < 0.1 )
393        {
394            s[i].eta = -1;
395        }
396        else
397        {
398            s[i].eta = (float) ( 1.0 - s[i].progress ) *
399                (float) inf->totalSize / s[i].rateDownload / 1024.0;
400            if( s[i].eta > 99 * 3600 + 59 * 60 + 59 )
401            {
402                s[i].eta = -1;
403            }
404        }
405
406        for( j = 0; j < 120; j++ )
407        {
408            piece = j * inf->pieceCount / 120;
409
410            if( tr_cpPieceIsComplete( tor->completion, piece ) )
411            {
412                s[i].pieces[j] = -1;
413                continue;
414            }
415
416            s[i].pieces[j] = 0;
417           
418            for( k = 0; k < tor->peerCount; k++ )
419            {
420                if( tr_peerBitfield( tor->peers[k] ) &&
421                    tr_bitfieldHas( tr_peerBitfield( tor->peers[k] ), piece ) )
422                {
423                    (s[i].pieces[j])++;
424                }
425            }
426        }
427
428        s[i].downloaded = tor->downloaded[9];
429        s[i].uploaded   = tor->uploaded[9];
430
431        s[i].folder = tor->destination;
432
433        tr_lockUnlock( tor->lock );
434    }
435
436    *stat = s;
437    return h->torrentCount;
438}
439
440/***********************************************************************
441 * tr_torrentClose
442 ***********************************************************************
443 * Frees memory allocated by tr_torrentInit.
444 **********************************************************************/
445void tr_torrentClose( tr_handle_t * h, int t )
446{
447    tr_torrent_t * tor = h->torrents[t];
448    tr_info_t    * inf = &tor->info;
449
450    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
451    {
452        /* Join the thread first */
453        torrentReallyStop( h, t );
454    }
455
456    h->torrentCount--;
457
458    tr_lockClose( tor->lock );
459    tr_cpClose( tor->completion );
460
461    if( tor->destination )
462    {
463        free( tor->destination );
464    }
465    free( inf->pieces );
466    free( inf->files );
467    free( tor );
468
469    memmove( &h->torrents[t], &h->torrents[t+1],
470             ( h->torrentCount - t ) * sizeof( void * ) );
471}
472
473void tr_close( tr_handle_t * h )
474{
475    tr_fdClose( h->fdlimit );
476    tr_uploadClose( h->upload );
477    free( h );
478}
479
480/***********************************************************************
481 * downloadLoop
482 **********************************************************************/
483static void downloadLoop( void * _tor )
484{
485    tr_torrent_t * tor = _tor;
486    uint64_t       date1, date2;
487
488    tr_dbg( "Thread started" );
489
490#ifdef SYS_BEOS
491    /* This is required because on BeOS, SIGINT is sent to each thread,
492       which kills them not nicely */
493    signal( SIGINT, SIG_IGN );
494#endif
495
496    tr_lockLock( tor->lock );
497
498    tr_cpReset( tor->completion );
499    tor->io     = tr_ioInit( tor );
500    tor->status = tr_cpIsSeeding( tor->completion ) ?
501                      TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
502
503    while( !tor->die )
504    {
505        date1 = tr_date();
506
507        /* Are we finished ? */
508        if( ( tor->status & TR_STATUS_DOWNLOAD ) &&
509            tr_cpIsSeeding( tor->completion ) )
510        {
511            /* Done */
512            tor->status = TR_STATUS_SEED;
513                        tor->finished = 1;
514            tr_trackerCompleted( tor->tracker );
515        }
516
517        /* Receive/send messages */
518        tr_peerPulse( tor );
519
520        /* Try to get new peers or to send a message to the tracker */
521        tr_trackerPulse( tor->tracker );
522
523        if( tor->status & TR_STATUS_STOPPED )
524        {
525            break;
526        }
527
528        /* Wait up to 20 ms */
529        date2 = tr_date();
530        if( date2 < date1 + 20 )
531        {
532            tr_lockUnlock( tor->lock );
533            tr_wait( date1 + 20 - date2 );
534            tr_lockLock( tor->lock );
535        }
536    }
537
538    tr_lockUnlock( tor->lock );
539
540    tr_ioClose( tor->io );
541
542    tor->status = TR_STATUS_STOPPED;
543
544    tr_dbg( "Thread exited" );
545}
546
547/***********************************************************************
548 * rateDownload, rateUpload
549 **********************************************************************/
550static float rateGeneric( uint64_t * dates, uint64_t * counts )
551{
552    float ret;
553    int i;
554
555    ret = 0.0;
556    for( i = 0; i < 9; i++ )
557    {
558        if( dates[i+1] == dates[i] )
559        {
560            continue;
561        }
562        ret += (float) ( i + 1 ) * 1000.0 / 1024.0 *
563            (float) ( counts[i+1] - counts[i] ) /
564            (float) ( dates[i+1] - dates[i] );
565    }
566    ret *= 1000.0 / 1024.0 / 45.0;
567
568    return ret;
569}
570static float rateDownload( tr_torrent_t * tor )
571{
572    return rateGeneric( tor->dates, tor->downloaded );
573}
574static float rateUpload( tr_torrent_t * tor )
575{
576    return rateGeneric( tor->dates, tor->uploaded );
577}
Note: See TracBrowser for help on using the repository browser.