source: branches/daemon/daemon/client.c @ 1623

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

Add a daemon frontend.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.1 KB
Line 
1/******************************************************************************
2 * $Id: client.c 1617 2007-03-31 20:00:40Z 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 "ipc.h"
44#include "misc.h"
45
46/* time out server after this many seconds */
47#define SERVER_TIMEOUT          ( 15 )
48
49struct con
50{
51    int                  fd;
52    struct ipc_info      ipc;
53    struct bufferevent * ev;
54};
55
56struct req
57{
58    enum ipc_msg       id;
59    int64_t            tag;
60    struct strlist   * strs;
61    int64_t            num;
62    char             * str;
63    size_t             listlen;
64    int64_t          * numlist;
65    int                types;
66    SLIST_ENTRY( req ) next;
67};
68
69SLIST_HEAD( reqlist, req );
70
71struct resp
72{
73    int64_t            tag;
74    cl_listfunc        listcb;
75    cl_infofunc        infocb;
76    cl_hashfunc        hashcb;
77    cl_statfunc        statcb;
78    RB_ENTRY( resp )   links;
79};
80
81RB_HEAD( resptree, resp );
82
83static struct req * addreq   ( enum ipc_msg, int64_t, struct resp ** );
84static int     addintlistreq ( enum ipc_msg, size_t, const int * );
85static void    didwrite      ( struct bufferevent *, void * );
86static void    ohshit        ( struct bufferevent *, short, void * );
87static void    canread       ( struct bufferevent *, void * );
88static void    flushreqs     ( struct con * );
89static int     sendvers      ( struct bufferevent * );
90static void    infomsg       ( enum ipc_msg, benc_val_t *, int64_t, void * );
91static void    statmsg       ( enum ipc_msg, benc_val_t *, int64_t, void * );
92static void    defmsg        ( enum ipc_msg, benc_val_t *, int64_t, void * );
93static void    cbdone        ( struct resp * );
94static int64_t getinfoint    ( enum ipc_msg, benc_val_t *, int, int64_t );
95static char *  getinfostr    ( enum ipc_msg, benc_val_t *, int, char * );
96static int     resptagcmp    ( struct resp *, struct resp * );
97
98RB_GENERATE_STATIC( resptree, resp, links, resptagcmp )
99INTCMP_FUNC( resptagcmp, resp, tag )
100
101static struct event_base * gl_base   = NULL;
102static struct ipc_funcs  * gl_tree   = NULL;
103static struct reqlist      gl_reqs   = SLIST_HEAD_INITIALIZER( &gl_reqs );
104static struct resptree     gl_resps  = RB_INITIALIZER( &gl_resps );
105static int64_t             gl_tag    = 0;
106
107int
108client_init( struct event_base * base )
109{
110    assert( NULL == gl_base && NULL == gl_tree );
111    gl_base   = base;
112    gl_tree   = ipc_initmsgs();
113    if( NULL == gl_tree )
114    {
115        return -1;
116    }
117
118    if( 0 > ipc_addmsg( gl_tree, IPC_MSG_INFO, infomsg ) ||
119        0 > ipc_addmsg( gl_tree, IPC_MSG_STAT, statmsg ) )
120    {
121        return -1;
122    }
123
124    ipc_setdefmsg( gl_tree, defmsg );
125
126    return 0;
127}
128
129int
130client_connect( const char * path )
131{
132    struct sockaddr_un sun;
133    int                fd;
134    struct con       * con;
135
136    assert( NULL != gl_base );
137    assert( NULL != path );
138
139    bzero( &sun, sizeof sun );
140    sun.sun_family = AF_LOCAL;
141    strlcpy( sun.sun_path, path, sizeof sun.sun_path );
142
143    fd = socket( AF_UNIX, SOCK_STREAM, 0 );
144    if( 0 > fd )
145    {
146        errnomsg( "failed to create socket" );
147        return -1;
148    }
149
150    if( 0 > connect( fd, ( struct sockaddr * )&sun, SUN_LEN( &sun ) ) )
151    {
152        errnomsg( "failed to connect to socket file: %s", path );
153        close( fd );
154        return -1;
155    }
156    con = calloc( 1, sizeof *con );
157    if( NULL == con )
158    {
159        mallocmsg( sizeof *con );
160        close( fd );
161        return -1;
162    }
163    ipc_newcon( &con->ipc, gl_tree );
164    con->fd   = fd;
165    con->ev   = bufferevent_new( fd, canread, didwrite, ohshit, con );
166    if( NULL == con->ev )
167    {
168        mallocmsg( -1 );
169        close( fd );
170        free( con );
171        return -1;
172    }
173    bufferevent_base_set( gl_base, con->ev );
174    bufferevent_settimeout( con->ev, SERVER_TIMEOUT, SERVER_TIMEOUT );
175    bufferevent_enable( con->ev, EV_READ );
176    if( 0 > sendvers( con->ev ) )
177    {
178        exit( 1 );
179    }
180
181    return 0;
182}
183
184struct req *
185addreq( enum ipc_msg id, int64_t tag, struct resp ** resp )
186{
187    struct req * req;
188
189    assert( ( 0 < tag && NULL != resp ) || ( 0 >= tag && NULL == resp ) );
190
191    req = calloc( 1, sizeof *req );
192    if( NULL == req )
193    {
194        mallocmsg( sizeof *req );
195        return NULL;
196    }
197
198    if( NULL != resp )
199    {
200        *resp = calloc( 1, sizeof **resp );
201        if( NULL == *resp )
202        {
203            mallocmsg( sizeof **resp );
204            free( req );
205            return NULL;
206        }
207        (*resp)->tag = tag;
208        RB_INSERT( resptree, &gl_resps, *resp );
209    }
210
211    req->id  = id;
212    req->tag = tag;
213    SLIST_INSERT_HEAD( &gl_reqs, req, next );
214
215    return req;
216}
217
218int
219client_quit( void )
220{
221    return ( NULL == addreq( IPC_MSG_QUIT, -1, NULL ) ? -1 : 0 );
222}
223
224int
225client_addfiles( struct strlist * list )
226{
227    struct req * req;
228
229    req = addreq( IPC_MSG_ADDFILES, -1, NULL );
230    if( NULL == req )
231    {
232        return -1;
233    }
234
235    /* XXX need to move arg parsing back here or something */
236    req->strs = list;
237
238    return 0;
239}
240
241int
242client_automap( int automap )
243{
244    struct req * req;
245
246    req = addreq( IPC_MSG_AUTOMAP, -1, NULL );
247    if( NULL == req )
248    {
249        return -1;
250    }
251
252    req->num = ( automap ? 1 : 0 );
253
254    return 0;
255}
256
257int
258client_port( int port )
259{
260    struct req * req;
261
262    req = addreq( IPC_MSG_PORT, -1, NULL );
263    if( NULL == req )
264    {
265        return -1;
266    }
267
268    req->num = port;
269
270    return 0;
271}
272
273int
274client_downlimit( int limit )
275{
276    struct req * req;
277
278    req = addreq( IPC_MSG_DOWNLIMIT, -1, NULL );
279    if( NULL == req )
280    {
281        return -1;
282    }
283
284    req->num = ( 0 > limit ? -1 : limit );
285
286    return 0;
287}
288
289int
290client_uplimit( int limit )
291{
292    struct req * req;
293
294    req = addreq( IPC_MSG_UPLIMIT, -1, NULL );
295    if( NULL == req )
296    {
297        return -1;
298    }
299
300    req->num = ( 0 > limit ? -1 : limit );
301
302    return 0;
303}
304
305int
306client_dir( const char * dir )
307{
308    struct req * req;
309    char       * dircpy;
310
311    dircpy = strdup( dir );
312    if( NULL == dircpy )
313    {
314        mallocmsg( strlen( dir ) );
315        return -1;
316    }
317
318    req = addreq( IPC_MSG_DIR, -1, NULL );
319    if( NULL == req )
320    {
321        free( dircpy );
322        return -1;
323    }
324
325    req->str = dircpy;
326
327    return 0;
328}
329
330int
331addintlistreq( enum ipc_msg which, size_t len, const int * list )
332{
333    struct req * req;
334    int64_t    * duplist;
335    size_t       ii;
336
337    assert( ( 0 == len && NULL == list ) || ( 0 < len && NULL != list ) );
338
339    duplist = NULL;
340    if( NULL != list )
341    {
342        duplist = calloc( len, sizeof duplist[0] );
343        if( NULL == duplist )
344        {
345            mallocmsg( len * sizeof( duplist[0] ) );
346            return -1;
347        }
348    }
349
350    req = addreq( which, -1, NULL );
351    if( NULL == req )
352    {
353        free( duplist );
354        return -1;
355    }
356
357    for( ii = 0; len > ii; ii++ )
358    {
359        duplist[ii] = list[ii];
360    }
361    req->listlen = len;
362    req->numlist = duplist;
363
364    return 0;
365}
366
367int
368client_start( size_t len, const int * list )
369{
370    enum ipc_msg id;
371
372    id = ( NULL == list ? IPC_MSG_STARTALL : IPC_MSG_START );
373
374    return addintlistreq( id, len, list );
375}
376
377int
378client_stop( size_t len, const int * list )
379{
380    enum ipc_msg id;
381
382    id = ( NULL == list ? IPC_MSG_STOPALL : IPC_MSG_STOP );
383
384    return addintlistreq( id, len, list );
385}
386
387int
388client_remove( size_t len, const int * list )
389{
390    enum ipc_msg id;
391
392    id = ( NULL == list ? IPC_MSG_REMOVEALL : IPC_MSG_REMOVE );
393
394    return addintlistreq( id, len, list );
395}
396
397int
398client_list( cl_listfunc func )
399{
400    struct req  * req;
401    struct resp * resp;
402
403    req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp );
404    if( NULL == req )
405    {
406        return -1;
407    }
408
409    resp->listcb = func;
410    req->types   = IPC_INF_NAME | IPC_INF_HASH;
411
412    return 0;
413}
414
415int
416client_info( cl_infofunc func )
417{
418    struct req  * req;
419    struct resp * resp;
420
421    req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp );
422    if( NULL == req )
423    {
424        return -1;
425    }
426
427    resp->infocb = func;
428    req->types   = IPC_INF_NAME | IPC_INF_HASH | IPC_INF_SIZE;
429
430    return 0;
431}
432
433int
434client_hashids( cl_hashfunc func )
435{
436    struct req  * req;
437    struct resp * resp;
438
439    req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp );
440    if( NULL == req )
441    {
442        return -1;
443    }
444
445    resp->hashcb = func;
446    req->types   = IPC_INF_HASH;
447
448    return 0;
449}
450
451int
452client_status( cl_statfunc func )
453{
454    struct req  * req;
455    struct resp * resp;
456
457    req = addreq( IPC_MSG_GETSTATALL, ++gl_tag, &resp );
458    if( NULL == req )
459    {
460        return -1;
461    }
462
463    resp->statcb = func;
464    req->types   = IPC_ST_STATE | IPC_ST_ETA | IPC_ST_COMPLETED |
465        IPC_ST_DOWNSPEED | IPC_ST_UPSPEED | IPC_ST_ERROR | IPC_ST_ERRMSG;
466
467    return 0;
468}
469
470void
471didwrite( struct bufferevent * ev, void * arg )
472{
473    struct con * con = arg;
474
475    assert( ev == con->ev );
476    flushreqs( con );
477}
478
479void
480ohshit( struct bufferevent * ev UNUSED, short what, void * arg UNUSED )
481{
482    if( EVBUFFER_EOF & what )
483    {
484        errmsg( "server closed connection" );
485    }
486    else if( EVBUFFER_TIMEOUT & what )
487    {
488        errmsg( "server connection timed out" );
489    }
490    else if( EVBUFFER_READ & what )
491    {
492        errmsg( "read error on server connection" );
493    }
494    else if( EVBUFFER_WRITE & what )
495    {
496        errmsg( "write error on server connection" );
497    }
498    else if( EVBUFFER_ERROR & what )
499    {
500        errmsg( "error on server connection" );
501    }
502    else
503    {
504        errmsg( "unknown error on server connection: 0x%x", what );
505    }
506    exit( 1 );
507}
508
509void
510canread( struct bufferevent * ev, void * arg )
511{
512    struct con * con = arg;
513    uint8_t    * buf;
514    size_t       len;
515    ssize_t      res;
516
517    buf = EVBUFFER_DATA( EVBUFFER_INPUT( ev ) );
518    len = EVBUFFER_LENGTH( EVBUFFER_INPUT( ev ) );
519
520    if( IPC_MIN_MSG_LEN > len )
521    {
522        return;
523    }
524
525    res = ipc_parse( &con->ipc, buf, len, con );
526    if( 0 > res )
527    {
528        switch( errno )
529        {
530            case EPERM:
531                errmsg( "unsupported protocol version" );
532                break;
533            case EINVAL:
534                errmsg( "protocol parse error" );
535                break;
536            default:
537                errnomsg( "parsing failed" );
538                break;
539        }
540        exit( 1 );
541    }
542
543    if( 0 < res )
544    {
545        evbuffer_drain( EVBUFFER_INPUT( ev ), res );
546        flushreqs( con );
547    }
548}
549
550void
551flushreqs( struct con * con )
552{
553    struct req * req;
554    uint8_t    * buf;
555    size_t       buflen;
556
557    if( !HASVERS( &con->ipc ) )
558    {
559        return;
560    }
561
562    if( SLIST_EMPTY( &gl_reqs ) && RB_EMPTY( &gl_resps ) )
563    {
564        exit( 0 );
565    }
566
567    while( !SLIST_EMPTY( &gl_reqs ) )
568    {
569        req = SLIST_FIRST( &gl_reqs );
570        SLIST_REMOVE_HEAD( &gl_reqs, next );
571        switch( req->id )
572        {
573            case IPC_MSG_QUIT:
574            case IPC_MSG_STARTALL:
575            case IPC_MSG_STOPALL:
576            case IPC_MSG_REMOVEALL:
577                buf = ipc_mkempty( &con->ipc, &buflen, req->id, req->tag );
578                break;
579            case IPC_MSG_ADDFILES:
580                buf = ipc_mkstrlist( &con->ipc, &buflen, req->id, -1,
581                                     req->strs );
582                SAFEFREESTRLIST( req->strs );
583                break;
584            case IPC_MSG_AUTOMAP:
585            case IPC_MSG_PORT:
586            case IPC_MSG_DOWNLIMIT:
587            case IPC_MSG_UPLIMIT:
588                buf = ipc_mkint( &con->ipc, &buflen, req->id, -1, req->num );
589                break;
590            case IPC_MSG_DIR:
591                buf = ipc_mkstr( &con->ipc, &buflen, req->id, -1, req->str );
592                SAFEFREE( req->str );
593                break;
594            case IPC_MSG_START:
595            case IPC_MSG_STOP:
596            case IPC_MSG_REMOVE:
597                buf = ipc_mkints( &con->ipc, &buflen, req->id, req->tag,
598                                  req->listlen, req->numlist );
599                SAFEFREE( req->numlist );
600                break;
601            case IPC_MSG_GETINFOALL:
602            case IPC_MSG_GETSTATALL:
603                buf = ipc_mkgetinfo( &con->ipc, &buflen, req->id, req->tag,
604                                     req->types, NULL );
605                break;
606            default:
607                assert( 0 );
608                return;
609        }
610
611        SAFEFREE( req );
612        if( NULL == buf )
613        {
614            if( EPERM == errno )
615            {
616                errmsg( "message not supported by server" );
617            }
618            else
619            {
620                errnomsg( "failed to create message" );
621            }
622            exit( 1 );
623        }
624        if( 0 > bufferevent_write( con->ev, buf, buflen ) )
625        {
626            errmsg( "failed to buffer %zd bytes of data for write", buflen );
627            exit( 1 );
628        }
629        free( buf );
630    }
631}
632
633int
634sendvers( struct bufferevent * ev )
635{
636    uint8_t   * buf;
637    size_t      len;
638
639    buf = ipc_mkvers( &len );
640    if( NULL == buf )
641    {
642        if( EPERM == errno )
643        {
644            errmsg( "message not supported by server" );
645        }
646        else
647        {
648            errnomsg( "failed to create message" );
649        }
650        return -1;
651    }
652
653    if( 0 > bufferevent_write( ev, buf, len ) )
654    {
655        free( buf );
656        errmsg( "failed to buffer %i bytes of data for write", ( int )len );
657        return -1;
658    }
659
660    free( buf );
661
662    return 0;
663}
664
665void
666infomsg( enum ipc_msg msgid, benc_val_t * list, int64_t tag,
667         void * arg UNUSED )
668{
669    benc_val_t  * dict;
670    int           ii;
671    int64_t       id, size;
672    char        * name, * hash;
673    struct resp * resp, key;
674
675    assert( IPC_MSG_INFO == msgid );
676
677    if( TYPE_LIST != list->type )
678    {
679        return;
680    }
681
682    bzero( &key, sizeof key );
683    key.tag = tag;
684    resp = RB_FIND( resptree, &gl_resps, &key );
685    if( NULL == resp )
686    {
687        return;
688    }
689    RB_REMOVE( resptree, &gl_resps, resp );
690
691    for( ii = 0; list->val.l.count > ii; ii++ )
692    {
693        dict = &list->val.l.vals[ii];
694        if( TYPE_DICT != dict->type )
695        {
696            continue;
697        }
698
699        id   = getinfoint( msgid, dict, IPC_INF_ID,   -1   );
700        name = getinfostr( msgid, dict, IPC_INF_NAME, NULL );
701        hash = getinfostr( msgid, dict, IPC_INF_HASH, NULL );
702        size = getinfoint( msgid, dict, IPC_INF_SIZE, -1   );
703
704        if( !TORRENT_ID_VALID( id ) )
705        {
706            continue;
707        }
708
709        if( NULL != resp->infocb )
710        {
711            resp->infocb( id, name, size );
712        }
713        else if( NULL != resp->listcb )
714        {
715            resp->listcb( id, name, hash );
716        }
717        else if( NULL != resp->hashcb )
718        {
719            resp->hashcb( id, hash );
720        }
721    }
722
723    cbdone( resp );
724    free( resp );
725}
726
727void
728statmsg( enum ipc_msg msgid, benc_val_t * list, int64_t tag,
729         void * arg UNUSED )
730{
731    benc_val_t  * dict;
732    int           ii;
733    int64_t       id, eta, done, down, up;
734    char        * state, * error, * errmsg;
735    struct resp * resp, key;
736
737    assert( IPC_MSG_STAT == msgid );
738
739    if( TYPE_LIST != list->type )
740    {
741        return;
742    }
743
744    bzero( &key, sizeof key );
745    key.tag = tag;
746    resp = RB_FIND( resptree, &gl_resps, &key );
747    if( NULL == resp )
748    {
749        return;
750    }
751    RB_REMOVE( resptree, &gl_resps, resp );
752
753    for( ii = 0; list->val.l.count > ii; ii++ )
754    {
755        dict   = &list->val.l.vals[ii];
756        if( TYPE_DICT != dict->type )
757        {
758            continue;
759        }
760
761        id     = getinfoint( msgid, dict, IPC_ST_ID,        -1   );
762        state  = getinfostr( msgid, dict, IPC_ST_STATE,     NULL );
763        eta    = getinfoint( msgid, dict, IPC_ST_ETA,       -1   );
764        done   = getinfoint( msgid, dict, IPC_ST_COMPLETED, -1   );
765        down   = getinfoint( msgid, dict, IPC_ST_DOWNSPEED, -1   );
766        up     = getinfoint( msgid, dict, IPC_ST_UPSPEED,   -1   );
767        error  = getinfostr( msgid, dict, IPC_ST_ERROR,     NULL );
768        errmsg = getinfostr( msgid, dict, IPC_ST_ERRMSG,    NULL );
769
770        if( !TORRENT_ID_VALID( id ) )
771        {
772            continue;
773        }
774
775        if( NULL != resp->statcb )
776        {
777            resp->statcb( id, state, eta, done, down, up, error, errmsg );
778        }
779    }
780
781    cbdone( resp );
782    free( resp );
783}
784
785void
786defmsg( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg UNUSED )
787{
788    struct resp * resp, key;
789
790    switch( msgid )
791    {
792        case IPC_MSG_FAIL:
793            if( TYPE_STR == val->type && NULL != val->val.s.s )
794            {
795                errmsg( "request failed: %s", val->val.s.s );
796            }
797            else
798            {
799                errmsg( "request failed" );
800            }
801            break;
802        case IPC_MSG_NOTSUP:
803            errmsg( "request message not supported" );
804            break;
805        default:
806            break;
807    }
808
809    bzero( &key, sizeof key );
810    key.tag = tag;
811    resp = RB_FIND( resptree, &gl_resps, &key );
812    if( NULL == resp )
813    {
814        return;
815    }
816    RB_REMOVE( resptree, &gl_resps, resp );
817
818    cbdone( resp );
819    free( resp );
820}
821
822void
823cbdone( struct resp * resp )
824{
825    if( NULL != resp->infocb )
826    {
827        resp->infocb( -1, NULL, -1 );
828    }
829    else if( NULL != resp->listcb )
830    {
831        resp->listcb( -1, NULL, NULL );
832    }
833    else if( NULL != resp->hashcb )
834    {
835        resp->hashcb( -1, NULL );
836    }
837    else if( NULL != resp->statcb )
838    {
839        resp->statcb( -1, NULL, -1, -1, -1, -1, NULL, NULL );
840    }
841}
842
843int64_t
844getinfoint( enum ipc_msg msgid, benc_val_t * dict, int type, int64_t defval  )
845{
846    benc_val_t * val;
847
848    val = tr_bencDictFind( dict, ipc_infoname( msgid, type ) );
849
850    if( NULL != val && TYPE_INT == val->type )
851    {
852        return val->val.i;
853    }
854
855    return defval;
856}
857
858char *
859getinfostr( enum ipc_msg msgid, benc_val_t * dict, int type, char * defval  )
860{
861    benc_val_t * val;
862
863    val = tr_bencDictFind( dict, ipc_infoname( msgid, type ) );
864
865    if( NULL != val && TYPE_STR == val->type )
866    {
867        return val->val.s.s ;
868    }
869
870    return defval;
871}
Note: See TracBrowser for help on using the repository browser.