source: trunk/libtransmission/transmission.c @ 1

Last change on this file since 1 was 1, checked in by root, 15 years ago

Import from 2005-10-26

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