/****************************************************************************** * $Id$ * * Copyright (c) 2007 Joshua Elsasser * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "errors.h" #include "misc.h" static void usage ( const char *, ... ); static enum confpathtype readargs ( int, char **, char ** ); static int makesock ( enum confpathtype, const char * ); static void inread ( struct bufferevent *, void * ); static void noop ( struct bufferevent *, void * ); static void inerr ( struct bufferevent *, short, void * ); static void wtf ( struct bufferevent *, void * ); static void outerr ( struct bufferevent *, short, void * ); static void sockread ( struct bufferevent *, void * ); static void sockwrite( struct bufferevent *, void * ); static void sockerr ( struct bufferevent *, short, void * ); static struct bufferevent * gl_in = NULL; static struct bufferevent * gl_out = NULL; static struct bufferevent * gl_sock = NULL; int main( int argc, char ** argv ) { struct event_base * base; enum confpathtype type; char * sockpath; int sockfd; setmyname( argv[0] ); type = readargs( argc, argv, &sockpath ); base = event_init(); sockfd = makesock( type, sockpath ); if( 0 > sockfd ) { return EXIT_FAILURE; } gl_in = bufferevent_new( STDIN_FILENO, inread, noop, inerr, NULL ); if( NULL == gl_in ) { errnomsg( "failed to set up event buffer for stdin" ); return EXIT_FAILURE; } /* XXX bufferevent_base_set( base, gl_in ); */ bufferevent_enable( gl_in, EV_READ ); bufferevent_disable( gl_in, EV_WRITE ); gl_out = bufferevent_new( STDOUT_FILENO, wtf, noop, outerr, NULL ); if( NULL == gl_in ) { errnomsg( "failed to set up event buffer for stdin" ); return EXIT_FAILURE; } /* XXX bufferevent_base_set( base, gl_out ); */ bufferevent_disable( gl_out, EV_READ ); bufferevent_enable( gl_out, EV_WRITE ); gl_sock = bufferevent_new( sockfd, sockread, sockwrite, sockerr, NULL ); if( NULL == gl_in ) { errnomsg( "failed to set up event buffer for stdin" ); return EXIT_FAILURE; } /* XXX bufferevent_base_set( base, gl_sock ); */ bufferevent_enable( gl_sock, EV_READ ); bufferevent_enable( gl_sock, EV_WRITE ); event_dispatch(); /* XXX event_base_dispatch( base ); */ return EXIT_FAILURE; } void usage( const char * msg, ... ) { va_list ap; if( NULL != msg ) { printf( "%s: ", getmyname() ); va_start( ap, msg ); vprintf( msg, ap ); va_end( ap ); printf( "\n" ); } printf( "usage: %s [options] [files]...\n" "\n" "Transmission %s http://www.transmissionbt.com/\n" "A fast and easy BitTorrent client\n" "\n" " -h --help Display this message and exit\n" " -t --type daemon Use the daemon frontend, transmission-daemon\n" " -t --type gtk Use the GTK+ frontend, transmission\n" " -t --type mac Use the Mac OS X frontend\n", getmyname(), LONG_VERSION_STRING ); exit( EXIT_SUCCESS ); } enum confpathtype readargs( int argc, char ** argv, char ** sockpath ) { char optstr[] = "ht:"; struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "type", required_argument, NULL, 't' }, { NULL, 0, NULL, 0 } }; enum confpathtype type; int opt; type = CONF_PATH_TYPE_DAEMON; *sockpath = NULL; while( 0 <= ( opt = getopt_long( argc, argv, optstr, longopts, NULL ) ) ) { switch( opt ) { case 't': if( 0 == strcasecmp( "daemon", optarg ) ) { type = CONF_PATH_TYPE_DAEMON; *sockpath = NULL; } else if( 0 == strcasecmp( "gtk", optarg ) ) { type = CONF_PATH_TYPE_GTK; *sockpath = NULL; } else if( 0 == strcasecmp( "mac", optarg ) || 0 == strcasecmp( "osx", optarg ) || 0 == strcasecmp( "macos", optarg ) || 0 == strcasecmp( "macosx", optarg ) ) { type = CONF_PATH_TYPE_OSX; *sockpath = NULL; } else { *sockpath = optarg; } break; default: usage( NULL ); break; } } return type; } int makesock( enum confpathtype type, const char * path ) { struct sockaddr_un sa; int fd; memset( &sa, 0, sizeof sa ); sa.sun_family = AF_LOCAL; if( NULL == path ) { confpath( sa.sun_path, sizeof sa.sun_path, CONF_FILE_SOCKET, type ); } else { strlcpy( sa.sun_path, path, sizeof sa.sun_path ); } fd = socket( AF_UNIX, SOCK_STREAM, 0 ); if( 0 > fd ) { errnomsg( "failed to create socket" ); return -1; } if( 0 > connect( fd, ( struct sockaddr * )&sa, SUN_LEN( &sa ) ) ) { errnomsg( "failed to connect to socket file: %s", sa.sun_path ); close( fd ); return -1; } return fd; } void inread( struct bufferevent * ev UNUSED, void * arg UNUSED ) { bufferevent_write_buffer( gl_sock, EVBUFFER_INPUT( gl_in ) ); } void noop( struct bufferevent * ev UNUSED, void * arg UNUSED ) { } void inerr( struct bufferevent * ev UNUSED, short what, void * arg UNUSED ) { if( EVBUFFER_EOF & what ) { bufferevent_free( gl_in ); gl_in = NULL; sockwrite( NULL, NULL ); return; } if( EVBUFFER_TIMEOUT & what ) { errmsg( "timed out reading from stdin" ); } else if( EVBUFFER_READ & what ) { errmsg( "read error on stdin" ); } else if( EVBUFFER_ERROR & what ) { errmsg( "error on stdin" ); } else { errmsg( "unknown error on stdin: 0x%x", what ); } exit( EXIT_FAILURE ); } void wtf( struct bufferevent * ev, void * arg UNUSED ) { /* this shouldn't happen, but let's drain the buffer anyway */ evbuffer_drain( EVBUFFER_INPUT( ev ), EVBUFFER_LENGTH( EVBUFFER_INPUT( ev ) ) ); } void outerr( struct bufferevent * ev UNUSED, short what, void * arg UNUSED ) { if( EVBUFFER_TIMEOUT & what ) { errmsg( "timed out writing to stdout" ); } else if( EVBUFFER_WRITE & what ) { errmsg( "write error on stdout" ); } else if( EVBUFFER_ERROR & what ) { errmsg( "error on client stdout" ); } else { errmsg( "unknown error on stdout connection: 0x%x", what ); } exit( EXIT_FAILURE ); } void sockread( struct bufferevent * ev UNUSED, void * arg UNUSED ) { bufferevent_write_buffer( gl_out, EVBUFFER_INPUT( gl_sock ) ); } void sockwrite( struct bufferevent * ev UNUSED, void * arg UNUSED ) { if( NULL == gl_in && 0 == EVBUFFER_LENGTH( EVBUFFER_OUTPUT( gl_sock ) ) ) { exit( EXIT_SUCCESS ); } } void sockerr( struct bufferevent * ev UNUSED, short what, void * arg UNUSED ) { if( EVBUFFER_EOF & what ) { errmsg( "server closed connection" ); } else if( EVBUFFER_TIMEOUT & what ) { errmsg( "server connection timed out" ); } else if( EVBUFFER_READ & what ) { errmsg( "read error on server connection" ); } else if( EVBUFFER_WRITE & what ) { errmsg( "write error on server connection" ); } else if( EVBUFFER_ERROR & what ) { errmsg( "error on server connection" ); } else { errmsg( "unknown error on server connection: 0x%x", what ); } exit( EXIT_FAILURE ); }