[svn r106] Added non-blocking socket connection facilities (configurable timeout when connection is broken).
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 )