diff options
Diffstat (limited to 'test/dbus/dms-info.c')
-rw-r--r-- | test/dbus/dms-info.c | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/test/dbus/dms-info.c b/test/dbus/dms-info.c new file mode 100644 index 0000000..8b9b075 --- /dev/null +++ b/test/dbus/dms-info.c @@ -0,0 +1,395 @@ +/* + * dms-info + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2012-2013 Intel Corporation. All rights reserved. + * + * Mark Ryan <mark.d.ryan@intel.com> + * + ******************************************************************************/ + +#include <string.h> +#include <stdio.h> +#include <sys/signalfd.h> +#include <signal.h> +#include <stdbool.h> + +#include <glib.h> +#include <gio/gio.h> + +#define DMS_INFO_SERVICE "com.intel.dleyna-server" +#define DMS_INFO_MANAGER_IF "com.intel.dLeynaServer.Manager" +#define DMS_INFO_MANAGER_OBJ "/com/intel/dLeynaServer" +#define DMS_INFO_GET_SERVERS "GetServers" +#define DMS_INFO_GET_ALL "GetAll" +#define DMS_INFO_PROPERTIES_IF "org.freedesktop.DBus.Properties" + +typedef struct dms_info_t_ dms_info_t; +struct dms_info_t_ +{ + guint sig_id; + GMainLoop *main_loop; + GDBusProxy *manager_proxy; + GCancellable *cancellable; + GHashTable *dmss; + unsigned int async; +}; + +typedef struct dms_server_data_t_ dms_server_data_t; +struct dms_server_data_t_ { + GCancellable *cancellable; + GDBusProxy *proxy; +}; + +static dms_server_data_t *prv_dms_server_data_new(GDBusProxy *proxy) +{ + dms_server_data_t *data = g_new(dms_server_data_t, 1); + data->proxy = proxy; + data->cancellable = g_cancellable_new(); + return data; +} + +static void prv_dms_server_data_delete(gpointer user_data) +{ + dms_server_data_t *data = user_data; + g_object_unref(data->cancellable); + g_object_unref(data->proxy); + g_free(data); +} + +static void prv_dms_info_free(dms_info_t *info) +{ + if (info->manager_proxy) + g_object_unref(info->manager_proxy); + + if (info->sig_id) + (void) g_source_remove(info->sig_id); + + if (info->main_loop) + g_main_loop_unref(info->main_loop); + + if (info->cancellable) + g_object_unref(info->cancellable); + + if (info->dmss) + g_hash_table_unref(info->dmss); +} + +static void prv_dump_container_props(GVariant *props) +{ + GVariantIter iter; + GVariant *dictionary; + gchar *key; + GVariant *value; + gchar *formatted_value; + + dictionary = g_variant_get_child_value(props, 0); + (void) g_variant_iter_init(&iter, dictionary); + + printf("\n"); + while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) { + formatted_value = g_variant_print(value, FALSE); + printf("%s: %s\n", key, formatted_value); + g_free(formatted_value); + g_variant_unref(value); + } +} + +static void prv_get_props_cb(GObject *source_object, GAsyncResult *res, + gpointer user_data) +{ + GVariant *variant; + dms_info_t *info = user_data; + dms_server_data_t *data; + const gchar *obj_path; + + obj_path = g_dbus_proxy_get_object_path((GDBusProxy *) source_object); + data = g_hash_table_lookup(info->dmss, obj_path); + + --info->async; + + if (g_cancellable_is_cancelled(data->cancellable)) { + if (info->async == 0) + g_main_loop_quit(info->main_loop); + printf("Get Properties cancelled.\n"); + } else { + variant = g_dbus_proxy_call_finish(info->manager_proxy, res, + NULL); + if (!variant) { + printf("Get Properties failed.\n"); + } else { + prv_dump_container_props(variant); + g_variant_unref(variant); + } + } +} + +static void prv_get_container_props(dms_info_t *info, const gchar *container) +{ + dms_server_data_t *data; + GDBusProxy *proxy; + GDBusProxyFlags flags; + + if (!g_hash_table_lookup_extended(info->dmss, container, NULL, NULL)) { + printf("Container Object Found: %s\n", container); + + /* We'll create these proxies synchronously. The server + is already started and we don't want to retrieve any + properties so it should be fast. Okay, we do want + to retrieve properties but we want to do so for all + interfaces and not just org.gnome.UPnP.MediaContainer2. + To do this we will need to call GetAll(""). + */ + + flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; + proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, + flags, NULL, + DMS_INFO_SERVICE, + container, + DMS_INFO_PROPERTIES_IF, + NULL, NULL); + if (!proxy) { + printf("Unable to create Container Proxy for %s\n", + container); + } else { + data = prv_dms_server_data_new(proxy); + g_hash_table_insert(info->dmss, g_strdup(container), + data); + ++info->async; + g_dbus_proxy_call(proxy, DMS_INFO_GET_ALL, + g_variant_new("(s)", ""), + G_DBUS_CALL_FLAGS_NONE, -1, + data->cancellable, + prv_get_props_cb, + info); + } + } +} + +static void prv_get_root_folders(dms_info_t *info, GVariant *servers) +{ + GVariantIter iter; + GVariant *array; + const gchar *container; + gsize count; + + /* Results always seem to be packed inside a tuple. We need + to extract our array from the tuple */ + + array = g_variant_get_child_value(servers, 0); + count = g_variant_iter_init(&iter, array); + + printf("Found %"G_GSIZE_FORMAT" DMS Root Containers\n", count); + + while (g_variant_iter_next(&iter, "o", &container)) + prv_get_container_props(info, container); +} + +static void prv_get_servers_cb(GObject *source_object, GAsyncResult *res, + gpointer user_data) +{ + GVariant *variant; + dms_info_t *info = user_data; + + --info->async; + + if (g_cancellable_is_cancelled(info->cancellable)) { + if (info->async == 0) + g_main_loop_quit(info->main_loop); + printf("Get Servers cancelled\n"); + } else { + variant = g_dbus_proxy_call_finish(info->manager_proxy, res, + NULL); + if (!variant) { + printf("Get Servers failed.\n"); + } else { + prv_get_root_folders(info, variant); + g_variant_unref(variant); + } + } +} + +static void prv_on_signal(GDBusProxy *proxy, gchar *sender_name, + gchar *signal_name, GVariant *parameters, + gpointer user_data) +{ + gchar *container; + dms_info_t *info = user_data; + + if (!strcmp(signal_name, "FoundServer")) { + g_variant_get(parameters, "(&o)", &container); + if (!g_hash_table_lookup_extended(info->dmss, container, NULL, + NULL)) { + printf("\nFound DMS %s\n", container); + prv_get_container_props(info, container); + } + } else if (!strcmp(signal_name, "LostServer")) { + g_variant_get(parameters, "(&o)", &container); + printf("\nLost DMS %s\n", container); + (void) g_hash_table_remove(info->dmss, container); + } +} + +static void prv_manager_proxy_created(GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + dms_info_t *info = user_data; + GDBusProxy *proxy; + + --info->async; + + if (g_cancellable_is_cancelled(info->cancellable)) { + printf("Manager proxy creation cancelled.\n"); + if (info->async == 0) + g_main_loop_quit(info->main_loop); + } else { + proxy = g_dbus_proxy_new_finish(result, NULL); + if (!proxy) { + printf("Unable to create manager proxy.\n"); + } else { + info->manager_proxy = proxy; + + /* Set up signals to be notified when servers + appear and dissapear */ + + g_signal_connect(proxy, "g-signal", + G_CALLBACK (prv_on_signal), info); + + /* Now we need to retrieve a list of the DMSes on + the network. This involves IPC so we will do + this asynchronously.*/ + + ++info->async; + g_cancellable_reset(info->cancellable); + g_dbus_proxy_call(proxy, DMS_INFO_GET_SERVERS, + NULL, G_DBUS_CALL_FLAGS_NONE, -1, + info->cancellable, prv_get_servers_cb, + info); + } + } +} + +static gboolean prv_quit_handler(GIOChannel *source, GIOCondition condition, + gpointer user_data) +{ + dms_info_t *info = user_data; + GHashTableIter iter; + gpointer value; + dms_server_data_t *data; + + /* We cannot quit straight away if asynchronous calls our outstanding. + First we need to cancel them. Each time one is cancel info->async + will drop by 1. Once it reaches 0 the callback function associated + with the command being cancelled will quit the mainloop. */ + + if (info->async == 0) { + g_main_loop_quit(info->main_loop); + } else { + g_cancellable_cancel(info->cancellable); + g_hash_table_iter_init(&iter, info->dmss); + while (g_hash_table_iter_next(&iter, NULL, &value)) { + data = value; + g_cancellable_cancel(data->cancellable); + } + } + + info->sig_id = 0; + + return FALSE; +} + +static bool prv_init_signal_handler(sigset_t mask, dms_info_t *info) +{ + bool retval = false; + int fd = -1; + GIOChannel *channel = NULL; + + fd = signalfd(-1, &mask, SFD_NONBLOCK); + if (fd == -1) + goto on_error; + + channel = g_io_channel_unix_new(fd); + g_io_channel_set_close_on_unref(channel, TRUE); + + if (g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL) != + G_IO_STATUS_NORMAL) + goto on_error; + + if (g_io_channel_set_encoding(channel, NULL, NULL) != + G_IO_STATUS_NORMAL) + goto on_error; + + info->sig_id = g_io_add_watch(channel, G_IO_IN | G_IO_PRI, + prv_quit_handler, + info); + + retval = true; + +on_error: + + if (channel) + g_io_channel_unref(channel); + + return retval; +} + +int main(int argc, char *argv[]) +{ + dms_info_t info; + sigset_t mask; + + memset(&info, 0, sizeof(info)); + + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGINT); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + goto on_error; + + g_type_init(); + + /* Create proxy for com.intel.dLeynaServer.Manager. The Manager + object has no properties. We will create the proxy asynchronously + and use G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES to ensure that + gio does not contact the server to retrieve remote properties. Creating + the proxy will force dleyna-media-service be to launched if it is not + already running. */ + + info.cancellable = g_cancellable_new(); + info.async = 1; + g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, DMS_INFO_SERVICE, DMS_INFO_MANAGER_OBJ, + DMS_INFO_MANAGER_IF, info.cancellable, + prv_manager_proxy_created, &info); + + info.dmss = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + prv_dms_server_data_delete); + + info.main_loop = g_main_loop_new(NULL, FALSE); + + if (!prv_init_signal_handler(mask, &info)) + goto on_error; + + g_main_loop_run(info.main_loop); + +on_error: + + prv_dms_info_free(&info); + + return 0; +} |