source: trunk/daemon/torrents.c @ 5818

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

fix daemon breakage

  • Property svn:keywords set to Date Rev Author Id
File size: 18.2 KB
Line 
1/******************************************************************************
2 * $Id: torrents.c 5818 2008-05-12 15:17:25Z 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_EDUPLICATE:
581                /* XXX not yet
582                found = hashlookup( tor->hash, 1 );
583                assert( NULL != found );
584                found->deleting = 0;
585                */
586                errmsg( "XXX loaded duplicate torrent" );
587                break;
588            default:
589                if( NULL == path )
590                {
591                    errmsg( "torrent file failed to load" );
592                }
593                else
594                {
595                    errmsg( "torrent file failed to load: %s", path );
596                }
597                break;
598        }
599        free( tor );
600        return found;
601    }
602    gl_lastid++;
603    tor->id       = gl_lastid;
604
605    assert( sizeof( inf->hash ) == sizeof( tor->hash ) );
606    inf = tr_torrentInfo( tor->tor );
607    memcpy( tor->hash, inf->hash, sizeof tor->hash );
608
609    found = RB_INSERT( tortree, &gl_tree, tor );
610    assert( NULL == found );
611    found = RB_INSERT( hashtree, &gl_hashes, tor );
612    assert( NULL == found );
613
614    return tor;
615}
616
617static void
618freetor( struct tor * tor )
619{
620    tr_torrentClose( tor->tor );
621    RB_REMOVE( tortree, &gl_tree, tor );
622    RB_REMOVE( hashtree, &gl_hashes, tor );
623    free( tor );
624}
625
626void
627closetor( struct tor * tor, int calltimer )
628{
629    if( NULL != tor )
630    {
631        freetor( tor );
632
633        starttimer( calltimer );
634    }
635}
636
637void
638starttimer( int callnow )
639{
640    if( !evtimer_initialized( &gl_event ) )
641    {
642        evtimer_set( &gl_event, timerfunc, NULL );
643        event_base_set( gl_base, &gl_event );
644    }
645
646    if( callnow )
647    {
648        timerfunc( -1, EV_TIMEOUT, NULL );
649    }
650}
651
652static void
653timerfunc( int fd UNUSED, short event UNUSED, void * arg UNUSED )
654{
655    struct tor             * tor, * next;
656    const tr_handle_status * hs;
657    int                      stillmore;
658    struct timeval           tv;
659
660    /* true if we've still got live torrents... */
661    stillmore = tr_torrentCount( gl_handle ) != 0;
662
663    if( gl_exiting )
664    {
665        if( !stillmore )
666        {
667            hs = tr_handleStatus( gl_handle );
668            if( TR_NAT_TRAVERSAL_UNMAPPED != hs->natTraversalStatus )
669            {
670                stillmore = 1;
671            }
672        }
673
674        if( !stillmore || EXIT_TIMEOUT <= time( NULL ) - gl_exiting )
675        {
676            if( stillmore )
677            {
678                errmsg( "timing out trackers and/or port mapping on exit" );
679            }
680            for( tor = RB_MIN( tortree, &gl_tree ); NULL != tor; tor = next )
681            {
682                next = RB_NEXT( tortree, &gl_tree, tor );
683                freetor( tor );
684            }
685            tr_close( gl_handle );
686            exit( gl_exitval );
687        }
688    }
689
690    if( stillmore )
691    {
692        memset( &tv, 0, sizeof tv );
693        tv.tv_sec  = TIMER_SECS;
694        tv.tv_usec = TIMER_USECS;
695        evtimer_add( &gl_event, &tv );
696    }
697}
698
699int
700savestate( void )
701{
702    benc_val_t   top, * list;
703    struct tor * ii;
704    int          torrentCount;
705 
706    torrentCount = 0;
707    RB_FOREACH( ii, tortree, &gl_tree )
708        ++torrentCount;
709
710    tr_bencInitDict( &top, 9 );
711    tr_bencDictAddInt( &top, "autostart",         gl_autostart );
712    tr_bencDictAddInt( &top, "port",              gl_port );
713    tr_bencDictAddInt( &top, "default-pex",       gl_pex );
714    tr_bencDictAddInt( &top, "port-mapping",      gl_mapping );
715    tr_bencDictAddInt( &top, "upload-limit",      gl_uplimit );
716    tr_bencDictAddInt( &top, "download-limit",    gl_downlimit );
717    tr_bencDictAddStr( &top, "default-directory", gl_dir );
718    tr_bencDictAddStr( &top, "encryption-mode",   TR_ENCRYPTION_REQUIRED == gl_crypto
719                                                  ? "required" : "preferred" );
720    list = tr_bencDictAddList( &top, "torrents", torrentCount );
721
722    RB_FOREACH( ii, tortree, &gl_tree )
723    {
724        const tr_info * inf = tr_torrentInfo( ii->tor );
725        const tr_stat * st  = tr_torrentStat( ii->tor );
726        tr_benc * tor = tr_bencListAddDict( list, 3 );
727        tr_bencDictAddStr( tor, "hash", inf->hashString );
728        tr_bencDictAddInt( tor, "paused", !TR_STATUS_IS_ACTIVE( st->status ) );
729        tr_bencDictAddStr( tor, "directory", tr_torrentGetFolder( ii->tor ) );
730    }
731
732    if( tr_bencSaveFile( gl_newstate, &top ) )
733    {
734        errnomsg( "failed to save state: failed to write to %s", gl_newstate );
735        return -1;
736    }
737
738    if( 0 > rename( gl_newstate, gl_state ) )
739    {
740        errnomsg( "failed to save state: failed to rename %s to %s",
741                  gl_newstate, CONF_FILE_STATE );
742        return -1;
743    }
744
745    return 0;
746}
Note: See TracBrowser for help on using the repository browser.