# HG changeset patch # User rosfran # Date 1164333107 0 # Node ID ba9bf90e7c4b6f93f12c0f912b7774ea3d274500 # Parent 1e1f1cb810fe2632b99f891598625ce4915b98e8 [svn r106] Added non-blocking socket connection facilities (configurable timeout when connection is broken). diff -r 1e1f1cb810fe -r ba9bf90e7c4b gmyth/src/gmyth_context.c --- a/gmyth/src/gmyth_context.c Thu Nov 23 20:51:44 2006 +0000 +++ b/gmyth/src/gmyth_context.c Fri Nov 24 01:51:47 2006 +0000 @@ -291,7 +291,6 @@ g_warning ("Database not open while trying to load setting: %s", key->str); } - return default_value; } diff -r 1e1f1cb810fe -r ba9bf90e7c4b gmyth/src/gmyth_file_transfer.c --- a/gmyth/src/gmyth_file_transfer.c Thu Nov 23 20:51:44 2006 +0000 +++ b/gmyth/src/gmyth_file_transfer.c Fri Nov 24 01:51:47 2006 +0000 @@ -439,9 +439,7 @@ (*transfer)->sock = sock; strlist = gmyth_string_list_new(); - g_print( "[%s] Protocol version = %d\n", __FUNCTION__, - gmyth_socket_get_protocol_version( (*transfer)->sock ) ); - if ( gmyth_socket_get_protocol_version( (*transfer)->sock ) > 26 ) + if ( (*transfer)->mythtv_version > 26 ) g_string_printf( base_str, "ANN FileTransfer %s %d %d", hostname->str, (*transfer)->userreadahead, (*transfer)->retries ); else diff -r 1e1f1cb810fe -r ba9bf90e7c4b gmyth/src/gmyth_socket.c --- a/gmyth/src/gmyth_socket.c Thu Nov 23 20:51:44 2006 +0000 +++ b/gmyth/src/gmyth_socket.c Fri Nov 24 01:51:47 2006 +0000 @@ -30,6 +30,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #include @@ -41,6 +45,11 @@ #include #include +#include +#include +#include +#include + #if defined(HAVE_IFADDRS_H) #include #else @@ -57,7 +66,7 @@ #define MYTH_PROTOCOL_FIELD_SIZE 8 /* max number of iterations */ -#define MYTHTV_MAX_VERSION_CHECKS 15 +#define MYTHTV_MAX_VERSION_CHECKS 40 static GStaticMutex mutex = G_STATIC_MUTEX_INIT; @@ -115,7 +124,7 @@ } static gint -find_match_address_uri( GMythURI* uri, gchar *address ) { +gmyth_socket_find_match_address_uri( GMythURI* uri, gchar *address ) { if ( g_ascii_strcasecmp( gmyth_uri_gethost( uri ), address ) == 0 ) { //g_printerr( "Found URI: %s !!!\n", rui_uri_getvalue(uri) ); @@ -128,8 +137,14 @@ #if defined(HAVE_IFADDRS_H) -GList * -get_local_addrs( GList *current_connections ) { +/** Gets the list of all local network interfaces. + * + * @param current_connections A list with all the network interfaces are valid, + * to be applied just like a filter. + * @return List with all the local net interfaces. + */ +static GList * +gmyth_socket_get_local_addrs( GList *current_connections ) { GList *local_addrs = NULL; @@ -155,14 +170,14 @@ ifname = i->ifa_name; ifIdx = if_nametoindex(ifname); - g_print( "[%s] Interface name: %s, index: %d, address: %s\n", __FUNCTION__, ifname, ifIdx, addr ); + /* g_debug( "[%s] Interface name: %s, index: %d, address: %s\n", __FUNCTION__, ifname, ifIdx, addr ); */ if ( current_connections == NULL || ( current_connections != NULL && g_list_find_custom( current_connections, (gchar *)addr, - (GCompareFunc)find_match_address_uri ) == NULL ) ) + (GCompareFunc)gmyth_socket_find_match_address_uri ) == NULL ) ) local_addrs = g_list_append( local_addrs, g_strdup( addr ) ); } - } // iterates over network interfaces + } /* iterates over network interfaces */ freeifaddrs(ifaddr); @@ -174,8 +189,14 @@ static const char *PATH_PROC_NET_DEV = "/proc/net/dev"; -GList * -get_local_addrs( GList *current_connections ) +/** Gets the list of all local network interfaces (using the /proc/net/dev directory). + * + * @param current_connections A list with all the network interfaces are valid, + * to be applied just like a filter. + * @return List with all the local net interfaces. + */ +static GList * +gmyth_socket_get_local_addrs( GList *current_connections ) { GList *local_addrs = NULL; @@ -215,7 +236,7 @@ g_strlcpy( ifaddr, inet_ntoa(((struct sockaddr_in*)&req.ifr_addr)->sin_addr), sizeof(struct ifaddr)-1 ); local_addrs = g_list_append( local_addrs, g_strdup( ifaddr ) ); - g_print( "[%s] ( from the /proc/net/dev) Interface name: %s, address: %s\n", __FUNCTION__, + g_debug( "[%s] ( from the /proc/net/dev) Interface name: %s, address: %s\n", __FUNCTION__, ifname, ifaddr ); } fclose(fd); @@ -229,16 +250,14 @@ /** * Get only the local addresses from the primary interface */ -gchar * -get_primary_addr() +static gchar * +gmyth_socket_get_primary_addr() { gchar *if_eth0 = g_new0( gchar, sizeof(struct ifaddr)-1 ); GList *if_tmp = NULL; - g_print( "[%s] net if size = %d.\n", __FUNCTION__, sizeof(struct ifaddr)-1 ); - - GList *interfs = get_local_addrs( NULL ); + GList *interfs = gmyth_socket_get_local_addrs( NULL ); if ( interfs != NULL && ( g_list_length( interfs ) > 0 ) ) { @@ -275,23 +294,24 @@ struct sockaddr_in* sa = NULL; - g_static_mutex_lock( &mutex ); - gethostname(localhostname, 1024); gint err = gmyth_socket_toaddrinfo( localhostname, -1, &addr_info_data ); + + if ( err == EADDRNOTAVAIL ) + { + g_warning( "[%s] Address (%s) not available. (reason = %d)\n", __FUNCTION__, localhostname, err ); + return str; + } + + g_static_mutex_lock( &mutex ); addr_info0 = addr_info_data; while( addr_info0 != NULL && addr_info0->ai_addr != NULL && ( sa = (struct sockaddr_in*)addr_info0->ai_addr ) != NULL && !found_addr ) { localaddr = inet_ntoa( sa->sin_addr ); - if ( localaddr != NULL ) - g_print( "[%s] localaddr = %s\n", __FUNCTION__, localaddr ); if ( localaddr != NULL && ( g_strrstr( localaddr, "127" ) == NULL ) ) { - g_print( "[%s] Trying the address %s (err = %d).\n", - __FUNCTION__, localaddr, err ); - g_print( "[%s] Found local address %s!\n", __FUNCTION__, localaddr ); str = g_string_assign( str, g_strdup( localaddr ) ); found_addr = TRUE; break; @@ -300,12 +320,12 @@ }; if ( found_addr == FALSE ) { - g_warning("[%s] Could not determine the local hostname address. Setting to %s\n", - __FUNCTION__, localhostname ); - - gchar *prim_addr = get_primary_addr(); + gchar *prim_addr = gmyth_socket_get_primary_addr(); + + if ( prim_addr != NULL ) { + g_warning("[%s] Could not determine the local alphanumerical hostname. Setting to %s\n", + __FUNCTION__, prim_addr ); - if ( prim_addr != NULL ) { str = g_string_assign( str, g_strdup( prim_addr ) ); g_free( prim_addr ); } else { @@ -359,6 +379,93 @@ return gmyth_socket; } +/** Try to open an asynchronous connection to the MythTV backend. + * + * @param fd Socket descriptor. + * @param remote Remote address. + * @param len Newly created socket length field. + * @param timeout Timeval argument with the time interval to timeout before closing. + * @param err Error message number. + * @return Any numerical value below 0, if an error had been found. + */ +static gint +gmyth_socket_try_connect ( gint fd, struct sockaddr *remote, gint len, + struct timeval *timeout, gint *err) +{ + gint saveflags, ret, back_err; + + fd_set fd_w; + + saveflags = fcntl( fd, F_GETFL, 0 ); + if( saveflags < 0 ) { + g_warning( "[%s] Problems when getting socket flags on fcntl.\n", __FUNCTION__ ); + *err=errno; + return -1; + } + + /* Set non blocking */ + if( fcntl( fd, F_SETFL, saveflags | O_NONBLOCK ) < 0) { + g_warning( "[%s] Problems when setting non-blocking using fcntl.\n", __FUNCTION__ ); + *err=errno; + return -1; + } + + /* This will return immediately */ + *err= connect ( fd, remote, len ); + back_err=errno; + + /* restore flags */ + if( fcntl( fd, F_SETFL, saveflags ) < 0) { + g_warning( "[%s] Problems when trying to restore flags with fcntl.\n", __FUNCTION__ ); + *err=errno; + return -1; + } + + /* return unless the connection was successful or the connect is + still in progress. */ + if( *err < 0 && back_err != EINPROGRESS) { + g_warning( "[%s] Connection unsucessfully (it is not in progress).\n", __FUNCTION__ ); + *err = errno; + return -1; + } + + FD_ZERO( &fd_w ); + FD_SET( fd, &fd_w ); + + *err = select( FD_SETSIZE, NULL, &fd_w, NULL, timeout); + if ( *err < 0 ) { + g_warning( "[%s] Connection unsucessfull (timed out).\n", __FUNCTION__ ); + *err=errno; + return -1; + } + + /* 0 means it timeout out & no fds changed */ + if(*err==0) { + close(fd); + *err=ETIMEDOUT; + return -1; + } + + /* Get the return code from the connect */ + len = sizeof( ret ); + *err=getsockopt( fd, SOL_SOCKET, SO_ERROR, &ret, &len ); + + if( *err < 0 ) { + g_warning( "[%s] Connection usnsucessfull.\n", __FUNCTION__ ); + *err=errno; + return -1; + } + + /* ret=0 means success, otherwise it contains the errno */ + if (ret) { + *err=ret; + return -1; + } + + *err=0; + return 0; +} + /** Connects to the backend. * * @param gmyth_socket The GMythSocket instance. @@ -400,9 +507,15 @@ ai_family = %d, ai_protocol = %d\n", __FUNCTION__, hostname, (*gmyth_socket)->sd, inet_ntoa( sa->sin_addr ), addr_info0->ai_addrlen, addr_info0->ai_family, addr_info0->ai_protocol ); - - if ( ( ret_code = connect( (*gmyth_socket)->sd, (struct sockaddr *)addr_info0->ai_addr, - addr_info0->ai_addrlen ) ) < 0 ) + + struct timeval *timeout = g_new0( struct timeval, 1 ); + + /* using 40 seconds timeout */ + timeout->tv_sec = 40; + timeout->tv_usec = 0; + + if ( gmyth_socket_try_connect( (*gmyth_socket)->sd, (struct sockaddr *)addr_info0->ai_addr, + addr_info0->ai_addrlen, timeout, &ret_code ) < 0 ) { g_printerr( "[%s] Error connecting to backend!\n", __FUNCTION__ ); if ( ret_code == ETIMEDOUT ) @@ -419,9 +532,6 @@ (*gmyth_socket)->sd_io_ch = g_io_channel_unix_new( (*gmyth_socket)->sd ); - //if (addr_info_data != NULL ) - //freeaddrinfo( addr_info_data ); - ret = ( ret_code == 0 ) ? TRUE : FALSE ; return ret; @@ -682,8 +792,10 @@ } /* change the return value to a valid one */ - if ( res ) + if ( res ) { mythtv_new_version = mythtv_version; + gmyth_socket->mythtv_version = mythtv_new_version; + } done: if ( payload != NULL )