source: trunk/libtransmission/transmission.c @ 2

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

Update 2005-11-01

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