source: trunk/daemon/client.c @ 2227

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

Add client/server name to IPC version handshake.

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