upower/src/dkp-daemon.c
2008-08-01 17:01:45 +01:00

794 lines
21 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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 "dkp-debug.h"
#include "dkp-daemon.h"
#include "dkp-device.h"
#include "dkp-daemon-glue.h"
#include "dkp-marshal.h"
enum
{
DEVICE_ADDED_SIGNAL,
DEVICE_REMOVED_SIGNAL,
DEVICE_CHANGED_SIGNAL,
ON_BATTERY_CHANGED_SIGNAL,
LOW_BATTERY_CHANGED_SIGNAL,
LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = { 0 };
struct DkpDaemonPrivate
{
DBusGConnection *system_bus_connection;
DBusGProxy *system_bus_proxy;
PolKitContext *pk_context;
PolKitTracker *pk_tracker;
GHashTable *map_native_path_to_device;
gboolean on_battery;
gboolean low_battery;
DevkitClient *devkit_client;
};
static void dkp_daemon_class_init (DkpDaemonClass *klass);
static void dkp_daemon_init (DkpDaemon *seat);
static void dkp_daemon_finalize (GObject *object);
G_DEFINE_TYPE (DkpDaemon, dkp_daemon, G_TYPE_OBJECT)
#define DKP_DAEMON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DKP_SOURCE_TYPE_DAEMON, DkpDaemonPrivate))
/**
* dkp_daemon_error_quark:
**/
GQuark
dkp_daemon_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0) {
ret = g_quark_from_static_string ("dkp_daemon_error");
}
return ret;
}
#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
/**
* dkp_daemon_error_get_type:
**/
GType
dkp_daemon_error_get_type (void)
{
static GType etype = 0;
if (etype == 0)
{
static const GEnumValue values[] =
{
ENUM_ENTRY (DKP_DAEMON_ERROR_GENERAL, "GeneralError"),
ENUM_ENTRY (DKP_DAEMON_ERROR_NOT_SUPPORTED, "NotSupported"),
ENUM_ENTRY (DKP_DAEMON_ERROR_NO_SUCH_DEVICE, "NoSuchDevice"),
{ 0, 0, 0 }
};
g_assert (DKP_DAEMON_NUM_ERRORS == G_N_ELEMENTS (values) - 1);
etype = g_enum_register_static ("DkpDaemonError", values);
}
return etype;
}
/**
* dkp_daemon_constructor:
**/
static GObject *
dkp_daemon_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties)
{
DkpDaemon *daemon;
DkpDaemonClass *klass;
klass = DKP_DAEMON_CLASS (g_type_class_peek (DKP_SOURCE_TYPE_DAEMON));
daemon = DKP_DAEMON (G_OBJECT_CLASS (dkp_daemon_parent_class)->constructor (type, n_construct_properties, construct_properties));
return G_OBJECT (daemon);
}
/**
* dkp_daemon_class_init:
**/
static void
dkp_daemon_class_init (DkpDaemonClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructor = dkp_daemon_constructor;
object_class->finalize = dkp_daemon_finalize;
g_type_class_add_private (klass, sizeof (DkpDaemonPrivate));
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);
signals[ON_BATTERY_CHANGED_SIGNAL] =
g_signal_new ("on-battery-changed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
signals[LOW_BATTERY_CHANGED_SIGNAL] =
g_signal_new ("low-battery-changed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
dbus_g_object_type_install_info (DKP_SOURCE_TYPE_DAEMON, &dbus_glib_dkp_daemon_object_info);
dbus_g_error_domain_register (DKP_DAEMON_ERROR, NULL, DKP_DAEMON_TYPE_ERROR);
}
/**
* dkp_daemon_init:
**/
static void
dkp_daemon_init (DkpDaemon *daemon)
{
daemon->priv = DKP_DAEMON_GET_PRIVATE (daemon);
daemon->priv->on_battery = FALSE;
daemon->priv->low_battery = FALSE;
daemon->priv->map_native_path_to_device = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}
/**
* dkp_daemon_finalize:
**/
static void
dkp_daemon_finalize (GObject *object)
{
DkpDaemon *daemon;
g_return_if_fail (object != NULL);
g_return_if_fail (DKP_IS_DAEMON (object));
daemon = DKP_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 (dkp_daemon_parent_class)->finalize (object);
}
/**
* pk_io_watch_have_data:
**/
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;
}
/**
* pk_io_add_watch:
**/
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;
}
/**
* pk_io_remove_watch:
**/
static void
pk_io_remove_watch (PolKitContext *pk_context, int watch_id)
{
g_source_remove (watch_id);
}
/**
* gpk_daemon_dbus_filter:
**/
static DBusHandlerResult
gpk_daemon_dbus_filter (DBusConnection *connection, DBusMessage *message, void *user_data)
{
DkpDaemon *daemon = DKP_DAEMON (user_data);
const gchar *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 gpk_daemon_device_add (DkpDaemon *daemon, DevkitDevice *d, gboolean emit_event);
static void gpk_daemon_device_remove (DkpDaemon *daemon, DevkitDevice *d);
/**
* gpk_daemon_device_changed:
**/
static void
gpk_daemon_device_changed (DkpDaemon *daemon, DevkitDevice *d, gboolean synthesized)
{
DkpDevice *device;
const gchar *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 (!dkp_device_changed (device, d, synthesized)) {
dkp_debug ("changed triggered remove on %s", native_path);
gpk_daemon_device_remove (daemon, d);
} else {
dkp_debug ("changed %s", native_path);
}
} else {
dkp_debug ("treating change event as add on %s", native_path);
gpk_daemon_device_add (daemon, d, TRUE);
}
}
/**
* gpk_daemon_device_went_away_remove_cb:
**/
static gboolean
gpk_daemon_device_went_away_remove_cb (gpointer key, gpointer value, gpointer user_data)
{
if (value == user_data) {
dkp_debug ("removed %s", (char *) key);
return TRUE;
}
return FALSE;
}
/**
* gpk_daemon_device_went_away:
**/
static void
gpk_daemon_device_went_away (gpointer user_data, GObject *where_the_object_was)
{
DkpDaemon *daemon = DKP_DAEMON (user_data);
g_hash_table_foreach_remove (daemon->priv->map_native_path_to_device,
gpk_daemon_device_went_away_remove_cb,
where_the_object_was);
}
/**
* gpk_daemon_device_add:
**/
static void
gpk_daemon_device_add (DkpDaemon *daemon, DevkitDevice *d, gboolean emit_event)
{
DkpDevice *device;
const gchar *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 */
dkp_debug ("treating add event as change event on %s", native_path);
gpk_daemon_device_changed (daemon, d, FALSE);
} else {
device = dkp_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), gpk_daemon_device_went_away, daemon);
g_hash_table_insert (daemon->priv->map_native_path_to_device,
g_strdup (native_path),
device);
dkp_debug ("added %s", native_path);
if (emit_event) {
g_signal_emit (daemon, signals[DEVICE_ADDED_SIGNAL], 0,
dkp_device_get_object_path (device));
}
} else {
dkp_debug ("ignoring add event on %s", native_path);
}
}
}
/**
* gpk_daemon_device_remove:
**/
static void
gpk_daemon_device_remove (DkpDaemon *daemon, DevkitDevice *d)
{
DkpDevice *device;
const gchar *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) {
dkp_debug ("ignoring remove event on %s", native_path);
} else {
dkp_device_removed (device);
g_signal_emit (daemon, signals[DEVICE_REMOVED_SIGNAL], 0,
dkp_device_get_object_path (device));
g_object_unref (device);
}
}
//TODO: hook into the devices
//g_signal_emit (daemon, signals[ON_BATTERY_CHANGED_SIGNAL], 0, FALSE);
//g_signal_emit (daemon, signals[LOW_BATTERY_CHANGED_SIGNAL], 0, FALSE);
/**
* gpk_daemon_device_event_signal_handler:
**/
static void
gpk_daemon_device_event_signal_handler (DevkitClient *client,
const char *action,
DevkitDevice *device,
gpointer user_data)
{
DkpDaemon *daemon = DKP_DAEMON (user_data);
if (strcmp (action, "add") == 0) {
gpk_daemon_device_add (daemon, device, TRUE);
} else if (strcmp (action, "remove") == 0) {
gpk_daemon_device_remove (daemon, device);
} else if (strcmp (action, "change") == 0) {
gpk_daemon_device_changed (daemon, device, FALSE);
} else {
dkp_warning ("unhandled action '%s' on %s", action, devkit_device_get_native_path (device));
}
}
/**
* gpk_daemon_register_power_daemon:
**/
static gboolean
gpk_daemon_register_power_daemon (DkpDaemon *daemon)
{
DBusConnection *connection;
DBusError dbus_error;
GError *error = NULL;
const gchar *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)) {
dkp_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)) {
dkp_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,
gpk_daemon_dbus_filter,
daemon,
NULL)) {
dkp_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)) {
dkp_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 (gpk_daemon_device_event_signal_handler), daemon);
return TRUE;
error:
return FALSE;
}
/**
* dkp_daemon_new:
**/
DkpDaemon *
dkp_daemon_new (void)
{
DkpDaemon *daemon;
GError *error = NULL;
GList *devices;
GList *l;
const gchar *subsystems[] = {"power_supply", NULL};
daemon = DKP_DAEMON (g_object_new (DKP_SOURCE_TYPE_DAEMON, NULL));
if (!gpk_daemon_register_power_daemon (DKP_DAEMON (daemon))) {
g_object_unref (daemon);
return NULL;
}
devices = devkit_client_enumerate_by_subsystem (daemon->priv->devkit_client,
subsystems,
&error);
if (error != NULL) {
dkp_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;
gpk_daemon_device_add (daemon, device, FALSE);
}
g_list_foreach (devices, (GFunc) g_object_unref, NULL);
g_list_free (devices);
return daemon;
}
/**
* dkp_daemon_local_get_caller_for_context:
**/
PolKitCaller *
dkp_daemon_local_get_caller_for_context (DkpDaemon *daemon, DBusGMethodInvocation *context)
{
const gchar *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 (DKP_DAEMON_ERROR,
DKP_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;
}
/**
* dkp_daemon_local_check_auth:
**/
gboolean
dkp_daemon_local_check_auth (DkpDaemon *daemon, PolKitCaller *pk_caller, const char *action_id, DBusGMethodInvocation *context)
{
gboolean ret = FALSE;
GError *error;
DBusError d_error;
PolKitAction *pk_action;
PolKitResult pk_result;
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
/**
* gpk_daemon_throw_error:
**/
static gboolean
gpk_daemon_throw_error (DBusGMethodInvocation *context, int error_code, const char *format, ...)
{
GError *error;
va_list args;
gchar *message;
va_start (args, format);
message = g_strdup_vprintf (format, args);
va_end (args);
error = g_error_new (DKP_DAEMON_ERROR,
error_code,
message);
dbus_g_method_return_error (context, error);
g_error_free (error);
g_free (message);
return TRUE;
}
#endif
/* exported methods */
/**
* gpk_daemon_enumerate_cb:
**/
static void
gpk_daemon_enumerate_cb (gpointer key, gpointer value, gpointer user_data)
{
DkpDevice *device = DKP_DEVICE (value);
GPtrArray *object_paths = user_data;
g_ptr_array_add (object_paths, g_strdup (dkp_device_get_object_path (device)));
}
/**
* dkp_daemon_enumerate_devices:
**/
gboolean
dkp_daemon_enumerate_devices (DkpDaemon *daemon, DBusGMethodInvocation *context)
{
GPtrArray *object_paths;
object_paths = g_ptr_array_new ();
g_hash_table_foreach (daemon->priv->map_native_path_to_device, gpk_daemon_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;
}
/**
* dkp_daemon_get_on_battery:
**/
gboolean
dkp_daemon_get_on_battery (DkpDaemon *daemon, DBusGMethodInvocation *context)
{
/* this is cached as it's expensive to check all sources */
dbus_g_method_return (context, daemon->priv->on_battery);
return TRUE;
}
/**
* dkp_daemon_get_low_battery:
**/
gboolean
dkp_daemon_get_low_battery (DkpDaemon *daemon, DBusGMethodInvocation *context)
{
/* this is cached as it's expensive to check all sources */
dbus_g_method_return (context, daemon->priv->low_battery);
return TRUE;
}
/**
* dkp_daemon_suspend:
**/
gboolean
dkp_daemon_suspend (DkpDaemon *daemon, DBusGMethodInvocation *context)
{
gboolean ret;
GError *error;
GError *error_local = NULL;
gchar *argv;
const gchar *quirks;
PolKitCaller *pk_caller;
pk_caller = dkp_daemon_local_get_caller_for_context (daemon, context);
if (pk_caller == NULL)
goto out;
if (!dkp_daemon_local_check_auth (daemon, pk_caller, "org.freedesktop.devicekit.power.suspend", context))
goto out;
/* TODO: where from? */
quirks = "--quirk-s3-bios --quirk-s3-mode";
argv = g_strdup_printf ("/usr/sbin/pm-suspend %s", quirks);
ret = g_spawn_command_line_async (argv, &error_local);
if (!ret) {
error = g_error_new (DKP_DAEMON_ERROR,
DKP_DAEMON_ERROR_GENERAL,
"Cannot spawn: %s", error_local->message);
g_error_free (error_local);
dbus_g_method_return_error (context, error);
goto out;
}
dbus_g_method_return (context, NULL);
out:
if (pk_caller != NULL)
polkit_caller_unref (pk_caller);
return TRUE;
}
/**
* dkp_daemon_hibernate:
**/
gboolean
dkp_daemon_hibernate (DkpDaemon *daemon, DBusGMethodInvocation *context)
{
gboolean ret;
GError *error;
GError *error_local = NULL;
gchar *argv;
const gchar *quirks;
PolKitCaller *pk_caller;
pk_caller = dkp_daemon_local_get_caller_for_context (daemon, context);
if (pk_caller == NULL)
goto out;
if (!dkp_daemon_local_check_auth (daemon, pk_caller, "org.freedesktop.devicekit.power.hibernate", context))
goto out;
/* TODO: where from? */
quirks = "--quirk-s3-bios --quirk-s3-mode";
argv = g_strdup_printf ("/usr/sbin/pm-hibernate %s", quirks);
ret = g_spawn_command_line_async (argv, &error_local);
if (!ret) {
error = g_error_new (DKP_DAEMON_ERROR,
DKP_DAEMON_ERROR_GENERAL,
"Cannot spawn: %s", error_local->message);
g_error_free (error_local);
dbus_g_method_return_error (context, error);
goto out;
}
dbus_g_method_return (context, NULL);
out:
if (pk_caller != NULL)
polkit_caller_unref (pk_caller);
return TRUE;
}