source: trunk/daemon/server.c @ 1806

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

Add debug mode to daemon.

  • Property svn:keywords set to Date Rev Author Id
File size: 26.7 KB
Line 
1/******************************************************************************
2 * $Id: server.c 1806 2007-04-26 07:03:36Z 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/socket.h>
27#include <sys/time.h>
28#include <sys/un.h>
29#include <assert.h>
30#include <errno.h>
31#include <event.h>
32#include <fcntl.h>
33#include <limits.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39#include "bsdtree.h"
40#include "bencode.h"
41#include "errors.h"
42#include "ipc.h"
43#include "misc.h"
44#include "server.h"
45#include "torrents.h"
46
47/* time out clients after this many seconds */
48#define CLIENT_TIMEOUT          ( 60 )
49
50struct client
51{
52    int                  fd;
53    struct bufferevent * ev;
54    struct ipc_info      ipc;
55    RB_ENTRY( client )   link;
56};
57
58RB_HEAD( allclients, client );
59
60static void newclient( int, short, void * );
61static void noop     ( struct bufferevent *, void * );
62static void byebye   ( struct bufferevent *, short, void * );
63static void doread   ( struct bufferevent *, void * );
64static int  queuemsg ( struct client *, uint8_t *, size_t );
65static void msgresp  ( struct client *, int64_t, enum ipc_msg );
66static void defmsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
67static void noopmsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
68static void addmsg1  ( enum ipc_msg, benc_val_t *, int64_t, void * );
69static void addmsg2  ( enum ipc_msg, benc_val_t *, int64_t, void * );
70static void quitmsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
71static void intmsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
72static void strmsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
73static void infomsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
74static int  addinfo  ( benc_val_t *, int, int );
75static int  addstat  ( benc_val_t *, int, int );
76static void tormsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
77static void lookmsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
78static void prefmsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
79static void supmsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
80static int  clientcmp( struct client *, struct client * );
81
82RB_GENERATE_STATIC( allclients, client, link, clientcmp )
83INTCMP_FUNC( clientcmp, client, ev )
84
85static struct event_base * gl_base    = NULL;
86static struct ipc_funcs  * gl_tree    = NULL;
87static int                 gl_debug   = 0;
88static int                 gl_exiting = 0;
89static struct allclients   gl_clients = RB_INITIALIZER( &gl_clients );
90
91int
92server_init( struct event_base * base )
93{
94    assert( NULL == gl_base && NULL == gl_tree );
95    gl_base = base;
96    gl_tree = ipc_initmsgs();
97    if( NULL == gl_tree )
98    {
99        return -1;
100    }
101
102    if( 0 > ipc_addmsg( gl_tree, IPC_MSG_ADDMANYFILES, addmsg1 ) ||
103        0 > ipc_addmsg( gl_tree, IPC_MSG_ADDONEFILE,   addmsg2 ) ||
104        0 > ipc_addmsg( gl_tree, IPC_MSG_AUTOMAP,      intmsg  ) ||
105        0 > ipc_addmsg( gl_tree, IPC_MSG_AUTOSTART,    intmsg  ) ||
106        0 > ipc_addmsg( gl_tree, IPC_MSG_DOWNLIMIT,    intmsg  ) ||
107        0 > ipc_addmsg( gl_tree, IPC_MSG_DIR,          strmsg  ) ||
108        0 > ipc_addmsg( gl_tree, IPC_MSG_GETAUTOMAP,   prefmsg ) ||
109        0 > ipc_addmsg( gl_tree, IPC_MSG_GETAUTOSTART, prefmsg ) ||
110        0 > ipc_addmsg( gl_tree, IPC_MSG_GETDOWNLIMIT, prefmsg ) ||
111        0 > ipc_addmsg( gl_tree, IPC_MSG_GETDIR,       prefmsg ) ||
112        0 > ipc_addmsg( gl_tree, IPC_MSG_GETINFO,      infomsg ) ||
113        0 > ipc_addmsg( gl_tree, IPC_MSG_GETINFOALL,   infomsg ) ||
114        0 > ipc_addmsg( gl_tree, IPC_MSG_GETPEX,       prefmsg ) ||
115        0 > ipc_addmsg( gl_tree, IPC_MSG_GETPORT,      prefmsg ) ||
116        0 > ipc_addmsg( gl_tree, IPC_MSG_GETSTAT,      infomsg ) ||
117        0 > ipc_addmsg( gl_tree, IPC_MSG_GETSTATALL,   infomsg ) ||
118        0 > ipc_addmsg( gl_tree, IPC_MSG_GETUPLIMIT,   prefmsg ) ||
119        0 > ipc_addmsg( gl_tree, IPC_MSG_GETSUP,       supmsg  ) ||
120        0 > ipc_addmsg( gl_tree, IPC_MSG_LOOKUP,       lookmsg ) ||
121        0 > ipc_addmsg( gl_tree, IPC_MSG_NOOP,         noopmsg ) ||
122        0 > ipc_addmsg( gl_tree, IPC_MSG_PEX,          intmsg  ) ||
123        0 > ipc_addmsg( gl_tree, IPC_MSG_PORT,         intmsg  ) ||
124        0 > ipc_addmsg( gl_tree, IPC_MSG_QUIT,         quitmsg ) ||
125        0 > ipc_addmsg( gl_tree, IPC_MSG_REMOVE,       tormsg  ) ||
126        0 > ipc_addmsg( gl_tree, IPC_MSG_REMOVEALL,    tormsg  ) ||
127        0 > ipc_addmsg( gl_tree, IPC_MSG_START,        tormsg  ) ||
128        0 > ipc_addmsg( gl_tree, IPC_MSG_STARTALL,     tormsg  ) ||
129        0 > ipc_addmsg( gl_tree, IPC_MSG_STOP,         tormsg  ) ||
130        0 > ipc_addmsg( gl_tree, IPC_MSG_STOPALL,      tormsg  ) ||
131        0 > ipc_addmsg( gl_tree, IPC_MSG_UPLIMIT,      intmsg  ) )
132    {
133        return -1;
134    }
135
136    ipc_setdefmsg( gl_tree, defmsg );
137
138    return 0;
139}
140
141void
142server_debug( int enable )
143{
144    gl_debug = enable;
145}
146
147int
148server_listen( int fd )
149{
150    struct event * ev;
151    int flags;
152
153    assert( NULL != gl_base );
154
155    flags = fcntl( fd, F_GETFL );
156    if( 0 > flags )
157    {
158        errnomsg( "failed to get flags on socket" );
159        return -1;
160    }
161    if( 0 > fcntl( fd, F_SETFL, flags | O_NONBLOCK ) )
162    {
163        errnomsg( "failed to set flags on socket" );
164        return -1;
165    }
166
167    if( 0 > listen( fd, 5 ) )
168    {
169        errnomsg( "failed to listen on socket" );
170        return -1;
171    }
172
173    ev = malloc( sizeof *ev );
174    if( NULL == ev )
175    {
176        mallocmsg( sizeof *ev );
177        return -1;
178    }
179
180    event_set( ev, fd, EV_READ | EV_PERSIST, newclient, ev );
181    /* XXX event_base_set( gl_base, ev ); */
182    event_add( ev, NULL );
183
184    return 0;
185}
186
187void
188newclient( int fd, short event UNUSED, void * arg )
189{
190    struct sockaddr_un   sun;
191    struct client      * client, * old;
192    socklen_t            socklen;
193    struct bufferevent * clev;
194    int                  clfd;
195    size_t               buflen;
196    uint8_t            * buf;
197
198    if( gl_exiting )
199    {
200        event_del( arg );
201        return;
202    }
203
204    for( ;; )
205    {
206        client = calloc( 1, sizeof *client );
207        if( NULL == client )
208        {
209            mallocmsg( sizeof *client );
210            return;
211        }
212
213        socklen = sizeof sun;
214        clfd = accept( fd, ( struct sockaddr * )&sun, &socklen );
215        if( 0 > clfd )
216        {
217            if( EWOULDBLOCK != errno && EAGAIN != errno &&
218                ECONNABORTED != errno )
219            {
220                errnomsg( "failed to accept ipc connection" );
221            }
222            free( client );
223            break;
224        }
225
226        clev = bufferevent_new( clfd, doread, noop, byebye, client );
227        if( NULL == clev )
228        {
229            close( clfd );
230            free( client );
231            mallocmsg( -1 );
232            return;
233        }
234        /* XXX bufferevent_base_set( gl_base, clev ); */
235        bufferevent_settimeout( clev, CLIENT_TIMEOUT, CLIENT_TIMEOUT );
236
237        client->fd      = clfd;
238        ipc_newcon( &client->ipc, gl_tree );
239        client->ev      = clev;
240        old = RB_INSERT( allclients, &gl_clients, client );
241        assert( NULL == old );
242
243        if( gl_debug )
244        {
245            printf( "*** new client %i\n", clfd );
246        }
247
248        bufferevent_enable( clev, EV_READ );
249        buf = ipc_mkvers( &buflen );
250        if( 0 > queuemsg( client, buf, buflen ) )
251        {
252            free( buf );
253            return;
254        }
255        free( buf );
256    }
257}
258
259void
260noop( struct bufferevent * ev UNUSED, void * arg UNUSED )
261{
262    /* libevent prior to 1.2 couldn't handle a NULL write callback */
263}
264
265void
266byebye( struct bufferevent * ev, short what, void * arg UNUSED )
267{
268    struct client * client, key;
269
270    if( !( EVBUFFER_EOF & what ) )
271    {
272        if( EVBUFFER_TIMEOUT & what )
273        {
274            errmsg( "client connection timed out" );
275        }
276        else if( EVBUFFER_READ & what )
277        {
278            errmsg( "read error on client connection" );
279        }
280        else if( EVBUFFER_WRITE & what )
281        {
282            errmsg( "write error on client connection" );
283        }
284        else if( EVBUFFER_ERROR & what )
285        {
286            errmsg( "error on client connection" );
287        }
288        else
289        {
290            errmsg( "unknown error on client connection: 0x%x", what );
291        }
292    }
293
294    bzero( &key, sizeof key );
295    key.ev = ev;
296    client = RB_FIND( allclients, &gl_clients, &key );
297    assert( NULL != client );
298    RB_REMOVE( allclients, &gl_clients, client );
299    bufferevent_free( ev );
300    close( client->fd );
301    if( gl_debug )
302    {
303        printf( "*** client %i went bye-bye\n", client->fd );
304    }
305    free( client );
306}
307
308void
309doread( struct bufferevent * ev, void * arg )
310{
311    struct client * client = arg;
312    ssize_t         res;
313    uint8_t       * buf;
314    size_t          len;
315
316    assert( !gl_exiting );
317
318    buf = EVBUFFER_DATA( EVBUFFER_INPUT( ev ) );
319    len = EVBUFFER_LENGTH( EVBUFFER_INPUT( ev ) );
320
321    if( gl_debug )
322    {
323        printf( "<<< %zu bytes from client %i: ", len, client->fd );
324        fwrite( buf, 1, len, stdout );
325        putc( '\n', stdout );
326    }
327
328    if( IPC_MIN_MSG_LEN > len )
329    {
330        return;
331    }
332
333    res = ipc_parse( &client->ipc, buf, len, client );
334
335    if( gl_exiting )
336    {
337        return;
338    }
339
340    if( 0 > res )
341    {
342        switch( errno )
343        {
344            case EPERM:
345                errmsg( "unsupported protocol version" );
346                break;
347            case EINVAL:
348                errmsg( "protocol parse error" );
349                break;
350            default:
351                errnomsg( "parsing failed" );
352                break;
353        }
354        byebye( ev, EVBUFFER_ERROR, NULL );
355    }
356    else if( 0 < res )
357    {
358        evbuffer_drain( EVBUFFER_INPUT( ev ), res );
359    }
360}
361
362int
363queuemsg( struct client * client, uint8_t * buf, size_t buflen )
364{
365    if( NULL == buf )
366    {
367        if( EPERM != errno )
368        {
369            errnomsg( "failed to build message" );
370            byebye( client->ev, EVBUFFER_EOF, NULL );
371        }
372        return -1;
373    }
374
375    if( gl_debug )
376    {
377        printf( ">>> %zu bytes to client %i: ", buflen, client->fd );
378        fwrite( buf, 1, buflen, stdout );
379        putc( '\n', stdout );
380    }
381
382    if( 0 > bufferevent_write( client->ev, buf, buflen ) )
383    {
384        errnomsg( "failed to buffer %zd bytes of data for write", buflen );
385        return -1;
386    }
387
388    return 0;
389}
390
391void
392msgresp( struct client * client, int64_t tag, enum ipc_msg id )
393{
394    uint8_t * buf;
395    size_t    buflen;
396
397    if( 0 >= tag )
398    {
399        return;
400    }
401
402    buf = ipc_mkempty( &client->ipc, &buflen, id, tag );
403    queuemsg( client, buf, buflen );
404    free( buf );
405}
406
407void
408defmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag,
409        void * arg )
410{
411    struct client * client = arg;
412
413    msgresp( client, tag, IPC_MSG_NOTSUP );
414}
415
416void
417noopmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag,
418         void * arg )
419{
420    struct client * client = arg;
421
422    msgresp( client, tag, IPC_MSG_OK );
423}
424
425void
426addmsg1( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
427{
428    struct client * client = arg;
429    benc_val_t      pk, * added, * file;
430    int             ii, tor;
431    size_t          buflen;
432    uint8_t       * buf;
433    tr_info_t     * inf;
434
435    if( NULL == val || TYPE_LIST != val->type )
436    {
437        msgresp( client, tag, IPC_MSG_NOTSUP );
438        return;
439    }
440
441    added = ipc_initval( &client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
442    if( NULL == added )
443    {
444        errnomsg( "failed to build message" );
445        byebye( client->ev, EVBUFFER_EOF, NULL );
446        return;
447    }
448
449    for( ii = 0; ii < val->val.l.count; ii++ )
450    {
451        file = &val->val.l.vals[ii];
452        if( TYPE_STR != file->type )
453        {
454            continue;
455        }
456        /* XXX need to somehow inform client of skipped or failed files */
457        tor = torrent_add_file( file->val.s.s, NULL, -1 );
458        if( TORRENT_ID_VALID( tor ) )
459        {
460            inf = torrent_info( tor );
461            if( 0 > ipc_addinfo( added, tor, inf, 0 ) )
462            {
463                errnomsg( "failed to build message" );
464                tr_bencFree( &pk );
465                byebye( client->ev, EVBUFFER_EOF, NULL );
466                return;
467            }
468        }
469    }
470
471    buf = ipc_mkval( &pk, &buflen );
472    tr_bencFree( &pk );
473    queuemsg( client, buf, buflen );
474    free( buf );
475}
476
477void
478addmsg2( enum ipc_msg id UNUSED, benc_val_t * dict, int64_t tag, void * arg )
479{
480    struct client * client = arg;
481    benc_val_t    * val, pk;
482    int             tor, start;
483    size_t          buflen;
484    uint8_t       * buf;
485    const char    * dir;
486    tr_info_t     * inf;
487
488    if( NULL == dict || TYPE_DICT != dict->type )
489    {
490        msgresp( client, tag, IPC_MSG_NOTSUP );
491        return;
492    }
493
494    val   = tr_bencDictFind( dict, "directory" );
495    dir   = ( NULL == val || TYPE_STR != val->type ? NULL : val->val.s.s );
496    val   = tr_bencDictFind( dict, "autostart" );
497    start = ( NULL == val || TYPE_INT != val->type ? -1 :
498              ( val->val.i ? 1 : 0 ) );
499    val   = tr_bencDictFind( dict, "data" );
500    if( NULL != val && TYPE_STR == val->type )
501    {
502        /* XXX detect duplicates and return a message indicating so */
503        tor = torrent_add_data( ( uint8_t * )val->val.s.s, val->val.s.i,
504                                dir, start );
505    }
506    else
507    {
508        val = tr_bencDictFind( dict, "file" );
509        if( NULL == val || TYPE_STR != val->type )
510        {
511            msgresp( client, tag, IPC_MSG_NOTSUP );
512            return;
513        }
514        /* XXX detect duplicates and return a message indicating so */
515        tor = torrent_add_file( val->val.s.s, dir, start );
516    }
517
518    if( TORRENT_ID_VALID( tor ) )
519    {
520        val = ipc_initval( &client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
521        if( NULL == val )
522        {
523            errnomsg( "failed to build message" );
524            byebye( client->ev, EVBUFFER_EOF, NULL );
525            return;
526        }
527        inf = torrent_info( tor );
528        if( 0 > ipc_addinfo( val, tor, inf, 0 ) )
529        {
530            errnomsg( "failed to build message" );
531            tr_bencFree( &pk );
532            byebye( client->ev, EVBUFFER_EOF, NULL );
533            return;
534        }
535        buf = ipc_mkval( &pk, &buflen );
536        tr_bencFree( &pk );
537        queuemsg( client, buf, buflen );
538        free( buf );
539    }
540    else
541    {
542        msgresp( client, tag, IPC_MSG_FAIL );
543    }
544}
545
546void
547quitmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag UNUSED,
548         void * arg UNUSED )
549{
550    struct client * ii, * next;
551
552    torrent_exit( 0 );
553    gl_exiting = 1;
554
555    for( ii = RB_MIN( allclients, &gl_clients ); NULL != ii; ii = next )
556    {
557        next = RB_NEXT( allclients, &gl_clients, ii );
558        byebye( ii->ev, EVBUFFER_EOF, NULL );
559    }
560}
561
562void
563intmsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
564{
565    struct client * client = arg;
566    int             num;
567
568    if( NULL == val || TYPE_INT != val->type )
569    {
570        msgresp( client, tag, IPC_MSG_NOTSUP );
571        return;
572    }
573
574    num = MAX( INT_MIN, MIN( INT_MAX, val->val.i ) );
575    switch( id )
576    {
577        case IPC_MSG_AUTOMAP:
578            torrent_enable_port_mapping( num ? 1 : 0 );
579            break;
580        case IPC_MSG_AUTOSTART:
581            torrent_set_autostart( num ? 1 : 0 );
582            break;
583        case IPC_MSG_DOWNLIMIT:
584            torrent_set_downlimit( num );
585            break;
586        case IPC_MSG_PEX:
587            torrent_set_pex( num ? 1 : 0 );
588            break;
589        case IPC_MSG_PORT:
590            torrent_set_port( num );
591            break;
592        case IPC_MSG_UPLIMIT:
593            torrent_set_uplimit( num );
594            break;
595        default:
596            assert( 0 );
597            return;
598    }
599
600    msgresp( client, tag, IPC_MSG_OK );
601}
602
603void
604strmsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
605{
606    struct client * client = arg;
607
608    if( NULL == val || TYPE_STR != val->type )
609    {
610        msgresp( client, tag, IPC_MSG_NOTSUP );
611        return;
612    }
613
614    switch( id )
615    {
616        case IPC_MSG_DIR:
617            torrent_set_directory( val->val.s.s );
618            break;
619        default:
620            assert( 0 );
621            return;
622    }
623
624    msgresp( client, tag, IPC_MSG_OK );
625}
626
627void
628infomsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
629{
630    struct client * client = arg;
631    uint8_t       * buf;
632    size_t          buflen;
633    benc_val_t      pk, * pkinf, * typelist, * idlist, * idval;
634    int             all, types, ii, tor;
635    void          * iter;
636    enum ipc_msg    respid;
637    int         ( * addfunc )( benc_val_t *, int, int );
638
639    all = 0;
640    switch( id )
641    {
642        case IPC_MSG_GETINFOALL:
643            all = 1;
644            /* FALLTHROUGH; */
645        case IPC_MSG_GETINFO:
646            respid = IPC_MSG_INFO;
647            addfunc = addinfo;
648            break;
649        case IPC_MSG_GETSTATALL:
650            all = 1;
651            /* FALLTHROUGH */
652        case IPC_MSG_GETSTAT:
653            respid = IPC_MSG_STAT;
654            addfunc = addstat;
655            break;
656        default:
657            assert( 0 );
658            return;
659    }
660
661    /* initialize packet */
662    pkinf = ipc_initval( &client->ipc, respid, tag, &pk, TYPE_LIST );
663    if( NULL == pkinf )
664    {
665        errnomsg( "failed to build message" );
666        byebye( client->ev, EVBUFFER_EOF, NULL );
667        return;
668    }
669
670    /* add info/status for all torrents */
671    if( all )
672    {
673        if( NULL == val || TYPE_LIST != val->type )
674        {
675            msgresp( client, tag, IPC_MSG_NOTSUP );
676            tr_bencFree( &pk );
677            return;
678        }
679        types = ipc_infotypes( respid, val );
680        iter = NULL;
681        while( NULL != ( iter = torrent_iter( iter, &tor ) ) )
682        {
683            if( 0 > addfunc( pkinf, tor, types ) )
684            {
685                errnomsg( "failed to build message" );
686                tr_bencFree( &pk );
687                byebye( client->ev, EVBUFFER_EOF, NULL );
688                return;
689            }
690        }
691    }
692    /* add info/status for the requested IDs */
693    else
694    {
695        if( NULL == val || TYPE_DICT != val->type )
696        {
697            msgresp( client, tag, IPC_MSG_NOTSUP );
698            tr_bencFree( &pk );
699            return;
700        }
701        typelist = tr_bencDictFind( val, "type" );
702        idlist   = tr_bencDictFind( val, "id" );
703        if( NULL == typelist || TYPE_LIST != typelist->type ||
704            NULL == idlist   || TYPE_LIST != idlist->type )
705        {
706            msgresp( client, tag, IPC_MSG_NOTSUP );
707            tr_bencFree( &pk );
708            return;
709        }
710        types = ipc_infotypes( respid, typelist );
711        for( ii = 0; idlist->val.l.count > ii; ii++ )
712        {
713            idval = &idlist->val.l.vals[ii];
714            if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) )
715            {
716                continue;
717            }
718            tor = idval->val.i;
719            if( 0 > addfunc( pkinf, idval->val.i, types ) )
720            {
721                errnomsg( "failed to build message" );
722                tr_bencFree( &pk );
723                byebye( client->ev, EVBUFFER_EOF, NULL );
724                return;
725            }
726        }
727    }
728
729    /* generate packet data and send it */
730    buf = ipc_mkval( &pk, &buflen );
731    tr_bencFree( &pk );
732    queuemsg( client, buf, buflen );
733    free( buf );
734}
735
736int
737addinfo( benc_val_t * list, int id, int types )
738{
739    tr_info_t * inf;
740
741    inf = torrent_info( id );
742    if( NULL == inf )
743    {
744        return 0;
745    }
746
747    return ipc_addinfo( list, id, inf, types );
748}
749
750int
751addstat( benc_val_t * list, int id, int types )
752{
753    tr_stat_t * st;
754    tr_info_t * inf;
755
756    st = torrent_stat( id );
757    if( NULL == st )
758    {
759        return 0;
760    }
761    inf = torrent_info( id );
762    assert( NULL != inf );
763
764    return ipc_addstat( list, id, inf, st, types );
765}
766
767void
768tormsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
769{
770    struct client * client = arg;
771    benc_val_t    * idval;
772    int             ii, all;
773    void          * iter;
774    void        ( * func )( int );
775
776    all = 0;
777    switch( id )
778    {
779        case IPC_MSG_REMOVEALL:
780            all = 1;
781            /* FALLTHROUGH */
782        case IPC_MSG_REMOVE:
783            func = torrent_remove;
784            break;
785        case IPC_MSG_STARTALL:
786            all = 1;
787            /* FALLTHROUGH */
788        case IPC_MSG_START:
789            func = torrent_start;
790            break;
791        case IPC_MSG_STOPALL:
792            all = 1;
793            /* FALLTHROUGH */
794        case IPC_MSG_STOP:
795            func = torrent_stop;
796            break;
797        default:
798            assert( 0 );
799            return;
800    }
801
802    /* remove/start/stop all torrents */
803    if( all )
804    {
805        iter = NULL;
806        while( NULL != ( iter = torrent_iter( iter, &ii ) ) )
807        {
808            func( ii );
809        }
810    }
811    /* remove/start/stop requested list of torrents */
812    else
813    {
814        if( NULL == val || TYPE_LIST != val->type )
815        {
816            msgresp( client, tag, IPC_MSG_NOTSUP );
817            return;
818        }
819        for( ii = 0; val->val.l.count > ii; ii++ )
820        {
821            idval = &val->val.l.vals[ii];
822            if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) )
823            {
824                continue;
825            }
826            func( idval->val.i );
827        }
828    }
829
830    msgresp( client, tag, IPC_MSG_OK );
831}
832
833void
834lookmsg( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
835{
836    struct client * client = arg;
837    uint8_t       * buf;
838    size_t          buflen;
839    int             ii;
840    benc_val_t    * hash, pk, * pkinf;
841    int64_t         found;
842    tr_info_t     * inf;
843
844    if( NULL == val || TYPE_LIST != val->type )
845    {
846        msgresp( client, tag, IPC_MSG_NOTSUP );
847        return;
848    }
849
850    pkinf = ipc_initval( &client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
851    if( NULL == pkinf )
852    {
853        errnomsg( "failed to build message" );
854        byebye( client->ev, EVBUFFER_EOF, NULL );
855        return;
856    }
857
858    for( ii = 0; val->val.l.count > ii; ii++ )
859    {
860        hash = &val->val.l.vals[ii];
861        if( NULL == hash || TYPE_STR != hash->type ||
862            SHA_DIGEST_LENGTH * 2 != hash->val.s.i )
863        {
864            tr_bencFree( &pk );
865            msgresp( client, tag, IPC_MSG_NOTSUP );
866            return;
867        }
868        found = torrent_lookup( ( uint8_t * )hash->val.s.s );
869        if( !TORRENT_ID_VALID( found ) )
870        {
871            continue;
872        }
873        inf = torrent_info( found );
874        assert( NULL != inf );
875        if( 0 > ipc_addinfo( pkinf, found, inf, IPC_INF_HASH ) )
876        {
877            errnomsg( "failed to build message" );
878            tr_bencFree( &pk );
879            byebye( client->ev, EVBUFFER_EOF, NULL );
880            return;
881        }
882    }
883
884    buf = ipc_mkval( &pk, &buflen );
885    tr_bencFree( &pk );
886    queuemsg( client, buf, buflen );
887    free( buf );
888}
889
890void
891prefmsg( enum ipc_msg id, benc_val_t * val UNUSED, int64_t tag, void * arg )
892{
893    struct client * client = arg;
894    uint8_t       * buf;
895    size_t          buflen;
896
897    switch( id )
898    {
899        case IPC_MSG_GETAUTOMAP:
900            buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_AUTOMAP, tag,
901                             torrent_get_port_mapping() );
902            break;
903        case IPC_MSG_GETAUTOSTART:
904            buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_AUTOSTART, tag,
905                             torrent_get_autostart() );
906            break;
907        case IPC_MSG_GETDIR:
908            buf = ipc_mkstr( &client->ipc, &buflen, IPC_MSG_DIR, tag,
909                             torrent_get_directory() );
910            break;
911        case IPC_MSG_GETDOWNLIMIT:
912            buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_DOWNLIMIT, tag,
913                             torrent_get_downlimit() );
914            break;
915        case IPC_MSG_GETPEX:
916            buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_PEX, tag,
917                             torrent_get_pex() );
918            break;
919        case IPC_MSG_GETPORT:
920            buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_PORT, tag,
921                             torrent_get_port() );
922            break;
923        case IPC_MSG_GETUPLIMIT:
924            buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_UPLIMIT, tag,
925                             torrent_get_uplimit() );
926            break;
927        default:
928            assert( 0 );
929            return;
930    }
931
932    queuemsg( client, buf, buflen );
933    free( buf );
934}
935
936void
937supmsg( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
938{
939    struct client  * client = arg;
940    uint8_t        * buf;
941    size_t           buflen;
942    int              ii;
943    benc_val_t       pk, *pkval, * name;
944    enum ipc_msg     found;
945
946    if( NULL == val || TYPE_LIST != val->type )
947    {
948        msgresp( client, tag, IPC_MSG_NOTSUP );
949        return;
950    }
951
952    pkval = ipc_initval( &client->ipc, IPC_MSG_SUP, tag, &pk, TYPE_LIST );
953    if( NULL == pkval )
954    {
955        errnomsg( "failed to build message" );
956        byebye( client->ev, EVBUFFER_EOF, NULL );
957        return;
958    }
959    /* XXX look at other initval to make sure we free pk */
960    if( tr_bencListReserve( pkval, val->val.l.count ) )
961    {
962        errnomsg( "failed to build message" );
963        tr_bencFree( &pk );
964        byebye( client->ev, EVBUFFER_EOF, NULL );
965        return;
966    }
967
968    for( ii = 0; val->val.l.count > ii; ii++ )
969    {
970        name = &val->val.l.vals[ii];
971        if( NULL == name || TYPE_STR != name->type )
972        {
973            tr_bencFree( &pk );
974            msgresp( client, tag, IPC_MSG_NOTSUP );
975            return;
976        }
977        found = ipc_msgid( &client->ipc, name->val.s.s );
978        if( IPC__MSG_COUNT == found )
979        {
980            continue;
981        }
982        tr_bencInitStr( tr_bencListAdd( pkval ),
983                        name->val.s.s, name->val.s.i, 1 );
984    }
985
986    buf = ipc_mkval( &pk, &buflen );
987    tr_bencFree( &pk );
988    queuemsg( client, buf, buflen );
989    free( buf );
990}
Note: See TracBrowser for help on using the repository browser.