source: trunk/libtransmission/transmission.c @ 5

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

Update 2005-11-18

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  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
176    /* Guess scrape URL */
177    s1 = strchr( inf->trackerAnnounce, '/' );
178    while( ( s2 = strchr( s1 + 1, '/' ) ) )
179    {
180        s1 = s2;
181    }
182    s1++;
183    if( !strncmp( s1, "announce", 8 ) )
184    {
185        int pre  = (long) s1 - (long) inf->trackerAnnounce;
186        int post = strlen( inf->trackerAnnounce ) - pre - 8;
187        memcpy( tor->scrape, inf->trackerAnnounce, pre );
188        sprintf( &tor->scrape[pre], "scrape" );
189        memcpy( &tor->scrape[pre+6], &inf->trackerAnnounce[pre+8], post );
190    }
191
192    /* Escaped info hash for HTTP queries */
193    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
194    {
195        sprintf( &tor->hashString[3*i], "%%%02x", inf->hash[i] );
196    }
197
198    /* Block size: usually 16 ko, or less if we have to */
199    tor->blockSize  = MIN( inf->pieceSize, 1 << 14 );
200    tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
201                        tor->blockSize;
202    tor->completion = tr_cpInit( tor );
203
204    tr_lockInit( &tor->lock );
205
206    tor->upload  = h->upload;
207    tor->fdlimit = h->fdlimit;
208    tor->prefsDirectory = (char *) h->prefsDirectory;
209 
210    /* We have a new torrent */
211    h->torrents[h->torrentCount] = tor;
212    (h->torrentCount)++;
213
214    return 0;
215}
216
217/***********************************************************************
218 * tr_torrentScrape
219 ***********************************************************************
220 * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
221 * to fill it.
222 **********************************************************************/
223int tr_torrentScrape( tr_handle_t * h, int t, int * s, int * l )
224{
225    return tr_trackerScrape( h->torrents[t], s, l );
226}
227
228void tr_torrentSetFolder( tr_handle_t * h, int t, const char * path )
229{
230    tr_torrent_t * tor = h->torrents[t];
231
232    tor->destination = strdup( path );
233}
234
235char * tr_torrentGetFolder( tr_handle_t * h, int t )
236{
237    tr_torrent_t * tor = h->torrents[t];
238
239    return tor->destination;
240}
241
242void tr_torrentStart( tr_handle_t * h, int t )
243{
244    tr_torrent_t * tor = h->torrents[t];
245    uint64_t       now;
246    int            i;
247
248    tor->status   = TR_STATUS_CHECK;
249    tor->tracker  = tr_trackerInit( h, tor );
250    tor->bindPort = h->bindPort;
251#ifndef BEOS_NETSERVER
252    /* BeOS net_server seems to be unable to set incoming connections to
253       non-blocking. Too bad. */
254    if( !tr_fdSocketWillCreate( h->fdlimit, 0 ) )
255    {
256        tor->bindSocket = tr_netBind( &tor->bindPort );
257    }
258#endif
259
260    now = tr_date();
261    for( i = 0; i < 10; i++ )
262    {
263        tor->dates[i] = now;
264    }
265
266    tor->die = 0;
267    tr_threadCreate( &tor->thread, downloadLoop, tor );
268}
269
270void tr_torrentStop( tr_handle_t * h, int t )
271{
272    tr_torrent_t * tor = h->torrents[t];
273
274    tr_lockLock( tor->lock );
275    tr_trackerStopped( tor->tracker );
276    tor->status = TR_STATUS_STOPPING;
277    tor->stopDate = tr_date();
278    tr_lockUnlock( tor->lock );
279}
280
281/***********************************************************************
282 * torrentReallyStop
283 ***********************************************************************
284 * Joins the download thread and frees/closes everything related to it.
285 **********************************************************************/
286static void torrentReallyStop( tr_handle_t * h, int t )
287{
288    tr_torrent_t * tor = h->torrents[t];
289
290    tor->die = 1;
291    tr_threadJoin( tor->thread );
292    tr_dbg( "Thread joined" );
293
294    tr_trackerClose( tor->tracker );
295
296    while( tor->peerCount > 0 )
297    {
298        tr_peerRem( tor, 0 );
299    }
300    if( tor->bindSocket > -1 )
301    {
302        tr_netClose( tor->bindSocket );
303        tr_fdSocketClosed( h->fdlimit, 0 );
304    }
305
306    memset( tor->downloaded, 0, sizeof( tor->downloaded ) );
307    memset( tor->uploaded,   0, sizeof( tor->uploaded ) );
308}
309
310/***********************************************************************
311 * tr_torrentCount
312 ***********************************************************************
313 *
314 **********************************************************************/
315int tr_torrentCount( tr_handle_t * h )
316{
317    return h->torrentCount;
318}
319
320int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat )
321{
322    tr_stat_t * s;
323    tr_torrent_t * tor;
324    tr_info_t * inf;
325    int i, j, k, piece;
326
327    if( h->torrentCount < 1 )
328    {
329        *stat = NULL;
330        return 0;
331    }
332
333    s = malloc( h->torrentCount * sizeof( tr_stat_t ) );
334
335    for( i = 0; i < h->torrentCount; i++ )
336    {
337        tor = h->torrents[i];
338        inf = &tor->info;
339
340        if( ( tor->status & TR_STATUS_STOPPED ) ||
341            ( ( tor->status & TR_STATUS_STOPPING ) &&
342              tr_date() > tor->stopDate + 60000 ) )
343        {
344            torrentReallyStop( h, i );
345            tor->status = TR_STATUS_PAUSE;
346        }
347
348        tr_lockLock( tor->lock );
349
350        memcpy( &s[i].info, &tor->info, sizeof( tr_info_t ) );
351        s[i].status = tor->status;
352        memcpy( s[i].error, tor->error, sizeof( s[i].error ) );
353
354        s[i].peersTotal       = 0;
355        s[i].peersUploading   = 0;
356        s[i].peersDownloading = 0;
357
358        for( j = 0; j < tor->peerCount; j++ )
359        {
360            if( tr_peerIsConnected( tor->peers[j] ) )
361            {
362                (s[i].peersTotal)++;
363                if( tr_peerIsUploading( tor->peers[j] ) )
364                {
365                    (s[i].peersUploading)++;
366                }
367                if( tr_peerIsDownloading( tor->peers[j] ) )
368                {
369                    (s[i].peersDownloading)++;
370                }
371            }
372        }
373
374        s[i].progress     = tr_cpCompletionAsFloat( tor->completion );
375        s[i].rateDownload = rateDownload( tor );
376        s[i].rateUpload   = rateUpload( tor );
377
378        if( s[i].rateDownload < 0.1 )
379        {
380            s[i].eta = -1;
381        }
382        else
383        {
384            s[i].eta = (float) ( 1.0 - s[i].progress ) *
385                (float) inf->totalSize / s[i].rateDownload / 1024.0;
386            if( s[i].eta > 99 * 3600 + 59 * 60 + 59 )
387            {
388                s[i].eta = -1;
389            }
390        }
391
392        for( j = 0; j < 120; j++ )
393        {
394            piece = j * inf->pieceCount / 120;
395
396            if( tr_cpPieceIsComplete( tor->completion, piece ) )
397            {
398                s[i].pieces[j] = -1;
399                continue;
400            }
401
402            s[i].pieces[j] = 0;
403           
404            for( k = 0; k < tor->peerCount; k++ )
405            {
406                if( tr_peerBitfield( tor->peers[k] ) &&
407                    tr_bitfieldHas( tr_peerBitfield( tor->peers[k] ), piece ) )
408                {
409                    (s[i].pieces[j])++;
410                }
411            }
412        }
413
414        s[i].downloaded = tor->downloaded[9];
415        s[i].uploaded   = tor->uploaded[9];
416
417        s[i].folder = tor->destination;
418
419        tr_lockUnlock( tor->lock );
420    }
421
422    *stat = s;
423    return h->torrentCount;
424}
425
426/***********************************************************************
427 * tr_torrentClose
428 ***********************************************************************
429 * Frees memory allocated by tr_torrentInit.
430 **********************************************************************/
431void tr_torrentClose( tr_handle_t * h, int t )
432{
433    tr_torrent_t * tor = h->torrents[t];
434    tr_info_t    * inf = &tor->info;
435
436    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
437    {
438        /* Join the thread first */
439        torrentReallyStop( h, t );
440    }
441
442    h->torrentCount--;
443
444    tr_lockClose( tor->lock );
445    tr_cpClose( tor->completion );
446
447    if( tor->destination )
448    {
449        free( tor->destination );
450    }
451    free( inf->pieces );
452    free( inf->files );
453    free( tor );
454
455    memmove( &h->torrents[t], &h->torrents[t+1],
456             ( h->torrentCount - t ) * sizeof( void * ) );
457}
458
459void tr_close( tr_handle_t * h )
460{
461    tr_fdClose( h->fdlimit );
462    tr_uploadClose( h->upload );
463    free( h );
464}
465
466/***********************************************************************
467 * downloadLoop
468 **********************************************************************/
469static void downloadLoop( void * _tor )
470{
471    tr_torrent_t * tor = _tor;
472    uint64_t       date1, date2;
473
474    tr_dbg( "Thread started" );
475
476#ifdef SYS_BEOS
477    /* This is required because on BeOS, SIGINT is sent to each thread,
478       which kills them not nicely */
479    signal( SIGINT, SIG_IGN );
480#endif
481
482    tr_lockLock( tor->lock );
483
484    tr_cpReset( tor->completion );
485    tor->io     = tr_ioInit( tor );
486    tor->status = tr_cpIsSeeding( tor->completion ) ?
487                      TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
488
489    while( !tor->die )
490    {
491        date1 = tr_date();
492
493        /* Are we finished ? */
494        if( ( tor->status & TR_STATUS_DOWNLOAD ) &&
495            tr_cpIsSeeding( tor->completion ) )
496        {
497            /* Done */
498            tor->status = TR_STATUS_SEED;
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.