branches/gmyth-0.1b/src/gmyth_monitor_handler.c
author rosfran
Fri Feb 09 20:28:36 2007 +0000 (2007-02-09)
branchtrunk
changeset 344 21a15c29957b
permissions -rw-r--r--
[svn r346] Fixes to the socket closing and memory cleanup.
     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 //GMainContext *io_watcher_context = NULL;
    74 
    75 //GThread *monitor_th = NULL;
    76 
    77 //static gboolean* myth_control_sock_listener( GIOChannel *io_channel );
    78 //static gboolean gmyth_monitor_handler_listener( GIOChannel *io_channel, 
    79 //						GIOCondition condition, gpointer data );
    80 
    81 //gboolean* gmyth_monitor_handler_listener( GMythMonitorHandler *monitor );
    82 
    83 void gmyth_monitor_handler_listener( GMythMonitorHandler *monitor, gpointer user_data );
    84 
    85 static void gmyth_monitor_handler_default_listener( GMythMonitorHandler *monitor, gint msg_code, gchar* message );
    86 
    87 static GMutex*				mutex 					 = NULL;
    88 
    89 //static GCond*					io_watcher_cond  = NULL;
    90 
    91 static void gmyth_monitor_handler_class_init          (GMythMonitorHandlerClass *klass);
    92 static void gmyth_monitor_handler_init                (GMythMonitorHandler *object);
    93 
    94 static void gmyth_monitor_handler_dispose  (GObject *object);
    95 static void gmyth_monitor_handler_finalize (GObject *object);
    96 
    97 static gboolean gmyth_connect_to_backend_monitor (GMythMonitorHandler *monitor);
    98 
    99 void gmyth_monitor_handler_close( GMythMonitorHandler *monitor );
   100 
   101 G_DEFINE_TYPE(GMythMonitorHandler, gmyth_monitor_handler, G_TYPE_OBJECT)
   102 
   103 static void
   104 gmyth_monitor_handler_class_init (GMythMonitorHandlerClass *klass)
   105 {
   106   GObjectClass *gobject_class;
   107   GMythMonitorHandlerClass *gmonitor_class;
   108 
   109   gobject_class = (GObjectClass *) klass;
   110   gmonitor_class = (GMythMonitorHandlerClass *) gobject_class;
   111 
   112   gobject_class->dispose  = gmyth_monitor_handler_dispose;
   113   gobject_class->finalize = gmyth_monitor_handler_finalize;
   114   
   115 	gmonitor_class->backend_events_handler_signal_id = 
   116 		  g_signal_new ("backend-events-handler",
   117 		                 G_TYPE_FROM_CLASS (gmonitor_class),
   118 		                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
   119 		                 0,
   120 										 NULL,
   121 										 NULL,
   122 										 gmyth_marshal_VOID__INT_STRING,
   123 										 G_TYPE_NONE,
   124 										 2,
   125 										 G_TYPE_INT,
   126 										 G_TYPE_STRING);
   127 										 
   128 	gmonitor_class->backend_events_handler = gmyth_monitor_handler_default_listener;
   129 
   130 }
   131 
   132 static void
   133 gmyth_monitor_handler_init (GMythMonitorHandler *monitor)
   134 { 
   135   g_return_if_fail( monitor != NULL );
   136 
   137   monitor->event_sock = NULL;
   138   monitor->hostname = NULL;
   139   monitor->port = 0;
   140   monitor->actual_index = 0;
   141   
   142   monitor->allow_msgs_listener = TRUE;
   143   
   144   //monitor->backend_msgs = g_hash_table_new( g_int_hash, g_int_equal );
   145     
   146   /* it is used for signalizing the event socket consumer thread */
   147   //io_watcher_cond = g_cond_new();
   148   
   149   /* mutex to control access to the event socket consumer thread */
   150   //mutex = g_mutex_new();
   151   
   152   monitor->monitor_th = NULL;
   153   
   154   monitor->gmyth_monitor_handler_listener = gmyth_monitor_handler_listener;
   155 }
   156 
   157 static void
   158 gmyth_monitor_handler_dispose  (GObject *object)
   159 {
   160 	
   161 	GMythMonitorHandler *monitor = GMYTH_MONITOR_HANDLER (object);
   162 	
   163 	monitor->allow_msgs_listener = FALSE;
   164 	
   165 	if ( monitor->monitor_th != NULL )  
   166   {
   167   	g_thread_pool_free( monitor->monitor_th, TRUE, FALSE );
   168   	//g_thread_exit( monitor->monitor_th );
   169   	if ( monitor->monitor_th != NULL )
   170   		g_object_unref( monitor->monitor_th );
   171   	monitor->monitor_th = NULL;
   172   }
   173 	
   174   if ( monitor->event_sock != NULL )  
   175   {
   176   	g_object_unref( monitor->event_sock );
   177   	monitor->event_sock = NULL;
   178   }
   179   
   180   if ( monitor->hostname != NULL )  
   181   {
   182   	g_free( monitor->hostname );
   183   	monitor->hostname = NULL;
   184   }
   185 
   186   if ( monitor->backend_msgs != NULL )
   187   {
   188   	g_hash_table_destroy ( monitor->backend_msgs );
   189   	monitor->backend_msgs = NULL;
   190   }
   191   
   192   if ( mutex != NULL )  
   193   {
   194   	g_mutex_free( mutex );
   195   	mutex = NULL;
   196   }
   197   
   198   /*
   199   if ( io_watcher_cond != NULL )  
   200   {
   201   	g_cond_free( io_watcher_cond );
   202   	io_watcher_cond = NULL;
   203   }
   204   */
   205   
   206   G_OBJECT_CLASS (gmyth_monitor_handler_parent_class)->dispose (object);
   207 }
   208 
   209 static void
   210 gmyth_monitor_handler_finalize (GObject *object)
   211 {
   212   g_signal_handlers_destroy (object);
   213 
   214   G_OBJECT_CLASS (gmyth_monitor_handler_parent_class)->finalize (object);
   215 }
   216 
   217 // fixme: do we need the card_id????
   218 GMythMonitorHandler*
   219 gmyth_monitor_handler_new ( void )
   220 {
   221   GMythMonitorHandler *monitor = GMYTH_MONITOR_HANDLER ( g_object_new ( GMYTH_MONITOR_HANDLER_TYPE, 
   222   					FALSE ) );
   223 				
   224   return monitor;
   225 }
   226 
   227 static gboolean 
   228 myth_control_acquire_context( gboolean do_wait ) 
   229 {
   230 	
   231 	gboolean ret = TRUE;	
   232 	//guint max_iter = 50;
   233 	
   234 	//g_mutex_lock( mutex );
   235 	
   236   //while ( !has_io_access ) 
   237   //	g_cond_wait( io_watcher_cond, mutex );
   238   	
   239   //has_io_access = FALSE;
   240   /*
   241   if ( do_wait ) {
   242   	while ( --max_iter > 0 && !g_main_context_wait( io_watcher_context, io_watcher_cond, mutex ) )
   243   		ret = FALSE;
   244   } else if ( !g_main_context_acquire( io_watcher_context ) )
   245   	ret = FALSE;
   246   */
   247   	
   248   //g_static_mutex_lock( &st_mutex );
   249   
   250   return ret;
   251   
   252 }
   253 
   254 static gboolean 
   255 myth_control_release_context( ) 
   256 {
   257 	
   258 	gboolean ret = TRUE;
   259     
   260   //g_static_mutex_unlock( &st_mutex );
   261   
   262 	//g_main_context_release( io_watcher_context );
   263   
   264   //g_main_context_wakeup( io_watcher_context );
   265   
   266   //has_io_access = TRUE;
   267 
   268   //g_cond_broadcast( io_watcher_cond );
   269   
   270   //g_mutex_unlock( mutex );  
   271  
   272   return ret;
   273   
   274 }
   275 
   276 gboolean
   277 gmyth_monitor_handler_open (GMythMonitorHandler *monitor, const gchar *hostname, gint port)
   278 {
   279   gboolean ret = TRUE;
   280   
   281   g_return_val_if_fail( hostname != NULL, FALSE );
   282 
   283   if (monitor->hostname != NULL) {
   284     g_free (monitor->hostname);
   285     monitor->hostname = NULL;
   286   }
   287 
   288   monitor->hostname = g_strdup( hostname );
   289   monitor->port = port;
   290 
   291   gmyth_debug ("Monitor event socket --- hostname: %s, port %d\n", monitor->hostname, monitor->port);
   292   
   293   /* configure the event socket */
   294   if ( NULL == monitor->event_sock ) { 
   295     if (!gmyth_connect_to_backend_monitor (monitor)) {
   296       g_printerr( "Connection to backend failed (Event Socket).\n" );
   297       ret = FALSE;
   298     }
   299   } else {
   300     g_warning("Remote monitor event socket already created.\n");
   301   }
   302 
   303   return ret;
   304 
   305 }
   306 
   307 static gint
   308 gmyth_monitor_handler_is_backend_message( GMythMonitorHandler *monitor,
   309                         GMythStringList* strlist, gchar **back_msg_action )
   310 {
   311 	gint msg_type = GMYTH_BACKEND_NO_MESSAGE;
   312 	GString *back_msg = NULL;
   313 	
   314 	back_msg = gmyth_string_list_get_string( strlist, 0 );
   315 	if ( back_msg != NULL && back_msg->str != NULL &&
   316 	                                strstr( back_msg->str, "BACKEND" ) != NULL )
   317 	{
   318 		gmyth_debug( "MONITOR HANDLER - Received backend message = %s", back_msg->str );
   319   	*back_msg_action = gmyth_string_list_get_char_array( strlist, 1 );
   320   	
   321   	if ( back_msg_action != NULL )
   322   	{	        	
   323   	
   324     	if ( g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "LIVETV_CHAIN" ) ||
   325     			g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "RECORDING_LIST_CHANGE" ) || 
   326     			g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "SCHEDULE_CHANGE" ) ||
   327     			g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "LIVETV_WATCH" ) )
   328     	{
   329     		gmyth_debug( "MONITOR: message type == GMYTH_BACKEND_PROGRAM_INFO_CHANGED, msg = %s", *back_msg_action );
   330     		msg_type = GMYTH_BACKEND_PROGRAM_INFO_CHANGED;
   331     	} else if ( g_strstr_len( *back_msg_action, strlen( *back_msg_action ), "DONE_RECORDING" ) ) {
   332     		gmyth_debug( "MONITOR: message type == GMYTH_BACKEND_DONE_RECORDING, msg = %s", *back_msg_action );
   333     		msg_type = GMYTH_BACKEND_DONE_RECORDING;
   334     	}    	  	
   335 
   336       //g_hash_table_insert ( monitor->backend_msgs,
   337       //                       &(monitor->actual_index), *back_msg_action );
   338 	    
   339 	  } // if
   340 	}
   341 	
   342 	if ( back_msg != NULL )
   343 	{
   344 		g_string_free( back_msg, TRUE );
   345 		back_msg = NULL;        	
   346 	}
   347 	
   348 	return msg_type;
   349 
   350 }
   351 
   352 static void
   353 gmyth_monitor_handler_default_listener( GMythMonitorHandler *monitor, gint msg_code, gchar* message )
   354 {
   355 	//assert( message!= NULL );  
   356 	gmyth_debug( "DEFAULT Signal handler ( msg = %s, code = %d )\n", 
   357 				message, msg_code );
   358 }
   359 
   360 static void
   361 gmyth_monitor_handler_print( GString *str, gpointer ptr )
   362 {
   363 	gmyth_debug( "Backend message event: %s --- ", str->str );
   364 }
   365 
   366 //static void
   367 //gmyth_monitor_handler_listener (GMythMonitorHandler *monitor, gpointer user_data)
   368 //static gboolean
   369 //gmyth_monitor_handler_listener( GIOChannel *io_channel, GIOCondition condition, gpointer data )
   370 //gboolean* gmyth_monitor_handler_listener( GMythMonitorHandler *monitor )
   371 void
   372 gmyth_monitor_handler_listener (GMythMonitorHandler *monitor, gpointer user_data)
   373 {
   374 	//GMythMonitorHandler *monitor = (GMythMonitorHandler*)data; 
   375   GIOStatus io_status;
   376   GIOCondition io_cond;  
   377   guint recv = 0;
   378   gboolean *ret = g_new0( gboolean, 1 );
   379   *ret = TRUE;
   380   //gboolean ret = TRUE;
   381   gsize len = 0;
   382   
   383   static guint count = 0;
   384   
   385   GIOChannel *io_channel = monitor->event_sock->sd_io_ch;
   386   //GIOCondition condition = g_io_channel_get_buffer_condition( io_channel );
   387   
   388   GMythStringList *strlist = NULL;
   389   
   390   //GMythMonitorHandler *monitor = (GMythMonitorHandler*)data;
   391   
   392   myth_control_acquire_context (TRUE);
   393   
   394   if ( io_channel == NULL ) {
   395   	g_debug ("Monitor socket is NULL!\n");
   396   	*ret = FALSE;
   397   	goto clean_up;
   398   }
   399     
   400   while (monitor->allow_msgs_listener) {
   401   	++count;
   402   	
   403   	gmyth_debug ("%d - Listening on Monitor socket...!\n", count);
   404 	
   405     do 
   406     {
   407     	
   408     	gint bytes_sent = 0;
   409     	
   410     	strlist = gmyth_string_list_new();
   411     	
   412     	if ( monitor->event_sock != NULL )
   413     	{
   414     		
   415 	      len = gmyth_socket_read_stringlist( monitor->event_sock, strlist );
   416 	      
   417 		    if ( strlist != NULL && gmyth_string_list_length( strlist ) > 0 ) 
   418 		    { 
   419 			    bytes_sent = gmyth_string_list_get_int( strlist,  0 ); // -1 on backend error
   420 	
   421 		      gmyth_debug ( "[%s] MONITOR: received data buffer from IO event channel... %d strings gone!\n", 
   422 		      		__FUNCTION__, len );
   423 		      		
   424 		      recv += len;
   425 		      
   426 		      /* debug purpose: prints out all the string list elements */
   427 		      g_list_foreach( strlist->glist, (GFunc)gmyth_monitor_handler_print, NULL );
   428 		      
   429 		      gchar *back_msg_action = g_new0( gchar, 1 );
   430 		      gint msg_type = gmyth_monitor_handler_is_backend_message( monitor, strlist, 
   431 		      		&back_msg_action );
   432 		      
   433 	        g_signal_emit ( monitor,
   434 	               GMYTH_MONITOR_HANDLER_GET_CLASS (monitor)->backend_events_handler_signal_id,
   435 	               0, /* details */
   436 	               msg_type, back_msg_action );
   437 	               
   438 	        if (back_msg_action!= NULL)
   439 	        	g_free( back_msg_action );
   440 		      
   441 		    }
   442 		    
   443     	}
   444 	    
   445 	    if (strlist!=NULL)
   446 	    	g_object_unref( strlist );
   447       	
   448       io_cond = g_io_channel_get_buffer_condition( io_channel );
   449 
   450     } while ( recv <= 0 && ( io_cond & G_IO_IN ) != 0 );
   451     
   452     gmyth_debug ("[%s]\tMONITOR EVENT: Read %d bytes\n", __FUNCTION__, recv );
   453 
   454 	  g_usleep( 300 );
   455 	  
   456   } /* main GThread while */
   457   
   458   myth_control_release_context ();
   459   
   460   if ( io_status == G_IO_STATUS_ERROR ) {
   461     //gmyth_debug ("[%s] Error reading: %s\n", __FUNCTION__, error != NULL ? error->message : "" );
   462     gmyth_debug ("Error reading MONITOR event socket.\n");
   463    	*ret = FALSE;
   464    	goto clean_up;   	
   465   }
   466   
   467 clean_up:
   468 
   469   if (strlist!=NULL)
   470   	g_object_unref( strlist );
   471   	
   472  	return;
   473 
   474 }
   475 
   476 static gboolean
   477 gmyth_connect_to_backend_monitor (GMythMonitorHandler *monitor)
   478 {
   479   gboolean ret = TRUE;
   480 
   481   monitor->event_sock = gmyth_socket_new();
   482 
   483   /* Connects the socket, send Mythtv ANN Monitor and verify Mythtv protocol version */ 
   484   if (!gmyth_socket_connect_to_backend_events ( monitor->event_sock,
   485           monitor->hostname, monitor->port, FALSE ) ) 
   486   {
   487     g_object_unref (monitor->event_sock);
   488     monitor->event_sock = NULL;
   489     ret = FALSE;
   490   }
   491   
   492   return ret;
   493 }    
   494 
   495 static gboolean*
   496 gmyth_monitor_handler_setup( GMythMonitorHandler *monitor, GIOChannel *channel )
   497 {
   498 	gboolean *ret = g_new0( gboolean, 1 );
   499 	guint src_id = 0;
   500 	
   501 	*ret = TRUE;
   502 	
   503   //io_watcher_context = g_main_context_default();
   504   //GMainLoop *loop = g_main_loop_new( io_watcher_context, TRUE );
   505 
   506   //GSource *source;
   507 
   508   if ( channel != NULL ) {
   509     //source = g_io_create_watch( channel, G_IO_IN | G_IO_HUP );
   510     src_id = g_io_add_watch( channel, G_IO_IN, 
   511     					(GIOFunc)gmyth_monitor_handler_listener, monitor );
   512   } else {
   513   	*ret = FALSE;
   514   	goto cleanup;
   515   }
   516 
   517   //g_source_set_callback ( source, (GSourceFunc)gmyth_monitor_handler_listener, NULL, NULL );
   518 
   519   //g_source_attach( source, io_watcher_context );
   520   
   521   //if (NULL == source){
   522   if (src_id < 0){
   523     gmyth_debug( "[%s] Error adding watch listener function to the IO control channel!\n", __FUNCTION__ );
   524     *ret = FALSE;
   525     goto cleanup;
   526   }
   527   
   528   //g_main_loop_run( loop );
   529   
   530 cleanup:
   531   //if ( source != NULL )
   532   //  g_source_unref( source );
   533     
   534   //if ( io_watcher_context != NULL )
   535   //  g_main_context_unref( io_watcher_context );
   536 
   537   //if ( loop != NULL )
   538   //  g_main_loop_unref( loop );
   539     
   540   return ret;
   541   
   542 }
   543 
   544 gboolean 
   545 gmyth_monitor_handler_start (GMythMonitorHandler *monitor)
   546 {
   547 	gboolean *ret = g_new0( gboolean, 1 ); 
   548 	*ret = TRUE;	
   549 	
   550 	/*if (!g_thread_supported () ) 	g_thread_init (NULL);*/
   551 	/*
   552   monitor->monitor_th = g_thread_pool_new( (GThreadFunc)gmyth_monitor_handler_listener, 
   553   					monitor, TRUE, NULL );
   554  */				
   555   monitor->monitor_th = g_thread_pool_new( (GFunc)gmyth_monitor_handler_listener, 
   556   					monitor, 3, TRUE, NULL );
   557   g_thread_pool_push( monitor->monitor_th, monitor, NULL ); 
   558   					
   559   //if ( ( ret = g_thread_join( monitor_th ) ) == FALSE )
   560   if ( monitor->monitor_th != NULL )
   561   //if ( gmyth_monitor_handler_setup( monitor, monitor->event_sock->sd_io_ch ) )
   562   {
   563   	gmyth_debug ( "\n[%s]\tOK! Starting listener on the MONITOR event socket...[thread location = %p]\n", 
   564   				__FUNCTION__, g_thread_self( ) );
   565   	*ret = TRUE;  	  	
   566   } else {
   567   	gmyth_debug ( "\n[%s]\tERROR! Coudn't start listener on the MONITOR event socket...[thread location = %p]\n", 
   568   				__FUNCTION__, g_thread_self( ) );
   569   	*ret = FALSE;
   570   }
   571 
   572 //cleanup:
   573     
   574   gmyth_debug( "[%s] Watch listener function over the IO control channel? %s!!!\n", 
   575   			__FUNCTION__, ( *ret == TRUE ? "YES" : "NO" ) );
   576     
   577     return *ret;
   578 }
   579 
   580 void
   581 gmyth_monitor_handler_close( GMythMonitorHandler *monitor )
   582 {
   583 	
   584   if (monitor->event_sock) {
   585     g_object_unref( monitor->event_sock );
   586     monitor->event_sock = NULL;
   587   }
   588 
   589   if (monitor->hostname) {
   590     g_free( monitor->hostname );
   591     monitor->hostname = NULL;
   592   }
   593   
   594 }
   595