source: branches/daemon/daemon/server.c @ 1732

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

Give up on using event bases for now so we can build with libevent 1.1.

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