summaryrefslogtreecommitdiff
path: root/src/devkit-power-daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devkit-power-daemon.c')
-rw-r--r--src/devkit-power-daemon.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/src/devkit-power-daemon.c b/src/devkit-power-daemon.c
new file mode 100644
index 0000000..453ff0b
--- /dev/null
+++ b/src/devkit-power-daemon.c
@@ -0,0 +1,631 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 David Zeuthen <david@fubar.dk>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <devkit-gobject.h>
+
+#include "devkit-power-daemon.h"
+#include "devkit-power-device.h"
+
+#include "devkit-power-daemon-glue.h"
+#include "devkit-power-marshal.h"
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+enum
+{
+ DEVICE_ADDED_SIGNAL,
+ DEVICE_REMOVED_SIGNAL,
+ DEVICE_CHANGED_SIGNAL,
+ LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct DevkitPowerDaemonPrivate
+{
+ DBusGConnection *system_bus_connection;
+ DBusGProxy *system_bus_proxy;
+ PolKitContext *pk_context;
+ PolKitTracker *pk_tracker;
+
+ GHashTable *map_native_path_to_device;
+
+ DevkitClient *devkit_client;
+};
+
+static void devkit_power_daemon_class_init (DevkitPowerDaemonClass *klass);
+static void devkit_power_daemon_init (DevkitPowerDaemon *seat);
+static void devkit_power_daemon_finalize (GObject *object);
+
+G_DEFINE_TYPE (DevkitPowerDaemon, devkit_power_daemon, G_TYPE_OBJECT)
+
+#define DEVKIT_POWER_DAEMON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEVKIT_TYPE_POWER_DAEMON, DevkitPowerDaemonPrivate))
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+GQuark
+devkit_power_daemon_error_quark (void)
+{
+ static GQuark ret = 0;
+
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("devkit_power_daemon_error");
+ }
+
+ return ret;
+}
+
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+devkit_power_daemon_error_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0)
+ {
+ static const GEnumValue values[] =
+ {
+ ENUM_ENTRY (DEVKIT_POWER_DAEMON_ERROR_GENERAL, "GeneralError"),
+ ENUM_ENTRY (DEVKIT_POWER_DAEMON_ERROR_NOT_SUPPORTED, "NotSupported"),
+ ENUM_ENTRY (DEVKIT_POWER_DAEMON_ERROR_NO_SUCH_DEVICE, "NoSuchDevice"),
+ { 0, 0, 0 }
+ };
+ g_assert (DEVKIT_POWER_DAEMON_NUM_ERRORS == G_N_ELEMENTS (values) - 1);
+ etype = g_enum_register_static ("DevkitPowerDaemonError", values);
+ }
+ return etype;
+}
+
+
+static GObject *
+devkit_power_daemon_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ DevkitPowerDaemon *daemon;
+ DevkitPowerDaemonClass *klass;
+
+ klass = DEVKIT_POWER_DAEMON_CLASS (g_type_class_peek (DEVKIT_TYPE_POWER_DAEMON));
+
+ daemon = DEVKIT_POWER_DAEMON (
+ G_OBJECT_CLASS (devkit_power_daemon_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+ return G_OBJECT (daemon);
+}
+
+static void
+devkit_power_daemon_class_init (DevkitPowerDaemonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructor = devkit_power_daemon_constructor;
+ object_class->finalize = devkit_power_daemon_finalize;
+
+ g_type_class_add_private (klass, sizeof (DevkitPowerDaemonPrivate));
+
+ signals[DEVICE_ADDED_SIGNAL] =
+ g_signal_new ("device-added",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ signals[DEVICE_REMOVED_SIGNAL] =
+ g_signal_new ("device-removed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ signals[DEVICE_CHANGED_SIGNAL] =
+ g_signal_new ("device-changed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ dbus_g_object_type_install_info (DEVKIT_TYPE_POWER_DAEMON, &dbus_glib_devkit_power_daemon_object_info);
+
+ dbus_g_error_domain_register (DEVKIT_POWER_DAEMON_ERROR,
+ NULL,
+ DEVKIT_POWER_DAEMON_TYPE_ERROR);
+}
+
+static void
+devkit_power_daemon_init (DevkitPowerDaemon *daemon)
+{
+ daemon->priv = DEVKIT_POWER_DAEMON_GET_PRIVATE (daemon);
+ daemon->priv->map_native_path_to_device = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+}
+
+static void
+devkit_power_daemon_finalize (GObject *object)
+{
+ DevkitPowerDaemon *daemon;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (DEVKIT_IS_POWER_DAEMON (object));
+
+ daemon = DEVKIT_POWER_DAEMON (object);
+
+ g_return_if_fail (daemon->priv != NULL);
+
+ if (daemon->priv->pk_context != NULL)
+ polkit_context_unref (daemon->priv->pk_context);
+
+ if (daemon->priv->pk_tracker != NULL)
+ polkit_tracker_unref (daemon->priv->pk_tracker);
+
+ if (daemon->priv->system_bus_proxy != NULL)
+ g_object_unref (daemon->priv->system_bus_proxy);
+
+ if (daemon->priv->system_bus_connection != NULL)
+ dbus_g_connection_unref (daemon->priv->system_bus_connection);
+
+ if (daemon->priv->devkit_client != NULL) {
+ g_object_unref (daemon->priv->devkit_client);
+ }
+
+ if (daemon->priv->map_native_path_to_device != NULL) {
+ g_hash_table_unref (daemon->priv->map_native_path_to_device);
+ }
+
+ G_OBJECT_CLASS (devkit_power_daemon_parent_class)->finalize (object);
+}
+
+static gboolean
+pk_io_watch_have_data (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+{
+ int fd;
+ PolKitContext *pk_context = user_data;
+ fd = g_io_channel_unix_get_fd (channel);
+ polkit_context_io_func (pk_context, fd);
+ return TRUE;
+}
+
+static int
+pk_io_add_watch (PolKitContext *pk_context, int fd)
+{
+ guint id = 0;
+ GIOChannel *channel;
+ channel = g_io_channel_unix_new (fd);
+ if (channel == NULL)
+ goto out;
+ id = g_io_add_watch (channel, G_IO_IN, pk_io_watch_have_data, pk_context);
+ if (id == 0) {
+ g_io_channel_unref (channel);
+ goto out;
+ }
+ g_io_channel_unref (channel);
+out:
+ return id;
+}
+
+static void
+pk_io_remove_watch (PolKitContext *pk_context, int watch_id)
+{
+ g_source_remove (watch_id);
+}
+
+static DBusHandlerResult
+_filter (DBusConnection *connection, DBusMessage *message, void *user_data)
+{
+ DevkitPowerDaemon *daemon = DEVKIT_POWER_DAEMON (user_data);
+ const char *interface;
+
+ interface = dbus_message_get_interface (message);
+
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+ /* pass NameOwnerChanged signals from the bus to PolKitTracker */
+ polkit_tracker_dbus_func (daemon->priv->pk_tracker, message);
+ }
+
+ if (interface != NULL && g_str_has_prefix (interface, "org.freedesktop.ConsoleKit")) {
+ /* pass ConsoleKit signals to PolKitTracker */
+ polkit_tracker_dbus_func (daemon->priv->pk_tracker, message);
+ }
+
+ /* other filters might want to process this message too */
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void device_add (DevkitPowerDaemon *daemon, DevkitDevice *d, gboolean emit_event);
+static void device_remove (DevkitPowerDaemon *daemon, DevkitDevice *d);
+
+static void
+device_changed (DevkitPowerDaemon *daemon, DevkitDevice *d, gboolean synthesized)
+{
+ DevkitPowerDevice *device;
+ const char *native_path;
+
+ native_path = devkit_device_get_native_path (d);
+ device = g_hash_table_lookup (daemon->priv->map_native_path_to_device, native_path);
+ if (device != NULL) {
+ if (!devkit_power_device_changed (device, d, synthesized)) {
+ g_print ("changed triggered remove on %s\n", native_path);
+ device_remove (daemon, d);
+ } else {
+ g_print ("changed %s\n", native_path);
+ }
+ } else {
+ g_print ("treating change event as add on %s\n", native_path);
+ device_add (daemon, d, TRUE);
+ }
+}
+
+static gboolean
+device_went_away_remove_cb (gpointer key, gpointer value, gpointer user_data)
+{
+ if (value == user_data) {
+ g_print ("removed %s\n", (char *) key);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+device_went_away (gpointer user_data, GObject *where_the_object_was)
+{
+ DevkitPowerDaemon *daemon = DEVKIT_POWER_DAEMON (user_data);
+
+ g_hash_table_foreach_remove (daemon->priv->map_native_path_to_device,
+ device_went_away_remove_cb,
+ where_the_object_was);
+}
+
+static void
+device_add (DevkitPowerDaemon *daemon, DevkitDevice *d, gboolean emit_event)
+{
+ DevkitPowerDevice *device;
+ const char *native_path;
+
+ native_path = devkit_device_get_native_path (d);
+ device = g_hash_table_lookup (daemon->priv->map_native_path_to_device, native_path);
+ if (device != NULL) {
+ /* we already have the device; treat as change event */
+ g_print ("treating add event as change event on %s\n", native_path);
+ device_changed (daemon, d, FALSE);
+ } else {
+ device = devkit_power_device_new (daemon, d);
+
+ if (device != NULL) {
+ /* only take a weak ref; the device will stay on the bus until
+ * it's unreffed. So if we ref it, it'll never go away. Stupid
+ * dbus-glib, no cookie for you.
+ */
+ g_object_weak_ref (G_OBJECT (device), device_went_away, daemon);
+ g_hash_table_insert (daemon->priv->map_native_path_to_device,
+ g_strdup (native_path),
+ device);
+ g_print ("added %s\n", native_path);
+ if (emit_event) {
+ g_signal_emit (daemon, signals[DEVICE_ADDED_SIGNAL], 0,
+ devkit_power_device_get_object_path (device));
+ }
+ } else {
+ g_print ("ignoring add event on %s\n", native_path);
+ }
+ }
+}
+
+static void
+device_remove (DevkitPowerDaemon *daemon, DevkitDevice *d)
+{
+ DevkitPowerDevice *device;
+ const char *native_path;
+
+ native_path = devkit_device_get_native_path (d);
+ device = g_hash_table_lookup (daemon->priv->map_native_path_to_device, native_path);
+ if (device == NULL) {
+ g_print ("ignoring remove event on %s\n", native_path);
+ } else {
+ devkit_power_device_removed (device);
+ g_signal_emit (daemon, signals[DEVICE_REMOVED_SIGNAL], 0,
+ devkit_power_device_get_object_path (device));
+ g_object_unref (device);
+ }
+}
+
+static void
+device_event_signal_handler (DevkitClient *client,
+ const char *action,
+ DevkitDevice *device,
+ gpointer user_data)
+{
+ DevkitPowerDaemon *daemon = DEVKIT_POWER_DAEMON (user_data);
+
+ if (strcmp (action, "add") == 0) {
+ device_add (daemon, device, TRUE);
+ } else if (strcmp (action, "remove") == 0) {
+ device_remove (daemon, device);
+ } else if (strcmp (action, "change") == 0) {
+ device_changed (daemon, device, FALSE);
+ } else {
+ g_warning ("unhandled action '%s' on %s", action, devkit_device_get_native_path (device));
+ }
+}
+
+static gboolean
+register_power_daemon (DevkitPowerDaemon *daemon)
+{
+ DBusConnection *connection;
+ DBusError dbus_error;
+ GError *error = NULL;
+ const char *subsystems[] = {"power_supply", NULL};
+
+ daemon->priv->pk_context = polkit_context_new ();
+ polkit_context_set_io_watch_functions (daemon->priv->pk_context, pk_io_add_watch, pk_io_remove_watch);
+ if (!polkit_context_init (daemon->priv->pk_context, NULL)) {
+ g_critical ("cannot initialize libpolkit");
+ goto error;
+ }
+
+ error = NULL;
+ daemon->priv->system_bus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (daemon->priv->system_bus_connection == NULL) {
+ if (error != NULL) {
+ g_critical ("error getting system bus: %s", error->message);
+ g_error_free (error);
+ }
+ goto error;
+ }
+ connection = dbus_g_connection_get_connection (daemon->priv->system_bus_connection);
+
+ daemon->priv->pk_tracker = polkit_tracker_new ();
+ polkit_tracker_set_system_bus_connection (daemon->priv->pk_tracker, connection);
+ polkit_tracker_init (daemon->priv->pk_tracker);
+
+ dbus_g_connection_register_g_object (daemon->priv->system_bus_connection, "/",
+ G_OBJECT (daemon));
+
+ daemon->priv->system_bus_proxy = dbus_g_proxy_new_for_name (daemon->priv->system_bus_connection,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+
+ /* TODO FIXME: I'm pretty sure dbus-glib blows in a way that
+ * we can't say we're interested in all signals from all
+ * members on all interfaces for a given service... So we do
+ * this..
+ */
+
+ dbus_error_init (&dbus_error);
+
+ /* need to listen to NameOwnerChanged */
+ dbus_bus_add_match (connection,
+ "type='signal'"
+ ",interface='"DBUS_INTERFACE_DBUS"'"
+ ",sender='"DBUS_SERVICE_DBUS"'"
+ ",member='NameOwnerChanged'",
+ &dbus_error);
+
+ if (dbus_error_is_set (&dbus_error)) {
+ g_warning ("Cannot add match rule: %s: %s", dbus_error.name, dbus_error.message);
+ dbus_error_free (&dbus_error);
+ goto error;
+ }
+
+ /* need to listen to ConsoleKit signals */
+ dbus_bus_add_match (connection,
+ "type='signal',sender='org.freedesktop.ConsoleKit'",
+ &dbus_error);
+
+ if (dbus_error_is_set (&dbus_error)) {
+ g_warning ("Cannot add match rule: %s: %s", dbus_error.name, dbus_error.message);
+ dbus_error_free (&dbus_error);
+ goto error;
+ }
+
+ if (!dbus_connection_add_filter (connection,
+ _filter,
+ daemon,
+ NULL)) {
+ g_warning ("Cannot add D-Bus filter: %s: %s", dbus_error.name, dbus_error.message);
+ goto error;
+ }
+
+ /* connect to the DeviceKit daemon */
+ daemon->priv->devkit_client = devkit_client_new (subsystems);
+ if (!devkit_client_connect (daemon->priv->devkit_client, &error)) {
+ g_warning ("Couldn't open connection to DeviceKit daemon: %s", error->message);
+ g_error_free (error);
+ goto error;
+ }
+ g_signal_connect (daemon->priv->devkit_client, "device-event",
+ G_CALLBACK (device_event_signal_handler), daemon);
+
+ return TRUE;
+error:
+ return FALSE;
+}
+
+
+DevkitPowerDaemon *
+devkit_power_daemon_new (void)
+{
+ DevkitPowerDaemon *daemon;
+ GError *error = NULL;
+ GList *devices;
+ GList *l;
+ const char *subsystems[] = {"power_supply", NULL};
+
+ daemon = DEVKIT_POWER_DAEMON (g_object_new (DEVKIT_TYPE_POWER_DAEMON, NULL));
+
+ if (!register_power_daemon (DEVKIT_POWER_DAEMON (daemon))) {
+ g_object_unref (daemon);
+ return NULL;
+ }
+
+
+ devices = devkit_client_enumerate_by_subsystem (daemon->priv->devkit_client,
+ subsystems,
+ &error);
+ if (error != NULL) {
+ g_warning ("Cannot enumerate devices: %s", error->message);
+ g_error_free (error);
+ g_object_unref (daemon);
+ return NULL;
+ }
+ for (l = devices; l != NULL; l = l->next) {
+ DevkitDevice *device = l->data;
+ device_add (daemon, device, FALSE);
+ }
+ g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+ g_list_free (devices);
+
+ return daemon;
+}
+
+PolKitCaller *
+devkit_power_damon_local_get_caller_for_context (DevkitPowerDaemon *daemon,
+ DBusGMethodInvocation *context)
+{
+ const char *sender;
+ GError *error;
+ DBusError dbus_error;
+ PolKitCaller *pk_caller;
+
+ sender = dbus_g_method_get_sender (context);
+ dbus_error_init (&dbus_error);
+ pk_caller = polkit_tracker_get_caller_from_dbus_name (daemon->priv->pk_tracker,
+ sender,
+ &dbus_error);
+ if (pk_caller == NULL) {
+ error = g_error_new (DEVKIT_POWER_DAEMON_ERROR,
+ DEVKIT_POWER_DAEMON_ERROR_GENERAL,
+ "Error getting information about caller: %s: %s",
+ dbus_error.name, dbus_error.message);
+ dbus_error_free (&dbus_error);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return NULL;
+ }
+
+ return pk_caller;
+}
+
+gboolean
+devkit_power_damon_local_check_auth (DevkitPowerDaemon *daemon,
+ PolKitCaller *pk_caller,
+ const char *action_id,
+ DBusGMethodInvocation *context)
+{
+ gboolean ret;
+ GError *error;
+ DBusError d_error;
+ PolKitAction *pk_action;
+ PolKitResult pk_result;
+
+ ret = FALSE;
+
+ pk_action = polkit_action_new ();
+ polkit_action_set_action_id (pk_action, action_id);
+ pk_result = polkit_context_is_caller_authorized (daemon->priv->pk_context,
+ pk_action,
+ pk_caller,
+ TRUE,
+ NULL);
+ if (pk_result == POLKIT_RESULT_YES) {
+ ret = TRUE;
+ } else {
+
+ dbus_error_init (&d_error);
+ polkit_dbus_error_generate (pk_action, pk_result, &d_error);
+ error = NULL;
+ dbus_set_g_error (&error, &d_error);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ dbus_error_free (&d_error);
+ }
+ polkit_action_unref (pk_action);
+ return ret;
+}
+
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+#if 0
+static gboolean
+throw_error (DBusGMethodInvocation *context, int error_code, const char *format, ...)
+{
+ GError *error;
+ va_list args;
+ char *message;
+
+ va_start (args, format);
+ message = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ error = g_error_new (DEVKIT_POWER_DAEMON_ERROR,
+ error_code,
+ message);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ g_free (message);
+ return TRUE;
+}
+#endif
+
+/*--------------------------------------------------------------------------------------------------------------*/
+/* exported methods */
+
+static void
+enumerate_cb (gpointer key, gpointer value, gpointer user_data)
+{
+ DevkitPowerDevice *device = DEVKIT_POWER_DEVICE (value);
+ GPtrArray *object_paths = user_data;
+ g_ptr_array_add (object_paths, g_strdup (devkit_power_device_get_object_path (device)));
+}
+
+gboolean
+devkit_power_daemon_enumerate_devices (DevkitPowerDaemon *daemon,
+ DBusGMethodInvocation *context)
+{
+ GPtrArray *object_paths;
+ object_paths = g_ptr_array_new ();
+ g_hash_table_foreach (daemon->priv->map_native_path_to_device, enumerate_cb, object_paths);
+ dbus_g_method_return (context, object_paths);
+ g_ptr_array_foreach (object_paths, (GFunc) g_free, NULL);
+ g_ptr_array_free (object_paths, TRUE);
+ return TRUE;
+}