source: trunk/daemon/torrents.c @ 5779

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

#923: daemon should use tr_initFull()

  • Property svn:keywords set to Date Rev Author Id
File size: 18.5 KB
Line 
1/******************************************************************************
2 * $Id: torrents.c 5779 2008-05-08 19:34:12Z charles $
3 *
4 * Copyright (c) 2007 Joshua Elsasser
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 <sys/types.h>
26#include <sys/param.h>
27#include <sys/stat.h>
28#include <sys/time.h>
29#include <sys/uio.h>
30#include <assert.h>
31#include <ctype.h>
32#include <errno.h>
33#include <event.h>
34#include <fcntl.h>
35#include <time.h>
36#include <stddef.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include <libtransmission/bencode.h>
43#include <libtransmission/transmission.h>
44#include <libtransmission/trcompat.h>
45
46#include "bsdtree.h"
47#include "errors.h"
48#include "misc.h"
49#include "torrents.h"
50
51#define EXIT_TIMEOUT 10         /* how many seconds to wait on exit */
52#define TIMER_SECS   1          /* timer interval seconds */
53#define TIMER_USECS  0          /* timer interval microseconds */
54
55struct tor
56{
57    int             id;
58    uint8_t         hash[SHA_DIGEST_LENGTH];
59    tr_torrent    * tor;
60    RB_ENTRY( tor ) idlinks;
61    RB_ENTRY( tor ) hashlinks;
62};
63
64RB_HEAD( tortree, tor );
65RB_HEAD( hashtree, tor );
66
67static struct tor * opentor    ( const char *, const char *, uint8_t *, size_t,
68                                 const char *, int start );
69static void         closetor   ( struct tor *, int );
70static void         starttimer ( int );
71static void         timerfunc  ( int, short, void * );
72static int          savestate  ( void );
73
74static struct event_base * gl_base      = NULL;
75static tr_handle         * gl_handle    = NULL;
76static struct tortree      gl_tree      = RB_INITIALIZER( &gl_tree );
77static struct hashtree     gl_hashes    = RB_INITIALIZER( &gl_hashes );
78static int                 gl_lastid    = 0;
79static struct event        gl_event;
80static time_t              gl_exiting   = 0;
81static int                 gl_exitval   = 0;
82static char                gl_state[MAXPATHLEN];
83static char                gl_newstate[MAXPATHLEN];
84
85static int                 gl_autostart = 1;
86static int                 gl_pex       = 1;
87static int                 gl_port      = TR_DEFAULT_PORT;
88static int                 gl_mapping   = 0;
89static int                 gl_uplimit   = -1;
90static int                 gl_downlimit = -1;
91static char                gl_dir[MAXPATHLEN];
92static tr_encryption_mode  gl_crypto    = TR_ENCRYPTION_PREFERRED;
93
94static int
95torhashcmp( struct tor * left, struct tor * right )
96{
97    return memcmp( left->hash, right->hash, sizeof left->hash );
98}
99
100RB_GENERATE_STATIC( hashtree, tor, hashlinks, torhashcmp )
101
102INTCMP_FUNC( toridcmp, tor, id )
103RB_GENERATE_STATIC( tortree, tor, idlinks, toridcmp )
104
105void
106torrent_init( const char * configdir, struct event_base * base )
107{
108    tr_benc state, * torrents;
109    int have_state;
110    assert( !gl_handle && !gl_base );
111
112    confpath( gl_state, sizeof gl_state, configdir, CONF_FILE_STATE, 0 );
113    snprintf( gl_newstate, sizeof( gl_newstate ), "%s.new", gl_state );
114    absolutify( gl_dir, sizeof gl_dir, "." );
115
116    /* initialize the session variables */
117    if(( have_state = !tr_bencLoadFile( gl_state, &state )))
118    {
119        int64_t i;
120        const char * str;
121
122        if( tr_bencDictFindInt( &state, "autostart", &i ) )
123            gl_autostart = i != 0;
124        if( tr_bencDictFindInt( &state, "port", &i ) && ( 0<i ) && ( i<=0xffff ) )
125            gl_port = i;
126        if( tr_bencDictFindInt( &state, "default-pex", &i ) )
127            gl_pex = i != 0;
128        if( tr_bencDictFindInt( &state, "port-mapping", &i ) )
129            gl_mapping = i != 0;
130        if( tr_bencDictFindInt( &state, "upload-limit", &i ) )
131            gl_uplimit = i;
132        if( tr_bencDictFindInt( &state, "download-limit", &i ) )
133            gl_downlimit = i;
134        if( tr_bencDictFindStr( &state, "default-directory", &str ) )
135            strlcpy( gl_dir, str, sizeof gl_dir );
136        if( tr_bencDictFindStr( &state, "encryption-mode", &str ) ) {
137            if( !strcmp( str, "required" ) )
138                gl_crypto = TR_ENCRYPTION_REQUIRED;
139            else
140                gl_crypto = TR_ENCRYPTION_PREFERRED;
141        }
142    }
143
144    /* start the session */
145    gl_base = base;
146    gl_handle = tr_initFull( configdir, "daemon", gl_pex,
147                             gl_mapping, gl_port,
148                             gl_crypto,
149                             gl_uplimit >= 0, gl_uplimit,
150                             gl_downlimit >= 0, gl_downlimit,
151                             TR_DEFAULT_GLOBAL_PEER_LIMIT,
152                             TR_MSG_INF, 0,
153                             0, /* is the blocklist enabled? */
154                             TR_DEFAULT_PEER_SOCKET_TOS );
155
156    /* now load the torrents */
157    if( have_state && tr_bencDictFindList( &state, "torrents", &torrents ) )
158    {
159        int i, n;
160        for( i=0, n=tr_bencListSize(torrents); i<n; ++i )
161        {
162            int start;
163            int64_t paused;
164            const char * directory = NULL;
165            const char * hash = NULL;
166
167            tr_benc * dict = tr_bencListChild( torrents, i );
168            if( !tr_bencIsDict( dict ) ||
169                !tr_bencDictFindStr( dict, "directory", &directory ) ||
170                !tr_bencDictFindStr( dict, "hash", &hash ) )
171                continue;
172
173            if( tr_bencDictFindInt( dict, "paused", &paused ) )
174                start = !paused;
175            else
176                start = gl_autostart;
177
178            opentor( NULL, hash, NULL, 0, directory, start );
179        }
180    }
181
182    /* cleanup */
183    if( have_state )
184        tr_bencFree( &state );
185}
186
187int
188torrent_add_file( const char * path, const char * dir, int start )
189{
190    struct tor * tor;
191
192    assert( gl_handle );
193    assert( !gl_exiting );
194
195    if( start < 0 )
196        start = gl_autostart;
197
198    tor = opentor( path, NULL, NULL, 0, dir, start );
199    if( !tor )
200        return -1;
201
202    savestate();
203
204    return tor->id;
205}
206
207int
208torrent_add_data( uint8_t * data, size_t size, const char * dir, int start )
209{
210    struct tor * tor;
211
212    assert( gl_handle );
213    assert( !gl_exiting );
214
215    if( start < 0 )
216        start = gl_autostart;
217
218    tor = opentor( NULL, NULL, data, size, dir, start );
219    if( !tor )
220        return -1;
221
222    savestate();
223
224    return tor->id;
225}
226
227static struct tor *
228idlookup( int id )
229{
230    struct tor * found = NULL;
231
232    if( gl_handle && !gl_exiting )
233    {
234        struct tor key;
235        memset( &key, 0, sizeof key );
236        key.id = id;
237        found = RB_FIND( tortree, &gl_tree, &key );
238    }
239
240    return found;
241}
242
243void
244torrent_start( int id )
245{
246    struct tor * tor = idlookup( id );
247    if( tor && !TR_STATUS_IS_ACTIVE( tr_torrentStat( tor->tor )->status ) ) {
248        tr_torrentStart( tor->tor );
249        savestate();
250    }
251
252}
253
254void
255torrent_stop( int id )
256{
257    struct tor * tor = idlookup( id );
258    if( tor && TR_STATUS_IS_ACTIVE( tr_torrentStat( tor->tor )->status ) ) {
259        tr_torrentStop( tor->tor );
260        savestate( );
261    }
262}
263
264void
265torrent_verify( int id )
266{
267    struct tor * tor = idlookup( id );
268    if( tor )
269        tr_torrentVerify( tor->tor );
270}
271
272void
273torrent_remove( int id )
274{
275    struct tor * tor = idlookup( id );
276    if( tor ) {
277        closetor( tor, 1 );
278        savestate();
279    }
280}
281
282tr_torrent *
283torrent_handle( int id )
284{
285    const struct tor * tor = idlookup( id );
286    return tor ? tor->tor : NULL;
287}
288   
289const tr_info *
290torrent_info( int id )
291{
292    return tr_torrentInfo( torrent_handle( id ) );
293}
294
295const tr_stat *
296torrent_stat( int id )
297{
298    return tr_torrentStat( torrent_handle( id ) );
299}
300
301static struct tor *
302hashlookup( const uint8_t * hash )
303{
304    struct tor key, * found;
305
306    memset( &key, 0, sizeof key );
307    memcpy( key.hash, hash, sizeof key.hash );
308    found = RB_FIND( hashtree, &gl_hashes, &key );
309
310    return found;
311}
312
313int
314torrent_lookup( const uint8_t * hashstr )
315{
316    uint8_t      hash[SHA_DIGEST_LENGTH];
317    size_t       ii;
318    struct tor * tor;
319    char         buf[3];
320
321    assert( NULL != gl_handle );
322    assert( !gl_exiting );
323
324    memset( buf, 0, sizeof buf );
325    for( ii = 0; sizeof( hash ) > ii; ii++ )
326    {
327        if( !isxdigit( hashstr[2*ii] ) || !isxdigit( hashstr[1+2*ii] ) )
328        {
329            return -1;
330        }
331        memcpy( buf, &hashstr[2*ii], 2 );
332        hash[ii] = strtol( buf, NULL, 16 );
333    }
334
335    tor = hashlookup( hash );
336    if( NULL == tor )
337    {
338        return -1;
339    }
340
341    return tor->id;
342}
343
344static struct tor *
345iterate( struct tor * tor )
346{
347    struct tor * next = NULL;
348
349    if( gl_handle && !gl_exiting )
350        next = tor ? RB_NEXT( tortree, &gl_tree, tor )
351                   : RB_MIN( tortree, &gl_tree );
352
353    return next;
354}
355
356void *
357torrent_iter( void * cur, int * id )
358{
359    struct tor * next = iterate( cur );
360    if( next )
361        *id = next->id;
362    return next;
363}
364
365void
366torrent_exit( int exitval )
367{
368    struct tor * tor;
369
370    assert( NULL != gl_handle );
371    assert( !gl_exiting );
372    gl_exiting = time( NULL );
373    gl_exitval = exitval;
374
375    RB_FOREACH( tor, tortree, &gl_tree )
376    {
377        closetor( tor, 0 );
378    }
379
380    tr_natTraversalEnable( gl_handle, 0 );
381    starttimer( 1 );
382}
383
384void
385torrent_set_autostart( int autostart )
386{
387    assert( NULL != gl_handle );
388    assert( !gl_exiting );
389    gl_autostart = autostart;
390    savestate();
391}
392
393int
394torrent_get_autostart( void )
395{
396    return gl_autostart;
397}
398
399void
400torrent_set_port( int port )
401{
402    assert( NULL != gl_handle );
403    assert( !gl_exiting );
404    if( 0 < port && 0xffff > port )
405    {
406        gl_port = port;
407        tr_setBindPort( gl_handle, port );
408        savestate();
409    }
410}
411
412int
413torrent_get_port( void )
414{
415    return gl_port;
416}
417
418void
419torrent_set_pex( int pex )
420{
421    assert( NULL != gl_handle );
422    assert( !gl_exiting );
423
424    if( gl_pex != pex )
425    {
426        gl_pex = pex;
427
428        tr_setPexEnabled( gl_handle, gl_pex );
429
430        savestate( );
431    }
432}
433
434int
435torrent_get_pex( void )
436{
437    return gl_pex;
438}
439
440void
441torrent_enable_port_mapping( int automap )
442{
443    assert( NULL != gl_handle );
444    assert( !gl_exiting );
445    gl_mapping = ( automap ? 1 : 0 );
446    tr_natTraversalEnable( gl_handle, gl_mapping );
447    savestate();
448}
449
450int
451torrent_get_port_mapping( void )
452{
453    return gl_mapping;
454}
455
456void
457torrent_set_uplimit( int uplimit )
458{
459    assert( NULL != gl_handle );
460    assert( !gl_exiting );
461    gl_uplimit = uplimit;
462    tr_setGlobalSpeedLimit   ( gl_handle, TR_UP, uplimit );
463    tr_setUseGlobalSpeedLimit( gl_handle, TR_UP, uplimit > 0 );
464    savestate();
465}
466
467int
468torrent_get_uplimit( void )
469{
470    return gl_uplimit;
471}
472
473void
474torrent_set_downlimit( int downlimit )
475{
476    assert( NULL != gl_handle );
477    assert( !gl_exiting );
478    gl_downlimit = downlimit;
479    tr_setGlobalSpeedLimit   ( gl_handle, TR_DOWN, downlimit );
480    tr_setUseGlobalSpeedLimit( gl_handle, TR_DOWN, downlimit > 0 );
481    savestate();
482}
483
484int
485torrent_get_downlimit( void )
486{
487    return gl_downlimit;
488}
489
490void
491torrent_set_directory( const char * path )
492{
493    assert( NULL != gl_handle );
494    assert( !gl_exiting );
495
496    absolutify( gl_dir, sizeof gl_dir, path );
497    savestate();
498}
499
500const char *
501torrent_get_directory( void )
502{
503    return gl_dir;
504}
505
506void
507torrent_set_encryption(tr_encryption_mode mode)
508{
509    tr_setEncryptionMode(gl_handle, mode);
510    gl_crypto = mode;
511    savestate();
512}
513
514tr_encryption_mode
515torrent_get_encryption(void)
516{
517    return tr_getEncryptionMode(gl_handle);
518}
519
520struct tor *
521opentor( const char * path,
522         const char * hash,
523         uint8_t    * data,
524         size_t       size,
525         const char * dir, 
526         int          start )
527{
528    struct tor * tor, * found;
529    int          errcode;
530    const tr_info  * inf;
531    tr_ctor        * ctor;
532
533    assert( (path?1:0) + (hash?1:0) + (data?1:0) == 1 );
534
535    /* XXX should probably wrap around back to 1 and avoid duplicates */
536    if( INT_MAX == gl_lastid )
537    {
538        errmsg( "Congratulations, you're the %ith torrent! Your prize the "
539                "inability to load any more torrents, enjoy!", INT_MAX );
540        return NULL;
541    }
542
543    tor = calloc( 1, sizeof *tor );
544    if( NULL == tor )
545    {
546        mallocmsg( sizeof *tor );
547        return NULL;
548    }
549
550    if( dir == NULL )
551        dir = gl_dir;
552
553    ctor = tr_ctorNew( gl_handle );
554    tr_ctorSetPaused( ctor, TR_FORCE, !start );
555    tr_ctorSetDestination( ctor, TR_FORCE, dir );
556    if( path != NULL )
557        tr_ctorSetMetainfoFromFile( ctor, path );
558    else if( hash != NULL )
559        tr_ctorSetMetainfoFromHash( ctor, hash );
560    else
561        tr_ctorSetMetainfo( ctor, data, size );
562    tor->tor = tr_torrentNew( gl_handle, ctor, &errcode );
563    tr_ctorFree( ctor );
564
565    if( NULL == tor->tor )
566    {
567        found = NULL;
568        switch( errcode )
569        {
570            case TR_EINVALID:
571                if( NULL == path )
572                {
573                    errmsg( "invalid torrent file" );
574                }
575                else
576                {
577                    errmsg( "invalid torrent file: %s", path );
578                }
579                break;
580            case TR_EUNSUPPORTED:
581                if( NULL == path )
582                {
583                    errmsg( "unsupported torrent file" );
584                }
585                else
586                {
587                    errmsg( "unsupported torrent file: %s", path );
588                }
589                break;
590            case TR_EDUPLICATE:
591                /* XXX not yet
592                found = hashlookup( tor->hash, 1 );
593                assert( NULL != found );
594                found->deleting = 0;
595                */
596                errmsg( "XXX loaded duplicate torrent" );
597                break;
598            default:
599                if( NULL == path )
600                {
601                    errmsg( "torrent file failed to load" );
602                }
603                else
604                {
605                    errmsg( "torrent file failed to load: %s", path );
606                }
607                break;
608        }
609        free( tor );
610        return found;
611    }
612    gl_lastid++;
613    tor->id       = gl_lastid;
614
615    assert( sizeof( inf->hash ) == sizeof( tor->hash ) );
616    inf = tr_torrentInfo( tor->tor );
617    memcpy( tor->hash, inf->hash, sizeof tor->hash );
618
619    found = RB_INSERT( tortree, &gl_tree, tor );
620    assert( NULL == found );
621    found = RB_INSERT( hashtree, &gl_hashes, tor );
622    assert( NULL == found );
623
624    return tor;
625}
626
627static void
628freetor( struct tor * tor )
629{
630    tr_torrentClose( tor->tor );
631    RB_REMOVE( tortree, &gl_tree, tor );
632    RB_REMOVE( hashtree, &gl_hashes, tor );
633    free( tor );
634}
635
636void
637closetor( struct tor * tor, int calltimer )
638{
639    if( NULL != tor )
640    {
641        freetor( tor );
642
643        starttimer( calltimer );
644    }
645}
646
647void
648starttimer( int callnow )
649{
650    if( !evtimer_initialized( &gl_event ) )
651    {
652        evtimer_set( &gl_event, timerfunc, NULL );
653        event_base_set( gl_base, &gl_event );
654    }
655
656    if( callnow )
657    {
658        timerfunc( -1, EV_TIMEOUT, NULL );
659    }
660}
661
662static void
663timerfunc( int fd UNUSED, short event UNUSED, void * arg UNUSED )
664{
665    struct tor             * tor, * next;
666    const tr_handle_status * hs;
667    int                      stillmore;
668    struct timeval           tv;
669
670    /* true if we've still got live torrents... */
671    stillmore = tr_torrentCount( gl_handle ) != 0;
672
673    if( gl_exiting )
674    {
675        if( !stillmore )
676        {
677            hs = tr_handleStatus( gl_handle );
678            if( TR_NAT_TRAVERSAL_UNMAPPED != hs->natTraversalStatus )
679            {
680                stillmore = 1;
681            }
682        }
683
684        if( !stillmore || EXIT_TIMEOUT <= time( NULL ) - gl_exiting )
685        {
686            if( stillmore )
687            {
688                errmsg( "timing out trackers and/or port mapping on exit" );
689            }
690            for( tor = RB_MIN( tortree, &gl_tree ); NULL != tor; tor = next )
691            {
692                next = RB_NEXT( tortree, &gl_tree, tor );
693                freetor( tor );
694            }
695            tr_close( gl_handle );
696            exit( gl_exitval );
697        }
698    }
699
700    if( stillmore )
701    {
702        memset( &tv, 0, sizeof tv );
703        tv.tv_sec  = TIMER_SECS;
704        tv.tv_usec = TIMER_USECS;
705        evtimer_add( &gl_event, &tv );
706    }
707}
708
709int
710savestate( void )
711{
712    benc_val_t   top, * list;
713    struct tor * ii;
714    int          torrentCount;
715 
716    torrentCount = 0;
717    RB_FOREACH( ii, tortree, &gl_tree )
718        ++torrentCount;
719
720    tr_bencInitDict( &top, 9 );
721    tr_bencDictAddInt( &top, "autostart",         gl_autostart );
722    tr_bencDictAddInt( &top, "port",              gl_port );
723    tr_bencDictAddInt( &top, "default-pex",       gl_pex );
724    tr_bencDictAddInt( &top, "port-mapping",      gl_mapping );
725    tr_bencDictAddInt( &top, "upload-limit",      gl_uplimit );
726    tr_bencDictAddInt( &top, "download-limit",    gl_downlimit );
727    tr_bencDictAddStr( &top, "default-directory", gl_dir );
728    tr_bencDictAddStr( &top, "encryption-mode",   TR_ENCRYPTION_REQUIRED == gl_crypto
729                                                  ? "required" : "preferred" );
730    list = tr_bencDictAddList( &top, "torrents", torrentCount );
731
732    RB_FOREACH( ii, tortree, &gl_tree )
733    {
734        const tr_info * inf = tr_torrentInfo( ii->tor );
735        const tr_stat * st  = tr_torrentStat( ii->tor );
736        tr_benc * tor = tr_bencListAddDict( list, 3 );
737        tr_bencDictAddStr( tor, "hash", inf->hashString );
738        tr_bencDictAddInt( tor, "paused", !TR_STATUS_IS_ACTIVE( st->status ) );
739        tr_bencDictAddStr( tor, "directory", tr_torrentGetFolder( ii->tor ) );
740    }
741
742    if( tr_bencSaveFile( gl_newstate, &top ) )
743    {
744        errnomsg( "failed to save state: failed to write to %s", gl_newstate );
745        return -1;
746    }
747
748    if( 0 > rename( gl_newstate, gl_state ) )
749    {
750        errnomsg( "failed to save state: failed to rename %s to %s",
751                  gl_newstate, CONF_FILE_STATE );
752        return -1;
753    }
754
755    return 0;
756}
Note: See TracBrowser for help on using the repository browser.