source: trunk/daemon/torrents.c @ 3578

Last change on this file since 3578 was 3578, checked in by joshe, 15 years ago

Add IPC messages to set and retrieve the encryption mode.
Implement encryption mode messages in -daemon and -remote.

  • Property svn:keywords set to Date Rev Author Id
File size: 21.9 KB
Line 
1/******************************************************************************
2 * $Id: torrents.c 3578 2007-10-26 03:43:27Z joshe $
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/bsdtree.h>
43#include <libtransmission/bencode.h>
44#include <libtransmission/transmission.h>
45#include <libtransmission/trcompat.h>
46
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    int             pexset;
61    int             pex;
62    RB_ENTRY( tor ) idlinks;
63    RB_ENTRY( tor ) hashlinks;
64};
65
66RB_HEAD( tortree, tor );
67RB_HEAD( hashtree, tor );
68
69static struct tor * opentor    ( const char *, const char *, uint8_t *, size_t,
70                                  const char * );
71static void         closetor   ( struct tor *, int );
72static void         starttimer ( int );
73static void         timerfunc  ( int, short, void * );
74static int          loadstate  ( void );
75static int          savestate  ( void );
76static int          toridcmp   ( struct tor *, struct tor * );
77static int          torhashcmp ( struct tor *, struct tor * );
78static struct tor * idlookup   ( int );
79static struct tor * hashlookup ( const uint8_t * );
80static struct tor * iterate    ( struct tor * );
81
82static struct event_base * gl_base      = NULL;
83static tr_handle         * gl_handle    = NULL;
84static struct tortree      gl_tree      = RB_INITIALIZER( &gl_tree );
85static struct hashtree     gl_hashes    = RB_INITIALIZER( &gl_hashes );
86static int                 gl_lastid    = 0;
87static struct event        gl_event;
88static time_t              gl_exiting   = 0;
89static int                 gl_exitval   = 0;
90static char                gl_state[MAXPATHLEN];
91static char                gl_newstate[MAXPATHLEN];
92
93static int                 gl_autostart = 1;
94static int                 gl_pex       = 1;
95static int                 gl_port      = TR_DEFAULT_PORT;
96static int                 gl_mapping   = 0;
97static int                 gl_uplimit   = -1;
98static int                 gl_downlimit = -1;
99static char                gl_dir[MAXPATHLEN];
100static tr_encryption_mode  gl_crypto    = TR_ENCRYPTION_PREFERRED;
101
102RB_GENERATE_STATIC( tortree, tor, idlinks, toridcmp )
103RB_GENERATE_STATIC( hashtree, tor, hashlinks, torhashcmp )
104INTCMP_FUNC( toridcmp, tor, id )
105
106void
107torrent_init( struct event_base * base )
108{
109    assert( NULL == gl_handle && NULL == gl_base );
110
111    gl_base   = base;
112    gl_handle = tr_init( "daemon" );
113
114    confpath( gl_state, sizeof gl_state, CONF_FILE_STATE, 0 );
115    strlcpy( gl_newstate, gl_state, sizeof gl_state );
116    strlcat( gl_newstate, ".new", sizeof gl_state );
117    absolutify( gl_dir, sizeof gl_dir, "." );
118
119    loadstate();
120}
121
122int
123torrent_add_file( const char * path, const char * dir, int autostart )
124{
125    struct tor * tor;
126
127    assert( NULL != gl_handle );
128    assert( !gl_exiting );
129
130    tor = opentor( path, NULL, NULL, 0, dir );
131    if( NULL == tor )
132    {
133        return -1;
134    }
135
136    if( 0 > autostart )
137    {
138        autostart = gl_autostart;
139    }
140    if( autostart )
141    {
142        tr_torrentStart( tor->tor );
143    }
144
145    savestate();
146
147    return tor->id;
148}
149
150int
151torrent_add_data( uint8_t * data, size_t size, const char * dir, int autostart )
152{
153    struct tor * tor;
154
155    assert( NULL != gl_handle );
156    assert( !gl_exiting );
157
158    tor = opentor( NULL, NULL, data, size, dir );
159    if( NULL == tor )
160    {
161        return -1;
162    }
163
164    if( 0 > autostart )
165    {
166        autostart = gl_autostart;
167    }
168    if( autostart )
169    {
170        tr_torrentStart( tor->tor );
171    }
172
173    savestate();
174
175    return tor->id;
176}
177
178void
179torrent_start( int id )
180{
181    struct tor * tor;
182
183    assert( NULL != gl_handle );
184    assert( !gl_exiting );
185
186    tor = idlookup( id );
187    if( tor != NULL )
188    {
189        const tr_stat  * st = tr_torrentStat( tor->tor );
190
191        if( !TR_STATUS_IS_ACTIVE( st->status ) )
192        {
193            tr_torrentStart( tor->tor );
194            savestate();
195        }
196    }
197
198}
199
200void
201torrent_stop( int id )
202{
203    struct tor * tor;
204
205    assert( NULL != gl_handle );
206    assert( !gl_exiting );
207
208    tor = idlookup( id );
209    if( tor != NULL )
210    {
211        const tr_stat  * st = tr_torrentStat( tor->tor );
212
213        if( TR_STATUS_IS_ACTIVE( st->status ) )
214        {
215            tr_torrentStop( tor->tor );
216            savestate();
217        }
218    }
219}
220
221void
222torrent_remove( int id )
223{
224    struct tor * tor;
225
226    assert( NULL != gl_handle );
227    assert( !gl_exiting );
228
229    tor = idlookup( id );
230    if( tor != NULL )
231    {
232        closetor( tor, 1 );
233        savestate();
234    }
235}
236
237const tr_info *
238torrent_info( int id )
239{
240    struct tor * tor;
241
242    assert( NULL != gl_handle );
243    assert( !gl_exiting );
244
245    tor = idlookup( id );
246    if( NULL == tor )
247    {
248        return NULL;
249    }
250
251    return tr_torrentInfo( tor->tor );
252}
253
254const tr_stat *
255torrent_stat( int id )
256{
257    struct tor * tor;
258
259    assert( NULL != gl_handle );
260    assert( !gl_exiting );
261
262    tor = idlookup( id );
263    if( NULL == tor )
264    {
265        return NULL;
266    }
267
268    return tr_torrentStat( tor->tor );
269}
270
271int
272torrent_lookup( const uint8_t * hashstr )
273{
274    uint8_t      hash[SHA_DIGEST_LENGTH];
275    size_t       ii;
276    struct tor * tor;
277    char         buf[3];
278
279    assert( NULL != gl_handle );
280    assert( !gl_exiting );
281
282    memset( buf, 0, sizeof buf );
283    for( ii = 0; sizeof( hash ) > ii; ii++ )
284    {
285        if( !isxdigit( hashstr[2*ii] ) || !isxdigit( hashstr[1+2*ii] ) )
286        {
287            return -1;
288        }
289        memcpy( buf, &hashstr[2*ii], 2 );
290        hash[ii] = strtol( buf, NULL, 16 );
291    }
292
293    tor = hashlookup( hash );
294    if( NULL == tor )
295    {
296        return -1;
297    }
298
299    return tor->id;
300}
301
302void *
303torrent_iter( void * iter, int * id )
304{
305    struct tor * tor = iter;
306
307    assert( NULL != gl_handle );
308    assert( !gl_exiting );
309
310    tor = iterate( tor );
311
312    if( NULL != tor )
313    {
314        *id = tor->id;
315    }
316
317    return tor;
318}
319
320void
321torrent_exit( int exitval )
322{
323    struct tor * tor;
324
325    assert( NULL != gl_handle );
326    assert( !gl_exiting );
327    gl_exiting = time( NULL );
328    gl_exitval = exitval;
329
330    RB_FOREACH( tor, tortree, &gl_tree )
331    {
332        closetor( tor, 0 );
333    }
334
335    tr_natTraversalEnable( gl_handle, 0 );
336    starttimer( 1 );
337}
338
339void
340torrent_set_autostart( int autostart )
341{
342    assert( NULL != gl_handle );
343    assert( !gl_exiting );
344    gl_autostart = autostart;
345    savestate();
346}
347
348int
349torrent_get_autostart( void )
350{
351    return gl_autostart;
352}
353
354void
355torrent_set_port( int port )
356{
357    assert( NULL != gl_handle );
358    assert( !gl_exiting );
359    if( 0 < port && 0xffff > port )
360    {
361        gl_port = port;
362        tr_setBindPort( gl_handle, port );
363        savestate();
364    }
365}
366
367int
368torrent_get_port( void )
369{
370    return gl_port;
371}
372
373void
374torrent_set_pex( int pex )
375{
376    struct tor * tor;
377
378    assert( NULL != gl_handle );
379    assert( !gl_exiting );
380
381    if( pex == gl_pex )
382    {
383        return;
384    }
385    gl_pex = pex;
386
387    for( tor = iterate( NULL ); NULL != tor; tor = iterate( tor ) )
388    {
389        if( tor->pexset )
390        {
391            continue;
392        }
393        tr_torrentDisablePex( tor->tor, !gl_pex );
394    }
395
396    savestate();
397}
398
399int
400torrent_get_pex( void )
401{
402    return gl_pex;
403}
404
405void
406torrent_enable_port_mapping( int automap )
407{
408    assert( NULL != gl_handle );
409    assert( !gl_exiting );
410    gl_mapping = ( automap ? 1 : 0 );
411    tr_natTraversalEnable( gl_handle, gl_mapping );
412    savestate();
413}
414
415int
416torrent_get_port_mapping( void )
417{
418    return gl_mapping;
419}
420
421void
422torrent_set_uplimit( int uplimit )
423{
424    assert( NULL != gl_handle );
425    assert( !gl_exiting );
426    gl_uplimit = uplimit;
427    tr_setGlobalSpeedLimit   ( gl_handle, TR_UP, uplimit );
428    tr_setUseGlobalSpeedLimit( gl_handle, TR_UP, uplimit > 0 );
429    savestate();
430}
431
432int
433torrent_get_uplimit( void )
434{
435    return gl_uplimit;
436}
437
438void
439torrent_set_downlimit( int downlimit )
440{
441    assert( NULL != gl_handle );
442    assert( !gl_exiting );
443    gl_downlimit = downlimit;
444    tr_setGlobalSpeedLimit   ( gl_handle, TR_DOWN, downlimit );
445    tr_setUseGlobalSpeedLimit( gl_handle, TR_DOWN, downlimit > 0 );
446    savestate();
447}
448
449int
450torrent_get_downlimit( void )
451{
452    return gl_downlimit;
453}
454
455void
456torrent_set_directory( const char * path )
457{
458    assert( NULL != gl_handle );
459    assert( !gl_exiting );
460
461    absolutify( gl_dir, sizeof gl_dir, path );
462    savestate();
463}
464
465const char *
466torrent_get_directory( void )
467{
468    return gl_dir;
469}
470
471void
472torrent_set_encryption(tr_encryption_mode mode)
473{
474    tr_setEncryptionMode(gl_handle, mode);
475    gl_crypto = mode;
476    savestate();
477}
478
479tr_encryption_mode
480torrent_get_encryption(void)
481{
482    return tr_getEncryptionMode(gl_handle);
483}
484
485struct tor *
486opentor( const char * path, const char * hash, uint8_t * data, size_t size,
487         const char * dir )
488{
489    struct tor * tor, * found;
490    int          errcode;
491    const tr_info  * inf;
492
493    assert( ( NULL != path && NULL == hash && NULL == data ) ||
494            ( NULL == path && NULL != hash && NULL == data ) ||
495            ( NULL == path && NULL == hash && NULL != data ) );
496
497    /* XXX should probably wrap around back to 1 and avoid duplicates */
498    if( INT_MAX == gl_lastid )
499    {
500        errmsg( "Congratulations, you're the %ith torrent! Your prize the "
501                "inability to load any more torrents, enjoy!", INT_MAX );
502        return NULL;
503    }
504
505    tor = calloc( 1, sizeof *tor );
506    if( NULL == tor )
507    {
508        mallocmsg( sizeof *tor );
509        return NULL;
510    }
511
512    if( dir == NULL )
513        dir = gl_dir;
514
515    if( NULL != path )
516    {
517        tor->tor = tr_torrentInit( gl_handle, path, dir, 1, &errcode );
518    }
519    else if( NULL != hash )
520    {
521        tor->tor = tr_torrentInitSaved( gl_handle, hash, dir, 1, &errcode );
522    }
523    else
524    {
525        tor->tor = tr_torrentInitData( gl_handle, data, size, dir, 1, &errcode );
526    }
527
528    if( NULL == tor->tor )
529    {
530        found = NULL;
531        switch( errcode )
532        {
533            case TR_EINVALID:
534                if( NULL == path )
535                {
536                    errmsg( "invalid torrent file" );
537                }
538                else
539                {
540                    errmsg( "invalid torrent file: %s", path );
541                }
542                break;
543            case TR_EUNSUPPORTED:
544                if( NULL == path )
545                {
546                    errmsg( "unsupported torrent file" );
547                }
548                else
549                {
550                    errmsg( "unsupported torrent file: %s", path );
551                }
552                break;
553            case TR_EDUPLICATE:
554                /* XXX not yet
555                found = hashlookup( tor->hash, 1 );
556                assert( NULL != found );
557                found->deleting = 0;
558                */
559                errmsg( "XXX loaded duplicate torrent" );
560                break;
561            default:
562                if( NULL == path )
563                {
564                    errmsg( "torrent file failed to load" );
565                }
566                else
567                {
568                    errmsg( "torrent file failed to load: %s", path );
569                }
570                break;
571        }
572        free( tor );
573        return found;
574    }
575    gl_lastid++;
576    tor->id       = gl_lastid;
577
578    assert( sizeof( inf->hash ) == sizeof( tor->hash ) );
579    inf = tr_torrentInfo( tor->tor );
580    memcpy( tor->hash, inf->hash, sizeof tor->hash );
581
582    if( inf->isPrivate )
583    {
584        tor->pexset = 1;
585        tor->pex    = 0;
586    }
587    else
588    {
589        tr_torrentDisablePex( tor->tor, !gl_pex );
590    }
591
592    found = RB_INSERT( tortree, &gl_tree, tor );
593    assert( NULL == found );
594    found = RB_INSERT( hashtree, &gl_hashes, tor );
595    assert( NULL == found );
596
597    return tor;
598}
599
600static void
601freetor( struct tor * tor )
602{
603    tr_torrentClose( tor->tor );
604    RB_REMOVE( tortree, &gl_tree, tor );
605    RB_REMOVE( hashtree, &gl_hashes, tor );
606    free( tor );
607}
608
609void
610closetor( struct tor * tor, int calltimer )
611{
612    if( NULL != tor )
613    {
614        freetor( tor );
615
616        starttimer( calltimer );
617    }
618}
619
620void
621starttimer( int callnow )
622{
623    if( !evtimer_initialized( &gl_event ) )
624    {
625        evtimer_set( &gl_event, timerfunc, NULL );
626        event_base_set( gl_base, &gl_event );
627    }
628
629    if( callnow )
630    {
631        timerfunc( -1, EV_TIMEOUT, NULL );
632    }
633}
634
635static void
636timerfunc( int fd UNUSED, short event UNUSED, void * arg UNUSED )
637{
638    struct tor       * tor, * next;
639    tr_handle_status * hs;
640    int                stillmore;
641    struct timeval     tv;
642
643    /* true if we've still got live torrents... */
644    stillmore = tr_torrentCount( gl_handle ) != 0;
645
646    if( gl_exiting )
647    {
648        if( !stillmore )
649        {
650            hs = tr_handleStatus( gl_handle );
651            if( TR_NAT_TRAVERSAL_DISABLED != hs->natTraversalStatus )
652            {
653                stillmore = 1;
654            }
655        }
656
657        if( !stillmore || EXIT_TIMEOUT <= time( NULL ) - gl_exiting )
658        {
659            if( stillmore )
660            {
661                errmsg( "timing out trackers and/or port mapping on exit" );
662            }
663            for( tor = RB_MIN( tortree, &gl_tree ); NULL != tor; tor = next )
664            {
665                next = RB_NEXT( tortree, &gl_tree, tor );
666                freetor( tor );
667            }
668            tr_close( gl_handle );
669            exit( gl_exitval );
670        }
671    }
672
673    if( stillmore )
674    {
675        memset( &tv, 0, sizeof tv );
676        tv.tv_sec  = TIMER_SECS;
677        tv.tv_usec = TIMER_USECS;
678        evtimer_add( &gl_event, &tv );
679    }
680}
681
682int
683loadstate( void )
684{
685    uint8_t   *  buf;
686    size_t       len;
687    benc_val_t   top, * num, * str, * list, * dict;
688    int          ii;
689    struct tor * tor;
690    const char * dir;
691
692    buf = readfile( gl_state, &len );
693    if( NULL == buf )
694    {
695        return -1;
696    }
697
698    if( tr_bencLoad( buf, len, &top, NULL ) )
699    {
700        free( buf );
701        errmsg( "failed to load bencoded data from %s", gl_state );
702        return -1;
703    }
704    free( buf );
705
706    num = tr_bencDictFind( &top, "autostart" );
707    if( NULL != num && TYPE_INT == num->type )
708    {
709        gl_autostart = ( num->val.i ? 1 : 0 );
710    }
711
712    num = tr_bencDictFind( &top, "port" );
713    if( NULL != num && TYPE_INT == num->type &&
714        0 < num->val.i && 0xffff > num->val.i )
715    {
716        gl_port = num->val.i;
717    }
718    tr_setBindPort( gl_handle, gl_port );
719
720    num = tr_bencDictFind( &top, "default-pex" );
721    if( NULL != num && TYPE_INT == num->type )
722    {
723        gl_pex = ( num->val.i ? 1 : 0 );
724    }
725
726    num = tr_bencDictFind( &top, "port-mapping" );
727    if( NULL != num && TYPE_INT == num->type )
728    {
729        gl_mapping = ( num->val.i ? 1 : 0 );
730    }
731    tr_natTraversalEnable( gl_handle, gl_mapping );
732
733    num = tr_bencDictFind( &top, "upload-limit" );
734    if( NULL != num && TYPE_INT == num->type )
735    {
736        gl_uplimit = num->val.i;
737    }
738    tr_setGlobalSpeedLimit( gl_handle, TR_UP, gl_uplimit );
739    tr_setUseGlobalSpeedLimit( gl_handle, TR_UP, gl_uplimit > 0 );
740
741    num = tr_bencDictFind( &top, "download-limit" );
742    if( NULL != num && TYPE_INT == num->type )
743    {
744        gl_downlimit = num->val.i;
745    }
746    tr_setGlobalSpeedLimit( gl_handle, TR_DOWN, gl_downlimit );
747    tr_setUseGlobalSpeedLimit( gl_handle, TR_DOWN, gl_downlimit > 0 );
748
749    str = tr_bencDictFind( &top, "default-directory" );
750    if( NULL != str && TYPE_STR == str->type )
751    {
752        strlcpy( gl_dir, str->val.s.s, sizeof gl_dir );
753    }
754
755    str = tr_bencDictFind( &top, "encryption-mode" );
756    if( NULL != str && TYPE_STR == str->type )
757    {
758        if(!strcasecmp(str->val.s.s, "preferred"))
759            gl_crypto = TR_ENCRYPTION_PREFERRED;
760        else if(!strcasecmp(str->val.s.s, "required"))
761            gl_crypto = TR_ENCRYPTION_REQUIRED;
762    }
763
764    tr_setEncryptionMode(gl_handle, gl_crypto);
765
766    list = tr_bencDictFind( &top, "torrents" );
767    if( NULL == list || TYPE_LIST != list->type )
768    {
769        return 0;
770    }
771
772    for( ii = 0; ii < list->val.l.count; ii++ )
773    {
774        dict = &list->val.l.vals[ii];
775        if( TYPE_DICT != dict->type )
776        {
777            continue;
778        }
779
780        str = tr_bencDictFind( dict, "directory" );
781        dir = ( NULL != str && TYPE_STR == str->type ? str->val.s.s : NULL );
782
783        str = tr_bencDictFind( dict, "hash" );
784        if( NULL == str || TYPE_STR != str->type ||
785            2 * SHA_DIGEST_LENGTH != str->val.s.i )
786        {
787            continue;
788        }
789
790        tor = opentor( NULL, str->val.s.s, NULL, 0, dir );
791        if( NULL == tor )
792        {
793            continue;
794        }
795
796        num = tr_bencDictFind( dict, "pex" );
797        if( NULL != num && TYPE_INT == num->type )
798        {
799            tor->pexset = 1;
800            tor->pex = ( num->val.i ? 1 : 0 );
801        }
802        tr_torrentDisablePex( tor->tor, !( tor->pexset ? tor->pex : gl_pex ) );
803
804        num = tr_bencDictFind( dict, "paused" );
805        if( NULL != num && TYPE_INT == num->type && !num->val.i )
806        {
807            tr_torrentStart( tor->tor );
808        }
809    }
810
811    return 0;
812}
813
814int
815savestate( void )
816{
817    benc_val_t   top, * list, * tor;
818    struct tor * ii;
819    uint8_t    * buf;
820    int          len, pexset;
821
822    tr_bencInit( &top, TYPE_DICT );
823    if( tr_bencDictReserve( &top, 9 ) )
824    {
825      nomem:
826        tr_bencFree( &top );
827        errmsg( "failed to save state: failed to allocate memory" );
828        return -1;
829    }
830    tr_bencInitInt( tr_bencDictAdd( &top, "autostart" ),      gl_autostart );
831    tr_bencInitInt( tr_bencDictAdd( &top, "port" ),           gl_port );
832    tr_bencInitInt( tr_bencDictAdd( &top, "default-pex" ),    gl_pex );
833    tr_bencInitInt( tr_bencDictAdd( &top, "port-mapping" ),   gl_mapping );
834    tr_bencInitInt( tr_bencDictAdd( &top, "upload-limit" ),   gl_uplimit );
835    tr_bencInitInt( tr_bencDictAdd( &top, "download-limit" ), gl_downlimit );
836    tr_bencInitStr( tr_bencDictAdd( &top, "default-directory" ),
837                    gl_dir, -1, 1 );
838    if(TR_ENCRYPTION_REQUIRED == gl_crypto)
839        tr_bencInitStr(tr_bencDictAdd(&top, "encryption-mode"), "required", -1, 1);
840    else
841        tr_bencInitStr(tr_bencDictAdd(&top, "encryption-mode"), "preferred", -1, 1);
842    list = tr_bencDictAdd( &top, "torrents" );
843    tr_bencInit( list, TYPE_LIST );
844
845    len = 0;
846    RB_FOREACH( ii, tortree, &gl_tree )
847    {
848        len++;
849    }
850    if( tr_bencListReserve( list, len ) )
851    {
852        goto nomem;
853    }
854
855    RB_FOREACH( ii, tortree, &gl_tree )
856    {
857        const tr_info * inf;
858        const tr_stat * st;
859        tor = tr_bencListAdd( list );
860        assert( NULL != tor );
861        tr_bencInit( tor, TYPE_DICT );
862        inf    = tr_torrentInfo( ii->tor );
863        st     = tr_torrentStat( ii->tor );
864        pexset = ( ii->pexset && !inf->isPrivate );
865        if( tr_bencDictReserve( tor, ( pexset ? 4 : 3 ) ) )
866        {
867            goto nomem;
868        }
869        tr_bencInitStr( tr_bencDictAdd( tor, "hash" ),
870                        inf->hashString, 2 * SHA_DIGEST_LENGTH, 1 );
871        tr_bencInitInt( tr_bencDictAdd( tor, "paused" ),
872                        !TR_STATUS_IS_ACTIVE( st->status ) );
873        tr_bencInitStr( tr_bencDictAdd( tor, "directory" ),
874                        tr_torrentGetFolder( ii->tor ), -1, 1 );
875        if( pexset )
876        {
877            tr_bencInitInt( tr_bencDictAdd( tor, "pex" ), ii->pex );
878        }
879    }
880
881    buf = ( uint8_t * )tr_bencSaveMalloc( &top, &len );
882    SAFEBENCFREE( &top );
883    if( NULL == buf )
884    {
885        errnomsg( "failed to save state: bencoding failed" );
886        return -1;
887    }
888
889    if( 0 > writefile( gl_newstate, buf, len ) )
890    {
891        free( buf );
892        return -1;
893    }
894    free( buf );
895
896    if( 0 > rename( gl_newstate, gl_state ) )
897    {
898        errnomsg( "failed to save state: failed to rename %s to %s",
899                  gl_newstate, CONF_FILE_STATE );
900        return -1;
901    }
902
903    return 0;
904}
905
906int
907torhashcmp( struct tor * left, struct tor * right )
908{
909    return memcmp( left->hash, right->hash, sizeof left->hash );
910}
911
912struct tor *
913idlookup( int id )
914{
915    struct tor key, * found;
916
917    memset( &key, 0, sizeof key );
918    key.id = id;
919    found = RB_FIND( tortree, &gl_tree, &key );
920
921    return found;
922}
923
924struct tor *
925hashlookup( const uint8_t * hash )
926{
927    struct tor key, * found;
928
929    memset( &key, 0, sizeof key );
930    memcpy( key.hash, hash, sizeof key.hash );
931    found = RB_FIND( hashtree, &gl_hashes, &key );
932
933    return found;
934}
935
936struct tor *
937iterate( struct tor * tor )
938{
939    assert( NULL != gl_handle );
940    assert( !gl_exiting );
941
942    if( NULL == tor )
943    {
944        tor = RB_MIN( tortree, &gl_tree );
945    }
946    else
947    {
948        tor = RB_NEXT( tortree, &gl_tree, tor );
949    }
950
951    return tor;
952}
Note: See TracBrowser for help on using the repository browser.