source: trunk/libtransmission/transmission.c @ 23

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

Update 2005-12-29

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