source: trunk/libtransmission/transmission.c @ 5013

Last change on this file since 5013 was 5013, checked in by charles, 14 years ago

fix a possible crash on shutdown

  • Property svn:keywords set to Date Rev Author Id
File size: 11.5 KB
Line 
1/******************************************************************************
2 * $Id: transmission.c 5013 2008-02-13 01:33:29Z charles $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <assert.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include <signal.h>
31#include <sys/types.h> /* stat */
32#include <sys/stat.h> /* stat */
33#include <unistd.h> /* stat */
34#include <dirent.h> /* opendir */
35
36#include "transmission.h"
37#include "fdlimit.h"
38#include "list.h"
39#include "net.h"
40#include "peer-mgr.h"
41#include "platform.h"
42#include "ratecontrol.h"
43#include "shared.h"
44#include "stats.h"
45#include "torrent.h"
46#include "tracker.h"
47#include "trevent.h"
48#include "utils.h"
49
50/* Generate a peer id : "-TRxyzb-" + 12 random alphanumeric
51   characters, where x is the major version number, y is the
52   minor version number, z is the maintenance number, and b
53   designates beta (Azureus-style) */
54uint8_t*
55tr_peerIdNew( void )
56{
57    int i;
58    int val;
59    int total = 0;
60    uint8_t * buf = tr_new( uint8_t, 21 );
61    const char * pool = "0123456789abcdefghijklmnopqrstuvwxyz";
62    const int base = 36;
63
64    memcpy( buf, PEERID_PREFIX, 8 );
65
66    for( i=8; i<19; ++i ) {
67        val = tr_rand( base );
68        total += val;
69        buf[i] = pool[val];
70    }
71
72    val = total % base ? base - (total % base) : 0;
73    total += val;
74    buf[19] = pool[val];
75    buf[20] = '\0';
76
77    return buf;
78}
79
80const uint8_t*
81tr_getPeerId( void )
82{
83    static uint8_t * id = NULL;
84    if( id == NULL )
85        id = tr_peerIdNew( );
86    return id;
87}
88
89/***
90****
91***/
92
93tr_encryption_mode
94tr_getEncryptionMode( tr_handle * handle )
95{
96    assert( handle != NULL );
97
98    return handle->encryptionMode;
99}
100
101void
102tr_setEncryptionMode( tr_handle * handle, tr_encryption_mode mode )
103{
104    assert( handle != NULL );
105    assert( mode==TR_ENCRYPTION_PREFERRED
106         || mode==TR_ENCRYPTION_REQUIRED
107         || mode==TR_PLAINTEXT_PREFERRED );
108
109    handle->encryptionMode = mode;
110}
111
112/***
113****
114***/
115
116tr_handle *
117tr_initFull( const char * tag,
118             int          isPexEnabled,
119             int          isNatEnabled,
120             int          publicPort,
121             int          encryptionMode,
122             int          isUploadLimitEnabled,
123             int          uploadLimit,
124             int          isDownloadLimitEnabled,
125             int          downloadLimit,
126             int          globalPeerLimit,
127             int          messageLevel,
128             int          isMessageQueueingEnabled )
129{
130    tr_handle * h;
131
132#ifndef WIN32
133    /* Don't exit when writing on a broken socket */
134    signal( SIGPIPE, SIG_IGN );
135#endif
136
137    tr_msgInit( );
138    tr_setMessageLevel( messageLevel );
139    tr_setMessageQueuing( isMessageQueueingEnabled );
140
141    h = tr_new0( tr_handle, 1 );
142    h->lock = tr_lockNew( );
143    h->isPexEnabled = isPexEnabled ? 1 : 0;
144    h->encryptionMode = encryptionMode;
145
146    tr_netInit(); /* must go before tr_eventInit */
147
148    tr_eventInit( h );
149    while( !h->events )
150        tr_wait( 50 );
151
152    h->tag = strdup( tag );
153    if( !h->tag ) {
154        free( h );
155        return NULL;
156    }
157
158    h->peerMgr = tr_peerMgrNew( h );
159
160    /* Initialize rate and file descripts controls */
161
162    h->upload = tr_rcInit();
163    tr_rcSetLimit( h->upload, uploadLimit );
164    h->useUploadLimit = isUploadLimitEnabled;
165
166    h->download = tr_rcInit();
167    tr_rcSetLimit( h->download, downloadLimit );
168    h->useDownloadLimit = isDownloadLimitEnabled;
169
170    tr_fdInit( globalPeerLimit );
171    h->shared = tr_sharedInit( h, isNatEnabled, publicPort );
172    h->isPortSet = publicPort >= 0;
173
174    tr_inf( TR_NAME " " LONG_VERSION_STRING " started" );
175
176    tr_statsInit( h );
177
178    return h;
179}
180
181tr_handle * tr_init( const char * tag )
182{
183    return tr_initFull( tag,
184                        TRUE, /* pex enabled */
185                        FALSE, /* nat enabled */
186                        -1, /* public port */
187                        TR_ENCRYPTION_PREFERRED, /* encryption mode */
188                        FALSE, /* use upload speed limit? */ 
189                        -1, /* upload speed limit */
190                        FALSE, /* use download speed limit? */
191                        -1, /* download speed limit */
192                        200, /* globalPeerLimit */
193                        TR_MSG_INF, /* message level */
194                        FALSE ); /* is message queueing enabled? */
195}
196
197/***
198****
199***/
200
201void
202tr_globalLock( struct tr_handle * handle )
203{
204    tr_lockLock( handle->lock );
205}
206
207void
208tr_globalUnlock( struct tr_handle * handle )
209{
210    tr_lockUnlock( handle->lock );
211}
212
213int
214tr_globalIsLocked( const struct tr_handle * handle )
215{
216    return tr_lockHave( handle->lock );
217}
218
219/***********************************************************************
220 * tr_setBindPort
221 ***********************************************************************
222 *
223 **********************************************************************/
224
225struct bind_port_data
226{
227    tr_handle * handle;
228    int port;
229};
230
231static void
232tr_setBindPortImpl( void * vdata )
233{
234    struct bind_port_data * data = vdata;
235    tr_handle * handle = data->handle;
236    const int port = data->port;
237
238    handle->isPortSet = 1;
239    tr_sharedSetPort( handle->shared, port );
240
241    tr_free( data );
242}
243
244void
245tr_setBindPort( tr_handle * handle, int port )
246{
247    struct bind_port_data * data = tr_new( struct bind_port_data, 1 );
248    data->handle = handle;
249    data->port = port;
250    tr_runInEventThread( handle, tr_setBindPortImpl, data );
251}
252
253int
254tr_getPublicPort( const tr_handle * h )
255{
256    assert( h != NULL );
257    return tr_sharedGetPublicPort( h->shared );
258}
259
260void tr_natTraversalEnable( tr_handle * h, int enable )
261{
262    tr_globalLock( h );
263    tr_sharedTraversalEnable( h->shared, enable );
264    tr_globalUnlock( h );
265}
266
267const tr_handle_status *
268tr_handleStatus( tr_handle * h )
269{
270    tr_handle_status * s;
271
272    h->statCur = ( h->statCur + 1 ) % 2;
273    s = &h->stats[h->statCur];
274
275    tr_globalLock( h );
276
277    s->natTraversalStatus = tr_sharedTraversalStatus( h->shared );
278    s->publicPort = tr_sharedGetPublicPort( h->shared );
279
280    tr_globalUnlock( h );
281
282    return s;
283}
284
285/***
286****
287***/
288
289void
290tr_setUseGlobalSpeedLimit( tr_handle  * h,
291                           int          up_or_down,
292                           int          use_flag )
293{
294    char * ch = up_or_down==TR_UP ? &h->useUploadLimit
295                                  : &h->useDownloadLimit;
296    *ch = use_flag;
297}
298
299void
300tr_setGlobalSpeedLimit( tr_handle  * h,
301                        int          up_or_down,
302                        int          KiB_sec )
303{
304    if( up_or_down == TR_DOWN )
305        tr_rcSetLimit( h->download, KiB_sec );
306    else
307        tr_rcSetLimit( h->upload, KiB_sec );
308}
309
310void
311tr_getGlobalSpeedLimit( tr_handle  * h,
312                        int          up_or_down,
313                        int        * setme_enabled,
314                        int          * setme_KiBsec )
315{
316    if( setme_enabled != NULL )
317       *setme_enabled = up_or_down==TR_UP ? h->useUploadLimit
318                                          : h->useDownloadLimit;
319    if( setme_KiBsec != NULL )
320       *setme_KiBsec = tr_rcGetLimit( up_or_down==TR_UP ? h->upload
321                                                        : h->download );
322}
323
324
325void
326tr_setGlobalPeerLimit( tr_handle * handle UNUSED,
327                       uint16_t    maxGlobalPeers )
328{
329    tr_fdSetPeerLimit( maxGlobalPeers );
330}
331
332uint16_t
333tr_getGlobalPeerLimit( const tr_handle * handle UNUSED )
334{
335    return tr_fdGetPeerLimit( );
336}
337
338void
339tr_torrentRates( tr_handle * h, float * toClient, float * toPeer )
340{
341    const tr_torrent * tor;
342    tr_globalLock( h );
343
344    *toClient = *toPeer = 0.0;
345    for( tor = h->torrentList; tor; tor = tor->next )
346    {
347        float c, p;
348        tr_torrentGetRates( tor, &c, &p );
349        *toClient += c;
350        *toPeer += p;
351    }
352
353    tr_globalUnlock( h );
354}
355
356int
357tr_torrentCount( const tr_handle * h )
358{
359    return h->torrentCount;
360}
361
362static void
363tr_closeImpl( void * vh )
364{
365    tr_handle * h = vh;
366    tr_torrent * t;
367
368    tr_sharedShuttingDown( h->shared );
369    tr_trackerShuttingDown( h );
370
371    for( t=h->torrentList; t!=NULL; ) {
372        tr_torrent * tmp = t;
373        t = t->next;
374        tr_torrentClose( tmp );
375    }
376
377    tr_peerMgrFree( h->peerMgr );
378
379    tr_rcClose( h->upload );
380    tr_rcClose( h->download );
381   
382    h->isClosed = TRUE;
383}
384
385static int
386deadlineReached( const uint64_t deadline )
387{
388    return tr_date( ) >= deadline;
389}
390
391#define SHUTDOWN_MAX_SECONDS 30
392
393void
394tr_close( tr_handle * h )
395{
396    const int maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000;
397    const uint64_t deadline = tr_date( ) + maxwait_msec;
398
399    tr_statsClose( h );
400
401    tr_runInEventThread( h, tr_closeImpl, h );
402    while( !h->isClosed && !deadlineReached( deadline ) )
403        tr_wait( 100 );
404
405    tr_eventClose( h );
406    while( h->events && !deadlineReached( deadline ) )
407        tr_wait( 100 );
408
409    tr_fdClose( );
410    tr_lockFree( h->lock );
411    free( h->tag );
412    free( h );
413}
414
415tr_torrent **
416tr_loadTorrents ( tr_handle   * h,
417                  tr_ctor     * ctor,
418                  int         * setmeCount )
419{
420    int i, n = 0;
421    struct stat sb;
422    DIR * odir = NULL;
423    const char * dirname = tr_getTorrentsDirectory( );
424    tr_torrent ** torrents;
425    tr_list *l=NULL, *list=NULL;
426
427    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
428
429    if( !stat( dirname, &sb )
430        && S_ISDIR( sb.st_mode )
431        && (( odir = opendir ( dirname ) )) )
432    {
433        struct dirent *d;
434        for (d = readdir( odir ); d!=NULL; d=readdir( odir ) )
435        {
436            if( d->d_name && d->d_name[0]!='.' ) /* skip dotfiles, ., and .. */
437            {
438                tr_torrent * tor;
439                char filename[MAX_PATH_LENGTH];
440                tr_buildPath( filename, sizeof(filename), dirname, d->d_name, NULL );
441                tr_ctorSetMetainfoFromFile( ctor, filename );
442                tor = tr_torrentNew( h, ctor, NULL );
443                if( tor != NULL ) {
444                    tr_list_append( &list, tor );
445                    n++;
446                }
447            }
448        }
449        closedir( odir );
450    }
451
452    torrents = tr_new( tr_torrent*, n );
453    for( i=0, l=list; l!=NULL; l=l->next )
454        torrents[i++] = (tr_torrent*) l->data;
455    assert( i==n );
456
457    tr_list_free( &list, NULL );
458
459    *setmeCount = n;
460    tr_inf( "Loaded %d torrents from disk", *setmeCount );
461    return torrents;
462}
463
464/***
465****
466***/
467
468void
469tr_setPexEnabled( tr_handle * handle, int isPexEnabled )
470{
471    handle->isPexEnabled = isPexEnabled ? 1 : 0;
472}
473
474int
475tr_isPexEnabled( const tr_handle * handle )
476{
477    return handle->isPexEnabled;
478}
Note: See TracBrowser for help on using the repository browser.