Files
pam-fprint-grosshack/src/egg-dbus-monitor.c
Bastien Nocera f93d8cbce2 Clean up device when the client disconnects
Track clients connected to each device using EggDbusMonitor.
When there are no more clients connected to the device, release it.

When no devices are used anymore, set up a timeout to exit within
30 seconds.
2008-11-03 22:56:12 +00:00

266 lines
7.5 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2006-2008 Richard Hughes <richard@hughsie.com>
*
* Licensed under the GNU General Public License Version 2
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <dbus/dbus.h>
#include "egg-dbus-monitor.h"
static void egg_dbus_monitor_class_init (EggDbusMonitorClass *klass);
static void egg_dbus_monitor_init (EggDbusMonitor *dbus_monitor);
static void egg_dbus_monitor_finalize (GObject *object);
#define EGG_DBUS_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_DBUS_MONITOR, EggDbusMonitorPrivate))
struct EggDbusMonitorPrivate
{
gchar *service;
DBusGProxy *proxy;
DBusGConnection *connection;
const gchar *unique_name;
};
enum {
EGG_DBUS_MONITOR_CONNECTION_CHANGED,
EGG_DBUS_MONITOR_CONNECTION_REPLACED,
EGG_DBUS_MONITOR_LAST_SIGNAL
};
static guint signals [EGG_DBUS_MONITOR_LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (EggDbusMonitor, egg_dbus_monitor, G_TYPE_OBJECT)
/**
* egg_dbus_monitor_name_owner_changed_cb:
**/
static void
egg_dbus_monitor_name_owner_changed_cb (DBusGProxy *proxy, const gchar *name,
const gchar *prev, const gchar *new,
EggDbusMonitor *monitor)
{
guint new_len;
guint prev_len;
g_return_if_fail (EGG_IS_DBUS_MONITOR (monitor));
if (monitor->priv->proxy == NULL)
return;
/* not us */
if (strcmp (name, monitor->priv->service) != 0)
return;
/* ITS4: ignore, not used for allocation */
new_len = strlen (new);
/* ITS4: ignore, not used for allocation */
prev_len = strlen (prev);
/* something --> nothing */
if (prev_len != 0 && new_len == 0) {
g_signal_emit (monitor, signals [EGG_DBUS_MONITOR_CONNECTION_CHANGED], 0, FALSE);
return;
}
/* nothing --> something */
if (prev_len == 0 && new_len != 0) {
g_signal_emit (monitor, signals [EGG_DBUS_MONITOR_CONNECTION_CHANGED], 0, TRUE);
return;
}
/* something --> something (we've replaced the old process) */
if (prev_len != 0 && new_len != 0) {
/* only send this to the prev client */
if (strcmp (monitor->priv->unique_name, prev) == 0)
g_signal_emit (monitor, signals [EGG_DBUS_MONITOR_CONNECTION_REPLACED], 0);
return;
}
}
/**
* egg_dbus_monitor_assign:
* @monitor: This class instance
* @connection: The bus connection
* @service: The EGG_DBUS_MONITOR service name
* Return value: success
*
* Emits connection-changed(TRUE) if connection is alive - this means you
* have to connect up the callback before this function is called.
**/
gboolean
egg_dbus_monitor_assign (EggDbusMonitor *monitor, DBusGConnection *connection, const gchar *service)
{
GError *error = NULL;
gboolean connected;
DBusConnection *conn;
g_return_val_if_fail (EGG_IS_DBUS_MONITOR (monitor), FALSE);
g_return_val_if_fail (service != NULL, FALSE);
g_return_val_if_fail (connection != NULL, FALSE);
if (monitor->priv->proxy != NULL) {
g_warning ("already assigned!");
return FALSE;
}
monitor->priv->service = g_strdup (service);
monitor->priv->connection = connection;
monitor->priv->proxy = dbus_g_proxy_new_for_name_owner (monitor->priv->connection,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
&error);
if (error != NULL) {
g_warning ("Cannot connect to DBUS: %s", error->message);
g_error_free (error);
return FALSE;
}
dbus_g_proxy_add_signal (monitor->priv->proxy, "NameOwnerChanged",
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (monitor->priv->proxy, "NameOwnerChanged",
G_CALLBACK (egg_dbus_monitor_name_owner_changed_cb),
monitor, NULL);
/* coldplug */
connected = egg_dbus_monitor_is_connected (monitor);
if (connected)
g_signal_emit (monitor, signals [EGG_DBUS_MONITOR_CONNECTION_CHANGED], 0, TRUE);
/* save this for the replaced check */
conn = dbus_g_connection_get_connection (monitor->priv->connection);
monitor->priv->unique_name = dbus_bus_get_unique_name (conn);
return TRUE;
}
/**
* egg_dbus_monitor_is_connected:
* @monitor: This class instance
* Return value: if we are connected to a valid watch
**/
gboolean
egg_dbus_monitor_is_connected (EggDbusMonitor *monitor)
{
DBusError error;
DBusConnection *conn;
gboolean ret;
g_return_val_if_fail (EGG_IS_DBUS_MONITOR (monitor), FALSE);
/* get raw connection */
conn = dbus_g_connection_get_connection (monitor->priv->connection);
dbus_error_init (&error);
ret = dbus_bus_name_has_owner (conn, monitor->priv->service, &error);
if (dbus_error_is_set (&error)) {
g_debug ("error: %s", error.message);
dbus_error_free (&error);
}
return ret;
}
/**
* egg_dbus_monitor_get_service:
* @monitor: This class instance
* Return value: the service name being monitored
**/
const gchar *
egg_dbus_monitor_get_service (EggDbusMonitor *monitor)
{
g_return_val_if_fail (EGG_IS_DBUS_MONITOR (monitor), FALSE);
return monitor->priv->service;
}
/**
* egg_dbus_monitor_class_init:
* @klass: The EggDbusMonitorClass
**/
static void
egg_dbus_monitor_class_init (EggDbusMonitorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = egg_dbus_monitor_finalize;
g_type_class_add_private (klass, sizeof (EggDbusMonitorPrivate));
signals [EGG_DBUS_MONITOR_CONNECTION_CHANGED] =
g_signal_new ("connection-changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EggDbusMonitorClass, connection_changed),
NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
signals [EGG_DBUS_MONITOR_CONNECTION_REPLACED] =
g_signal_new ("connection-replaced",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EggDbusMonitorClass, connection_replaced),
NULL, NULL, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
/**
* egg_dbus_monitor_init:
* @monitor: This class instance
**/
static void
egg_dbus_monitor_init (EggDbusMonitor *monitor)
{
monitor->priv = EGG_DBUS_MONITOR_GET_PRIVATE (monitor);
monitor->priv->service = NULL;
monitor->priv->connection = NULL;
monitor->priv->proxy = NULL;
}
/**
* egg_dbus_monitor_finalize:
* @object: The object to finalize
**/
static void
egg_dbus_monitor_finalize (GObject *object)
{
EggDbusMonitor *monitor;
g_return_if_fail (EGG_IS_DBUS_MONITOR (object));
monitor = EGG_DBUS_MONITOR (object);
g_return_if_fail (monitor->priv != NULL);
if (monitor->priv->proxy != NULL)
g_object_unref (monitor->priv->proxy);
G_OBJECT_CLASS (egg_dbus_monitor_parent_class)->finalize (object);
}
/**
* egg_dbus_monitor_new:
*
* Return value: a new EggDbusMonitor object.
**/
EggDbusMonitor *
egg_dbus_monitor_new (void)
{
EggDbusMonitor *monitor;
monitor = g_object_new (EGG_TYPE_DBUS_MONITOR, NULL);
return EGG_DBUS_MONITOR (monitor);
}