diff options
author | Zeeshan Ali (Khattak) <zeeshanak@gnome.org> | 2013-09-18 21:58:57 +0300 |
---|---|---|
committer | Zeeshan Ali (Khattak) <zeeshanak@gnome.org> | 2013-09-26 01:03:26 +0300 |
commit | 5f92b19a4a7b206ffeeff0f916c9dfc6d82010f5 (patch) | |
tree | b277b88d8d30c07903d22b7d24c6535aa198c9db /src/gclue-client-info.c | |
parent | 7358612650e1873e15e05d8711c8cba92b443bb5 (diff) | |
download | geoclue-5f92b19a4a7b206ffeeff0f916c9dfc6d82010f5.tar.gz |
service: Add a ClientInfo class
This will be used to detect and carry around client info such as:
* Unique bus name
* Commandline
* ID of user client is running as
Also it emits 'peer-vanished' signal when the client disapears from bus.
Diffstat (limited to 'src/gclue-client-info.c')
-rw-r--r-- | src/gclue-client-info.c | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/src/gclue-client-info.c b/src/gclue-client-info.c new file mode 100644 index 0000000..f7e34b9 --- /dev/null +++ b/src/gclue-client-info.c @@ -0,0 +1,458 @@ +/* vim: set et ts=8 sw=8: */ +/* gclue-client-info.c + * + * Copyright (C) 2013 Red Hat, Inc. + * + * Geoclue is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Geoclue is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with Geoclue; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org> + */ + +#include <glib/gi18n.h> + +#include "gclue-client-info.h" + +#define MAX_CMDLINE_LEN 4096 + +static void +gclue_client_info_async_initable_init (GAsyncInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GClueClientInfo, + gclue_client_info, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, + gclue_client_info_async_initable_init)); + +struct _GClueClientInfoPrivate +{ + const char *bus_name; + GDBusConnection *connection; + GDBusProxy *dbus_proxy; + guint watch_id; + + char cmdline[MAX_CMDLINE_LEN]; + gsize cmdline_len; + guint32 user_id; +}; + +enum +{ + PROP_0, + PROP_PEER, + PROP_CONNECTION, + LAST_PROP +}; + +static GParamSpec *gParamSpecs[LAST_PROP]; + +enum { + PEER_VANISHED, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST]; + +static void +gclue_client_info_finalize (GObject *object) +{ + GClueClientInfoPrivate *priv = GCLUE_CLIENT_INFO (object)->priv; + + if (priv->watch_id != 0) { + g_bus_unwatch_name (priv->watch_id); + priv->watch_id = 0; + } + + g_clear_pointer (&priv->bus_name, g_free); + g_clear_object (&priv->connection); + + /* Chain up to the parent class */ + G_OBJECT_CLASS (gclue_client_info_parent_class)->finalize (object); +} + +static void +gclue_client_info_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GClueClientInfo *info = GCLUE_CLIENT_INFO (object); + + switch (prop_id) { + case PROP_PEER: + g_value_set_string (value, info->priv->bus_name); + break; + + case PROP_CONNECTION: + g_value_set_object (value, info->priv->connection); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gclue_client_info_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GClueClientInfo *info = GCLUE_CLIENT_INFO (object); + + switch (prop_id) { + case PROP_PEER: + info->priv->bus_name = g_value_dup_string (value); + break; + + case PROP_CONNECTION: + info->priv->connection = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gclue_client_info_class_init (GClueClientInfoClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = gclue_client_info_finalize; + object_class->get_property = gclue_client_info_get_property; + object_class->set_property = gclue_client_info_set_property; + + g_type_class_add_private (object_class, sizeof (GClueClientInfoPrivate)); + + gParamSpecs[PROP_PEER] = g_param_spec_string ("bus-name", + "BusName", + "Bus name of client", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, + PROP_PEER, + gParamSpecs[PROP_PEER]); + + gParamSpecs[PROP_CONNECTION] = g_param_spec_object ("connection", + "Connection", + "DBus Connection", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, + PROP_CONNECTION, + gParamSpecs[PROP_CONNECTION]); + + signals[PEER_VANISHED] = + g_signal_new ("peer-vanished", + GCLUE_TYPE_CLIENT_INFO, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GClueClientInfoClass, + peer_vanished), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0, + G_TYPE_NONE); +} + +static void +on_name_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_signal_emit (GCLUE_CLIENT_INFO (user_data), + signals[PEER_VANISHED], + 0); +} + +static void +on_get_user_id_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = G_TASK (user_data); + gpointer *info = g_task_get_source_object (task); + GClueClientInfoPrivate *priv = GCLUE_CLIENT_INFO (info)->priv; + GError *error = NULL; + GVariant *results = NULL; + + results = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), + res, + &error); + if (results == NULL) { + g_task_return_error (task, error); + g_object_unref (task); + + return; + } + + g_assert (g_variant_n_children (results) > 0); + g_variant_get_child (results, 0, "u", &priv->user_id); + g_variant_unref (results); + + priv->watch_id = g_bus_watch_name_on_connection (priv->connection, + priv->bus_name, + G_BUS_NAME_WATCHER_FLAGS_NONE, + NULL, + on_name_vanished, + info, + NULL); + + g_task_return_boolean (task, TRUE); + + g_object_unref (task); +} + +static void +on_proc_stream_read_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GInputStream *proc_stream = G_INPUT_STREAM (source_object); + GTask *task = G_TASK (user_data); + gpointer *info = g_task_get_source_object (task); + GClueClientInfoPrivate *priv = GCLUE_CLIENT_INFO (info)->priv; + GError *error = NULL; + gsize bytes_read, i; + + bytes_read = g_input_stream_read_finish (proc_stream, res, &error); + if (bytes_read < 0) { + g_task_return_error (task, error); + g_object_unref (task); + + return; + } + + priv->cmdline_len += bytes_read; + if (bytes_read != 0 && priv->cmdline_len < MAX_CMDLINE_LEN) { + /* Keep reading */ + g_input_stream_read_async (proc_stream, + priv->cmdline + priv->cmdline_len, + MAX_CMDLINE_LEN - priv->cmdline_len, + G_PRIORITY_DEFAULT, + g_task_get_cancellable (task), + on_proc_stream_read_ready, + task); + return; + } + + priv->cmdline[priv->cmdline_len] = '\0'; + for (i = 0; i < priv->cmdline_len - 1; i++) { + if (priv->cmdline[i] == '\0') + priv->cmdline[i] = ' '; + } + + g_dbus_proxy_call (priv->dbus_proxy, + "GetConnectionUnixUser", + g_variant_new ("(s)", priv->bus_name), + G_DBUS_CALL_FLAGS_NONE, + -1, + g_task_get_cancellable (task), + on_get_user_id_ready, + task); + + g_object_unref (proc_stream); +} + +static void +on_get_process_id_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = G_TASK (user_data); + gpointer *info = g_task_get_source_object (task); + GClueClientInfoPrivate *priv = GCLUE_CLIENT_INFO (info)->priv; + GError *error = NULL; + GVariant *results = NULL; + guint32 process_id; + char *proc_path; + GFile *proc_file; + GFileInputStream *proc_stream; + + results = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), + res, + &error); + if (results == NULL) + goto error_out; + + g_assert (g_variant_n_children (results) > 0); + g_variant_get_child (results, 0, "u", &process_id); + g_variant_unref (results); + + proc_path = g_strdup_printf ("/proc/%u/cmdline", process_id); + proc_file = g_file_new_for_path (proc_path); + g_free (proc_path); + proc_stream = g_file_read (proc_file, NULL, &error); + g_object_unref (proc_file); + if (proc_stream == NULL) + goto error_out; + + g_input_stream_read_async (G_INPUT_STREAM (proc_stream), + priv->cmdline, + MAX_CMDLINE_LEN, + G_PRIORITY_DEFAULT, + g_task_get_cancellable (task), + on_proc_stream_read_ready, + task); + return; + +error_out: + g_task_return_error (task, error); + g_object_unref (task); +} + +static void +on_dbus_proxy_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = G_TASK (user_data); + gpointer *info = g_task_get_source_object (task); + GClueClientInfoPrivate *priv = GCLUE_CLIENT_INFO (info)->priv; + GError *error = NULL; + + priv->dbus_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + if (priv->dbus_proxy == NULL) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_dbus_proxy_call (priv->dbus_proxy, + "GetConnectionUnixProcessID", + g_variant_new ("(s)", priv->bus_name), + G_DBUS_CALL_FLAGS_NONE, + -1, + g_task_get_cancellable (task), + on_get_process_id_ready, + task); +} + +static void +gclue_client_info_init_async (GAsyncInitable *initable, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (initable, cancellable, callback, user_data); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + cancellable, + on_dbus_proxy_ready, + task); +} + +static gboolean +gclue_client_info_init_finish (GAsyncInitable *initable, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +gclue_client_info_async_initable_init (GAsyncInitableIface *iface) +{ + iface->init_async = gclue_client_info_init_async; + iface->init_finish = gclue_client_info_init_finish; +} + +static void +gclue_client_info_init (GClueClientInfo *info) +{ + info->priv = G_TYPE_INSTANCE_GET_PRIVATE (info, + GCLUE_TYPE_CLIENT_INFO, + GClueClientInfoPrivate); +} + +void +gclue_client_info_new_async (const char *bus_name, + GDBusConnection *connection, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (GCLUE_TYPE_CLIENT_INFO, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + "bus-name", bus_name, + "connection", connection, + NULL); +} + +GClueClientInfo * +gclue_client_info_new_finish (GAsyncResult *res, + GError **error) +{ + GObject *object; + GObject *source_object; + + source_object = g_async_result_get_source_object (res); + object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), + res, + error); + g_object_unref (source_object); + if (object != NULL) + return GCLUE_CLIENT_INFO (object); + else + return NULL; +} + +const gchar * +gclue_client_info_get_bus_name (GClueClientInfo *info) +{ + g_return_val_if_fail (GCLUE_IS_CLIENT_INFO(info), NULL); + + return info->priv->bus_name; +} + +const gchar * +gclue_client_info_get_cmdline (GClueClientInfo *info) +{ + g_return_val_if_fail (GCLUE_IS_CLIENT_INFO(info), NULL); + + return info->priv->cmdline; +} + +guint32 +gclue_client_info_get_user_id (GClueClientInfo *info) +{ + g_return_val_if_fail (GCLUE_IS_CLIENT_INFO(info), 0); + + return info->priv->user_id; +} + +gboolean +gclue_client_info_check_bus_name (GClueClientInfo *info, + const char *bus_name) +{ + g_return_val_if_fail (GCLUE_IS_CLIENT_INFO(info), FALSE); + + return (strcmp (bus_name, info->priv->bus_name) == 0); +} |