1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/branches/gmyth-0.1b/src/gmyth_socket.c Wed Feb 14 23:06:17 2007 +0000
1.3 @@ -0,0 +1,1082 @@
1.4 +/**
1.5 + * GMyth Library
1.6 + *
1.7 + * @file gmyth/gmyth_socket.c
1.8 + *
1.9 + * @brief <p> MythTV socket implementation, according to the MythTV Project
1.10 + * (www.mythtv.org).
1.11 + *
1.12 + * This component provides basic socket functionalities to interact with
1.13 + * the Mythtv backend.
1.14 + * <p>
1.15 + *
1.16 + * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
1.17 + * @author Rosfran Lins Borges <rosfran.borges@indt.org.br>
1.18 + *
1.19 + *//*
1.20 + *
1.21 + * This program is free software; you can redistribute it and/or modify
1.22 + * it under the terms of the GNU Lesser General Public License as published by
1.23 + * the Free Software Foundation; either version 2 of the License, or
1.24 + * (at your option) any later version.
1.25 + *
1.26 + * This program is distributed in the hope that it will be useful,
1.27 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.28 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.29 + * GNU General Public License for more details.
1.30 + *
1.31 + * You should have received a copy of the GNU Lesser General Public License
1.32 + * along with this program; if not, write to the Free Software
1.33 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1.34 + */
1.35 +
1.36 +#ifdef HAVE_CONFIG_H
1.37 +#include "config.h"
1.38 +#endif
1.39 +
1.40 +#include "gmyth_socket.h"
1.41 +
1.42 +#include <glib.h>
1.43 +#include <glib/gprintf.h>
1.44 +
1.45 +#include <arpa/inet.h>
1.46 +#include <sys/types.h>
1.47 +#include <sys/socket.h>
1.48 +#include <sys/param.h>
1.49 +#include <netdb.h>
1.50 +#include <net/if.h>
1.51 +#include <errno.h>
1.52 +#include <stdlib.h>
1.53 +
1.54 +#include <unistd.h>
1.55 +#include <netinet/in.h>
1.56 +#include <fcntl.h>
1.57 +#include <signal.h>
1.58 +
1.59 +#include <sys/ioctl.h>
1.60 +
1.61 +#include "gmyth_stringlist.h"
1.62 +#include "gmyth_uri.h"
1.63 +#include "gmyth_debug.h"
1.64 +
1.65 +#define BUFLEN 512
1.66 +#define MYTH_SEPARATOR "[]:[]"
1.67 +#define MYTH_PROTOCOL_FIELD_SIZE 8
1.68 +
1.69 +/* max number of iterations */
1.70 +#define MYTHTV_MAX_VERSION_CHECKS 40
1.71 +
1.72 +// FIXME: put this in the right place
1.73 +#define MYTHTV_VERSION_DEFAULT 30
1.74 +
1.75 +static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
1.76 +
1.77 +static gchar* local_hostname = NULL;
1.78 +
1.79 +static void gmyth_socket_class_init (GMythSocketClass *klass);
1.80 +static void gmyth_socket_init (GMythSocket *object);
1.81 +
1.82 +static void gmyth_socket_dispose (GObject *object);
1.83 +static void gmyth_socket_finalize (GObject *object);
1.84 +
1.85 +G_DEFINE_TYPE(GMythSocket, gmyth_socket, G_TYPE_OBJECT)
1.86 +
1.87 +static void
1.88 +gmyth_socket_class_init (GMythSocketClass *klass)
1.89 +{
1.90 + GObjectClass *gobject_class;
1.91 +
1.92 + gobject_class = (GObjectClass *) klass;
1.93 +
1.94 + gobject_class->dispose = gmyth_socket_dispose;
1.95 + gobject_class->finalize = gmyth_socket_finalize;
1.96 +}
1.97 +
1.98 +static void
1.99 +gmyth_socket_init (GMythSocket *gmyth_socket)
1.100 +{
1.101 +
1.102 + /* gmyth_socket->local_hostname = NULL; */
1.103 +
1.104 +}
1.105 +
1.106 +/** Gets the some important address translation info, from the client socket
1.107 + * that will open a connection.
1.108 + *
1.109 + * @return gint that represents the error number from getaddrinfo().
1.110 + */
1.111 +static gint
1.112 +gmyth_socket_toaddrinfo (const gchar *addr, gint port, struct addrinfo **addrInfo )
1.113 +{
1.114 + struct addrinfo hints;
1.115 + gchar *portStr = NULL;
1.116 + gint errorn = EADDRNOTAVAIL;
1.117 +
1.118 + g_return_val_if_fail ( addr != NULL, -1 );
1.119 + g_debug ("Calling %s\n", __FUNCTION__);
1.120 +
1.121 + /* hints = g_malloc0 ( sizeof(struct addrinfo) ); */
1.122 + memset ( &hints, 0, sizeof(struct addrinfo) );
1.123 + hints.ai_family = AF_INET;
1.124 + hints.ai_socktype = SOCK_STREAM;
1.125 + /* hints.ai_flags = AI_NUMERICHOST; */
1.126 +
1.127 + if ( port != -1 )
1.128 + portStr = g_strdup_printf ( "%d", port );
1.129 + else
1.130 + portStr = NULL;
1.131 +
1.132 + gmyth_debug ("Getting name resolution for: %s, %d\n", addr, port);
1.133 +
1.134 + if ( ( errorn = getaddrinfo(addr, portStr, &hints, addrInfo) ) != 0 ) {
1.135 + g_printerr( "[%s] Socket ERROR: %s\n", __FUNCTION__, gai_strerror(errorn) );
1.136 + }
1.137 + g_free (portStr);
1.138 + /* g_free (hints); */
1.139 + return errorn;
1.140 +}
1.141 +
1.142 +static gint
1.143 +gmyth_socket_find_match_address_uri( GMythURI* uri, gchar *address ) {
1.144 +
1.145 + if ( g_ascii_strcasecmp( gmyth_uri_get_host( uri ), address ) == 0 ) {
1.146 + //g_printerr( "Found URI: %s !!!\n", rui_uri_getvalue(uri) );
1.147 + return 0;
1.148 + } else {
1.149 + return -1;
1.150 + }
1.151 +
1.152 +}
1.153 +
1.154 +static const gchar *PATH_PROC_NET_DEV = "/proc/net/dev";
1.155 +
1.156 +/** Gets the list of all local network interfaces (using the /proc/net/dev directory).
1.157 + *
1.158 + * @param current_connections A list with all the network interfaces are valid,
1.159 + * to be applied just like a filter.
1.160 + * @return List with all the local net interfaces.
1.161 + */
1.162 +static GList *
1.163 +gmyth_socket_get_local_addrs( GList *current_connections )
1.164 +{
1.165 +
1.166 + GList *local_addrs = NULL;
1.167 + FILE *fd;
1.168 + gint s;
1.169 + gchar buffer[256+1];
1.170 + gchar ifaddr[20+1];
1.171 + gchar *ifname;
1.172 + gchar *sep;
1.173 +
1.174 + s = socket(AF_INET, SOCK_DGRAM, 0);
1.175 + if (s < 0)
1.176 + return 0;
1.177 + fd = fopen(PATH_PROC_NET_DEV, "r");
1.178 + fgets(buffer, sizeof(buffer)-1, fd);
1.179 + fgets(buffer, sizeof(buffer)-1, fd);
1.180 + while (!feof(fd)) {
1.181 + ifname = buffer;
1.182 + sep;
1.183 + if (fgets(buffer, sizeof(buffer)-1, fd) == NULL)
1.184 + break;
1.185 + sep = strrchr(buffer, ':');
1.186 + if (sep)
1.187 + *sep = 0;
1.188 + while (*ifname == ' ')
1.189 + ifname++;
1.190 + struct ifreq req;
1.191 + strcpy(req.ifr_name, ifname);
1.192 + if (ioctl(s, SIOCGIFFLAGS, &req) < 0)
1.193 + continue;
1.194 + if (!(req.ifr_flags & IFF_UP))
1.195 + continue;
1.196 + if (req.ifr_flags & IFF_LOOPBACK)
1.197 + continue;
1.198 + if (ioctl(s, SIOCGIFADDR, &req) < 0)
1.199 + continue;
1.200 + g_strlcpy( ifaddr, inet_ntoa(((struct sockaddr_in*)&req.ifr_addr)->sin_addr), sizeof(struct ifaddr)-1 );
1.201 + local_addrs = g_list_append( local_addrs, g_strdup( ifaddr ) );
1.202 +
1.203 + gmyth_debug( "( from the /proc/net/dev) Interface name: %s, address: %s\n",
1.204 + ifname, ifaddr );
1.205 + }
1.206 + fclose(fd);
1.207 + close(s);
1.208 + return local_addrs;
1.209 +
1.210 +}
1.211 +
1.212 +
1.213 +/**
1.214 + * Get only the local addresses from the primary interface
1.215 + */
1.216 +static gchar *
1.217 +gmyth_socket_get_primary_addr()
1.218 +{
1.219 +
1.220 + gchar *if_eth0 = g_new0( gchar, sizeof(struct ifaddr)-1 );
1.221 + GList *if_tmp = NULL;
1.222 +
1.223 + GList *interfs = gmyth_socket_get_local_addrs( NULL );
1.224 +
1.225 + if ( interfs != NULL && ( g_list_length( interfs ) > 0 ) )
1.226 + {
1.227 + /* get the first occurrence (primary interface) */
1.228 + if_tmp = g_list_first( interfs );
1.229 +
1.230 + if ( if_tmp != NULL )
1.231 + g_strlcpy (if_eth0, (gchar *)if_tmp->data, sizeof(struct ifaddr)-1 );
1.232 +
1.233 + }
1.234 +
1.235 + if ( interfs != NULL )
1.236 + g_list_free( interfs );
1.237 +
1.238 + return if_eth0;
1.239 +}
1.240 +
1.241 +/** This function retrieves the local hostname of the
1.242 + * client machine.
1.243 + *
1.244 + * @return GString* get local hostname.
1.245 + */
1.246 +GString *
1.247 +gmyth_socket_get_local_hostname ()
1.248 +{
1.249 +
1.250 + char hname[50];
1.251 + gint res = gethostname (hname, 50);
1.252 +
1.253 + if (res == -1) {
1.254 + g_debug ("Error while getting hostname");
1.255 + return NULL;
1.256 + }
1.257 +
1.258 + return g_string_new (hname);
1.259 +
1.260 +#if 0
1.261 + GString *str = NULL;
1.262 +
1.263 + if ( local_hostname != NULL && strlen(local_hostname) > 0 )
1.264 + return g_string_new( local_hostname );
1.265 +
1.266 + gchar *localaddr = NULL;
1.267 + gboolean found_addr = FALSE;
1.268 + struct addrinfo* addr_info_data = NULL, *addr_info0 = NULL;
1.269 + struct sockaddr_in* sa = NULL;
1.270 + gchar localhostname[MAXHOSTNAMELEN];
1.271 +
1.272 +
1.273 + if (gethostname (localhostname, MAXHOSTNAMELEN) != 0 ) {
1.274 + gmyth_debug ( "Error on gethostname" );
1.275 + }
1.276 + localhostname[MAXHOSTNAMELEN-1] = 0;
1.277 +
1.278 + gint err = gmyth_socket_toaddrinfo (localhostname, -1, &addr_info_data );
1.279 +
1.280 + if ( err == EADDRNOTAVAIL )
1.281 + {
1.282 + g_warning( "[%s] Address (%s) not available. (reason = %d)\n", __FUNCTION__, localhostname, err );
1.283 + return str;
1.284 + }
1.285 +
1.286 + g_static_mutex_lock( &mutex );
1.287 +
1.288 + addr_info0 = addr_info_data;
1.289 +
1.290 + while( addr_info0 != NULL && addr_info0->ai_addr != NULL &&
1.291 + ( sa = (struct sockaddr_in*)addr_info0->ai_addr ) != NULL && !found_addr ) {
1.292 + localaddr = inet_ntoa( sa->sin_addr );
1.293 +
1.294 + if ( localaddr != NULL && ( g_strrstr( localaddr, "127" ) == NULL ) ) {
1.295 + str = g_string_new (localaddr);
1.296 + found_addr = TRUE;
1.297 + g_free (localaddr);
1.298 + break;
1.299 + }
1.300 +/*
1.301 + if (localaddr != NULL) {
1.302 + g_free (localaddr);
1.303 + localaddr = NULL;
1.304 + }
1.305 + */
1.306 +
1.307 + addr_info0 = addr_info0->ai_next;
1.308 + };
1.309 +
1.310 + freeaddrinfo (addr_info_data);
1.311 + addr_info_data = NULL;
1.312 +
1.313 + if ( found_addr == FALSE ) {
1.314 + gchar *prim_addr = gmyth_socket_get_primary_addr();
1.315 +
1.316 + if ( prim_addr != NULL ) {
1.317 + g_warning("[%s] Could not determine the local alphanumerical hostname. Setting to %s\n",
1.318 + __FUNCTION__, prim_addr );
1.319 +
1.320 + str = g_string_new (prim_addr);
1.321 + g_free (prim_addr);
1.322 + } else {
1.323 + str = g_string_new (localhostname);
1.324 + }
1.325 + }
1.326 +
1.327 + g_static_mutex_unlock (&mutex);
1.328 +
1.329 + if ( str != NULL && str->str != NULL )
1.330 + local_hostname = g_strdup( str->str );
1.331 +
1.332 + return str;
1.333 +#endif
1.334 +}
1.335 +
1.336 +static void
1.337 +gmyth_socket_dispose (GObject *object)
1.338 +{
1.339 + GMythSocket *gmyth_socket = GMYTH_SOCKET(object);
1.340 +
1.341 + /* disconnect socket */
1.342 + gmyth_socket_close_connection (gmyth_socket);
1.343 +
1.344 + g_free (gmyth_socket->hostname);
1.345 + gmyth_socket->hostname = NULL;
1.346 +
1.347 + g_free (local_hostname);
1.348 +
1.349 + local_hostname = NULL;
1.350 +
1.351 + G_OBJECT_CLASS (gmyth_socket_parent_class)->dispose (object);
1.352 +}
1.353 +
1.354 +static void
1.355 +gmyth_socket_finalize (GObject *object)
1.356 +{
1.357 + g_signal_handlers_destroy (object);
1.358 +
1.359 + G_OBJECT_CLASS (gmyth_socket_parent_class)->finalize (object);
1.360 +}
1.361 +
1.362 +/** Creates a new instance of GMythSocket.
1.363 + *
1.364 + * @return a new instance of GMythSocket.
1.365 + */
1.366 +GMythSocket*
1.367 +gmyth_socket_new ()
1.368 +{
1.369 + GMythSocket *gmyth_socket = GMYTH_SOCKET (g_object_new(GMYTH_SOCKET_TYPE, NULL));
1.370 +
1.371 + gmyth_socket->mythtv_version = MYTHTV_VERSION_DEFAULT;
1.372 +
1.373 + return gmyth_socket;
1.374 +}
1.375 +
1.376 +/** Try to open an asynchronous connection to the MythTV backend.
1.377 + *
1.378 + * @param fd Socket descriptor.
1.379 + * @param remote Remote address.
1.380 + * @param len Newly created socket length field.
1.381 + * @param timeout Timeval argument with the time interval to timeout before closing.
1.382 + * @param err Error message number.
1.383 + * @return Any numerical value below 0, if an error had been found.
1.384 + */
1.385 +static gint
1.386 +gmyth_socket_try_connect ( gint fd, struct sockaddr *remote, gint len,
1.387 + struct timeval *timeout, gint *err)
1.388 +{
1.389 + /*g_return_val_if_fail( timeout != NULL, 0 );*/
1.390 + gint saveflags, ret, back_err;
1.391 +
1.392 + fd_set fd_w;
1.393 +
1.394 + saveflags = fcntl( fd, F_GETFL, 0 );
1.395 + if( saveflags < 0 ) {
1.396 + g_warning( "[%s] Problems when getting socket flags on fcntl.\n", __FUNCTION__ );
1.397 + *err=errno;
1.398 + return -1;
1.399 + }
1.400 +
1.401 + /* Set non blocking */
1.402 + if( fcntl( fd, F_SETFL, saveflags | O_NONBLOCK ) < 0) {
1.403 + g_warning( "[%s] Problems when setting non-blocking using fcntl.\n", __FUNCTION__ );
1.404 + *err=errno;
1.405 + return -1;
1.406 + }
1.407 +
1.408 + /* This will return immediately */
1.409 + *err= connect ( fd, remote, len );
1.410 + back_err=errno;
1.411 +
1.412 + /* restore flags */
1.413 + if( fcntl( fd, F_SETFL, saveflags ) < 0) {
1.414 + g_warning( "[%s] Problems when trying to restore flags with fcntl.\n", __FUNCTION__ );
1.415 + *err=errno;
1.416 + return -1;
1.417 + }
1.418 +
1.419 + /* return unless the connection was successful or the connect is
1.420 + still in progress. */
1.421 + if( *err < 0 && back_err != EINPROGRESS) {
1.422 + g_warning( "[%s] Connection unsucessfully (it is not in progress).\n", __FUNCTION__ );
1.423 + *err = errno;
1.424 + return -1;
1.425 + }
1.426 +
1.427 + FD_ZERO( &fd_w );
1.428 + FD_SET( fd, &fd_w );
1.429 +
1.430 + *err = select( FD_SETSIZE, NULL, &fd_w, NULL, timeout);
1.431 + if ( *err < 0 ) {
1.432 + g_warning( "[%s] Connection unsucessfull (timed out).\n", __FUNCTION__ );
1.433 + *err=errno;
1.434 + return -1;
1.435 + }
1.436 +
1.437 + /* 0 means it timeout out & no fds changed */
1.438 + if(*err==0) {
1.439 + close(fd);
1.440 + *err=ETIMEDOUT;
1.441 + return -1;
1.442 + }
1.443 +
1.444 + /* Get the return code from the connect */
1.445 + len = sizeof( ret );
1.446 + *err=getsockopt( fd, SOL_SOCKET, SO_ERROR, &ret, (socklen_t *) &len);
1.447 +
1.448 + if( *err < 0 ) {
1.449 + g_warning( "[%s] Connection usnsucessfull.\n", __FUNCTION__ );
1.450 + *err=errno;
1.451 + return -1;
1.452 + }
1.453 +
1.454 + /* ret=0 means success, otherwise it contains the errno */
1.455 + if (ret) {
1.456 + *err=ret;
1.457 + return -1;
1.458 + }
1.459 +
1.460 + *err=0;
1.461 + return 0;
1.462 +}
1.463 +
1.464 +/** Connects to the backend.
1.465 + *
1.466 + * @param gmyth_socket The GMythSocket instance.
1.467 + * @param hostname The backend hostname or IP address.
1.468 + * @param port The backend port.
1.469 + * @return TRUE if success, FALSE if error.
1.470 + */
1.471 +
1.472 +
1.473 +gboolean
1.474 +gmyth_socket_connect (GMythSocket *gmyth_socket,
1.475 + const gchar *hostname, gint port)
1.476 +{
1.477 + return gmyth_socket_connect_with_timeout (gmyth_socket,
1.478 + hostname, port, 0);
1.479 +}
1.480 +
1.481 +gboolean
1.482 +gmyth_socket_connect_with_timeout (GMythSocket *gmyth_socket,
1.483 + const gchar *hostname, gint port, guint timeout)
1.484 +{
1.485 + struct addrinfo *addr_info_data = NULL, *addr_info0 = NULL;
1.486 + gint ret_code = -1;
1.487 + gint errno;
1.488 + gboolean ret = TRUE;
1.489 +
1.490 + gmyth_debug ("CONNECTING %s:%d", hostname, port);
1.491 +
1.492 + if ( hostname == NULL )
1.493 + gmyth_debug ( "Invalid hostname parameter!\n");
1.494 +
1.495 + /* store hostname and port number */
1.496 + if (gmyth_socket->hostname != NULL) {
1.497 + //g_free (gmyth_socket->hostname);
1.498 + gmyth_socket->hostname = NULL;
1.499 + }
1.500 +
1.501 + errno = gmyth_socket_toaddrinfo ( hostname, port, &addr_info_data );
1.502 +
1.503 + g_return_val_if_fail( addr_info_data != NULL && hostname != NULL, FALSE );
1.504 +
1.505 + gmyth_socket->hostname = g_strdup( hostname );
1.506 + gmyth_socket->port = port;
1.507 +
1.508 + for ( addr_info0 = addr_info_data; addr_info0; addr_info0 = addr_info_data->ai_next ) {
1.509 + /* init socket descriptor */
1.510 + gmyth_socket->sd = socket( addr_info0->ai_family, addr_info0->ai_socktype,
1.511 + addr_info0->ai_protocol );
1.512 +
1.513 + if ( gmyth_socket->sd < 0 )
1.514 + continue;
1.515 +
1.516 + struct timeval *timeout_val = g_new0 (struct timeval, 1);
1.517 + if (timeout != 0) {
1.518 + /*timeout_val = g_new0 (struct timeval, 1);*/
1.519 +
1.520 + timeout_val->tv_sec = timeout;
1.521 + timeout_val->tv_usec = 0;
1.522 + } else {
1.523 + timeout_val->tv_sec = 5;
1.524 + timeout_val->tv_usec = 100;
1.525 + }
1.526 +
1.527 + if (gmyth_socket_try_connect (gmyth_socket->sd, (struct sockaddr *)addr_info0->ai_addr,
1.528 + addr_info0->ai_addrlen, timeout_val, &ret_code ) < 0 )
1.529 + {
1.530 + g_printerr( "[%s] Error connecting to backend!\n", __FUNCTION__ );
1.531 + if (ret_code == ETIMEDOUT)
1.532 + g_printerr( "[%s]\tBackend host unreachable!\n", __FUNCTION__ );
1.533 +
1.534 + close (gmyth_socket->sd);
1.535 + gmyth_socket->sd = -1;
1.536 + g_printerr ("ERROR: %s\n", gai_strerror(ret_code));
1.537 + g_free (timeout_val);
1.538 + continue;
1.539 + }
1.540 +
1.541 + g_free (timeout_val);
1.542 +
1.543 + /* only will be reached if none of the error above occurred */
1.544 + break;
1.545 + }
1.546 +
1.547 + freeaddrinfo (addr_info_data);
1.548 + addr_info_data = NULL;
1.549 +
1.550 + if (gmyth_socket->sd_io_ch != NULL) {
1.551 + g_io_channel_unref (gmyth_socket->sd_io_ch);
1.552 + gmyth_socket->sd_io_ch = NULL;
1.553 + }
1.554 +
1.555 + gmyth_socket->sd_io_ch = g_io_channel_unix_new (gmyth_socket->sd);
1.556 +
1.557 + //GIOFlags flags = g_io_channel_get_flags (gmyth_socket->sd_io_ch);
1.558 + /* unset the nonblock flag */
1.559 + //flags &= ~G_IO_FLAG_NONBLOCK;
1.560 + /* unset the nonblocking stuff for some time, because GNUTLS doesn't like
1.561 + * that */
1.562 + //g_io_channel_set_flags (gmyth_socket->sd_io_ch, flags, NULL);
1.563 +
1.564 + ret = ( ret_code == 0 ) ? TRUE : FALSE ;
1.565 + return ret;
1.566 +}
1.567 +
1.568 +/** Gets the GIOChannel associated to the given GMythSocket.
1.569 + *
1.570 + * @param gmyth_socket The GMythSocket instance.
1.571 + */
1.572 +GIOChannel *
1.573 +gmyth_socket_get_io_channel( GMythSocket *gmyth_socket )
1.574 +{
1.575 + g_return_val_if_fail( gmyth_socket != NULL, NULL );
1.576 +
1.577 + return gmyth_socket->sd_io_ch;
1.578 +}
1.579 +
1.580 +/** Verifies if the socket is able to read.
1.581 + *
1.582 + * @param gmyth_socket The GMythSocket instance.
1.583 + * @return TRUE if the socket is able to read, FALSE if not.
1.584 + */
1.585 +gboolean
1.586 +gmyth_socket_is_able_to_read( GMythSocket *gmyth_socket )
1.587 +{
1.588 + gboolean ret = TRUE;
1.589 +
1.590 + /* verify if the input (read) buffer is ready to receive data */
1.591 + GIOCondition io_cond = g_io_channel_get_buffer_condition( gmyth_socket->sd_io_ch );
1.592 +
1.593 + if ( ( io_cond & G_IO_IN ) == 0 ) {
1.594 + g_warning ("[%s] IO channel is not able to send data!\n", __FUNCTION__);
1.595 + ret = FALSE;
1.596 + }
1.597 +
1.598 + return ret;
1.599 +
1.600 +}
1.601 +
1.602 +/** Verifies if the socket is able to write.
1.603 + *
1.604 + * @param gmyth_socket The GMythSocket instance.
1.605 + * @return TRUE if the socket is able to write, FALSE if not.
1.606 + */
1.607 +gboolean
1.608 +gmyth_socket_is_able_to_write( GMythSocket *gmyth_socket )
1.609 +{
1.610 + gboolean ret = TRUE;
1.611 +
1.612 + /* verify if the input (read) buffer is ready to receive data */
1.613 + GIOCondition io_cond = g_io_channel_get_buffer_condition( gmyth_socket->sd_io_ch );
1.614 +
1.615 + if ( ( ( io_cond & G_IO_OUT ) == 0 ) || ( ( io_cond & G_IO_HUP ) == 0 ) ) {
1.616 + g_warning ("[%s] IO channel is not able to send data!\n", __FUNCTION__);
1.617 + ret = FALSE;
1.618 + }
1.619 +
1.620 + return ret;
1.621 +
1.622 +}
1.623 +
1.624 +/** Sends a command to the backend.
1.625 + *
1.626 + * @param gmyth_socket the GMythSocket instance.
1.627 + * @param command The string command to be sent.
1.628 + */
1.629 +gboolean
1.630 +gmyth_socket_send_command(GMythSocket *gmyth_socket, GString *command)
1.631 +{
1.632 + gboolean ret = TRUE;
1.633 +
1.634 + GIOStatus io_status = G_IO_STATUS_NORMAL;
1.635 + //GIOCondition io_cond;
1.636 + GError* error = NULL;
1.637 +
1.638 +
1.639 + gchar *buffer = NULL;
1.640 +
1.641 + gsize bytes_written = 0;
1.642 +
1.643 + if( command == NULL || ( command->len <= 0 ) || command->str == NULL ) {
1.644 + g_warning ("[%s] Invalid NULL command parameter!\n", __FUNCTION__);
1.645 + ret = FALSE;
1.646 + goto done;
1.647 + }
1.648 +
1.649 + //g_static_mutex_lock( &mutex );
1.650 + gmyth_debug ("Sending command to backend: %s\n", command->str);
1.651 +
1.652 + buffer = g_strnfill( BUFLEN, ' ' );
1.653 + g_snprintf( buffer, MYTH_PROTOCOL_FIELD_SIZE+1, "%-8d", command->len);
1.654 +
1.655 + command = g_string_prepend(command, buffer);
1.656 +
1.657 + /* write bytes to socket */
1.658 + io_status = g_io_channel_write_chars( gmyth_socket->sd_io_ch, command->str,
1.659 + command->len, &bytes_written, &error );
1.660 +
1.661 +
1.662 + if( (io_status == G_IO_STATUS_ERROR) || ( bytes_written <= 0 ) ) {
1.663 + g_warning ("[%s] Error while writing to socket", __FUNCTION__);
1.664 + ret = FALSE;
1.665 + } else if ( bytes_written < command->len ) {
1.666 + g_warning ("[%s] Not all data was written socket", __FUNCTION__);
1.667 + ret = FALSE;
1.668 + }
1.669 +
1.670 + io_status = g_io_channel_flush( gmyth_socket->sd_io_ch, &error );
1.671 +
1.672 + if ( ( bytes_written != command->len ) || ( io_status == G_IO_STATUS_ERROR ) )
1.673 + {
1.674 + g_warning ("[%s] Some problem occurred when sending data to the socket\n", __FUNCTION__);
1.675 +
1.676 + ret = TRUE;
1.677 + }
1.678 +
1.679 + //g_static_mutex_unlock( &mutex );
1.680 +done:
1.681 + if ( error != NULL ) {
1.682 + g_printerr( "[%s] Error found reading data from IO channel: (%d, %s)\n", __FUNCTION__, error->code, error->message );
1.683 + ret = FALSE;
1.684 + g_error_free( error );
1.685 + }
1.686 +
1.687 + if ( buffer!= NULL )
1.688 + g_free( buffer );
1.689 +
1.690 + return ret;
1.691 +}
1.692 +
1.693 +/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
1.694 + * supported by the backend and send the "ANN" command.
1.695 + *
1.696 + * @param gmyth_socket the GMythSocket instance.
1.697 + * @param hostname_backend The backend hostname or IP address.
1.698 + * @param port The backend port to connect.
1.699 + * @param blocking_client A flag to choose between blocking and non-blocking
1.700 + * @param with_events Sets the connection flag to receive events.
1.701 + * backend connection.
1.702 + */
1.703 +static gboolean
1.704 +gmyth_socket_connect_to_backend_and_events (GMythSocket *gmyth_socket,
1.705 + const gchar *hostname_backend, gint port, gboolean blocking_client,
1.706 + gboolean with_events)
1.707 +{
1.708 + if (!gmyth_socket_connect (gmyth_socket, hostname_backend, port)) {
1.709 + g_warning ("[%s] Could not open socket to backend machine [%s]\n", __FUNCTION__,
1.710 + hostname_backend );
1.711 + return FALSE;
1.712 + }
1.713 +
1.714 + if ( gmyth_socket_check_protocol_version (gmyth_socket) ) {
1.715 +
1.716 + GString *result;
1.717 + GString *base_str = g_string_new("");
1.718 + GString *hostname = NULL;
1.719 +
1.720 + hostname = gmyth_socket_get_local_hostname();
1.721 + if (hostname == NULL) {
1.722 + g_debug ("Hostname not available, setting to n800frontend\n");
1.723 + hostname = g_strdup ("n800frontend");
1.724 + }
1.725 +
1.726 + g_string_printf(base_str, "ANN %s %s %u",
1.727 + (blocking_client ? "Playback" : "Monitor"),
1.728 + hostname->str, with_events);
1.729 +
1.730 + gmyth_socket_send_command (gmyth_socket, base_str);
1.731 + result = gmyth_socket_receive_response (gmyth_socket);
1.732 +
1.733 + if (result != NULL) {
1.734 + gmyth_debug ("Response received from backend: %s", result->str);
1.735 + g_string_free (result, TRUE);
1.736 + }
1.737 +
1.738 + g_string_free (hostname, TRUE);
1.739 + g_string_free (base_str, TRUE);
1.740 +
1.741 + return TRUE;
1.742 + } else {
1.743 + g_warning ("[%s] GMythSocket could not connect to the backend", __FUNCTION__);
1.744 + return FALSE;
1.745 + }
1.746 +}
1.747 +
1.748 +/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
1.749 + * supported by the backend and send the "ANN" command.
1.750 + *
1.751 + * @param gmyth_socket the GMythSocket instance.
1.752 + * @param hostname_backend The backend hostname or IP address.
1.753 + * @param port The backend port to connect.
1.754 + * @param blocking_client A flag to choose between blocking and non-blocking
1.755 + */
1.756 +gboolean
1.757 +gmyth_socket_connect_to_backend (GMythSocket *gmyth_socket,
1.758 + const gchar *hostname_backend, gint port, gboolean blocking_client)
1.759 +{
1.760 + if (!gmyth_socket_connect_to_backend_and_events ( gmyth_socket, hostname_backend, port,
1.761 + blocking_client, FALSE) ) {
1.762 + gmyth_debug ("Could not open socket to backend machine [%s]\n",
1.763 + hostname_backend );
1.764 + return FALSE;
1.765 + }
1.766 +
1.767 + return TRUE;
1.768 +
1.769 +}
1.770 +
1.771 +/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
1.772 + * supported by the backend and send the "ANN" command.
1.773 + *
1.774 + * @param gmyth_socket the GMythSocket instance.
1.775 + * @param hostname_backend The backend hostname or IP address.
1.776 + * @param port The backend port to connect.
1.777 + * @param blocking_client A flag to choose between blocking and non-blocking
1.778 + */
1.779 +gboolean
1.780 +gmyth_socket_connect_to_backend_events (GMythSocket *gmyth_socket,
1.781 + const gchar *hostname_backend, gint port, gboolean blocking_client)
1.782 +{
1.783 + if (!gmyth_socket_connect_to_backend_and_events ( gmyth_socket, hostname_backend, port,
1.784 + blocking_client, TRUE) ) {
1.785 + gmyth_debug ("Could not open socket to backend machine in order to receive events [%s]\n",
1.786 + hostname_backend );
1.787 + return FALSE;
1.788 + }
1.789 +
1.790 + return TRUE;
1.791 +}
1.792 +
1.793 +/** Closes the socket connection to the backend.
1.794 + *
1.795 + * @param gmyth_socket The GMythSocket instance.
1.796 + */
1.797 +void
1.798 +gmyth_socket_close_connection (GMythSocket *gmyth_socket)
1.799 +{
1.800 + close (gmyth_socket->sd);
1.801 + gmyth_socket->sd = -1;
1.802 +
1.803 + if (gmyth_socket->sd_io_ch != NULL) {
1.804 + g_io_channel_unref (gmyth_socket->sd_io_ch);
1.805 + gmyth_socket->sd_io_ch = NULL;
1.806 + }
1.807 +}
1.808 +
1.809 +
1.810 +/** Try the MythTV version numbers, and get the version returned by
1.811 + * the possible REJECT message, in order to contruct a new
1.812 + * MythTV version request.
1.813 + *
1.814 + * @param gmyth_socket The GMythSocket instance.
1.815 + * @param mythtv_version The Mythtv protocol version to be tested
1.816 + *
1.817 + * @return The actual MythTV the client is connected to.
1.818 + */
1.819 +gint
1.820 +gmyth_socket_check_protocol_version_number (GMythSocket *gmyth_socket, gint mythtv_version)
1.821 +{
1.822 + GString *response = NULL;
1.823 + GString *payload = NULL;
1.824 + gboolean res = TRUE;
1.825 + gint mythtv_new_version = MYTHTV_CANNOT_NEGOTIATE_VERSION;
1.826 + guint max_iterations = MYTHTV_MAX_VERSION_CHECKS;
1.827 +
1.828 +try_new_version:
1.829 + payload = g_string_new ("MYTH_PROTO_VERSION");
1.830 + g_string_append_printf( payload, " %d", mythtv_version );
1.831 +
1.832 + gmyth_socket_send_command(gmyth_socket, payload);
1.833 + response = gmyth_socket_receive_response(gmyth_socket);
1.834 +
1.835 + if (response == NULL) {
1.836 + g_warning ("[%s] Check protocol version error! Not answered!", __FUNCTION__);
1.837 + res = FALSE;
1.838 + goto done;
1.839 + }
1.840 +
1.841 + res = g_str_has_prefix (response->str, "ACCEPT");
1.842 + if (!res) {
1.843 + g_warning ("[%s] Protocol version request error: %s", __FUNCTION__, response->str);
1.844 + /* get the version number returned by the REJECT message */
1.845 + if ( ( res = g_str_has_prefix (response->str, "REJECT") ) == TRUE ) {
1.846 + gchar *new_version = NULL;
1.847 + new_version = g_strrstr( response->str, "]" );
1.848 + if (new_version!=NULL) {
1.849 + ++new_version; /* skip ']' character */
1.850 + if ( new_version != NULL ) {
1.851 + gmyth_debug ( "[%s] got MythTV version = %s.\n", __FUNCTION__, new_version );
1.852 + mythtv_version = (gint)g_ascii_strtoull (new_version, NULL, 10 );
1.853 + /* do reconnection to the socket (socket is closed if the MythTV version was wrong) */
1.854 + gmyth_socket_connect( gmyth_socket, gmyth_socket->hostname, gmyth_socket->port );
1.855 + new_version =NULL;
1.856 + if ( --max_iterations > 0 )
1.857 + goto try_new_version;
1.858 + else
1.859 + goto done;
1.860 + }
1.861 + }
1.862 + }
1.863 + }
1.864 +
1.865 + /* change the return value to a valid one */
1.866 + if ( res ) {
1.867 + mythtv_new_version = mythtv_version;
1.868 + gmyth_socket->mythtv_version = mythtv_new_version;
1.869 + }
1.870 +
1.871 +done:
1.872 + if ( payload != NULL )
1.873 + g_string_free (payload, TRUE);
1.874 + if ( response != NULL )
1.875 + g_string_free (response, TRUE);
1.876 +
1.877 + return mythtv_new_version;
1.878 +}
1.879 +
1.880 +/** Verifies if the Mythtv backend supported the GMyth supported version.
1.881 + *
1.882 + * @param gmyth_socket The GMythSocket instance.
1.883 + * @return TRUE if supports, FALSE if not.
1.884 + */
1.885 +gboolean
1.886 +gmyth_socket_check_protocol_version (GMythSocket *gmyth_socket)
1.887 +{
1.888 + return ( ( gmyth_socket->mythtv_version =
1.889 + gmyth_socket_check_protocol_version_number ( gmyth_socket,
1.890 + MYTHTV_VERSION_DEFAULT ) ) != MYTHTV_CANNOT_NEGOTIATE_VERSION );
1.891 +}
1.892 +
1.893 +/** Returns the Mythtv backend supported version.
1.894 + *
1.895 + * @param gmyth_socket The GMythSocket instance.
1.896 + * @return The actual MythTV version number.
1.897 + */
1.898 +gint
1.899 +gmyth_socket_get_protocol_version (GMythSocket *gmyth_socket)
1.900 +{
1.901 + return gmyth_socket->mythtv_version;
1.902 +}
1.903 +
1.904 +/** Receives a backend answer after a gmyth_socket_send_command_call ().
1.905 + *
1.906 + * @param gmyth_socket The GMythSocket instance.
1.907 + * @return The response received, or NULL if error or nothing was received.
1.908 + */
1.909 +GString*
1.910 +gmyth_socket_receive_response(GMythSocket *gmyth_socket)
1.911 +{
1.912 + GIOStatus io_status = G_IO_STATUS_NORMAL;
1.913 + GError* error = NULL;
1.914 + gchar *buffer;
1.915 +
1.916 + GString *str = NULL;
1.917 +
1.918 + gsize bytes_read = 0;
1.919 + gint len = 0;
1.920 + GIOCondition io_cond = g_io_channel_get_buffer_condition (gmyth_socket->sd_io_ch);
1.921 +
1.922 + g_return_val_if_fail( gmyth_socket != NULL, NULL );
1.923 +
1.924 + /* verify if the input (read) buffer is ready to receive data */
1.925 +
1.926 + //g_static_mutex_lock( &mutex );
1.927 +
1.928 + //buffer = g_new0 (gchar, MYTH_PROTOCOL_FIELD_SIZE);
1.929 + buffer = g_strnfill (MYTH_PROTOCOL_FIELD_SIZE, ' ');
1.930 + io_status = g_io_channel_read_chars (gmyth_socket->sd_io_ch, buffer, MYTH_PROTOCOL_FIELD_SIZE, &bytes_read, &error);
1.931 +
1.932 + /* verify if the input (read) buffer is ready to receive data */
1.933 + io_cond = g_io_channel_get_buffer_condition (gmyth_socket->sd_io_ch);
1.934 +
1.935 + gmyth_debug ( "[%s] Bytes read = %d\n", __FUNCTION__, bytes_read );
1.936 +
1.937 + if( (io_status == G_IO_STATUS_ERROR) || (bytes_read <= 0) ) {
1.938 + g_warning ("[%s] Error in mythprotocol response from backend\n", __FUNCTION__);
1.939 + str = NULL;
1.940 + //return NULL;
1.941 + } else {
1.942 +
1.943 + io_status = g_io_channel_flush( gmyth_socket->sd_io_ch, &error );
1.944 + /* verify if the input (read) buffer is ready to receive data */
1.945 + io_cond = g_io_channel_get_buffer_condition( gmyth_socket->sd_io_ch );
1.946 +
1.947 + //if ( ( io_cond & G_IO_IN ) != 0 ) {
1.948 + //gchar *buffer_aux = NULL;
1.949 +
1.950 + /* removes trailing whitespace */
1.951 + //buffer_aux = g_strstrip (buffer);
1.952 + len = (gint)g_ascii_strtoull ( g_strstrip (buffer), NULL, 10 );
1.953 +
1.954 + if (buffer != NULL) {
1.955 + g_free (buffer);
1.956 + buffer = NULL;
1.957 + }
1.958 +
1.959 + /*
1.960 + if (buffer_aux != NULL) {
1.961 + g_free (buffer_aux);
1.962 + buffer_aux = NULL;
1.963 + }
1.964 + */
1.965 +
1.966 + buffer = g_new0 (gchar, len+1);
1.967 +
1.968 + bytes_read = 0;
1.969 + io_status = g_io_channel_read_chars( gmyth_socket->sd_io_ch, buffer, len, &bytes_read, &error);
1.970 + buffer[bytes_read] = '\0';
1.971 + //}
1.972 + }
1.973 +
1.974 + //g_static_mutex_unlock( &mutex );
1.975 +
1.976 + gmyth_debug ("Response received from backend: {%s}\n", buffer);
1.977 +
1.978 + if ( ( bytes_read != len ) || ( io_status == G_IO_STATUS_ERROR ) )
1.979 + str = NULL;
1.980 + else
1.981 + str = g_string_new (buffer);
1.982 +
1.983 + if ( error != NULL ) {
1.984 + g_printerr( "[%s] Error found receiving response from the IO channel: (%d, %s)\n", __FUNCTION__, error->code, error->message );
1.985 + str = NULL;
1.986 + g_error_free (error);
1.987 + }
1.988 +
1.989 + g_free (buffer);
1.990 + return str;
1.991 +}
1.992 +
1.993 +/** Format a Mythtv command from the str_list entries and send it to backend.
1.994 + *
1.995 + * @param gmyth_socket The GMythSocket instance.
1.996 + * @param str_list The string list to form the command
1.997 + * @return TRUE if command was sent, FALSE if any error happens.
1.998 + */
1.999 +gboolean
1.1000 +gmyth_socket_write_stringlist(GMythSocket *gmyth_socket, GMythStringList* str_list)
1.1001 +{
1.1002 +
1.1003 + GList *tmp_list = NULL;
1.1004 + GPtrArray *ptr_array = NULL;
1.1005 + gchar *str_array = NULL;
1.1006 +
1.1007 + g_static_mutex_lock( &mutex );
1.1008 +
1.1009 + ptr_array = g_ptr_array_sized_new (g_list_length(str_list->glist));
1.1010 +
1.1011 + // FIXME: change this implementation!
1.1012 + tmp_list = str_list->glist;
1.1013 + for(; tmp_list; tmp_list = tmp_list->next) {
1.1014 + if ( tmp_list->data != NULL ) {
1.1015 + g_ptr_array_add(ptr_array, ((GString*)tmp_list->data)->str);
1.1016 + } else {
1.1017 + g_ptr_array_add (ptr_array, "");
1.1018 + }
1.1019 + }
1.1020 + g_ptr_array_add(ptr_array, NULL); // g_str_joinv() needs a NULL terminated string
1.1021 +
1.1022 + str_array = g_strjoinv (MYTH_SEPARATOR, (gchar **) (ptr_array->pdata));
1.1023 +
1.1024 + g_static_mutex_unlock( &mutex );
1.1025 +
1.1026 + gmyth_debug ( "[%s] Sending socket request: %s\n", __FUNCTION__, str_array );
1.1027 +
1.1028 + // Sends message to backend
1.1029 + // TODO: implement looping to send remaining data, and add timeout testing!
1.1030 + GString *command = g_string_new(str_array);
1.1031 + gmyth_socket_send_command(gmyth_socket, command);
1.1032 + g_string_free (command, TRUE);
1.1033 +
1.1034 + g_free (str_array);
1.1035 + g_ptr_array_free (ptr_array, TRUE);
1.1036 +
1.1037 + return TRUE;
1.1038 +}
1.1039 +
1.1040 +/* Receives a backend command response and split it into the given string list.
1.1041 + *
1.1042 + * @param gmyth_socket The GMythSocket instance.
1.1043 + * @param str_list the string list to be filled.
1.1044 + * @return The number of received strings.
1.1045 + */
1.1046 +gint
1.1047 +gmyth_socket_read_stringlist (GMythSocket *gmyth_socket, GMythStringList* str_list)
1.1048 +{
1.1049 + GString *response;
1.1050 + gchar **str_array;
1.1051 + gint i;
1.1052 +
1.1053 + response = gmyth_socket_receive_response(gmyth_socket);
1.1054 + g_static_mutex_lock( &mutex );
1.1055 +
1.1056 + gmyth_string_list_clear_all (str_list);
1.1057 + str_array = g_strsplit (response->str, MYTH_SEPARATOR, -1);
1.1058 +
1.1059 + for (i=0; i< g_strv_length (str_array); i++) {
1.1060 + gmyth_string_list_append_char_array (str_list, str_array[i] );
1.1061 + }
1.1062 + g_static_mutex_unlock( &mutex );
1.1063 +
1.1064 + g_string_free (response, TRUE);
1.1065 + g_strfreev (str_array);
1.1066 +
1.1067 + return gmyth_string_list_length (str_list);
1.1068 +}
1.1069 +
1.1070 +/** Formats a Mythtv protocol command based on str_list and sends it to
1.1071 + * the connected backend. The backend response is overwritten into str_list.
1.1072 + *
1.1073 + * @param gmyth_socket The GMythSocket instance.
1.1074 + * @param str_list The string list to be sent, and on which the answer
1.1075 + * will be written.
1.1076 + * @return TRUE if command was sent and an answer was received, FALSE if any
1.1077 + * error happens.
1.1078 + */
1.1079 +gint
1.1080 +gmyth_socket_sendreceive_stringlist (GMythSocket *gmyth_socket, GMythStringList *str_list)
1.1081 +{
1.1082 + gmyth_socket_write_stringlist (gmyth_socket, str_list);
1.1083 +
1.1084 + return gmyth_socket_read_stringlist (gmyth_socket, str_list);
1.1085 +}