source: trunk/daemon/server.c @ 2818

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

Shut down ipc server on fatal signal, not just torrent client.

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