source: trunk/libtransmission/session.c @ 5908

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

(libt) more janitorial work on cleaning up tr_session*() and tr_torrent*() functions: session stats, torrent count, and manual update.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.7 KB
Line 
1/******************************************************************************
2 * $Id: session.c 5908 2008-05-22 20:44:41Z 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_sessionSetPublicPort( 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_sessionGetPublicPort( const tr_handle * h )
313{
314    assert( h != NULL );
315    return tr_sharedGetPublicPort( 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_torrentRates( tr_handle * h, float * toClient, float * toPeer )
385{
386    if( h )
387    {
388        tr_globalLock( h );
389
390        if( toClient )
391            *toClient = tr_rcRate( h->download );
392        if( toPeer )
393            *toPeer = tr_rcRate( h->upload );
394
395        tr_globalUnlock( h );
396    }
397}
398
399int
400tr_sessionCountTorrents( const tr_handle * h )
401{
402    return h->torrentCount;
403}
404
405static void
406tr_closeAllConnections( void * vh )
407{
408    tr_handle * h = vh;
409    tr_torrent * tor;
410
411    tr_sharedShuttingDown( h->shared );
412    tr_trackerShuttingDown( h );
413    tr_rpcClose( &h->rpcServer );
414
415    while(( tor = tr_torrentNext( h, NULL )))
416        tr_torrentFree( tor );
417
418    tr_peerMgrFree( h->peerMgr );
419
420    tr_rcClose( h->upload );
421    tr_rcClose( h->download );
422   
423    h->isClosed = TRUE;
424}
425
426static int
427deadlineReached( const uint64_t deadline )
428{
429    return tr_date( ) >= deadline;
430}
431
432#define SHUTDOWN_MAX_SECONDS 30
433
434void
435tr_sessionClose( tr_handle * h )
436{
437    int i;
438    const int maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000;
439    const uint64_t deadline = tr_date( ) + maxwait_msec;
440
441    tr_deepLog( __FILE__, __LINE__, NULL, "shutting down transmission session %p", h );
442    tr_statsClose( h );
443
444    tr_runInEventThread( h, tr_closeAllConnections, h );
445    while( !h->isClosed && !deadlineReached( deadline ) )
446        tr_wait( 100 );
447
448    _tr_blocklistFree( h->blocklist );
449    h->blocklist = NULL;
450    tr_webClose( &h->web );
451
452    tr_eventClose( h );
453    while( h->events && !deadlineReached( deadline ) )
454        tr_wait( 100 );
455
456    tr_fdClose( );
457    tr_lockFree( h->lock );
458    for( i=0; i<h->metainfoLookupCount; ++i )
459        tr_free( h->metainfoLookup[i].filename );
460    tr_free( h->metainfoLookup );
461    tr_free( h->tag );
462    tr_free( h->configDir );
463    tr_free( h->resumeDir );
464    tr_free( h->torrentDir );
465    tr_free( h->downloadDir );
466    free( h );
467}
468
469tr_torrent **
470tr_sessionLoadTorrents ( tr_handle   * h,
471                         tr_ctor     * ctor,
472                         int         * setmeCount )
473{
474    int i, n = 0;
475    struct stat sb;
476    DIR * odir = NULL;
477    const char * dirname = tr_getTorrentDir( h );
478    tr_torrent ** torrents;
479    tr_list *l=NULL, *list=NULL;
480
481    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
482
483    if( !stat( dirname, &sb )
484        && S_ISDIR( sb.st_mode )
485        && (( odir = opendir ( dirname ) )) )
486    {
487        struct dirent *d;
488        for (d = readdir( odir ); d!=NULL; d=readdir( odir ) )
489        {
490            if( d->d_name && d->d_name[0]!='.' ) /* skip dotfiles, ., and .. */
491            {
492                tr_torrent * tor;
493                char filename[MAX_PATH_LENGTH];
494                tr_buildPath( filename, sizeof(filename), dirname, d->d_name, NULL );
495                tr_ctorSetMetainfoFromFile( ctor, filename );
496                tor = tr_torrentNew( h, ctor, NULL );
497                if( tor ) {
498                    tr_list_append( &list, tor );
499                    n++;
500                }
501            }
502        }
503        closedir( odir );
504    }
505
506    torrents = tr_new( tr_torrent*, n );
507    for( i=0, l=list; l!=NULL; l=l->next )
508        torrents[i++] = (tr_torrent*) l->data;
509    assert( i==n );
510
511    tr_list_free( &list, NULL );
512
513    if( n )
514        tr_inf( _( "Loaded %d torrents" ), n );
515
516    if( setmeCount )
517        *setmeCount = n;
518    return torrents;
519}
520
521/***
522****
523***/
524
525void
526tr_sessionSetPexEnabled( tr_handle * handle, int isPexEnabled )
527{
528    handle->isPexEnabled = isPexEnabled ? 1 : 0;
529}
530
531int
532tr_sessionIsPexEnabled( const tr_handle * handle )
533{
534    return handle->isPexEnabled;
535}
536
537/***
538****
539***/
540
541void
542tr_sessionSetPortForwardingEnabled( tr_handle * h, int enable )
543{
544    tr_globalLock( h );
545    tr_sharedTraversalEnable( h->shared, enable );
546    tr_globalUnlock( h );
547}
548
549int
550tr_sessionIsPortForwardingEnabled( const tr_handle * h )
551{
552    return tr_sharedTraversalIsEnabled( h->shared );
553}
554
555/***
556****
557***/
558
559int
560tr_blocklistGetRuleCount( tr_handle * handle )
561{
562    return _tr_blocklistGetRuleCount( handle->blocklist );
563}
564
565int
566tr_blocklistIsEnabled( const tr_handle * handle )
567{
568    return _tr_blocklistIsEnabled( handle->blocklist );
569}
570
571void
572tr_blocklistSetEnabled( tr_handle * handle, int isEnabled )
573{
574    _tr_blocklistSetEnabled( handle->blocklist, isEnabled );
575}
576
577int
578tr_blocklistExists( const tr_handle * handle )
579{
580    return _tr_blocklistExists( handle->blocklist );
581}
582
583int
584tr_blocklistSetContent( tr_handle  * handle, const char * filename )
585{
586    return _tr_blocklistSetContent( handle->blocklist, filename );
587}
588
589int
590tr_blocklistHasAddress( tr_handle * handle, const struct in_addr * addr )
591{
592    return _tr_blocklistHasAddress( handle->blocklist, addr );
593}
594
595/***
596****
597***/
598
599static int
600compareLookupEntries( const void * va, const void * vb )
601{
602    const struct tr_metainfo_lookup * a = va;
603    const struct tr_metainfo_lookup * b = vb;
604    return strcmp( a->hashString, b->hashString );
605}
606
607static void
608metainfoLookupResort( tr_handle * h )
609{
610    qsort( h->metainfoLookup, 
611           h->metainfoLookupCount,
612           sizeof( struct tr_metainfo_lookup ),
613           compareLookupEntries );
614}
615
616static int
617compareHashStringToLookupEntry( const void * va, const void * vb )
618{
619    const char * a = va;
620    const struct tr_metainfo_lookup * b = vb;
621    return strcmp( a, b->hashString );
622}
623
624const char*
625tr_sessionFindTorrentFile( const tr_handle  * h,
626                           const char       * hashStr )
627{
628    struct tr_metainfo_lookup * l = bsearch( hashStr,
629                                             h->metainfoLookup,
630                                             h->metainfoLookupCount,
631                                             sizeof( struct tr_metainfo_lookup ),
632                                             compareHashStringToLookupEntry );
633    return l ? l->filename : NULL;
634}
635
636static void
637metainfoLookupRescan( tr_handle * h )
638{
639    int i;
640    int n;
641    struct stat sb;
642    const char * dirname = tr_getTorrentDir( h );
643    DIR * odir = NULL;
644    tr_ctor * ctor = NULL;
645    tr_list * list = NULL;
646
647    /* walk through the directory and find the mappings */
648    ctor = tr_ctorNew( h );
649    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
650    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && (( odir = opendir( dirname ))))
651    {
652        struct dirent *d;
653        for (d = readdir( odir ); d!=NULL; d=readdir( odir ) )
654        {
655            if( d->d_name && d->d_name[0]!='.' ) /* skip dotfiles, ., and .. */
656            {
657                tr_info inf;
658                char filename[MAX_PATH_LENGTH];
659                tr_buildPath( filename, sizeof(filename), dirname, d->d_name, NULL );
660                tr_ctorSetMetainfoFromFile( ctor, filename );
661                if( !tr_torrentParse( h, ctor, &inf ) )
662                {
663                    tr_list_append( &list, tr_strdup( inf.hashString ) );
664                    tr_list_append( &list, tr_strdup( filename ) );
665                    tr_metainfoFree( &inf );
666                }
667            }
668        }
669        closedir( odir );
670    }
671    tr_ctorFree( ctor );
672
673    n = tr_list_size( list ) / 2;
674    h->metainfoLookup = tr_new0( struct tr_metainfo_lookup, n );
675    h->metainfoLookupCount = n;
676    for( i=0; i<n; ++i )
677    {
678        char * hashString = tr_list_pop_front( &list );
679        char * filename = tr_list_pop_front( &list );
680
681        memcpy( h->metainfoLookup[i].hashString, hashString, 2*SHA_DIGEST_LENGTH+1 );
682        tr_free( hashString );
683        h->metainfoLookup[i].filename = filename;
684    }
685
686    metainfoLookupResort( h );
687    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
688}
689
690void
691tr_sessionSetTorrentFile( tr_handle    * h,
692                          const char   * hashString,
693                          const char   * filename )
694{
695    struct tr_metainfo_lookup * l = bsearch( hashString,
696                                             h->metainfoLookup,
697                                             h->metainfoLookupCount,
698                                             sizeof( struct tr_metainfo_lookup ),
699                                             compareHashStringToLookupEntry );
700    if( l != NULL )
701    {
702        if( l->filename != filename )
703        {
704            tr_free( l->filename );
705            l->filename = tr_strdup( filename );
706        }
707    }
708    else
709    {
710        const int n = h->metainfoLookupCount++;
711        struct tr_metainfo_lookup * node;
712        h->metainfoLookup = tr_renew( struct tr_metainfo_lookup,
713                                      h->metainfoLookup,
714                                      h->metainfoLookupCount );
715        node = h->metainfoLookup + n;
716        memcpy( node->hashString, hashString, 2*SHA_DIGEST_LENGTH+1 );
717        node->filename = tr_strdup( filename );
718        metainfoLookupResort( h );
719    }
720}
721
722tr_torrent*
723tr_torrentNext( tr_handle * session, tr_torrent * tor )
724{
725    return tor ? tor->next : session->torrentList;
726}
727
728/***
729****
730***/
731
732void
733tr_sessionSetRPCEnabled( tr_handle * session, int isEnabled )
734{
735    tr_rpcSetEnabled( session->rpcServer, isEnabled );
736}
737int
738tr_sessionIsRPCEnabled( const tr_handle * session )
739{
740    return tr_rpcIsEnabled( session->rpcServer );
741}
742void
743tr_sessionSetRPCPort( tr_handle * session, int port )
744{
745    tr_rpcSetPort( session->rpcServer, port );
746}
747int
748tr_sessionGetRPCPort( const tr_handle * session )
749{
750    return tr_rpcGetPort( session->rpcServer );
751}
752void
753tr_sessionSetRPCCallback( tr_handle    * session,
754                          tr_rpc_func    func,
755                          void         * user_data )
756{
757    session->rpc_func = func;
758    session->rpc_func_user_data = user_data;
759}
760
761void
762tr_sessionSetRPCACL( tr_handle * session, const char * acl )
763{
764    tr_rpcSetACL( session->rpcServer, acl );
765}
766
767const char*
768tr_sessionGetRPCACL( const tr_session * session )
769{
770    return tr_rpcGetACL( session->rpcServer );
771}
Note: See TracBrowser for help on using the repository browser.