00001 #include <sys/types.h>
00002 #include <sys/socket.h>
00003 #include <sys/un.h>
00004
00005 #include <unistd.h>
00006 #include <fcntl.h>
00007
00008 #include <gtask/gtask-connection.h>
00009
00010 #include "gtask-internals.h"
00011
00014
00015
00016 enum {
00017 PROP_0,
00018 PROP_NAME,
00019 PROP_ROLE,
00020 PROP_STREAM_PARSER,
00021 PROP_EVENT_PARSER
00022 };
00023
00024 static gpointer parent_class;
00025
00026 static gboolean
00027 gtask_connection_write( GTaskConnection *conn, char *buff );
00028
00029 static void
00030 gtask_connection_class_init( GTaskConnectionClass *klass );
00031
00032 static void
00033 gtask_connection_init( GTaskConnection *connection, gpointer g_class );
00034
00035 static void
00036 gtask_connection_finalize( GObject *obj );
00037
00038 static gboolean
00039 gtask_connection_init_xml_buffers( GTaskConnection *conn );
00040
00041 static gboolean
00042 gtask_connection_init_gio_channels( GTaskConnection *conn );
00043
00044 static void
00045 gtask_connection_set_property( GObject *object,
00046 guint param_id,
00047 const GValue *value,
00048 GParamSpec *pspec );
00049
00050 static void
00051 gtask_connection_get_property( GObject *object,
00052 guint param_id,
00053 GValue *value,
00054 GParamSpec *pspec );
00055
00056 GType gtask_role_get_type( ) {
00057 static GType type = 0;
00058
00059 if( type == 0 ) {
00060 static const GEnumValue values[] = {
00061 { GTASK_ROLE_NONE, "GTASK_ROLE_NONE", "none" },
00062 { GTASK_ROLE_NONE, "GTASK_ROLE_PROXY", "proxy" },
00063 { GTASK_ROLE_NONE, "GTASK_ROLE_REPORTER", "reporter" },
00064 { GTASK_ROLE_NONE, "GTASK_ROLE_PROVIDER", "provider" },
00065 { 0, NULL, NULL }
00066 };
00067
00068 type = g_enum_register_static( "GTaskRole", values );
00069 }
00070
00071 return type;
00072 }
00073
00083 GType gtask_connection_get_type( void ) {
00084 static GType type = 0;
00085
00086 if( type == 0 ) {
00087 static const GTypeInfo info = {
00088 sizeof( GTaskConnectionClass ),
00089 NULL,
00090 NULL,
00091 (GClassInitFunc) gtask_connection_class_init,
00092 NULL,
00093 NULL,
00094 sizeof( GTaskConnection ),
00095 0,
00096 (GInstanceInitFunc) gtask_connection_init
00097 };
00098
00099 type = g_type_register_static( G_TYPE_OBJECT,
00100 "GTaskConnection",
00101 &info,
00102 0 );
00103 }
00104
00105 return type;
00106 }
00107
00108 static void
00109 gtask_connection_class_init( GTaskConnectionClass *klass ) {
00110 GObjectClass *gobject_class = G_OBJECT_CLASS( klass );
00111
00112 parent_class = g_type_class_peek_parent( klass );
00113
00114 gobject_class->finalize = gtask_connection_finalize;
00115 gobject_class->set_property = gtask_connection_set_property;
00116 gobject_class->get_property = gtask_connection_get_property;
00117
00118 klass->connected_signal_id = g_signal_newv( "connected",
00119 G_TYPE_FROM_CLASS( klass ),
00120 G_SIGNAL_RUN_LAST |
00121 G_SIGNAL_NO_RECURSE |
00122 G_SIGNAL_NO_HOOKS,
00123 NULL,
00124 NULL,
00125 NULL,
00126 g_cclosure_marshal_VOID__VOID,
00127 G_TYPE_NONE,
00128 0,
00129 NULL );
00130
00131 klass->disconnected_signal_id = g_signal_newv( "disconnected",
00132 G_TYPE_FROM_CLASS( klass ),
00133 G_SIGNAL_RUN_LAST |
00134 G_SIGNAL_NO_RECURSE |
00135 G_SIGNAL_NO_HOOKS,
00136 NULL,
00137 NULL,
00138 NULL,
00139 g_cclosure_marshal_VOID__VOID,
00140 G_TYPE_NONE,
00141 0,
00142 NULL );
00143
00144 g_object_class_install_property( gobject_class,
00145 PROP_NAME,
00146 g_param_spec_string( "name",
00147 "Name",
00148 "The name of the connection.",
00149 "-unamed-",
00150 G_PARAM_READWRITE ) );
00151
00152 g_object_class_install_property( gobject_class,
00153 PROP_ROLE,
00154 g_param_spec_enum( "role",
00155 "Role",
00156 "The GTaskRole of the connection. This item should be set before a connection is connected.",
00157 GTASK_ROLE_TYPE,
00158 GTASK_ROLE_NONE,
00159 G_PARAM_READWRITE ) );
00160 }
00161
00162 static void
00163 gtask_connection_set_property( GObject *object,
00164 guint param_id,
00165 const GValue *value,
00166 GParamSpec *pspec )
00167 {
00168 GTaskConnection *conn = GTASK_CONNECTION( object );
00169
00170 switch( param_id ) {
00171 case PROP_NAME:
00172 gtask_connection_set_name( conn, g_value_get_string( value ) );
00173 break;
00174 case PROP_ROLE:
00175 gtask_connection_set_role( conn, g_value_get_enum( value ) );
00176 break;
00177 default:
00178 G_OBJECT_WARN_INVALID_PROPERTY_ID( object, param_id, pspec );
00179 }
00180 }
00181
00182 static void
00183 gtask_connection_get_property( GObject *object,
00184 guint param_id,
00185 GValue *value,
00186 GParamSpec *pspec )
00187 {
00188 GTaskConnection *conn = GTASK_CONNECTION( object );
00189
00190 switch( param_id ) {
00191 case PROP_NAME:
00192 g_value_set_string( value, gtask_connection_get_name( conn ) );
00193 break;
00194 case PROP_ROLE:
00195 g_value_set_enum( value, gtask_connection_get_role( conn ) );
00196 break;
00197 default:
00198 G_OBJECT_WARN_INVALID_PROPERTY_ID( object, param_id, pspec );
00199 }
00200 }
00201
00214 static gboolean
00215 gtask_connection_write( GTaskConnection *conn, char *buff ) {
00216 int fd = gtask_connection_get_output_fd( conn );
00217
00218 int len = strlen( buff );
00219
00220 while( len ) {
00221 int ret = write( fd, buff, len );
00222
00223 if( ret < 1 )
00224 return FALSE;
00225
00226 len -= ret;
00227 }
00228
00229 return TRUE;
00230 }
00231
00237 static void
00238 gtask_connection_init( GTaskConnection *connection, gpointer g_class ) {
00239 GTaskConnectionPrivate *private;
00240
00241 private = g_new0( GTaskConnectionPrivate, 1 );
00242
00243 private->inp_fd = -1;
00244 private->out_fd = -1;
00245 private->name = g_string_new( "-unamed-" );
00246
00247 private->role = GTASK_ROLE_NONE;
00248 private->parser = gtask_stream_parser_new( );
00249 private->dispatcher = gtask_event_dispatcher_new_with_parser( private->parser );
00250
00251 private->conn_method = GTASK_CONNECTION_METHOD_UNKNOWN;
00252 private->socket_uri = NULL;
00253
00254 private->auto_reconnect = TRUE;
00255 private->auto_parse = TRUE;
00256
00257 connection->private = private;
00258 }
00259
00267 GTaskConnection *
00268 gtask_connection_new( ) {
00269 return g_object_new( GTASK_CONNECTION_TYPE, NULL );
00270 }
00271
00272 GTaskConnection *
00273 gtask_connection_new_with_properties( const gchar *name, GTaskRole role ) {
00274 GTaskConnection *conn = gtask_connection_new( );
00275
00276 if( name )
00277 gtask_connection_set_name( conn, name );
00278
00279 gtask_connection_set_role( conn, role );
00280
00281 return conn;
00282 }
00283
00294 gchar *
00295 gtask_connection_default_server_bind_path( ) {
00296 return g_strdup_printf( "%s/.gtaskd/server/socket", g_get_home_dir( ) );
00297 }
00298
00309 gboolean
00310 gtask_connection_connect_domain_socket_default( GTaskConnection *conn ) {
00311 char *location;
00312 gboolean ret;
00313
00314 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), FALSE );
00315 g_return_val_if_fail( !gtask_connection_is_connected( conn ), FALSE );
00316
00317 location = gtask_connection_default_server_bind_path( );
00318
00319 ret = gtask_connection_connect_domain_socket( conn, location );
00320
00321 g_free( location );
00322
00323 return ret;
00324 }
00325
00337 gboolean
00338 gtask_connection_connect_domain_socket( GTaskConnection *conn,
00339 const char *file )
00340 {
00341 GTaskConnectionPrivate *private;
00342 struct sockaddr_un sock_un;
00343 int fd;
00344
00345 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), FALSE );
00346 g_return_val_if_fail( !gtask_connection_is_connected( conn ), FALSE );
00347
00348 private = conn->private;
00349
00350 fd = socket( AF_LOCAL, SOCK_STREAM, 0 );
00351
00353 if( fd < 0 ) {
00354 g_warning( "Error creating socket" );
00355 return FALSE;
00356 }
00357
00358 bzero( &sock_un, sizeof( sock_un ) );
00359 sock_un.sun_family = AF_LOCAL;
00360 strncpy( sock_un.sun_path, file, sizeof( sock_un.sun_path ) - 1 );
00361
00362 if( connect( fd, (struct sockaddr *) &sock_un, sizeof( sock_un ) ) < 0 ) {
00363 g_warning( "Error on connect" );
00364 close( fd );
00365 return FALSE;
00366 }
00367
00368 private->conn_method = GTASK_CONNECTION_METHOD_SOCKET;
00369
00370 g_free( private->socket_uri );
00371 private->socket_uri = g_strdup( file );
00372
00373 if( !gtask_connection_connect_fd( conn, fd, fd ) ) {
00374 close( fd );
00375 return FALSE;
00376 }
00377
00378 return TRUE;
00379 }
00380
00381 gboolean
00382 gtask_connection_connect_loopback( GTaskConnection *conn ) {
00383 int fd[2];
00384
00385 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), FALSE );
00386 g_return_val_if_fail( !gtask_connection_is_connected( conn ), FALSE );
00387
00388 if( pipe( fd ) < 0 ) {
00389 g_warning( "error creating pipe" );
00390
00391 return FALSE;
00392 }
00393
00394 conn->private->conn_method = GTASK_CONNECTION_METHOD_LOOPBACK;
00395
00397 shutdown( fd[0], 0 );
00398 shutdown( fd[1], 1 );
00399
00400 if( !gtask_connection_connect_fd( conn, fd[0], fd[1] ) ) {
00401 close( fd[0] );
00402 close( fd[1] );
00403
00404 return FALSE;
00405 }
00406
00407 return TRUE;
00408 }
00409
00419 static gboolean
00420 gtask_connection_init_xml_buffers( GTaskConnection *conn ) {
00421 GTaskConnectionPrivate *private = conn->private;
00422
00423 g_return_val_if_fail( private->inp_fd >= 0, FALSE );
00424 g_return_val_if_fail( private->out_fd >= 0, FALSE );
00425 g_return_val_if_fail( !private->xml_output, FALSE );
00426
00427 private->xml_output = xmlOutputBufferCreateFd( private->out_fd,
00428 XML_CHAR_ENCODING_NONE );
00429
00430 if( !private->xml_output )
00431 return FALSE;
00432
00433 return TRUE;
00434 }
00435
00445 static gboolean
00446 gtask_connection_init_gio_channels( GTaskConnection *conn ) {
00447 GTaskConnectionPrivate *private = conn->private;
00448 int status;
00449 int flags;
00450
00451 g_return_val_if_fail( private->inp_fd >= 0, FALSE );
00452 g_return_val_if_fail( !private->g_input, FALSE );
00453
00454 private->g_input = g_io_channel_unix_new( private->inp_fd );
00455
00456 g_io_channel_set_encoding( private->g_input, NULL, NULL );
00457 g_io_channel_set_buffered( private->g_input, FALSE );
00458
00459 flags = g_io_channel_get_flags( private->g_input );
00460
00461 status = g_io_channel_set_flags( private->g_input,
00462 flags & G_IO_FLAG_NONBLOCK,
00463 NULL );
00464
00465 if( status != G_IO_STATUS_NORMAL ) {
00466 g_io_channel_unref( private->g_input );
00467 private->g_input = NULL;
00468
00469 return FALSE;
00470 }
00471
00472 return TRUE;
00473 }
00474
00475
00476
00477
00478 static gboolean
00479 gtask_connection_connection_broken( GIOChannel *source,
00480 GIOCondition cond,
00481 gpointer data )
00482 {
00483 if( cond == G_IO_HUP ) {
00484 GTaskConnection *conn = GTASK_CONNECTION( data );
00485
00486 conn->private->g_hup_source_id = 0;
00487
00491 gtask_connection_close( conn );
00492
00493 g_warning( "connection to server broken, closing it down" );
00494
00495 if( conn->private->auto_parse )
00496 gtask_stream_parser_reset_state( conn->private->parser );
00497
00498 g_signal_emit( conn,
00499 GTASK_CONNECTION_GET_CLASS( conn )->disconnected_signal_id,
00500 0,
00501 NULL );
00502
00504 g_warning( "we would attempt to reconnect here" );
00505 } else {
00506 g_warning( "gtask_connection_connection_broken called, but the GIOCondition != G_IO_HUP" );
00507 }
00508
00509 return FALSE;
00510 }
00511
00523 gboolean
00524 gtask_connection_connect_fd( GTaskConnection *conn, int inp_fd, int out_fd ) {
00525 GTaskConnectionPrivate *private = conn->private;
00526
00528 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), FALSE );
00529 g_return_val_if_fail( !gtask_connection_is_connected( conn ), FALSE );
00530
00531 private->inp_fd = inp_fd;
00532 private->out_fd = out_fd;
00533
00534 if( !gtask_connection_init_xml_buffers( conn ) ) {
00535 private->inp_fd = -1;
00536 private->out_fd = -1;
00537
00538 return FALSE;
00539 }
00540
00541 if( !gtask_connection_init_gio_channels( conn ) ) {
00542 xmlOutputBufferClose( private->xml_output );
00543 private->inp_fd = -1;
00544 private->out_fd = -1;
00545 private->xml_output = NULL;
00546
00547 return FALSE;
00548 }
00549
00550 if( !gtask_connection_write( conn, "<gtask-stream>" ) ) {
00551 xmlOutputBufferClose( private->xml_output );
00552 private->xml_output = NULL;
00553
00554 g_io_channel_unref( private->g_input );
00555 private->g_input = NULL;
00556
00557 private->inp_fd = -1;
00558 private->out_fd = -1;
00559
00560 return FALSE;
00561 }
00562
00563 if( private->conn_method == GTASK_CONNECTION_METHOD_UNKNOWN )
00564 private->conn_method = GTASK_CONNECTION_METHOD_FD;
00565
00566 g_debug( "==> Emitting connected signal" );
00567
00568 g_signal_emit( conn,
00569 GTASK_CONNECTION_GET_CLASS( conn )->connected_signal_id,
00570 0,
00571 NULL );
00572
00573 if( private->auto_parse ) {
00574 private->g_in_source_id = g_io_add_watch( private->g_input,
00575 G_IO_IN,
00576 gtask_event_dispatcher_private_parse_callback,
00577 private->dispatcher );
00578 }
00579
00580
00581 private->g_hup_source_id = g_io_add_watch( private->g_input,
00582 G_IO_HUP,
00583 gtask_connection_connection_broken,
00584 conn );
00585 return TRUE;
00586 }
00587
00597 gboolean
00598 gtask_connection_is_connected( GTaskConnection *conn ) {
00599 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), FALSE );
00600
00601 return ( conn->private->inp_fd >= 0 && conn->private->out_fd >= 0 );
00602 }
00603
00604
00612 static void
00613 gtask_connection_finalize( GObject *obj ) {
00614 GTaskConnection *conn = GTASK_CONNECTION( obj );
00615
00616 gtask_connection_close( conn );
00617
00618 g_free( conn->private );
00619
00620 if( G_OBJECT_CLASS( parent_class )->finalize )
00621 G_OBJECT_CLASS( parent_class )->finalize( obj );
00622 }
00623
00634 gint
00635 gtask_connection_get_input_fd( GTaskConnection *conn ) {
00636 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), -1 );
00637
00638 return conn->private->inp_fd;
00639 }
00640
00651 gint
00652 gtask_connection_get_output_fd( GTaskConnection *conn ) {
00653 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), -1 );
00654
00655 return conn->private->out_fd;
00656 }
00657
00667 gboolean
00668 gtask_connection_close( GTaskConnection *conn ) {
00669 GTaskConnectionPrivate *private;
00670 gboolean same;
00671
00672 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), FALSE );
00673
00674 private = conn->private;
00675
00676 same = ( private->inp_fd == private->out_fd );
00677
00678 if( private->g_hup_source_id ) {
00679 g_source_remove( private->g_hup_source_id );
00680 private->g_hup_source_id = 0;
00681 }
00682
00683 if( private->g_in_source_id ) {
00684 g_source_remove( private->g_in_source_id );
00685 private->g_in_source_id = 0;
00686 }
00687
00688 if( private->xml_output ) {
00689 xmlOutputBufferClose( private->xml_output );
00690 private->xml_output = NULL;
00691 }
00692
00693 if( private->g_input ) {
00694 g_io_channel_unref( private->g_input );
00695 private->g_input = NULL;
00696 }
00697
00698 if( private->inp_fd > -1 ) {
00699 close( private->inp_fd );
00700
00701 private->inp_fd = -1;
00702 }
00703
00704 if( !same && private->out_fd > -1 ) {
00705 close( private->out_fd );
00706
00707 private->out_fd = -1;
00708 }
00709
00710 return TRUE;
00711 }
00712
00725 GIOChannel *
00726 gtask_connection_get_gio_input_channel( GTaskConnection *conn ) {
00727 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), NULL );
00728
00729 return conn->private->g_input;
00730 }
00731
00739 xmlOutputBufferPtr
00740 gtask_connection_get_xml_output_buffer( GTaskConnection *conn ) {
00741 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), NULL );
00742
00743 return conn->private->xml_output;
00744 }
00745
00746
00756 const gchar *
00757 gtask_connection_get_name( GTaskConnection *conn ) {
00758 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), "" );
00759
00760 return conn->private->name->str;
00761 }
00762
00769 void
00770 gtask_connection_set_name( GTaskConnection *conn, const gchar *name ) {
00771 g_return_if_fail( GTASK_IS_CONNECTION( conn ) );
00772 g_return_if_fail( name != NULL );
00773
00774 g_string_assign( conn->private->name, name );
00775
00776 g_object_notify( (GObject *) conn, "name" );
00777 }
00778
00786 GTaskRole
00787 gtask_connection_get_role( GTaskConnection *conn ) {
00788 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), GTASK_ROLE_NONE );
00789
00790 return conn->private->role;
00791 }
00792
00799 void
00800 gtask_connection_set_role( GTaskConnection *conn, GTaskRole role ) {
00801 g_return_if_fail( GTASK_IS_CONNECTION( conn ) );
00802
00803 conn->private->role = role;
00804
00805 g_object_notify( (GObject *) conn, "role" );
00806 }
00807
00819 GTaskStreamParser *
00820 gtask_connection_get_stream_parser( GTaskConnection *conn ) {
00821 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), NULL );
00822
00823 return conn->private->parser;
00824 }
00825
00836 GTaskEventDispatcher *
00837 gtask_connection_get_event_dispatcher( GTaskConnection *conn ) {
00838 g_return_val_if_fail( GTASK_IS_CONNECTION( conn ), NULL );
00839
00840 return conn->private->dispatcher;
00841 }
00842
00843