4 * @file gmyth/gmyth_monitor_handler.c
6 * @brief <p> GMythMonitorHandler deals with the streaming media events remote/local
7 * that are sent to the MythTV frontend.
9 * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
10 * @author Rosfran Lins Borges <rosfran.borges@indt.org.br>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 * GStreamer MythTV plug-in properties:
28 * - location (backend server hostname/URL) [ex.: myth://192.168.1.73:28722/1000_1092091.nuv]
29 * - path (qurl - remote file to be opened)
39 #include <arpa/inet.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
47 #include "gmyth_marshal.h"
49 #include "gmyth_monitor_handler.h"
50 #include "gmyth_debug.h"
52 #define GMYTHTV_QUERY_HEADER "QUERY_FILETRANSFER "
54 #define GMYTHTV_VERSION 30
56 #define GMYTHTV_TRANSFER_MAX_WAITS 700
58 #define GMYTHTV_BUFFER_SIZE 8*1024
60 #ifdef GMYTHTV_ENABLE_DEBUG
61 #define GMYTHTV_ENABLE_DEBUG 1
63 #undef GMYTHTV_ENABLE_DEBUG
67 * this NDEBUG is to maintain compatibility with GMyth library
70 #define GMYTHTV_ENABLE_DEBUG 1
73 gpointer gmyth_monitor_handler_listener(gpointer data);
75 static void gmyth_monitor_handler_default_listener(GMythMonitorHandler
80 static void gmyth_monitor_handler_class_init(GMythMonitorHandlerClass *
82 static void gmyth_monitor_handler_init(GMythMonitorHandler * object);
84 static void gmyth_monitor_handler_dispose(GObject * object);
85 static void gmyth_monitor_handler_finalize(GObject * object);
87 static gboolean gmyth_connect_to_backend_monitor(GMythMonitorHandler *
90 static gboolean gmyth_monitor_handler_setup(GMythMonitorHandler * monitor,
91 GIOChannel * channel);
93 void gmyth_monitor_handler_close(GMythMonitorHandler * monitor);
95 G_DEFINE_TYPE(GMythMonitorHandler, gmyth_monitor_handler, G_TYPE_OBJECT)
139 gmyth_monitor_handler_class_init(GMythMonitorHandlerClass * klass)
141 GObjectClass *gobject_class;
142 GMythMonitorHandlerClass *gmonitor_class;
144 gobject_class = (GObjectClass *) klass;
145 gmonitor_class = (GMythMonitorHandlerClass *) gobject_class;
147 gobject_class->dispose = gmyth_monitor_handler_dispose;
148 gobject_class->finalize = gmyth_monitor_handler_finalize;
150 gmonitor_class->backend_events_handler_signal_id =
151 g_signal_new("backend-events-handler",
152 G_TYPE_FROM_CLASS(gmonitor_class),
153 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE |
154 G_SIGNAL_NO_HOOKS, 0, NULL, NULL,
155 gmyth_marshal_VOID__INT_STRING, G_TYPE_NONE, 2,
156 G_TYPE_INT, G_TYPE_STRING);
158 gmonitor_class->backend_events_handler =
159 gmyth_monitor_handler_default_listener;
164 gmyth_monitor_handler_init(GMythMonitorHandler * monitor)
166 g_return_if_fail(monitor != NULL);
168 monitor->event_sock = NULL;
169 monitor->hostname = NULL;
171 monitor->actual_index = 0;
173 monitor->allow_msgs_listener = FALSE;
176 * monitor->backend_msgs = g_hash_table_new( g_int_hash, g_int_equal
181 * it is used for signalizing the event socket consumer thread
183 monitor->mutex = g_mutex_new();
187 monitor->gmyth_monitor_handler_listener =
188 gmyth_monitor_handler_listener;
192 gmyth_monitor_handler_dispose(GObject * object)
194 GMythMonitorHandler *monitor = GMYTH_MONITOR_HANDLER(object);
196 gmyth_monitor_handler_close(monitor);
198 monitor->allow_msgs_listener = FALSE;
200 if (monitor->th != NULL) {
201 gboolean *ret = (gboolean *) g_thread_join(monitor->th);
204 gmyth_debug("Error closing GThread listener socket!");
206 gmyth_debug("Closed GThread listener socket.");
207 // g_object_unref( monitor->th );
211 * mutex to control access to the event socket consumer thread
213 if (monitor->mutex != NULL) {
214 // g_mutex_unlock( monitor->mutex );
215 g_mutex_free(monitor->mutex);
216 monitor->mutex = NULL;
219 if (monitor->event_sock != NULL) {
220 g_object_unref(monitor->event_sock);
221 monitor->event_sock = NULL;
224 if (monitor->hostname != NULL) {
225 g_free(monitor->hostname);
226 monitor->hostname = NULL;
229 if (monitor->backend_msgs != NULL) {
230 g_hash_table_destroy(monitor->backend_msgs);
231 monitor->backend_msgs = NULL;
235 * if ( io_watcher_cond != NULL ) { g_cond_free( io_watcher_cond );
236 * io_watcher_cond = NULL; }
239 G_OBJECT_CLASS(gmyth_monitor_handler_parent_class)->dispose(object);
243 gmyth_monitor_handler_finalize(GObject * object)
245 g_signal_handlers_destroy(object);
247 G_OBJECT_CLASS(gmyth_monitor_handler_parent_class)->finalize(object);
251 * Creates a new instance of GMyth Monitor Handler.
253 * @return a new instance of the Monitor Handler.
255 GMythMonitorHandler *
256 gmyth_monitor_handler_new(void)
258 GMythMonitorHandler *monitor =
259 GMYTH_MONITOR_HANDLER(g_object_new
260 (GMYTH_MONITOR_HANDLER_TYPE, FALSE));
266 * Acquire the mutex to have access to the IO Watcher listener.
268 * @param monitor The GMythMonitorHandler instance.
269 * @param do_wait Tells the IO Watcher to wait on the GCond. (obsolete)
271 * @return <code>true</code>, if the access to IO Watcher was acquired.
274 myth_control_acquire_context(GMythMonitorHandler * monitor,
280 g_mutex_lock(monitor->mutex);
287 * Release the mutex to have access to the IO Watcher listener.
289 * @param monitor The GMythMonitorHandler instance.
291 * @return <code>true</code>, if the access to IO Watcher was released.
294 myth_control_release_context(GMythMonitorHandler * monitor)
299 g_mutex_unlock(monitor->mutex);
305 gmyth_monitor_handler_close(GMythMonitorHandler * monitor)
307 monitor->allow_msgs_listener = FALSE;
310 if (monitor->monitor_th != NULL) {
311 g_thread_pool_free(monitor->monitor_th, TRUE, FALSE);
312 // g_thread_exit( monitor->monitor_th );
314 * if ( monitor->monitor_th != NULL ) g_object_unref(
315 * monitor->monitor_th );
317 monitor->monitor_th = NULL;
320 if (monitor->event_sock != NULL) {
321 gmyth_socket_close_connection(monitor->event_sock);
328 * Opens connection the the Monitor socket on MythTV backend server,
329 * where all status messages are notified to the client.
331 * @param monitor The GMythMonitorHandler instance.
332 * @param hostname The remote host name of the MythTV backend server.
333 * @param port The remote port number of the MythTV backend server.
335 * @return <code>true</code>, if the connection was successfully opened.
338 gmyth_monitor_handler_open(GMythMonitorHandler * monitor,
339 const gchar * hostname, gint port)
343 g_return_val_if_fail(hostname != NULL, FALSE);
345 if (monitor->hostname != NULL) {
346 g_free(monitor->hostname);
347 monitor->hostname = NULL;
350 monitor->hostname = g_strdup(hostname);
351 monitor->port = port;
353 gmyth_debug("Monitor event socket --- hostname: %s, port %d\n",
354 monitor->hostname, monitor->port);
356 if (NULL != monitor->event_sock) {
357 g_object_unref(monitor->event_sock);
358 monitor->event_sock = NULL;
362 * configure the event socket
364 if (NULL == monitor->event_sock) {
365 if (!gmyth_connect_to_backend_monitor(monitor)) {
366 gmyth_debug("Connection to backend failed (Event Socket)!");
370 ("Remote monitor event socket had been succesfully created. (io_fd == %d)\n",
371 g_io_channel_unix_get_fd(monitor->event_sock->sd_io_ch));
375 ("ASSERT ERROR: Remote monitor event socket is not NULL at the setup...\n");
383 * Reads the data got from the connection to the Monitor socket,
384 * and looks for some important status messages.
386 * @param monitor The GMythMonitorHandler instance.
387 * @param strlist The GMythStringList instance got from the Monitor remote socket.
388 * @param back_msg_action A string pointer to the status message detailed description.
390 * @return The backend status message code ID.
393 gmyth_monitor_handler_is_backend_message(GMythMonitorHandler * monitor,
394 GMythStringList * strlist,
395 gchar ** back_msg_action)
397 gint msg_type = GMYTH_BACKEND_NO_MESSAGE;
398 GString *back_msg = NULL;
400 if (gmyth_string_list_length(strlist) > 0) {
402 back_msg = gmyth_string_list_get_string(strlist, 0);
403 if (back_msg != NULL && back_msg->str != NULL &&
404 strstr(back_msg->str, "BACKEND") != NULL) {
405 gmyth_debug("MONITOR HANDLER - Received backend message = %s",
408 gmyth_string_list_get_char_array(strlist, 1);
410 if (back_msg_action != NULL) {
413 (*back_msg_action, strlen(*back_msg_action),
415 || g_strstr_len(*back_msg_action,
416 strlen(*back_msg_action),
417 "RECORDING_LIST_CHANGE")
418 || g_strstr_len(*back_msg_action,
419 strlen(*back_msg_action),
421 || g_strstr_len(*back_msg_action,
422 strlen(*back_msg_action),
425 ("MONITOR: message type == GMYTH_BACKEND_PROGRAM_INFO_CHANGED, msg = %s",
427 msg_type = GMYTH_BACKEND_PROGRAM_INFO_CHANGED;
428 } else if (g_strstr_len
429 (*back_msg_action, strlen(*back_msg_action),
432 ("MONITOR: message type == GMYTH_BACKEND_DONE_RECORDING, msg = %s",
434 msg_type = GMYTH_BACKEND_DONE_RECORDING;
435 } else if (g_strstr_len
436 (*back_msg_action, strlen(*back_msg_action),
439 ("MONITOR: message type == GMYTH_BACKEND_STOP_LIVETV, msg = %s",
441 msg_type = GMYTH_BACKEND_STOP_LIVETV;
445 * g_hash_table_insert ( monitor->backend_msgs,
446 * &(monitor->actual_index), *back_msg_action );
457 if (back_msg != NULL) {
458 g_string_free(back_msg, TRUE);
462 } /* if - Does Monitor got any message from
465 *back_msg_action = g_strdup("");
473 gmyth_monitor_handler_default_listener(GMythMonitorHandler * monitor,
474 gint msg_code, gchar * message)
476 // assert( message!= NULL );
477 gmyth_debug("DEFAULT Signal handler ( msg = %s, code = %d )\n",
482 gmyth_monitor_handler_print(GString * str, gpointer ptr)
484 gmyth_debug("Backend message event: %s --- ", str->str);
488 * Opens connection the the Monitor socket on MythTV backend server,
489 * where all status messages are notified to the client.
491 * @param data Pointer to the GMythMonitorHandler.
493 * @return Pointer to a gboolean <code>true</code> value, if the data was
497 gmyth_monitor_handler_listener(gpointer data)
499 GMythMonitorHandler *monitor = (GMythMonitorHandler *) data;
501 gboolean *ret = g_new0(gboolean, 1);
503 GIOChannel *io_channel = monitor->event_sock->sd_io_ch;
504 GIOCondition io_cond =
505 g_io_channel_get_buffer_condition(io_channel);
506 static guint count = 0;
510 gmyth_debug("Entering MONITOR handler listener...");
512 myth_control_acquire_context(monitor, TRUE);
514 if ((io_cond & G_IO_HUP) != 0) {
519 GMythStringList *strlist = NULL;
521 if (NULL == io_channel) {
522 gmyth_debug("Monitor socket is NULL! (GIOChannel)");
527 while (monitor->allow_msgs_listener) {
530 gmyth_debug("%d - Listening on Monitor socket...!\n", count);
536 strlist = gmyth_string_list_new();
538 if (monitor->event_sock != NULL) {
541 gmyth_socket_read_stringlist(monitor->event_sock,
544 if ((len > 0) && strlist != NULL
545 && gmyth_string_list_length(strlist) > 0) {
546 bytes_sent = gmyth_string_list_get_int(strlist, 0); // -1
554 ("[%s] MONITOR: received data buffer from IO event channel... %d strings gone!\n",
560 * debug purpose: prints out all the string list
563 g_list_foreach(strlist->glist,
564 (GFunc) gmyth_monitor_handler_print,
567 gchar *back_msg_action = g_new0(gchar, 1);
569 gmyth_monitor_handler_is_backend_message(monitor,
574 && msg_type != GMYTH_BACKEND_NO_MESSAGE)
575 g_signal_emit(monitor, GMYTH_MONITOR_HANDLER_GET_CLASS(monitor)->backend_events_handler_signal_id, 0, /* details
577 msg_type, back_msg_action);
579 if (back_msg_action != NULL)
580 g_free(back_msg_action);
586 if (strlist != NULL) {
587 g_object_unref(strlist);
591 io_cond = g_io_channel_get_buffer_condition(io_channel);
596 while (recv <= 0 && ((io_cond & G_IO_HUP) == 0));
598 gmyth_debug("\tMONITOR EVENT: Read %d bytes\n", recv);
600 } /* main GThread while */
603 myth_control_release_context(monitor);
607 return (gpointer) ret;
612 * Opens connection events' socket the the Monitor socket on
613 * MythTV backend server.
615 * @param monitor The GMythMonitorHandler instance.
617 * @return <code>true</code>, if the socket was successfully opened.
620 gmyth_connect_to_backend_monitor(GMythMonitorHandler * monitor)
624 monitor->event_sock = gmyth_socket_new();
627 * Connects the socket, send Mythtv ANN Monitor and verify Mythtv
630 if (!gmyth_socket_connect_to_backend_events(monitor->event_sock,
632 monitor->port, FALSE)) {
633 g_object_unref(monitor->event_sock);
634 monitor->event_sock = NULL;
642 * Opens connection the the Monitor socket on MythTV backend server,
643 * where all status messages are notified to the client.
645 * @param monitor The GMythMonitorHandler instance.
646 * @param channel The GIOChannel instance to the Monitor socket.
648 * @return Pointer to the boolean value, and it is <code>true</code> only if the
649 * GMythMonitorHandler could be configured.
652 gmyth_monitor_handler_setup(GMythMonitorHandler * monitor,
653 GIOChannel * channel)
657 if (channel != NULL) {
658 monitor->allow_msgs_listener = TRUE;
661 g_thread_create((GThreadFunc) gmyth_monitor_handler_listener,
662 monitor, TRUE, NULL);
663 gmyth_debug("MONITOR GThread created!");
669 if (NULL == monitor->th) {
671 ("[%s] Error adding GThread listener function to the IO control channel!\n",
683 * Starts the MonitorHandler thread to the GIOWatcher.
685 * @param monitor The GMythMonitorHandler instance.
687 * @return <code>true</code>, if the MonitorHandler was started.
690 gmyth_monitor_handler_start(GMythMonitorHandler * monitor)
694 if (!(ret = g_thread_supported())) {
695 gmyth_debug("Thread system wasn't initialized, starting NOW!!!");
700 gmyth_monitor_handler_setup(monitor,
701 monitor->event_sock->sd_io_ch);
704 ("\n[%s]\tOK! Starting listener on the MONITOR event socket...[thread location = %p]\n",
705 __FUNCTION__, g_thread_self());
708 ("\n[%s]\tERROR! Coudn't start listener on the MONITOR event socket...[thread location = %p]\n",
709 __FUNCTION__, g_thread_self());
714 ("[%s] Watch listener function over the IO control channel? %s!!!\n",
715 __FUNCTION__, (ret == TRUE ? "YES" : "NO"));