source: trunk/daemon/client.c @ 1926

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

Rename ipc to ipcparse

  • Property svn:keywords set to Date Rev Author Id
File size: 24.1 KB
Line 
1/******************************************************************************
2 * $Id: client.c 1926 2007-05-24 04:05:50Z 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 <limits.h>
33#include <stdarg.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38#include "bsdqueue.h"
39#include "bsdtree.h"
40#include "client.h"
41#include "bencode.h"
42#include "errors.h"
43#include "ipcparse.h"
44#include "misc.h"
45#include "trcompat.h"
46
47/* time out server after this many seconds */
48#define SERVER_TIMEOUT          ( 15 )
49
50struct con
51{
52    int                  infd;
53    int                  outfd;
54    struct ipc_info      ipc;
55    struct bufferevent * evin;
56    struct bufferevent * evout;
57};
58
59struct req
60{
61    enum ipc_msg       id;
62    int64_t            tag;
63    struct strlist   * strs;
64    int64_t            num;
65    char             * str;
66    size_t             listlen;
67    int64_t          * numlist;
68    uint8_t          * buf;
69    int                types;
70    SLIST_ENTRY( req ) next;
71};
72
73SLIST_HEAD( reqlist, req );
74
75struct resp
76{
77    int64_t            tag;
78    cl_infofunc        infocb;
79    cl_statfunc        statcb;
80    RB_ENTRY( resp )   links;
81};
82
83RB_HEAD( resptree, resp );
84
85static struct req * addreq   ( enum ipc_msg, int64_t, struct resp ** );
86static int     addintlistreq ( enum ipc_msg, size_t, const int * );
87static void    noop          ( struct bufferevent *, void * );
88static void    noway         ( struct bufferevent *, void * );
89static void    didwrite      ( struct bufferevent *, void * );
90static void    ohshit        ( struct bufferevent *, short, void * );
91static void    canread       ( struct bufferevent *, void * );
92static void    flushreqs     ( struct con * );
93static int     sendvers      ( struct con * );
94static void    infomsg       ( enum ipc_msg, benc_val_t *, int64_t, void * );
95static void    statmsg       ( enum ipc_msg, benc_val_t *, int64_t, void * );
96static void    defmsg        ( enum ipc_msg, benc_val_t *, int64_t, void * );
97static void    cbdone        ( struct resp * );
98static int64_t getinfoint    ( enum ipc_msg, benc_val_t *, int, int64_t );
99static char *  getinfostr    ( enum ipc_msg, benc_val_t *, int, char * );
100static int     resptagcmp    ( struct resp *, struct resp * );
101
102RB_GENERATE_STATIC( resptree, resp, links, resptagcmp )
103INTCMP_FUNC( resptagcmp, resp, tag )
104
105static struct event_base * gl_base   = NULL;
106static struct ipc_funcs  * gl_tree   = NULL;
107static struct reqlist      gl_reqs   = SLIST_HEAD_INITIALIZER( &gl_reqs );
108static struct resptree     gl_resps  = RB_INITIALIZER( &gl_resps );
109static int64_t             gl_tag    = 0;
110static int                 gl_proxy  = -1;
111
112int
113client_init( struct event_base * base )
114{
115    assert( NULL == gl_base && NULL == gl_tree );
116    gl_base   = base;
117    gl_tree   = ipc_initmsgs();
118    if( NULL == gl_tree )
119    {
120        return -1;
121    }
122
123    if( 0 > ipc_addmsg( gl_tree, IPC_MSG_INFO, infomsg ) ||
124        0 > ipc_addmsg( gl_tree, IPC_MSG_STAT, statmsg ) )
125    {
126        return -1;
127    }
128
129    ipc_setdefmsg( gl_tree, defmsg );
130
131    return 0;
132}
133
134int
135client_new_sock( const char * path )
136{
137    struct sockaddr_un sun;
138    int                fd;
139    struct con       * con;
140
141    assert( NULL != gl_base );
142    assert( 0 > gl_proxy );
143    assert( NULL != path );
144
145    gl_proxy = 0;
146
147    bzero( &sun, sizeof sun );
148    sun.sun_family = AF_LOCAL;
149    strlcpy( sun.sun_path, path, sizeof sun.sun_path );
150
151    fd = socket( AF_UNIX, SOCK_STREAM, 0 );
152    if( 0 > fd )
153    {
154        errnomsg( "failed to create socket" );
155        return -1;
156    }
157
158    if( 0 > connect( fd, ( struct sockaddr * )&sun, SUN_LEN( &sun ) ) )
159    {
160        errnomsg( "failed to connect to socket file: %s", path );
161        close( fd );
162        return -1;
163    }
164    con = calloc( 1, sizeof *con );
165    if( NULL == con )
166    {
167        mallocmsg( sizeof *con );
168        close( fd );
169        return -1;
170    }
171    ipc_newcon( &con->ipc, gl_tree );
172    con->infd = fd;
173    con->evin = bufferevent_new( fd, canread, didwrite, ohshit, con );
174    if( NULL == con->evin )
175    {
176        mallocmsg( -1 );
177        close( fd );
178        free( con );
179        return -1;
180    }
181    con->outfd = con->infd;
182    con->evout = con->evin;
183    /* XXX bufferevent_base_set( gl_base, con->evin ); */
184    bufferevent_settimeout( con->evin, SERVER_TIMEOUT, SERVER_TIMEOUT );
185    bufferevent_enable( con->evin, EV_READ );
186    if( 0 > sendvers( con ) )
187    {
188        exit( 1 );
189    }
190
191    return 0;
192}
193
194int
195client_new_cmd( char * const * cmd )
196{
197    struct con * con;
198    int          tocmd[2], fromcmd[2];
199    pid_t        kid;
200
201    assert( NULL != gl_base );
202    assert( 0 > gl_proxy );
203    assert( NULL != cmd && NULL != cmd[0] );
204
205    gl_proxy = 1;
206
207    if( 0 > pipe( tocmd ) )
208    {
209        errnomsg( "failed to create pipe" );
210        return -1;
211    }
212
213    if( 0 > pipe( fromcmd ) )
214    {
215        errnomsg( "failed to create pipe" );
216        close( tocmd[0] );
217        close( tocmd[1] );
218        return -1;
219    }
220
221    kid = fork();
222    if( 0 > kid )
223    {
224        close( tocmd[0] );
225        close( tocmd[1] );
226        close( fromcmd[0] );
227        close( fromcmd[1] );
228        return -1;
229    }
230    else if( 0 == kid )
231    {
232        if( 0 > dup2( tocmd[0], STDIN_FILENO ) ||
233            0 > dup2( fromcmd[1], STDOUT_FILENO ) )
234        {
235            errnomsg( "failed to duplicate descriptors" );
236            _exit( 1 );
237        }
238        close( tocmd[0] );
239        close( tocmd[1] );
240        close( fromcmd[0] );
241        close( fromcmd[1] );
242        execvp( cmd[0], cmd );
243        errnomsg( "failed to execute: %s", cmd[0] );
244        _exit( 1 );
245    }
246
247    close( tocmd[0] );
248    close( fromcmd[1] );
249
250    con = calloc( 1, sizeof *con );
251    if( NULL == con )
252    {
253        mallocmsg( sizeof *con );
254        close( tocmd[1] );
255        close( fromcmd[0] );
256        return -1;
257    }
258
259    con->infd = fromcmd[0];
260    con->evin = bufferevent_new( con->infd, canread, noop, ohshit, con );
261    if( NULL == con->evin )
262    {
263        free( con );
264        close( tocmd[1] );
265        close( fromcmd[0] );
266        return -1;
267    }
268    /* XXX bufferevent_base_set( gl_base, con->evin ); */
269    bufferevent_settimeout( con->evin, SERVER_TIMEOUT, SERVER_TIMEOUT );
270    bufferevent_enable( con->evin, EV_READ );
271
272    con->outfd = tocmd[1];
273    con->evout = bufferevent_new( con->outfd, noway, didwrite, ohshit, con );
274    if( NULL == con->evout )
275    {
276        bufferevent_free( con->evin );
277        free( con );
278        close( tocmd[1] );
279        close( fromcmd[0] );
280        return -1;
281    }
282    /* XXX bufferevent_base_set( gl_base, con->evout ); */
283    bufferevent_settimeout( con->evout, SERVER_TIMEOUT, SERVER_TIMEOUT );
284    bufferevent_enable( con->evout, EV_READ );
285
286    ipc_newcon( &con->ipc, gl_tree );
287    if( 0 > sendvers( con ) )
288    {
289        exit( 1 );
290    }
291
292    return 0;
293}
294
295struct req *
296addreq( enum ipc_msg id, int64_t tag, struct resp ** resp )
297{
298    struct req * req;
299
300    assert( ( 0 < tag && NULL != resp ) || ( 0 >= tag && NULL == resp ) );
301
302    req = calloc( 1, sizeof *req );
303    if( NULL == req )
304    {
305        mallocmsg( sizeof *req );
306        return NULL;
307    }
308
309    if( NULL != resp )
310    {
311        *resp = calloc( 1, sizeof **resp );
312        if( NULL == *resp )
313        {
314            mallocmsg( sizeof **resp );
315            free( req );
316            return NULL;
317        }
318        (*resp)->tag = tag;
319        RB_INSERT( resptree, &gl_resps, *resp );
320    }
321
322    req->id  = id;
323    req->tag = tag;
324    SLIST_INSERT_HEAD( &gl_reqs, req, next );
325
326    return req;
327}
328
329int
330client_quit( void )
331{
332    return ( NULL == addreq( IPC_MSG_QUIT, -1, NULL ) ? -1 : 0 );
333}
334
335int
336client_addfiles( struct strlist * list )
337{
338    struct stritem * ii;
339    uint8_t        * buf;
340    size_t           size;
341    struct req     * req;
342
343    if( gl_proxy )
344    {
345        SLIST_FOREACH( ii, list, next )
346        {
347            buf = readfile( ii->str, &size );
348            req = addreq( IPC_MSG_ADDONEFILE, -1, NULL );
349            if( NULL == req )
350            {
351                free( buf );
352                return -1;
353            }
354            req->buf     = buf;
355            req->listlen = size;
356        }
357    }
358    else
359    {
360        req = addreq( IPC_MSG_ADDMANYFILES, -1, NULL );
361        if( NULL == req )
362        {
363            return -1;
364        }
365
366        /* XXX need to move arg parsing back here or something */
367        req->strs = list;
368    }
369
370    return 0;
371}
372
373int
374client_automap( int automap )
375{
376    struct req * req;
377
378    req = addreq( IPC_MSG_AUTOMAP, -1, NULL );
379    if( NULL == req )
380    {
381        return -1;
382    }
383
384    req->num = ( automap ? 1 : 0 );
385
386    return 0;
387}
388
389int
390client_pex( int pex )
391{
392    struct req * req;
393
394    req = addreq( IPC_MSG_PEX, -1, NULL );
395    if( NULL == req )
396    {
397        return -1;
398    }
399
400    req->num = ( pex ? 1 : 0 );
401
402    return 0;
403}
404
405int
406client_port( int port )
407{
408    struct req * req;
409
410    req = addreq( IPC_MSG_PORT, -1, NULL );
411    if( NULL == req )
412    {
413        return -1;
414    }
415
416    req->num = port;
417
418    return 0;
419}
420
421int
422client_downlimit( int limit )
423{
424    struct req * req;
425
426    req = addreq( IPC_MSG_DOWNLIMIT, -1, NULL );
427    if( NULL == req )
428    {
429        return -1;
430    }
431
432    req->num = ( 0 > limit ? -1 : limit );
433
434    return 0;
435}
436
437int
438client_uplimit( int limit )
439{
440    struct req * req;
441
442    req = addreq( IPC_MSG_UPLIMIT, -1, NULL );
443    if( NULL == req )
444    {
445        return -1;
446    }
447
448    req->num = ( 0 > limit ? -1 : limit );
449
450    return 0;
451}
452
453int
454client_dir( const char * dir )
455{
456    struct req * req;
457    char       * dircpy;
458
459    dircpy = strdup( dir );
460    if( NULL == dircpy )
461    {
462        mallocmsg( strlen( dir ) );
463        return -1;
464    }
465
466    req = addreq( IPC_MSG_DIR, -1, NULL );
467    if( NULL == req )
468    {
469        free( dircpy );
470        return -1;
471    }
472
473    req->str = dircpy;
474
475    return 0;
476}
477
478int
479addintlistreq( enum ipc_msg which, size_t len, const int * list )
480{
481    struct req * req;
482    int64_t    * duplist;
483    size_t       ii;
484
485    assert( ( 0 == len && NULL == list ) || ( 0 < len && NULL != list ) );
486
487    duplist = NULL;
488    if( NULL != list )
489    {
490        duplist = calloc( len, sizeof duplist[0] );
491        if( NULL == duplist )
492        {
493            mallocmsg( len * sizeof( duplist[0] ) );
494            return -1;
495        }
496    }
497
498    req = addreq( which, -1, NULL );
499    if( NULL == req )
500    {
501        free( duplist );
502        return -1;
503    }
504
505    for( ii = 0; len > ii; ii++ )
506    {
507        duplist[ii] = list[ii];
508    }
509    req->listlen = len;
510    req->numlist = duplist;
511
512    return 0;
513}
514
515int
516client_start( size_t len, const int * list )
517{
518    enum ipc_msg id;
519
520    id = ( NULL == list ? IPC_MSG_STARTALL : IPC_MSG_START );
521
522    return addintlistreq( id, len, list );
523}
524
525int
526client_stop( size_t len, const int * list )
527{
528    enum ipc_msg id;
529
530    id = ( NULL == list ? IPC_MSG_STOPALL : IPC_MSG_STOP );
531
532    return addintlistreq( id, len, list );
533}
534
535int
536client_remove( size_t len, const int * list )
537{
538    enum ipc_msg id;
539
540    id = ( NULL == list ? IPC_MSG_REMOVEALL : IPC_MSG_REMOVE );
541
542    return addintlistreq( id, len, list );
543}
544
545int
546client_list( cl_infofunc func )
547{
548    struct req  * req;
549    struct resp * resp;
550
551    req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp );
552    if( NULL == req )
553    {
554        return -1;
555    }
556
557    resp->infocb = func;
558    req->types   = IPC_INF_NAME | IPC_INF_HASH;
559
560    return 0;
561}
562
563int
564client_info( cl_infofunc func )
565{
566    struct req  * req;
567    struct resp * resp;
568
569    req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp );
570    if( NULL == req )
571    {
572        return -1;
573    }
574
575    resp->infocb = func;
576    req->types   = IPC_INF_NAME | IPC_INF_HASH | IPC_INF_SIZE;
577
578    return 0;
579}
580
581int
582client_hashids( cl_infofunc func )
583{
584    struct req  * req;
585    struct resp * resp;
586
587    req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp );
588    if( NULL == req )
589    {
590        return -1;
591    }
592
593    resp->infocb = func;
594    req->types   = IPC_INF_HASH;
595
596    return 0;
597}
598
599int
600client_status( cl_statfunc func )
601{
602    struct req  * req;
603    struct resp * resp;
604
605    req = addreq( IPC_MSG_GETSTATALL, ++gl_tag, &resp );
606    if( NULL == req )
607    {
608        return -1;
609    }
610
611    resp->statcb = func;
612    req->types   = IPC_ST_STATE | IPC_ST_ETA | IPC_ST_COMPLETED |
613        IPC_ST_DOWNSPEED | IPC_ST_UPSPEED | IPC_ST_DOWNTOTAL | IPC_ST_UPTOTAL |
614        IPC_ST_ERROR | IPC_ST_ERRMSG;
615
616    return 0;
617}
618
619void
620noop( struct bufferevent * ev UNUSED, void * arg UNUSED )
621{
622    /* libevent prior to 1.2 couldn't handle a NULL write callback */
623}
624
625void
626noway( struct bufferevent * evin, void * arg UNUSED )
627{
628    /* this shouldn't happen, but let's drain the buffer anyway */
629    evbuffer_drain( EVBUFFER_INPUT( evin ),
630                    EVBUFFER_LENGTH( EVBUFFER_INPUT( evin ) ) );
631}
632
633void
634didwrite( struct bufferevent * evout, void * arg )
635{
636    struct con * con = arg;
637
638    assert( evout == con->evout );
639    flushreqs( con );
640}
641
642void
643ohshit( struct bufferevent * ev UNUSED, short what, void * arg UNUSED )
644{
645    if( EVBUFFER_EOF & what )
646    {
647        errmsg( "server closed connection" );
648    }
649    else if( EVBUFFER_TIMEOUT & what )
650    {
651        errmsg( "server connection timed out" );
652    }
653    else if( EVBUFFER_READ & what )
654    {
655        errmsg( "read error on server connection" );
656    }
657    else if( EVBUFFER_WRITE & what )
658    {
659        errmsg( "write error on server connection" );
660    }
661    else if( EVBUFFER_ERROR & what )
662    {
663        errmsg( "error on server connection" );
664    }
665    else
666    {
667        errmsg( "unknown error on server connection: 0x%x", what );
668    }
669    exit( 1 );
670}
671
672void
673canread( struct bufferevent * evin, void * arg )
674{
675    struct con * con = arg;
676    uint8_t    * buf;
677    size_t       len;
678    ssize_t      res;
679
680    assert( evin == con->evin );
681    buf = EVBUFFER_DATA( EVBUFFER_INPUT( evin ) );
682    len = EVBUFFER_LENGTH( EVBUFFER_INPUT( evin ) );
683
684    if( IPC_MIN_MSG_LEN > len )
685    {
686        return;
687    }
688
689    res = ipc_parse( &con->ipc, buf, len, con );
690    if( 0 > res )
691    {
692        switch( errno )
693        {
694            case EPERM:
695                errmsg( "unsupported protocol version" );
696                break;
697            case EINVAL:
698                errmsg( "protocol parse error" );
699                break;
700            default:
701                errnomsg( "parsing failed" );
702                break;
703        }
704        exit( 1 );
705    }
706
707    if( 0 < res )
708    {
709        evbuffer_drain( EVBUFFER_INPUT( evin ), res );
710        flushreqs( con );
711    }
712}
713
714void
715flushreqs( struct con * con )
716{
717    struct req     * req;
718    uint8_t        * buf;
719    size_t           buflen, ii;
720    benc_val_t       pk, * val;
721    struct stritem * jj;
722
723    if( !HASVERS( &con->ipc ) )
724    {
725        return;
726    }
727
728    if( SLIST_EMPTY( &gl_reqs ) && RB_EMPTY( &gl_resps ) )
729    {
730        exit( 0 );
731    }
732
733    while( !SLIST_EMPTY( &gl_reqs ) )
734    {
735        req = SLIST_FIRST( &gl_reqs );
736        SLIST_REMOVE_HEAD( &gl_reqs, next );
737        buf = NULL;
738        switch( req->id )
739        {
740            case IPC_MSG_QUIT:
741            case IPC_MSG_STARTALL:
742            case IPC_MSG_STOPALL:
743            case IPC_MSG_REMOVEALL:
744                buf = ipc_mkempty( &con->ipc, &buflen, req->id, req->tag );
745                break;
746            case IPC_MSG_ADDMANYFILES:
747                ii = 0;
748                SLIST_FOREACH( jj, req->strs, next )
749                {
750                    ii++;
751                }
752                val = ipc_initval( &con->ipc, req->id, -1, &pk, TYPE_LIST );
753                if( NULL != val && !tr_bencListReserve( val, ii ) )
754                {
755                    SLIST_FOREACH( jj, req->strs, next )
756                    {
757                        tr_bencInitStr( tr_bencListAdd( val ),
758                                        jj->str, -1, 1 );
759                    }
760                    buf = ipc_mkval( &pk, &buflen );
761                    SAFEBENCFREE( &pk );
762                }
763                SAFEFREESTRLIST( req->strs );
764                break;
765            case IPC_MSG_ADDONEFILE:
766                val = ipc_initval( &con->ipc, req->id, -1, &pk, TYPE_DICT );
767                if( NULL != val && !tr_bencDictReserve( val, 1 ) )
768                {
769                    tr_bencInitStr( tr_bencDictAdd( val, "data" ),
770                                    req->buf, req->listlen, 1 );
771                    buf = ipc_mkval( &pk, &buflen );
772                    SAFEBENCFREE( &pk );
773                }
774                SAFEFREE( req->buf );
775                break;
776            case IPC_MSG_AUTOMAP:
777            case IPC_MSG_PORT:
778            case IPC_MSG_DOWNLIMIT:
779            case IPC_MSG_UPLIMIT:
780            case IPC_MSG_PEX:
781                buf = ipc_mkint( &con->ipc, &buflen, req->id, -1, req->num );
782                break;
783            case IPC_MSG_DIR:
784                buf = ipc_mkstr( &con->ipc, &buflen, req->id, -1, req->str );
785                SAFEFREE( req->str );
786                break;
787            case IPC_MSG_START:
788            case IPC_MSG_STOP:
789            case IPC_MSG_REMOVE:
790                val = ipc_initval( &con->ipc, req->id, -1, &pk, TYPE_LIST );
791                if( NULL != val && !tr_bencListReserve( val, req->listlen ) )
792                {
793                    for( ii = 0; ii < req->listlen; ii++ )
794                    {
795                        tr_bencInitInt( tr_bencListAdd( val ),
796                                        req->numlist[ii] );
797                    }
798                    buf = ipc_mkval( &pk, &buflen );
799                    SAFEBENCFREE( &pk );
800                }
801                SAFEFREE( req->numlist );
802                break;
803            case IPC_MSG_GETINFOALL:
804            case IPC_MSG_GETSTATALL:
805                buf = ipc_mkgetinfo( &con->ipc, &buflen, req->id, req->tag,
806                                     req->types, NULL );
807                break;
808            default:
809                assert( 0 );
810                return;
811        }
812
813        SAFEFREE( req );
814        if( NULL == buf )
815        {
816            if( EPERM == errno )
817            {
818                errmsg( "message not supported by server" );
819            }
820            else
821            {
822                errnomsg( "failed to create message" );
823            }
824            exit( 1 );
825        }
826        if( 0 > bufferevent_write( con->evout, buf, buflen ) )
827        {
828            errmsg( "failed to buffer %zd bytes of data for write", buflen );
829            exit( 1 );
830        }
831        free( buf );
832    }
833}
834
835int
836sendvers( struct con * con )
837{
838    uint8_t   * buf;
839    size_t      len;
840
841    buf = ipc_mkvers( &len );
842    if( NULL == buf )
843    {
844        if( EPERM == errno )
845        {
846            errmsg( "message not supported by server" );
847        }
848        else
849        {
850            errnomsg( "failed to create message" );
851        }
852        return -1;
853    }
854
855    if( 0 > bufferevent_write( con->evout, buf, len ) )
856    {
857        free( buf );
858        errmsg( "failed to buffer %i bytes of data for write", ( int )len );
859        return -1;
860    }
861
862    free( buf );
863
864    return 0;
865}
866
867void
868infomsg( enum ipc_msg msgid, benc_val_t * list, int64_t tag,
869         void * arg UNUSED )
870{
871    benc_val_t   * dict;
872    int            ii;
873    struct cl_info inf;
874    int64_t        id;
875    struct resp  * resp, key;
876
877    assert( IPC_MSG_INFO == msgid );
878
879    if( TYPE_LIST != list->type )
880    {
881        return;
882    }
883
884    bzero( &key, sizeof key );
885    key.tag = tag;
886    resp = RB_FIND( resptree, &gl_resps, &key );
887    if( NULL == resp || NULL == resp->infocb )
888    {
889        return;
890    }
891    RB_REMOVE( resptree, &gl_resps, resp );
892
893    for( ii = 0; list->val.l.count > ii; ii++ )
894    {
895        dict = &list->val.l.vals[ii];
896        if( TYPE_DICT != dict->type )
897        {
898            continue;
899        }
900
901        id       = getinfoint( msgid, dict, IPC_INF_ID,   -1   );
902        inf.name = getinfostr( msgid, dict, IPC_INF_NAME, NULL );
903        inf.hash = getinfostr( msgid, dict, IPC_INF_HASH, NULL );
904        inf.size = getinfoint( msgid, dict, IPC_INF_SIZE, -1   );
905
906        if( !TORRENT_ID_VALID( id ) )
907        {
908            continue;
909        }
910
911        inf.id = id;
912        resp->infocb( &inf );
913    }
914
915    cbdone( resp );
916    free( resp );
917}
918
919void
920statmsg( enum ipc_msg msgid, benc_val_t * list, int64_t tag,
921         void * arg UNUSED )
922{
923    benc_val_t   * dict;
924    int            ii;
925    int64_t        id;
926    struct cl_stat st;
927    struct resp  * resp, key;
928
929    assert( IPC_MSG_STAT == msgid );
930
931    if( TYPE_LIST != list->type )
932    {
933        return;
934    }
935
936    bzero( &key, sizeof key );
937    key.tag = tag;
938    resp = RB_FIND( resptree, &gl_resps, &key );
939    if( NULL == resp || NULL == resp->statcb )
940    {
941        return;
942    }
943    RB_REMOVE( resptree, &gl_resps, resp );
944
945    for( ii = 0; list->val.l.count > ii; ii++ )
946    {
947        dict   = &list->val.l.vals[ii];
948        if( TYPE_DICT != dict->type )
949        {
950            continue;
951        }
952
953        id           = getinfoint( msgid, dict, IPC_ST_ID,        -1   );
954        st.state     = getinfostr( msgid, dict, IPC_ST_STATE,     NULL );
955        st.eta       = getinfoint( msgid, dict, IPC_ST_ETA,       -1   );
956        st.done      = getinfoint( msgid, dict, IPC_ST_COMPLETED, -1   );
957        st.ratedown  = getinfoint( msgid, dict, IPC_ST_DOWNSPEED, -1   );
958        st.rateup    = getinfoint( msgid, dict, IPC_ST_UPSPEED,   -1   );
959        st.totaldown = getinfoint( msgid, dict, IPC_ST_DOWNTOTAL, -1   );
960        st.totalup   = getinfoint( msgid, dict, IPC_ST_UPTOTAL,   -1   );
961        st.error     = getinfostr( msgid, dict, IPC_ST_ERROR,     NULL );
962        st.errmsg    = getinfostr( msgid, dict, IPC_ST_ERRMSG,    NULL );
963
964        if( !TORRENT_ID_VALID( id ) )
965        {
966            continue;
967        }
968
969        st.id = id;
970        resp->statcb( &st );
971    }
972
973    cbdone( resp );
974    free( resp );
975}
976
977void
978defmsg( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg UNUSED )
979{
980    struct resp * resp, key;
981
982    switch( msgid )
983    {
984        case IPC_MSG_FAIL:
985            if( TYPE_STR == val->type && NULL != val->val.s.s )
986            {
987                errmsg( "request failed: %s", val->val.s.s );
988            }
989            else
990            {
991                errmsg( "request failed" );
992            }
993            break;
994        case IPC_MSG_NOTSUP:
995            errmsg( "request message not supported" );
996            break;
997        default:
998            break;
999    }
1000
1001    bzero( &key, sizeof key );
1002    key.tag = tag;
1003    resp = RB_FIND( resptree, &gl_resps, &key );
1004    if( NULL == resp )
1005    {
1006        return;
1007    }
1008    RB_REMOVE( resptree, &gl_resps, resp );
1009
1010    cbdone( resp );
1011    free( resp );
1012}
1013
1014void
1015cbdone( struct resp * resp )
1016{
1017    if( NULL != resp->infocb )
1018    {
1019        resp->infocb( NULL );
1020    }
1021    else if( NULL != resp->statcb )
1022    {
1023        resp->statcb( NULL );
1024    }
1025}
1026
1027int64_t
1028getinfoint( enum ipc_msg msgid, benc_val_t * dict, int type, int64_t defval  )
1029{
1030    benc_val_t * val;
1031
1032    val = tr_bencDictFind( dict, ipc_infoname( msgid, type ) );
1033
1034    if( NULL != val && TYPE_INT == val->type )
1035    {
1036        return val->val.i;
1037    }
1038
1039    return defval;
1040}
1041
1042char *
1043getinfostr( enum ipc_msg msgid, benc_val_t * dict, int type, char * defval  )
1044{
1045    benc_val_t * val;
1046
1047    val = tr_bencDictFind( dict, ipc_infoname( msgid, type ) );
1048
1049    if( NULL != val && TYPE_STR == val->type )
1050    {
1051        return val->val.s.s ;
1052    }
1053
1054    return defval;
1055}
Note: See TracBrowser for help on using the repository browser.