[svn r106] Added non-blocking socket connection facilities (configurable timeout when connection is broken). trunk
authorrosfran
Fri Nov 24 01:51:47 2006 +0000 (2006-11-24)
branchtrunk
changeset 105ba9bf90e7c4b
parent 104 1e1f1cb810fe
child 106 a4a151d4735d
[svn r106] Added non-blocking socket connection facilities (configurable timeout when connection is broken).
gmyth/src/gmyth_context.c
gmyth/src/gmyth_file_transfer.c
gmyth/src/gmyth_socket.c
     1.1 --- a/gmyth/src/gmyth_context.c	Thu Nov 23 20:51:44 2006 +0000
     1.2 +++ b/gmyth/src/gmyth_context.c	Fri Nov 24 01:51:47 2006 +0000
     1.3 @@ -291,7 +291,6 @@
     1.4  		g_warning ("Database not open while trying to load setting: %s", key->str);
     1.5  	}
     1.6  
     1.7 -
     1.8  	return default_value;
     1.9  }
    1.10  
     2.1 --- a/gmyth/src/gmyth_file_transfer.c	Thu Nov 23 20:51:44 2006 +0000
     2.2 +++ b/gmyth/src/gmyth_file_transfer.c	Fri Nov 24 01:51:47 2006 +0000
     2.3 @@ -439,9 +439,7 @@
     2.4        (*transfer)->sock = sock;
     2.5        strlist = gmyth_string_list_new();
     2.6        
     2.7 -      g_print( "[%s] Protocol version = %d\n", __FUNCTION__, 
     2.8 -      						gmyth_socket_get_protocol_version( (*transfer)->sock ) );      
     2.9 -      if ( gmyth_socket_get_protocol_version( (*transfer)->sock ) > 26 )      
    2.10 +      if ( (*transfer)->mythtv_version > 26 )      
    2.11        	g_string_printf( base_str, "ANN FileTransfer %s %d %d", hostname->str,
    2.12          		(*transfer)->userreadahead, (*transfer)->retries );
    2.13        else
     3.1 --- a/gmyth/src/gmyth_socket.c	Thu Nov 23 20:51:44 2006 +0000
     3.2 +++ b/gmyth/src/gmyth_socket.c	Fri Nov 24 01:51:47 2006 +0000
     3.3 @@ -30,6 +30,10 @@
     3.4   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     3.5   */
     3.6  
     3.7 +#ifdef HAVE_CONFIG_H
     3.8 +#  include "config.h"
     3.9 +#endif
    3.10 +
    3.11  #include <glib.h> 
    3.12  #include <glib/gprintf.h>
    3.13  
    3.14 @@ -41,6 +45,11 @@
    3.15  #include <errno.h>
    3.16  #include <stdlib.h>
    3.17  
    3.18 +#include <unistd.h>
    3.19 +#include <netinet/in.h>
    3.20 +#include <fcntl.h>
    3.21 +#include <signal.h>
    3.22 +
    3.23  #if defined(HAVE_IFADDRS_H)
    3.24  	#include <ifaddrs.h>
    3.25  #else
    3.26 @@ -57,7 +66,7 @@
    3.27  #define MYTH_PROTOCOL_FIELD_SIZE		8
    3.28  
    3.29  /* max number of iterations */
    3.30 -#define MYTHTV_MAX_VERSION_CHECKS		15
    3.31 +#define MYTHTV_MAX_VERSION_CHECKS		40
    3.32  
    3.33  static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
    3.34  
    3.35 @@ -115,7 +124,7 @@
    3.36  }
    3.37  
    3.38  static gint
    3.39 -find_match_address_uri( GMythURI* uri, gchar *address ) {
    3.40 +gmyth_socket_find_match_address_uri( GMythURI* uri, gchar *address ) {
    3.41  
    3.42          if ( g_ascii_strcasecmp( gmyth_uri_gethost( uri ), address ) == 0 ) {
    3.43                  //g_printerr( "Found URI: %s !!!\n", rui_uri_getvalue(uri) );
    3.44 @@ -128,8 +137,14 @@
    3.45  
    3.46  #if defined(HAVE_IFADDRS_H)
    3.47  
    3.48 -GList *
    3.49 -get_local_addrs( GList *current_connections ) {
    3.50 +/** Gets the list of all local network interfaces.
    3.51 + * 
    3.52 + * @param current_connections	A list with all the network interfaces are valid, 
    3.53 + * 		to be applied just like a filter.
    3.54 + * @return List with all the local net interfaces. 
    3.55 + */
    3.56 +static GList *
    3.57 +gmyth_socket_get_local_addrs( GList *current_connections ) {
    3.58  	
    3.59  	GList *local_addrs = NULL;
    3.60  	
    3.61 @@ -155,14 +170,14 @@
    3.62  
    3.63  			ifname = i->ifa_name;
    3.64  			ifIdx = if_nametoindex(ifname);
    3.65 -			g_print( "[%s] Interface name: %s, index: %d, address: %s\n", __FUNCTION__, ifname, ifIdx, addr );
    3.66 +			/* g_debug( "[%s] Interface name: %s, index: %d, address: %s\n", __FUNCTION__, ifname, ifIdx, addr ); */
    3.67  			if ( current_connections == NULL || ( current_connections != NULL && 
    3.68  					g_list_find_custom( current_connections, (gchar *)addr, 
    3.69 -						(GCompareFunc)find_match_address_uri ) == NULL ) )
    3.70 +						(GCompareFunc)gmyth_socket_find_match_address_uri ) == NULL ) )
    3.71  				local_addrs = g_list_append( local_addrs, g_strdup( addr ) );
    3.72  
    3.73  		}
    3.74 -	} // iterates over network interfaces
    3.75 +	} /* iterates over network interfaces */
    3.76  	
    3.77  	freeifaddrs(ifaddr);
    3.78  	
    3.79 @@ -174,8 +189,14 @@
    3.80  
    3.81  static const char *PATH_PROC_NET_DEV = "/proc/net/dev";
    3.82  
    3.83 -GList *
    3.84 -get_local_addrs( GList *current_connections )
    3.85 +/** Gets the list of all local network interfaces (using the /proc/net/dev directory).
    3.86 + * 
    3.87 + * @param current_connections	A list with all the network interfaces are valid, 
    3.88 + * 		to be applied just like a filter.
    3.89 + * @return List with all the local net interfaces. 
    3.90 + */
    3.91 +static GList *
    3.92 +gmyth_socket_get_local_addrs( GList *current_connections )
    3.93  {
    3.94  
    3.95  	GList *local_addrs = NULL;
    3.96 @@ -215,7 +236,7 @@
    3.97  		g_strlcpy( ifaddr, inet_ntoa(((struct sockaddr_in*)&req.ifr_addr)->sin_addr), sizeof(struct ifaddr)-1 );
    3.98  		local_addrs = g_list_append( local_addrs, g_strdup( ifaddr ) );
    3.99  
   3.100 -		g_print( "[%s] ( from the /proc/net/dev) Interface name: %s, address: %s\n", __FUNCTION__, 
   3.101 +		g_debug( "[%s] ( from the /proc/net/dev) Interface name: %s, address: %s\n", __FUNCTION__, 
   3.102  								ifname, ifaddr );
   3.103  	}
   3.104  	fclose(fd);
   3.105 @@ -229,16 +250,14 @@
   3.106  /**
   3.107   * Get only the local addresses from the primary interface
   3.108   */
   3.109 -gchar *
   3.110 -get_primary_addr()
   3.111 +static gchar *
   3.112 +gmyth_socket_get_primary_addr()
   3.113  {
   3.114  	
   3.115  	gchar *if_eth0 = g_new0( gchar, sizeof(struct ifaddr)-1 );
   3.116  	GList *if_tmp = NULL;
   3.117  	
   3.118 -	g_print( "[%s] net if size = %d.\n", __FUNCTION__, sizeof(struct ifaddr)-1 );
   3.119 -	
   3.120 -	GList *interfs = get_local_addrs( NULL );
   3.121 +	GList *interfs = gmyth_socket_get_local_addrs( NULL );
   3.122  	
   3.123  	if ( interfs != NULL && ( g_list_length( interfs ) > 0 ) ) 
   3.124  	{
   3.125 @@ -275,23 +294,24 @@
   3.126  
   3.127      struct sockaddr_in* sa = NULL;
   3.128  
   3.129 -    g_static_mutex_lock( &mutex );
   3.130 -
   3.131      gethostname(localhostname, 1024);
   3.132      gint err = gmyth_socket_toaddrinfo( localhostname, -1,  &addr_info_data );
   3.133 +    
   3.134 +    if ( err == EADDRNOTAVAIL )
   3.135 +    {
   3.136 +    	g_warning( "[%s] Address (%s) not available. (reason = %d)\n", __FUNCTION__, localhostname, err );
   3.137 +    	return str;
   3.138 +    }
   3.139 +    
   3.140 +    g_static_mutex_lock( &mutex );    	
   3.141  
   3.142      addr_info0 = addr_info_data;
   3.143  
   3.144      while( addr_info0 != NULL && addr_info0->ai_addr != NULL && 
   3.145  	    ( sa = (struct sockaddr_in*)addr_info0->ai_addr ) != NULL && !found_addr ) {
   3.146      	localaddr = inet_ntoa( sa->sin_addr );
   3.147 -	    if ( localaddr != NULL ) 
   3.148 -            g_print( "[%s] localaddr = %s\n", __FUNCTION__, localaddr );
   3.149  
   3.150  	    if ( localaddr != NULL && ( g_strrstr( localaddr, "127" ) == NULL ) ) {
   3.151 -    	    g_print( "[%s] Trying the address %s (err = %d).\n", 
   3.152 -                    __FUNCTION__, localaddr, err );
   3.153 -	        g_print( "[%s] Found local address %s!\n", __FUNCTION__, localaddr );
   3.154  	        str = g_string_assign( str, g_strdup( localaddr ) );
   3.155  	        found_addr = TRUE;
   3.156  	        break;
   3.157 @@ -300,12 +320,12 @@
   3.158      };
   3.159  
   3.160      if ( found_addr == FALSE ) {
   3.161 -	    g_warning("[%s] Could not determine the local hostname address. Setting to %s\n",
   3.162 -                    __FUNCTION__, localhostname );
   3.163 -                    
   3.164 -      gchar *prim_addr = get_primary_addr();
   3.165 +      gchar *prim_addr = gmyth_socket_get_primary_addr();
   3.166 +
   3.167 +    	if ( prim_addr != NULL ) {
   3.168 +			    g_warning("[%s] Could not determine the local alphanumerical hostname. Setting to %s\n",
   3.169 +    				__FUNCTION__, prim_addr );
   3.170        
   3.171 -    	if ( prim_addr != NULL ) {
   3.172  	        str = g_string_assign( str, g_strdup( prim_addr ) );
   3.173  	        g_free( prim_addr );
   3.174      	} else {
   3.175 @@ -359,6 +379,93 @@
   3.176      return gmyth_socket;
   3.177  }
   3.178  
   3.179 +/** Try to open an asynchronous connection to the MythTV backend.
   3.180 + * 
   3.181 + * @param fd 			Socket descriptor.
   3.182 + * @param remote 	Remote address.
   3.183 + * @param len 		Newly created socket length field.
   3.184 + * @param timeout	Timeval argument with the time interval to timeout before closing.
   3.185 + * @param err			Error message number.
   3.186 + * @return Any numerical value below 0, if an error had been found.
   3.187 + */
   3.188 +static gint 
   3.189 +gmyth_socket_try_connect ( gint fd, struct sockaddr *remote, gint len,
   3.190 +               struct timeval *timeout, gint *err)
   3.191 +{
   3.192 +	  gint saveflags, ret, back_err;
   3.193 +	  
   3.194 +	  fd_set fd_w;
   3.195 +	
   3.196 +	  saveflags = fcntl( fd, F_GETFL, 0 );
   3.197 +	  if( saveflags < 0 ) {
   3.198 +	    g_warning( "[%s] Problems when getting socket flags on fcntl.\n", __FUNCTION__ );
   3.199 +	    *err=errno;
   3.200 +	    return -1;
   3.201 +	  }
   3.202 +	
   3.203 +	  /* Set non blocking */
   3.204 +	  if( fcntl( fd, F_SETFL, saveflags | O_NONBLOCK ) < 0) {
   3.205 +	  	g_warning( "[%s] Problems when setting non-blocking using fcntl.\n", __FUNCTION__ );
   3.206 +	    *err=errno;
   3.207 +	    return -1;
   3.208 +	  }
   3.209 +	
   3.210 +	  /* This will return immediately */
   3.211 +	  *err= connect ( fd, remote, len );
   3.212 +		back_err=errno;
   3.213 +	
   3.214 +		/* restore flags */
   3.215 +	  if( fcntl( fd, F_SETFL, saveflags ) < 0) {
   3.216 +	    g_warning( "[%s] Problems when trying to restore flags with fcntl.\n", __FUNCTION__ );
   3.217 +	    *err=errno;
   3.218 +	    return -1;
   3.219 +	  }
   3.220 +	
   3.221 +	  /* return unless the connection was successful or the connect is
   3.222 +	     still in progress. */
   3.223 +	  if( *err < 0 && back_err != EINPROGRESS) {
   3.224 +	    g_warning( "[%s] Connection unsucessfully (it is not in progress).\n", __FUNCTION__ );
   3.225 +	    *err = errno;
   3.226 +	    return -1;
   3.227 +	  }
   3.228 +	
   3.229 +	  FD_ZERO( &fd_w );
   3.230 +	  FD_SET( fd, &fd_w );
   3.231 +	
   3.232 +	  *err = select( FD_SETSIZE, NULL, &fd_w, NULL, timeout);
   3.233 +	  if ( *err < 0 ) {
   3.234 +	    g_warning( "[%s] Connection unsucessfull (timed out).\n", __FUNCTION__ );
   3.235 +	    *err=errno;
   3.236 +	    return -1;
   3.237 +	  }
   3.238 +	
   3.239 +	  /* 0 means it timeout out & no fds changed */
   3.240 +	  if(*err==0) {
   3.241 +	    close(fd);
   3.242 +	    *err=ETIMEDOUT;
   3.243 +	    return -1;
   3.244 +	  }
   3.245 +	
   3.246 +	  /* Get the return code from the connect */
   3.247 +	  len = sizeof( ret );
   3.248 +	  *err=getsockopt( fd, SOL_SOCKET, SO_ERROR, &ret, &len );
   3.249 +	  
   3.250 +	  if( *err < 0 ) {
   3.251 +	    g_warning( "[%s] Connection usnsucessfull.\n", __FUNCTION__ );
   3.252 +	    *err=errno;
   3.253 +	    return -1;
   3.254 +	  }
   3.255 +	
   3.256 +	  /* ret=0 means success, otherwise it contains the errno */
   3.257 +	  if (ret) {
   3.258 +	    *err=ret;
   3.259 +	    return -1;
   3.260 +	  }
   3.261 +	
   3.262 +	  *err=0;
   3.263 +	  return 0;
   3.264 +}
   3.265 +
   3.266  /** Connects to the backend.
   3.267   * 
   3.268   * @param gmyth_socket The GMythSocket instance.
   3.269 @@ -400,9 +507,15 @@
   3.270  		ai_family = %d, ai_protocol = %d\n",
   3.271  		__FUNCTION__, hostname, (*gmyth_socket)->sd, inet_ntoa( sa->sin_addr ),
   3.272  		addr_info0->ai_addrlen, addr_info0->ai_family, addr_info0->ai_protocol );
   3.273 -
   3.274 -	if ( ( ret_code = connect( (*gmyth_socket)->sd, (struct sockaddr *)addr_info0->ai_addr,
   3.275 -			addr_info0->ai_addrlen ) ) < 0 )
   3.276 +		
   3.277 +	struct timeval *timeout = g_new0( struct timeval, 1 );
   3.278 +	
   3.279 +	/* using 40 seconds timeout */
   3.280 +	timeout->tv_sec = 40;
   3.281 +	timeout->tv_usec = 0;
   3.282 +		
   3.283 +	if ( gmyth_socket_try_connect( (*gmyth_socket)->sd, (struct sockaddr *)addr_info0->ai_addr,
   3.284 +			addr_info0->ai_addrlen, timeout, &ret_code ) < 0 )
   3.285  	{
   3.286  	    g_printerr( "[%s] Error connecting to backend!\n", __FUNCTION__ );
   3.287  	    if ( ret_code == ETIMEDOUT )
   3.288 @@ -419,9 +532,6 @@
   3.289  
   3.290      (*gmyth_socket)->sd_io_ch = g_io_channel_unix_new( (*gmyth_socket)->sd );
   3.291  
   3.292 -    //if (addr_info_data != NULL )
   3.293 -    //freeaddrinfo( addr_info_data );
   3.294 -
   3.295      ret = ( ret_code == 0 ) ? TRUE : FALSE ;
   3.296  
   3.297      return ret;
   3.298 @@ -682,8 +792,10 @@
   3.299      }
   3.300      
   3.301      /* change the return value to a valid one */
   3.302 -    if ( res )
   3.303 +    if ( res ) {
   3.304      	mythtv_new_version = mythtv_version;
   3.305 +    	gmyth_socket->mythtv_version = mythtv_new_version;
   3.306 +    }
   3.307  
   3.308  done:
   3.309      if ( payload != NULL )