source: trunk/libtransmission/transmission.c @ 14

Last change on this file since 14 was 14, checked in by root, 11 years ago

Update 2005-12-13

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