source: trunk/libtransmission/session.c @ 5975

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

API cleanup: s/tr_torrentRates/tr_sessionGetSpeed/

  • Property svn:keywords set to Date Rev Author Id
File size: 19.7 KB
Line 
1/******************************************************************************
2 * $Id: session.c 5975 2008-05-30 15:19:07Z 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 <stdlib.h>
27#include <string.h> /* memcpy */
28
29#include <signal.h>
30#include <sys/types.h> /* stat */
31#include <sys/stat.h> /* stat */
32#include <unistd.h> /* stat */
33#include <dirent.h> /* opendir */
34
35#include "transmission.h"
36#include "blocklist.h"
37#include "fdlimit.h"
38#include "list.h"
39#include "metainfo.h" /* tr_metainfoFree */
40#include "net.h"
41#include "peer-mgr.h"
42#include "platform.h" /* tr_lock */
43#include "port-forwarding.h"
44#include "ratecontrol.h"
45#include "rpc-server.h"
46#include "stats.h"
47#include "torrent.h"
48#include "tracker.h"
49#include "trevent.h"
50#include "utils.h"
51#include "web.h"
52
53/* Generate a peer id : "-TRxyzb-" + 12 random alphanumeric
54   characters, where x is the major version number, y is the
55   minor version number, z is the maintenance number, and b
56   designates beta (Azureus-style) */
57uint8_t*
58tr_peerIdNew( void )
59{
60    int i;
61    int val;
62    int total = 0;
63    uint8_t * buf = tr_new( uint8_t, 21 );
64    const char * pool = "0123456789abcdefghijklmnopqrstuvwxyz";
65    const int base = 36;
66
67    memcpy( buf, PEERID_PREFIX, 8 );
68
69    for( i=8; i<19; ++i ) {
70        val = tr_rand( base );
71        total += val;
72        buf[i] = pool[val];
73    }
74
75    val = total % base ? base - (total % base) : 0;
76    total += val;
77    buf[19] = pool[val];
78    buf[20] = '\0';
79
80    return buf;
81}
82
83const uint8_t*
84tr_getPeerId( void )
85{
86    static uint8_t * id = NULL;
87    if( id == NULL )
88        id = tr_peerIdNew( );
89    return id;
90}
91
92/***
93****
94***/
95
96tr_encryption_mode
97tr_sessionGetEncryption( tr_session * session )
98{
99    assert( session != NULL );
100
101    return session->encryptionMode;
102}
103
104void
105tr_sessionSetEncryption( tr_session * session, tr_encryption_mode mode )
106{
107    assert( session != NULL );
108    assert( mode==TR_ENCRYPTION_PREFERRED
109         || mode==TR_ENCRYPTION_REQUIRED
110         || mode==TR_PLAINTEXT_PREFERRED );
111
112    session->encryptionMode = mode;
113}
114
115/***
116****
117***/
118
119static void metainfoLookupRescan( tr_handle * h );
120
121tr_handle *
122tr_sessionInitFull( const char * configDir,
123                    const char * downloadDir,
124                    const char * tag,
125                    int          isPexEnabled,
126                    int          isPortForwardingEnabled,
127                    int          publicPort,
128                    int          encryptionMode,
129                    int          isUploadLimitEnabled,
130                    int          uploadLimit,
131                    int          isDownloadLimitEnabled,
132                    int          downloadLimit,
133                    int          globalPeerLimit,
134                    int          messageLevel,
135                    int          isMessageQueueingEnabled,
136                    int          isBlocklistEnabled,
137                    int          peerSocketTOS,
138                    int          rpcIsEnabled,
139                    int          rpcPort,
140                    const char * rpcACL )
141{
142    tr_handle * h;
143    char filename[MAX_PATH_LENGTH];
144
145#ifndef WIN32
146    /* Don't exit when writing on a broken socket */
147    signal( SIGPIPE, SIG_IGN );
148#endif
149
150    if( configDir == NULL )
151        configDir = tr_getDefaultConfigDir( );
152
153    tr_msgInit( );
154    tr_setMessageLevel( messageLevel );
155    tr_setMessageQueuing( isMessageQueueingEnabled );
156
157    h = tr_new0( tr_handle, 1 );
158    h->lock = tr_lockNew( );
159    h->isPexEnabled = isPexEnabled ? 1 : 0;
160    h->encryptionMode = encryptionMode;
161    h->peerSocketTOS = peerSocketTOS;
162    h->downloadDir = tr_strdup( downloadDir );
163
164    tr_setConfigDir( h, configDir );
165
166    tr_netInit(); /* must go before tr_eventInit */
167
168    tr_eventInit( h );
169    while( !h->events )
170        tr_wait( 50 );
171
172    h->tag = tr_strdup( tag );
173    h->peerMgr = tr_peerMgrNew( h );
174
175    /* Initialize rate and file descripts controls */
176
177    h->upload = tr_rcInit();
178    tr_rcSetLimit( h->upload, uploadLimit );
179    h->useUploadLimit = isUploadLimitEnabled;
180
181    h->download = tr_rcInit();
182    tr_rcSetLimit( h->download, downloadLimit );
183    h->useDownloadLimit = isDownloadLimitEnabled;
184
185    tr_fdInit( globalPeerLimit );
186    h->shared = tr_sharedInit( h, isPortForwardingEnabled, publicPort );
187    h->isPortSet = publicPort >= 0;
188
189    /* first %s is the application name
190       second %s is the version number */
191    tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING );
192
193    /* initialize the blocklist */
194    tr_buildPath( filename, sizeof( filename ), h->configDir, "blocklists", NULL );
195    tr_mkdirp( filename, 0777 );
196    tr_buildPath( filename, sizeof( filename ), h->configDir, "blocklists", "level1.bin", NULL );
197    h->blocklist = _tr_blocklistNew( filename, isBlocklistEnabled );
198
199    tr_statsInit( h );
200
201    h->web = tr_webInit( h );
202    h->rpcServer = tr_rpcInit( h, rpcIsEnabled, rpcPort, rpcACL );
203
204    metainfoLookupRescan( h );
205
206    return h;
207}
208
209tr_handle *
210tr_sessionInit( const char * configDir,
211                const char * downloadDir,
212                const char * tag )
213{
214    return tr_sessionInitFull( configDir,
215                               downloadDir,
216                               tag,
217                               TR_DEFAULT_PEX_ENABLED,
218                               TR_DEFAULT_PORT_FORWARDING_ENABLED,
219                               -1, /* public port */
220                               TR_ENCRYPTION_PREFERRED, /* encryption mode */
221                               FALSE, /* use upload speed limit? */ 
222                               -1, /* upload speed limit */
223                               FALSE, /* use download speed limit? */
224                               -1, /* download speed limit */
225                               TR_DEFAULT_GLOBAL_PEER_LIMIT,
226                               TR_MSG_INF, /* message level */
227                               FALSE, /* is message queueing enabled? */
228                               FALSE, /* is the blocklist enabled? */
229                               TR_DEFAULT_PEER_SOCKET_TOS,
230                               TR_DEFAULT_RPC_ENABLED,
231                               TR_DEFAULT_RPC_PORT,
232                               TR_DEFAULT_RPC_ACL );
233}
234
235/***
236****
237***/
238
239void
240tr_sessionSetDownloadDir( tr_handle * handle, const char * dir )
241{
242    if( handle->downloadDir != dir )
243    {
244        tr_free( handle->downloadDir );
245        handle->downloadDir = tr_strdup( dir );
246    }
247}
248
249const char *
250tr_sessionGetDownloadDir( const tr_handle * handle )
251{
252    return handle->downloadDir;
253}
254
255/***
256****
257***/
258
259void
260tr_globalLock( struct tr_handle * handle )
261{
262    tr_lockLock( handle->lock );
263}
264
265void
266tr_globalUnlock( struct tr_handle * handle )
267{
268    tr_lockUnlock( handle->lock );
269}
270
271int
272tr_globalIsLocked( const struct tr_handle * handle )
273{
274    return handle && tr_lockHave( handle->lock );
275}
276
277/***********************************************************************
278 * tr_setBindPort
279 ***********************************************************************
280 *
281 **********************************************************************/
282
283struct bind_port_data
284{
285    tr_handle * handle;
286    int port;
287};
288
289static void
290tr_setBindPortImpl( void * vdata )
291{
292    struct bind_port_data * data = vdata;
293    tr_handle * handle = data->handle;
294    const int port = data->port;
295
296    handle->isPortSet = 1;
297    tr_sharedSetPort( handle->shared, port );
298
299    tr_free( data );
300}
301
302void
303tr_sessionSetPeerPort( tr_handle * handle, int port )
304{
305    struct bind_port_data * data = tr_new( struct bind_port_data, 1 );
306    data->handle = handle;
307    data->port = port;
308    tr_runInEventThread( handle, tr_setBindPortImpl, data );
309}
310
311int
312tr_sessionGetPeerPort( const tr_handle * h )
313{
314    assert( h != NULL );
315    return tr_sharedGetPeerPort( h->shared );
316}
317
318tr_port_forwarding
319tr_sessionGetPortForwarding( const tr_handle * h )
320{
321    return tr_sharedTraversalStatus( h->shared );
322}
323
324/***
325****
326***/
327
328void
329tr_sessionSetSpeedLimitEnabled( tr_handle  * h,
330                                int          up_or_down,
331                                int          use_flag )
332{
333    if( up_or_down == TR_UP )
334        h->useUploadLimit = use_flag ? 1 : 0;
335    else
336        h->useDownloadLimit = use_flag ? 1 : 0;
337}
338
339int
340tr_sessionIsSpeedLimitEnabled( const tr_handle * h, int up_or_down )
341{
342       return up_or_down==TR_UP ? h->useUploadLimit : h->useDownloadLimit;
343}
344
345void
346tr_sessionSetSpeedLimit( tr_handle  * h,
347                         int          up_or_down,
348                         int          KiB_sec )
349{
350    if( up_or_down == TR_DOWN )
351        tr_rcSetLimit( h->download, KiB_sec );
352    else
353        tr_rcSetLimit( h->upload, KiB_sec );
354}
355
356int
357tr_sessionGetSpeedLimit( const tr_handle * h, int up_or_down )
358{
359    return tr_rcGetLimit( up_or_down==TR_UP ? h->upload : h->download );
360}
361
362/***
363****
364***/
365
366void
367tr_sessionSetPeerLimit( tr_handle * handle UNUSED,
368                        uint16_t    maxGlobalPeers )
369{
370    tr_fdSetPeerLimit( maxGlobalPeers );
371}
372
373uint16_t
374tr_sessionGetPeerLimit( const tr_handle * handle UNUSED )
375{
376    return tr_fdGetPeerLimit( );
377}
378
379/***
380****
381***/
382
383void
384tr_sessionGetSpeed( const tr_handle  * session,
385                    float            * toClient,
386                    float            * toPeer )
387{
388    if( session && toClient )
389        *toClient = tr_rcRate( session->download );
390    if( session && toPeer )
391        *toPeer = tr_rcRate( session->upload );
392}
393
394int
395tr_sessionCountTorrents( const tr_handle * h )
396{
397    return h->torrentCount;
398}
399
400static void
401tr_closeAllConnections( void * vh )
402{
403    tr_handle * h = vh;
404    tr_torrent * tor;
405
406    tr_sharedShuttingDown( h->shared );
407    tr_trackerShuttingDown( h );
408    tr_rpcClose( &h->rpcServer );
409
410    while(( tor = tr_torrentNext( h, NULL )))
411        tr_torrentFree( tor );
412
413    tr_peerMgrFree( h->peerMgr );
414
415    tr_rcClose( h->upload );
416    tr_rcClose( h->download );
417   
418    h->isClosed = TRUE;
419}
420
421static int
422deadlineReached( const uint64_t deadline )
423{
424    return tr_date( ) >= deadline;
425}
426
427#define SHUTDOWN_MAX_SECONDS 30
428
429void
430tr_sessionClose( tr_handle * h )
431{
432    int i;
433    const int maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000;
434    const uint64_t deadline = tr_date( ) + maxwait_msec;
435
436    tr_deepLog( __FILE__, __LINE__, NULL, "shutting down transmission session %p", h );
437    tr_statsClose( h );
438
439    tr_runInEventThread( h, tr_closeAllConnections, h );
440    while( !h->isClosed && !deadlineReached( deadline ) )
441        tr_wait( 100 );
442
443    _tr_blocklistFree( h->blocklist );
444    h->blocklist = NULL;
445    tr_webClose( &h->web );
446
447    tr_eventClose( h );
448    while( h->events && !deadlineReached( deadline ) )
449        tr_wait( 100 );
450
451    tr_fdClose( );
452    tr_lockFree( h->lock );
453    for( i=0; i<h->metainfoLookupCount; ++i )
454        tr_free( h->metainfoLookup[i].filename );
455    tr_free( h->metainfoLookup );
456    tr_free( h->tag );
457    tr_free( h->configDir );
458    tr_free( h->resumeDir );
459    tr_free( h->torrentDir );
460    tr_free( h->downloadDir );
461    free( h );
462}
463
464tr_torrent **
465tr_sessionLoadTorrents ( tr_handle   * h,
466                         tr_ctor     * ctor,
467                         int         * setmeCount )
468{
469    int i, n = 0;
470    struct stat sb;
471    DIR * odir = NULL;
472    const char * dirname = tr_getTorrentDir( h );
473    tr_torrent ** torrents;
474    tr_list *l=NULL, *list=NULL;
475
476    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
477
478    if( !stat( dirname, &sb )
479        && S_ISDIR( sb.st_mode )
480        && (( odir = opendir ( dirname ) )) )
481    {
482        struct dirent *d;
483        for (d = readdir( odir ); d!=NULL; d=readdir( odir ) )
484        {
485            if( d->d_name && d->d_name[0]!='.' ) /* skip dotfiles, ., and .. */
486            {
487                tr_torrent * tor;
488                char filename[MAX_PATH_LENGTH];
489                tr_buildPath( filename, sizeof(filename), dirname, d->d_name, NULL );
490                tr_ctorSetMetainfoFromFile( ctor, filename );
491                tor = tr_torrentNew( h, ctor, NULL );
492                if( tor ) {
493                    tr_list_append( &list, tor );
494                    n++;
495                }
496            }
497        }
498        closedir( odir );
499    }
500
501    torrents = tr_new( tr_torrent*, n );
502    for( i=0, l=list; l!=NULL; l=l->next )
503        torrents[i++] = (tr_torrent*) l->data;
504    assert( i==n );
505
506    tr_list_free( &list, NULL );
507
508    if( n )
509        tr_inf( _( "Loaded %d torrents" ), n );
510
511    if( setmeCount )
512        *setmeCount = n;
513    return torrents;
514}
515
516/***
517****
518***/
519
520void
521tr_sessionSetPexEnabled( tr_handle * handle, int isPexEnabled )
522{
523    handle->isPexEnabled = isPexEnabled ? 1 : 0;
524}
525
526int
527tr_sessionIsPexEnabled( const tr_handle * handle )
528{
529    return handle->isPexEnabled;
530}
531
532/***
533****
534***/
535
536void
537tr_sessionSetPortForwardingEnabled( tr_handle * h, int enable )
538{
539    tr_globalLock( h );
540    tr_sharedTraversalEnable( h->shared, enable );
541    tr_globalUnlock( h );
542}
543
544int
545tr_sessionIsPortForwardingEnabled( const tr_handle * h )
546{
547    return tr_sharedTraversalIsEnabled( h->shared );
548}
549
550/***
551****
552***/
553
554int
555tr_blocklistGetRuleCount( tr_handle * handle )
556{
557    return _tr_blocklistGetRuleCount( handle->blocklist );
558}
559
560int
561tr_blocklistIsEnabled( const tr_handle * handle )
562{
563    return _tr_blocklistIsEnabled( handle->blocklist );
564}
565
566void
567tr_blocklistSetEnabled( tr_handle * handle, int isEnabled )
568{
569    _tr_blocklistSetEnabled( handle->blocklist, isEnabled );
570}
571
572int
573tr_blocklistExists( const tr_handle * handle )
574{
575    return _tr_blocklistExists( handle->blocklist );
576}
577
578int
579tr_blocklistSetContent( tr_handle  * handle, const char * filename )
580{
581    return _tr_blocklistSetContent( handle->blocklist, filename );
582}
583
584int
585tr_blocklistHasAddress( tr_handle * handle, const struct in_addr * addr )
586{
587    return _tr_blocklistHasAddress( handle->blocklist, addr );
588}
589
590/***
591****
592***/
593
594static int
595compareLookupEntries( const void * va, const void * vb )
596{
597    const struct tr_metainfo_lookup * a = va;
598    const struct tr_metainfo_lookup * b = vb;
599    return strcmp( a->hashString, b->hashString );
600}
601
602static void
603metainfoLookupResort( tr_handle * h )
604{
605    qsort( h->metainfoLookup, 
606           h->metainfoLookupCount,
607           sizeof( struct tr_metainfo_lookup ),
608           compareLookupEntries );
609}
610
611static int
612compareHashStringToLookupEntry( const void * va, const void * vb )
613{
614    const char * a = va;
615    const struct tr_metainfo_lookup * b = vb;
616    return strcmp( a, b->hashString );
617}
618
619const char*
620tr_sessionFindTorrentFile( const tr_handle  * h,
621                           const char       * hashStr )
622{
623    struct tr_metainfo_lookup * l = bsearch( hashStr,
624                                             h->metainfoLookup,
625                                             h->metainfoLookupCount,
626                                             sizeof( struct tr_metainfo_lookup ),
627                                             compareHashStringToLookupEntry );
628    return l ? l->filename : NULL;
629}
630
631static void
632metainfoLookupRescan( tr_handle * h )
633{
634    int i;
635    int n;
636    struct stat sb;
637    const char * dirname = tr_getTorrentDir( h );
638    DIR * odir = NULL;
639    tr_ctor * ctor = NULL;
640    tr_list * list = NULL;
641
642    /* walk through the directory and find the mappings */
643    ctor = tr_ctorNew( h );
644    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
645    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && (( odir = opendir( dirname ))))
646    {
647        struct dirent *d;
648        for (d = readdir( odir ); d!=NULL; d=readdir( odir ) )
649        {
650            if( d->d_name && d->d_name[0]!='.' ) /* skip dotfiles, ., and .. */
651            {
652                tr_info inf;
653                char filename[MAX_PATH_LENGTH];
654                tr_buildPath( filename, sizeof(filename), dirname, d->d_name, NULL );
655                tr_ctorSetMetainfoFromFile( ctor, filename );
656                if( !tr_torrentParse( h, ctor, &inf ) )
657                {
658                    tr_list_append( &list, tr_strdup( inf.hashString ) );
659                    tr_list_append( &list, tr_strdup( filename ) );
660                    tr_metainfoFree( &inf );
661                }
662            }
663        }
664        closedir( odir );
665    }
666    tr_ctorFree( ctor );
667
668    n = tr_list_size( list ) / 2;
669    h->metainfoLookup = tr_new0( struct tr_metainfo_lookup, n );
670    h->metainfoLookupCount = n;
671    for( i=0; i<n; ++i )
672    {
673        char * hashString = tr_list_pop_front( &list );
674        char * filename = tr_list_pop_front( &list );
675
676        memcpy( h->metainfoLookup[i].hashString, hashString, 2*SHA_DIGEST_LENGTH+1 );
677        tr_free( hashString );
678        h->metainfoLookup[i].filename = filename;
679    }
680
681    metainfoLookupResort( h );
682    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
683}
684
685void
686tr_sessionSetTorrentFile( tr_handle    * h,
687                          const char   * hashString,
688                          const char   * filename )
689{
690    struct tr_metainfo_lookup * l = bsearch( hashString,
691                                             h->metainfoLookup,
692                                             h->metainfoLookupCount,
693                                             sizeof( struct tr_metainfo_lookup ),
694                                             compareHashStringToLookupEntry );
695    if( l != NULL )
696    {
697        if( l->filename != filename )
698        {
699            tr_free( l->filename );
700            l->filename = tr_strdup( filename );
701        }
702    }
703    else
704    {
705        const int n = h->metainfoLookupCount++;
706        struct tr_metainfo_lookup * node;
707        h->metainfoLookup = tr_renew( struct tr_metainfo_lookup,
708                                      h->metainfoLookup,
709                                      h->metainfoLookupCount );
710        node = h->metainfoLookup + n;
711        memcpy( node->hashString, hashString, 2*SHA_DIGEST_LENGTH+1 );
712        node->filename = tr_strdup( filename );
713        metainfoLookupResort( h );
714    }
715}
716
717tr_torrent*
718tr_torrentNext( tr_handle * session, tr_torrent * tor )
719{
720    return tor ? tor->next : session->torrentList;
721}
722
723/***
724****
725***/
726
727void
728tr_sessionSetRPCEnabled( tr_handle * session, int isEnabled )
729{
730    tr_rpcSetEnabled( session->rpcServer, isEnabled );
731}
732int
733tr_sessionIsRPCEnabled( const tr_handle * session )
734{
735    return tr_rpcIsEnabled( session->rpcServer );
736}
737void
738tr_sessionSetRPCPort( tr_handle * session, int port )
739{
740    tr_rpcSetPort( session->rpcServer, port );
741}
742int
743tr_sessionGetRPCPort( const tr_handle * session )
744{
745    return tr_rpcGetPort( session->rpcServer );
746}
747void
748tr_sessionSetRPCCallback( tr_handle    * session,
749                          tr_rpc_func    func,
750                          void         * user_data )
751{
752    session->rpc_func = func;
753    session->rpc_func_user_data = user_data;
754}
755
756void
757tr_sessionSetRPCACL( tr_handle * session, const char * acl )
758{
759    tr_rpcSetACL( session->rpcServer, acl );
760}
761
762const char*
763tr_sessionGetRPCACL( const tr_session * session )
764{
765    return tr_rpcGetACL( session->rpcServer );
766}
Note: See TracBrowser for help on using the repository browser.