source: trunk/daemon/client.c @ 5137

Last change on this file since 5137 was 5137, checked in by charles, 14 years ago

tear out about 1/3 of ipcparse's overhead.

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