source: trunk/daemon/client.c @ 5142

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

make struct ipc_info opaque. const-ify the ipcparse functions where possible.

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