source: trunk/libtransmission/transmission.c @ 20

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

Update 2005-12-25

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