source: trunk/daemon/server.c @ 3577

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

Don't trip assertion when the daemon gets a fatal signal after a quit message is sent.

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