source: trunk/daemon/client.c @ 3175

Last change on this file since 3175 was 3175, checked in by charles, 15 years ago

add bsdqueue.h to daemon dir

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