gmyth/src/gmyth_monitor_handler.c
author rosfran
Tue Mar 20 15:08:35 2007 +0000 (2007-03-20)
branchtrunk
changeset 419 18916d01fd45
parent 374 296e08ba9339
child 436 7304e78d6307
permissions -rw-r--r--
[svn r424] Added documention.
     1 /**
     2  * GMyth Library
     3  *
     4  * @file gmyth/gmyth_monitor_handler.c
     5  * 
     6  * @brief <p> GMythMonitorHandler deals with the streaming media events remote/local
     7  * that are sent to the MythTV frontend.
     8  *
     9  * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
    10  * @author Rosfran Lins Borges <rosfran.borges@indt.org.br>
    11  *
    12  *//*
    13  * 
    14  * This program is free software; you can redistribute it and/or modify
    15  * it under the terms of the GNU Lesser General Public License as published by
    16  * the Free Software Foundation; either version 2 of the License, or
    17  * (at your option) any later version.
    18  *
    19  * This program is distributed in the hope that it will be useful,
    20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    22  * GNU General Public License for more details.
    23  *
    24  * You should have received a copy of the GNU Lesser General Public License
    25  * along with this program; if not, write to the Free Software
    26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    27  *
    28  * GStreamer MythTV plug-in properties:
    29  * - location (backend server hostname/URL) [ex.: myth://192.168.1.73:28722/1000_1092091.nuv]
    30  * - path (qurl - remote file to be opened)
    31  * - port number *   
    32  */
    33 
    34 #ifdef HAVE_CONFIG_H
    35 #include "config.h"
    36 #endif
    37 
    38 #include <unistd.h>
    39 #include <glib.h>
    40 #include <arpa/inet.h>
    41 #include <sys/types.h>
    42 #include <sys/socket.h>
    43 #include <netdb.h>
    44 #include <errno.h>
    45 #include <stdlib.h>
    46 #include <assert.h>
    47 
    48 #include "gmyth_marshal.h"
    49 
    50 #include "gmyth_monitor_handler.h"
    51 
    52 #include "gmyth.h"
    53  
    54 #define GMYTHTV_QUERY_HEADER		"QUERY_FILETRANSFER "
    55 
    56 #define GMYTHTV_VERSION							30
    57 
    58 #define GMYTHTV_TRANSFER_MAX_WAITS	700
    59 
    60 #define GMYTHTV_BUFFER_SIZE					8*1024
    61 
    62 #ifdef GMYTHTV_ENABLE_DEBUG
    63 #define GMYTHTV_ENABLE_DEBUG				1
    64 #else
    65 #undef GMYTHTV_ENABLE_DEBUG
    66 #endif
    67 
    68 /* this NDEBUG is to maintain compatibility with GMyth library */
    69 #ifndef NDEBUG
    70 #define GMYTHTV_ENABLE_DEBUG				1
    71 #endif
    72 
    73 gboolean gmyth_monitor_handler_listener( GIOChannel *io_channel, 
    74 						GIOCondition io_cond, gpointer data );
    75 
    76 static void gmyth_monitor_handler_default_listener( GMythMonitorHandler *monitor, gint msg_code, gchar* message );
    77 
    78 static void gmyth_monitor_handler_class_init          (GMythMonitorHandlerClass *klass);
    79 static void gmyth_monitor_handler_init                (GMythMonitorHandler *object);
    80 
    81 static void gmyth_monitor_handler_dispose  (GObject *object);
    82 static void gmyth_monitor_handler_finalize (GObject *object);
    83 
    84 static gboolean gmyth_connect_to_backend_monitor (GMythMonitorHandler *monitor);
    85 
    86 void gmyth_monitor_handler_close( GMythMonitorHandler *monitor );
    87 
    88 G_DEFINE_TYPE(GMythMonitorHandler, gmyth_monitor_handler, G_TYPE_OBJECT)
    89 
    90 static void
    91 gmyth_monitor_handler_class_init (GMythMonitorHandlerClass *klass)
    92 {
    93   GObjectClass *gobject_class;
    94   GMythMonitorHandlerClass *gmonitor_class;
    95 
    96   gobject_class = (GObjectClass *) klass;
    97   gmonitor_class = (GMythMonitorHandlerClass *) gobject_class;
    98 
    99   gobject_class->dispose  = gmyth_monitor_handler_dispose;
   100   gobject_class->finalize = gmyth_monitor_handler_finalize;
   101   
   102 	gmonitor_class->backend_events_handler_signal_id = 
   103 		  g_signal_new ("backend-events-handler",
   104 		                 G_TYPE_FROM_CLASS (gmonitor_class),
   105 		                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
   106 		                 0,
   107 										 NULL,
   108 										 NULL,
   109 										 gmyth_marshal_VOID__INT_STRING,
   110 										 G_TYPE_NONE,
   111 										 2,
   112 										 G_TYPE_INT,
   113 										 G_TYPE_STRING);
   114 										 
   115 	gmonitor_class->backend_events_handler = gmyth_monitor_handler_default_listener;
   116 
   117 }
   118 
   119 static void
   120 gmyth_monitor_handler_init (GMythMonitorHandler *monitor)
   121 { 
   122   g_return_if_fail( monitor != NULL );
   123 
   124   monitor->event_sock = NULL;
   125   monitor->hostname = NULL;
   126   monitor->port = 0;
   127   monitor->actual_index = 0;
   128   
   129   monitor->allow_msgs_listener = FALSE;
   130   
   131   /* monitor->backend_msgs = g_hash_table_new( g_int_hash, g_int_equal ); */
   132     
   133   /* it is used for signalizing the event socket consumer thread */
   134   monitor->mutex = g_mutex_new();
   135   
   136   monitor->sid_io_watch = -1;
   137   
   138   monitor->gmyth_monitor_handler_listener = gmyth_monitor_handler_listener;
   139 }
   140 
   141 static void
   142 gmyth_monitor_handler_dispose  (GObject *object)
   143 {
   144 	GMythMonitorHandler *monitor = GMYTH_MONITOR_HANDLER (object);
   145 	GSource* source = NULL;
   146 	GMainContext* context = g_main_context_default();
   147   
   148   gmyth_monitor_handler_close(monitor);
   149 
   150 	monitor->allow_msgs_listener = FALSE;
   151 
   152 	if ( context != NULL && monitor->sid_io_watch != -1 )
   153 	{
   154 		g_main_context_acquire( context );
   155 		
   156 		source = g_main_context_find_source_by_id( context, 
   157 						monitor->sid_io_watch );
   158 		
   159 		if ( source != NULL )
   160 		{
   161 			g_source_destroy( source );
   162 		}
   163 		
   164 		g_main_context_release( context );
   165 		
   166 	}
   167 
   168   /* mutex to control access to the event socket consumer thread */
   169   if ( monitor->mutex != NULL )
   170   {
   171   	g_mutex_free( monitor->mutex );
   172   	monitor->mutex = NULL;
   173   }
   174 	
   175   if ( monitor->event_sock != NULL )  
   176   {
   177   	g_object_unref( monitor->event_sock );
   178   	/*gmyth_socket_close_connection( monitor->event_sock );*/  	
   179   	monitor->event_sock = NULL;
   180   }
   181 
   182   if ( monitor->hostname != NULL )  
   183   {
   184   	g_free( monitor->hostname );
   185   	monitor->hostname = NULL;
   186   }
   187 
   188   if ( monitor->backend_msgs != NULL )
   189   {
   190   	g_hash_table_destroy ( monitor->backend_msgs );
   191   	monitor->backend_msgs = NULL;
   192   }
   193  
   194   /*
   195   if ( io_watcher_cond != NULL )  
   196   {
   197   	g_cond_free( io_watcher_cond );
   198   	io_watcher_cond = NULL;
   199   }
   200   */
   201   
   202   G_OBJECT_CLASS (gmyth_monitor_handler_parent_class)->dispose (object);
   203 }
   204 
   205 static void
   206 gmyth_monitor_handler_finalize (GObject *object)
   207 {
   208   g_signal_handlers_destroy (object);
   209 
   210   G_OBJECT_CLASS (gmyth_monitor_handler_parent_class)->finalize (object);
   211 }
   212 
   213 /** 
   214  * Creates a new instance of GMyth Monitor Handler.
   215  * 
   216  * @return a new instance of the Monitor Handler. 
   217  */
   218 GMythMonitorHandler*
   219 gmyth_monitor_handler_new ( void )
   220 {
   221   GMythMonitorHandler *monitor = GMYTH_MONITOR_HANDLER ( 
   222   					g_object_new ( GMYTH_MONITOR_HANDLER_TYPE, FALSE ) );
   223 
   224   return monitor;
   225 }
   226 
   227 /** 
   228  * Acquire the mutex to have access to the IO Watcher listener.
   229  * 
   230  * @param monitor The GMythMonitorHandler instance.
   231  * @param do_wait Tells the IO Watcher to wait on the GCond. (obsolete)
   232  * 
   233  * @return <code>true</code>, if the access to IO Watcher was acquired. 
   234  */
   235 static gboolean 
   236 myth_control_acquire_context( GMythMonitorHandler *monitor, gboolean do_wait ) 
   237 {
   238 	
   239 	gboolean ret = TRUE;	
   240   	
   241   g_mutex_lock( monitor->mutex );
   242   
   243   return ret;
   244   
   245 }
   246 
   247 /** 
   248  * Release the mutex to have access to the IO Watcher listener.
   249  * 
   250  * @param monitor The GMythMonitorHandler instance.
   251  * 
   252  * @return <code>true</code>, if the access to IO Watcher was released. 
   253  */
   254 static gboolean 
   255 myth_control_release_context( GMythMonitorHandler *monitor ) 
   256 {
   257 	
   258 	gboolean ret = TRUE;
   259     
   260   g_mutex_unlock( monitor->mutex );
   261   
   262   return ret;
   263   
   264 }
   265 
   266 void
   267 gmyth_monitor_handler_close (GMythMonitorHandler *monitor)
   268 {
   269 	monitor->allow_msgs_listener = FALSE;
   270 
   271 #if 0	
   272 	if ( monitor->monitor_th != NULL ) 
   273   {
   274   	g_thread_pool_free( monitor->monitor_th, TRUE, FALSE );
   275   	//g_thread_exit( monitor->monitor_th );
   276   	/*if ( monitor->monitor_th != NULL )
   277   		g_object_unref( monitor->monitor_th );*/
   278   	monitor->monitor_th = NULL;
   279   }
   280 	
   281   if ( monitor->event_sock != NULL )  
   282   {
   283   	gmyth_socket_close_connection( monitor->event_sock );  	
   284   }
   285 #endif
   286 
   287 }
   288 
   289 /** 
   290  * Opens connection the the Monitor socket on MythTV backend server,
   291  * where all status messages are notified to the client.
   292  * 
   293  * @param monitor The GMythMonitorHandler instance.
   294  * @param hostname The remote host name of the MythTV backend server.
   295  * @param port The remote port number of the MythTV backend server.
   296  * 
   297  * @return <code>true</code>, if the connection was successfully opened.
   298  */
   299 gboolean
   300 gmyth_monitor_handler_open (GMythMonitorHandler *monitor, const gchar *hostname, gint port)
   301 {
   302   gboolean ret = TRUE;
   303   
   304   g_return_val_if_fail( hostname != NULL, FALSE );
   305 
   306   if (monitor->hostname != NULL) {
   307     g_free (monitor->hostname);
   308     monitor->hostname = NULL;
   309   }
   310 
   311   monitor->hostname = g_strdup( hostname );
   312   monitor->port = port;
   313 
   314   gmyth_debug ("Monitor event socket --- hostname: %s, port %d\n", monitor->hostname, monitor->port);
   315   
   316   /* configure the event socket */
   317   //if ( NULL == monitor->event_sock ) { 
   318     if (!gmyth_connect_to_backend_monitor (monitor)) {
   319       gmyth_debug( "Connection to backend failed (Event Socket)." );
   320       ret = FALSE;
   321     }
   322    else {
   323     g_warning("Remote monitor event socket already created.\n");
   324   }
   325 
   326   return ret;
   327 
   328 }
   329 
   330 /** 
   331  * Reads the data got from the connection to the Monitor socket,
   332  * and looks for some important status messages.
   333  * 
   334  * @param monitor The GMythMonitorHandler instance.
   335  * @param strlist The GMythStringList instance got from the Monitor remote socket.
   336  * @param back_msg_action A string pointer to the status message detailed description.
   337  * 
   338  * @return The backend status message code ID.
   339  */
   340 static gint
   341 gmyth_monitor_handler_is_backend_message( GMythMonitorHandler *monitor,
   342                         GMythStringList* strlist, gchar **back_msg_action )
   343 {
   344 	gint msg_type = GMYTH_BACKEND_NO_MESSAGE;
   345 	GString *back_msg = NULL;
   346 	
   347 	if ( gmyth_string_list_length(strlist) > 0 )
   348 	{
   349 		
   350 		back_msg = gmyth_string_list_get_string( strlist, 0 );
   351 		if ( back_msg != NULL && back_msg->str != NULL &&
   352 		                                strstr( back_msg->str, "BACKEND" ) != NULL )
   353 		{
   354 			gmyth_debug( "MONITOR HANDLER - Received backend message = %s", back_msg->str );
   355 	  	*back_msg_action = gmyth_string_list_get_char_array( strlist, 1 );
   356 	  	
   357 	  	if ( back_msg_action != NULL )
   358 	  	{	        	
   359 	  	
   360 	    	if ( g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "LIVETV_CHAIN" ) ||
   361 	    			g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "RECORDING_LIST_CHANGE" ) || 
   362 	    			g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "SCHEDULE_CHANGE" ) ||
   363 	    			g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "LIVETV_WATCH" ) )
   364 	    	{
   365 	    		gmyth_debug( "MONITOR: message type == GMYTH_BACKEND_PROGRAM_INFO_CHANGED, msg = %s", *back_msg_action );
   366 	    		msg_type = GMYTH_BACKEND_PROGRAM_INFO_CHANGED;
   367 	    	} else if ( g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "DONE_RECORDING" ) ) {
   368 	    		gmyth_debug( "MONITOR: message type == GMYTH_BACKEND_DONE_RECORDING, msg = %s", *back_msg_action );
   369 	    		msg_type = GMYTH_BACKEND_DONE_RECORDING;
   370 	    	} else if ( g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "QUIT" ) ) {
   371 	    		gmyth_debug( "MONITOR: message type == GMYTH_BACKEND_STOP_LIVETV, msg = %s", *back_msg_action );
   372 	    		msg_type = GMYTH_BACKEND_STOP_LIVETV;	    		
   373 	  		}	    	
   374 	
   375 	      /* g_hash_table_insert ( monitor->backend_msgs,
   376 	                             &(monitor->actual_index), *back_msg_action ); */
   377 		    
   378 		  } /*  if  */
   379 		  
   380 		} /* if */
   381 		
   382 		if ( back_msg != NULL )
   383 		{
   384 			g_string_free( back_msg, TRUE );
   385 			back_msg = NULL;        	
   386 		}
   387 		
   388 	} /* if - Does Monitor got any message from backend? */
   389 	else {
   390 		*back_msg_action = g_strdup("");
   391 	}
   392 	
   393 	return msg_type;
   394 
   395 }
   396 
   397 static void
   398 gmyth_monitor_handler_default_listener( GMythMonitorHandler *monitor, gint msg_code, gchar* message )
   399 {
   400 	//assert( message!= NULL );  
   401 	gmyth_debug( "DEFAULT Signal handler ( msg = %s, code = %d )\n", 
   402 				message, msg_code );
   403 }
   404 
   405 static void
   406 gmyth_monitor_handler_print( GString *str, gpointer ptr )
   407 {
   408 	gmyth_debug( "Backend message event: %s --- ", str->str );
   409 }
   410 
   411 /** 
   412  * Opens connection the the Monitor socket on MythTV backend server,
   413  * where all status messages are notified to the client.
   414  * 
   415  * @param io_channel The GIOChannel instance to the Monitor connection.
   416  * @param io_cond The GIOCondition describing the actual status of the IO Channel.
   417  * @param data Pointer to the GMythMonitorHandler.
   418  * 
   419  * @return <code>true</code>, if the data was successfully read.
   420  */
   421 gboolean
   422 gmyth_monitor_handler_listener( GIOChannel *io_channel, GIOCondition io_cond, gpointer data )
   423 {
   424 	GMythMonitorHandler *monitor = (GMythMonitorHandler*)data; 
   425   GIOStatus io_status;
   426   //GIOCondition io_cond;  
   427   guint recv = 0;
   428   gboolean *ret = g_new0( gboolean, 1 );
   429   *ret = TRUE;
   430   //gboolean ret = TRUE;
   431   gsize len = 0;
   432   
   433   static guint count = 0;
   434 
   435   myth_control_acquire_context ( monitor, TRUE );
   436   
   437   if ( ( io_cond & G_IO_HUP ) != 0 )
   438   {
   439   	*ret = FALSE;
   440   	goto clean_up;
   441 	}
   442   
   443   //GIOChannel *io_channel = monitor->event_sock->sd_io_ch;
   444   //GIOCondition condition = g_io_channel_get_buffer_condition( io_channel );
   445   
   446   GMythStringList *strlist = NULL;
   447   
   448   if ( NULL == io_channel ) {
   449   	gmyth_debug ("Monitor socket is NULL! (GIOChannel)");
   450   	*ret = FALSE;
   451   	goto clean_up;
   452   }
   453     
   454   if (monitor->allow_msgs_listener) {
   455   	++count;
   456   	
   457   	gmyth_debug ("%d - Listening on Monitor socket...!\n", count);
   458 	
   459     do 
   460     {
   461     	
   462     	gint bytes_sent = 0;
   463     	
   464     	strlist = gmyth_string_list_new();
   465     	
   466     	if ( monitor->event_sock != NULL )
   467     	{
   468     		
   469 	      len = gmyth_socket_read_stringlist( monitor->event_sock, strlist );
   470 	      
   471 		    if ( ( len > 0 ) && strlist != NULL && gmyth_string_list_length( strlist ) > 0 ) 
   472 		    { 
   473 			    bytes_sent = gmyth_string_list_get_int( strlist,  0 ); // -1 on backend error
   474 	
   475 		      gmyth_debug ( "[%s] MONITOR: received data buffer from IO event channel... %d strings gone!\n", 
   476 		      		__FUNCTION__, len );
   477 		      		
   478 		      recv += len;
   479 		      
   480 		      /* debug purpose: prints out all the string list elements */
   481 		      g_list_foreach( strlist->glist, (GFunc)gmyth_monitor_handler_print, NULL );
   482 		      
   483 		      gchar *back_msg_action = g_new0( gchar, 1 );
   484 		      gint msg_type = gmyth_monitor_handler_is_backend_message( monitor, strlist, 
   485 		      		&back_msg_action );
   486 		      		
   487 		      if (monitor!=NULL)		      
   488 		        g_signal_emit ( monitor,
   489 		               GMYTH_MONITOR_HANDLER_GET_CLASS (monitor)->backend_events_handler_signal_id,
   490 		               0, /* details */
   491 		               msg_type, back_msg_action );
   492 	               
   493 	        if (back_msg_action!= NULL)
   494 	        	g_free( back_msg_action );
   495 		      
   496 		    }
   497 		    
   498     	}
   499 	    
   500 	    if (strlist!=NULL)
   501 	    {
   502 	    	g_object_unref( strlist );
   503 	    	strlist = NULL;
   504 	    }
   505 
   506       io_cond = g_io_channel_get_buffer_condition( io_channel );
   507 
   508     } while ( recv <= 0 && ( ( io_cond & G_IO_HUP ) == 0 ) );
   509     
   510     gmyth_debug ("\tMONITOR EVENT: Read %d bytes\n", recv );
   511 
   512   } /* main GThread while */
   513   
   514   if ( io_status == G_IO_STATUS_ERROR ) {
   515     gmyth_debug ("Error reading MONITOR event socket.\n");
   516    	*ret = FALSE;
   517    	goto clean_up;   	
   518   }
   519   
   520 clean_up:
   521   myth_control_release_context (monitor);
   522   	
   523  	return *ret;
   524 
   525 }
   526 
   527 /** 
   528  * Opens connection events' socket the the Monitor socket on 
   529  * MythTV backend server.
   530  * 
   531  * @param monitor The GMythMonitorHandler instance.
   532  * 
   533  * @return <code>true</code>, if the socket was successfully opened.
   534  */
   535 static gboolean
   536 gmyth_connect_to_backend_monitor (GMythMonitorHandler *monitor)
   537 {
   538   gboolean ret = TRUE;
   539 
   540   monitor->event_sock = gmyth_socket_new();
   541 
   542   /* Connects the socket, send Mythtv ANN Monitor and verify Mythtv protocol version */ 
   543   if (!gmyth_socket_connect_to_backend_events ( monitor->event_sock,
   544           monitor->hostname, monitor->port, FALSE ) ) 
   545   {
   546     g_object_unref (monitor->event_sock);
   547     monitor->event_sock = NULL;
   548     ret = FALSE;
   549   }
   550   
   551   return ret;
   552 }    
   553 
   554 /** 
   555  * Opens connection the the Monitor socket on MythTV backend server,
   556  * where all status messages are notified to the client.
   557  * 
   558  * @param monitor The GMythMonitorHandler instance.
   559  * @param channel The GIOChannel instance to the Monitor socket.
   560  * 
   561  * @return Pointer to the boolean value, and it is <code>true</code> only if the 
   562  * 				 GMythMonitorHandler could be configured.
   563  */
   564 static gboolean*
   565 gmyth_monitor_handler_setup( GMythMonitorHandler *monitor, GIOChannel *channel )
   566 {
   567 	gboolean *ret = g_new0( gboolean, 1 );
   568 	
   569 	*ret = TRUE;
   570 	
   571   if ( channel != NULL ) {
   572   	
   573   	monitor->allow_msgs_listener = TRUE;
   574   	
   575     monitor->sid_io_watch = g_io_add_watch( channel, G_IO_IN | G_IO_HUP, 
   576     					(GIOFunc)gmyth_monitor_handler_listener, monitor );
   577   } else {
   578   	*ret = FALSE;
   579   	goto cleanup;
   580   }
   581 
   582   if (monitor->sid_io_watch < 0){
   583     gmyth_debug( "[%s] Error adding watch listener function to the IO control channel!\n", __FUNCTION__ );
   584     *ret = FALSE;
   585     goto cleanup;
   586   }
   587   
   588 cleanup:
   589     
   590   return ret;
   591   
   592 }
   593 
   594 /** 
   595  * Starts the MonitorHandler thread to the GIOWatcher.
   596  * 
   597  * @param monitor The GMythMonitorHandler instance.
   598  * 
   599  * @return <code>true</code>, if the MonitorHandler was started.
   600  */
   601 gboolean 
   602 gmyth_monitor_handler_start (GMythMonitorHandler *monitor)
   603 {
   604 	gboolean *ret = g_new0( gboolean, 1 ); 
   605 	*ret = TRUE;
   606 	
   607   ret = gmyth_monitor_handler_setup( monitor, monitor->event_sock->sd_io_ch ); 
   608   if ( *ret )
   609   {
   610   	gmyth_debug ( "\n[%s]\tOK! Starting listener on the MONITOR event socket...[thread location = %p]\n", 
   611   				__FUNCTION__, g_thread_self( ) );
   612   	*ret = TRUE;  	  	
   613   } else {
   614   	gmyth_debug ( "\n[%s]\tERROR! Coudn't start listener on the MONITOR event socket...[thread location = %p]\n", 
   615   				__FUNCTION__, g_thread_self( ) );
   616   	*ret = FALSE;
   617   }
   618 
   619   gmyth_debug( "[%s] Watch listener function over the IO control channel? %s!!!\n", 
   620   			__FUNCTION__, ( *ret == TRUE ? "YES" : "NO" ) );
   621     
   622     return *ret;
   623 }