mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-07-25 04:04:36 +00:00
cc90f1010e
Add an "object_type" field to NMPropertiesInfo, and use that with DBUS_TYPE_G_OBJECT_PATH and DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH properties so that we don't need custom marshallers for each one. When creating an NMDevice or NMActiveConnection, we need to fetch an extra property first to figure out the exact subclass to use, so add a bit of infrastructure for that as well. Also, do that preprocessing asynchronously when processing a property change notification, so that it doesn't block the main loop.
853 lines
22 KiB
C
853 lines
22 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
/*
|
|
* libnm_glib -- Access network status & information from glib applications
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA.
|
|
*
|
|
* Copyright (C) 2007 - 2008 Novell, Inc.
|
|
* Copyright (C) 2007 - 2011 Red Hat, Inc.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <gio/gio.h>
|
|
#include <nm-utils.h>
|
|
#include "NetworkManager.h"
|
|
#include "nm-object.h"
|
|
#include "nm-object-cache.h"
|
|
#include "nm-object-private.h"
|
|
#include "nm-dbus-glib-types.h"
|
|
#include "nm-glib-compat.h"
|
|
#include "nm-types.h"
|
|
|
|
#define DEBUG 0
|
|
|
|
G_DEFINE_ABSTRACT_TYPE (NMObject, nm_object, G_TYPE_OBJECT)
|
|
|
|
#define NM_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_OBJECT, NMObjectPrivate))
|
|
|
|
static GHashTable *type_funcs, *type_async_funcs;
|
|
|
|
typedef struct {
|
|
PropertyMarshalFunc func;
|
|
GType object_type;
|
|
|
|
gpointer field;
|
|
} PropertyInfo;
|
|
|
|
typedef struct {
|
|
DBusGConnection *connection;
|
|
char *path;
|
|
DBusGProxy *properties_proxy;
|
|
GSList *property_interfaces;
|
|
GSList *property_tables;
|
|
NMObject *parent;
|
|
|
|
GSList *notify_props;
|
|
guint32 notify_id;
|
|
gboolean inited, disposed;
|
|
} NMObjectPrivate;
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_CONNECTION,
|
|
PROP_PATH,
|
|
|
|
LAST_PROP
|
|
};
|
|
|
|
static void
|
|
nm_object_init (NMObject *object)
|
|
{
|
|
}
|
|
|
|
static GObject*
|
|
constructor (GType type,
|
|
guint n_construct_params,
|
|
GObjectConstructParam *construct_params)
|
|
{
|
|
GObject *object;
|
|
NMObjectPrivate *priv;
|
|
|
|
object = G_OBJECT_CLASS (nm_object_parent_class)->constructor (type,
|
|
n_construct_params,
|
|
construct_params);
|
|
if (!object)
|
|
return NULL;
|
|
|
|
priv = NM_OBJECT_GET_PRIVATE (object);
|
|
|
|
if (priv->connection == NULL || priv->path == NULL) {
|
|
g_warning ("%s: bus connection and path required.", __func__);
|
|
g_object_unref (object);
|
|
return NULL;
|
|
}
|
|
|
|
priv->properties_proxy = dbus_g_proxy_new_for_name (priv->connection,
|
|
NM_DBUS_SERVICE,
|
|
priv->path,
|
|
"org.freedesktop.DBus.Properties");
|
|
|
|
_nm_object_cache_add (NM_OBJECT (object));
|
|
|
|
return object;
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
|
|
|
if (priv->disposed) {
|
|
G_OBJECT_CLASS (nm_object_parent_class)->dispose (object);
|
|
return;
|
|
}
|
|
|
|
priv->disposed = TRUE;
|
|
|
|
if (priv->notify_id) {
|
|
g_source_remove (priv->notify_id);
|
|
priv->notify_id = 0;
|
|
}
|
|
|
|
g_slist_foreach (priv->notify_props, (GFunc) g_free, NULL);
|
|
g_slist_free (priv->notify_props);
|
|
|
|
g_slist_foreach (priv->property_interfaces, (GFunc) g_free, NULL);
|
|
g_slist_free (priv->property_interfaces);
|
|
|
|
g_object_unref (priv->properties_proxy);
|
|
dbus_g_connection_unref (priv->connection);
|
|
|
|
G_OBJECT_CLASS (nm_object_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
finalize (GObject *object)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
|
|
|
g_slist_foreach (priv->property_tables, (GFunc) g_hash_table_destroy, NULL);
|
|
g_slist_free (priv->property_tables);
|
|
g_free (priv->path);
|
|
|
|
G_OBJECT_CLASS (nm_object_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
set_property (GObject *object, guint prop_id,
|
|
const GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
|
DBusGConnection *connection;
|
|
|
|
switch (prop_id) {
|
|
case PROP_CONNECTION:
|
|
/* Construct only */
|
|
connection = (DBusGConnection *) g_value_get_boxed (value);
|
|
if (!connection)
|
|
connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL);
|
|
priv->connection = dbus_g_connection_ref (connection);
|
|
break;
|
|
case PROP_PATH:
|
|
/* Construct only */
|
|
priv->path = g_value_dup_string (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_property (GObject *object, guint prop_id,
|
|
GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_CONNECTION:
|
|
g_value_set_boxed (value, priv->connection);
|
|
break;
|
|
case PROP_PATH:
|
|
g_value_set_string (value, priv->path);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
nm_object_class_init (NMObjectClass *nm_object_class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (nm_object_class);
|
|
|
|
g_type_class_add_private (nm_object_class, sizeof (NMObjectPrivate));
|
|
|
|
if (!type_funcs) {
|
|
type_funcs = g_hash_table_new (NULL, NULL);
|
|
type_async_funcs = g_hash_table_new (NULL, NULL);
|
|
}
|
|
|
|
/* virtual methods */
|
|
object_class->constructor = constructor;
|
|
object_class->set_property = set_property;
|
|
object_class->get_property = get_property;
|
|
object_class->dispose = dispose;
|
|
object_class->finalize = finalize;
|
|
|
|
/* porperties */
|
|
|
|
/**
|
|
* NMObject:connection:
|
|
*
|
|
* The #DBusGConnection of the object.
|
|
**/
|
|
g_object_class_install_property
|
|
(object_class, PROP_CONNECTION,
|
|
g_param_spec_boxed (NM_OBJECT_DBUS_CONNECTION,
|
|
"Connection",
|
|
"Connection",
|
|
DBUS_TYPE_G_CONNECTION,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
/**
|
|
* NMObject:path:
|
|
*
|
|
* The DBus object path.
|
|
**/
|
|
g_object_class_install_property
|
|
(object_class, PROP_PATH,
|
|
g_param_spec_string (NM_OBJECT_DBUS_PATH,
|
|
"Object Path",
|
|
"DBus Object Path",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
}
|
|
|
|
/**
|
|
* nm_object_get_connection:
|
|
* @object: a #NMObject
|
|
*
|
|
* Gets the #NMObject's DBusGConnection.
|
|
*
|
|
* Returns: (transfer none): the connection
|
|
**/
|
|
DBusGConnection *
|
|
nm_object_get_connection (NMObject *object)
|
|
{
|
|
g_return_val_if_fail (NM_IS_OBJECT (object), NULL);
|
|
|
|
return NM_OBJECT_GET_PRIVATE (object)->connection;
|
|
}
|
|
|
|
/**
|
|
* nm_object_get_path:
|
|
* @object: a #NMObject
|
|
*
|
|
* Gets the DBus path of the #NMObject.
|
|
*
|
|
* Returns: the object's path. This is the internal string used by the
|
|
* device, and must not be modified.
|
|
**/
|
|
const char *
|
|
nm_object_get_path (NMObject *object)
|
|
{
|
|
g_return_val_if_fail (NM_IS_OBJECT (object), NULL);
|
|
|
|
return NM_OBJECT_GET_PRIVATE (object)->path;
|
|
}
|
|
|
|
static gboolean
|
|
deferred_notify_cb (gpointer data)
|
|
{
|
|
NMObject *object = NM_OBJECT (data);
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
|
GSList *props, *iter;
|
|
|
|
priv->notify_id = 0;
|
|
|
|
/* Clear priv->notify_props early so that an NMObject subclass that
|
|
* listens to property changes can queue up other property changes
|
|
* during the g_object_notify() call separately from the property
|
|
* list we're iterating.
|
|
*/
|
|
props = g_slist_reverse (priv->notify_props);
|
|
priv->notify_props = NULL;
|
|
|
|
for (iter = props; iter; iter = g_slist_next (iter)) {
|
|
g_object_notify (G_OBJECT (object), (const char *) iter->data);
|
|
g_free (iter->data);
|
|
}
|
|
g_slist_free (props);
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
_nm_object_queue_notify (NMObject *object, const char *property)
|
|
{
|
|
NMObjectPrivate *priv;
|
|
gboolean found = FALSE;
|
|
GSList *iter;
|
|
|
|
g_return_if_fail (NM_IS_OBJECT (object));
|
|
g_return_if_fail (property != NULL);
|
|
|
|
priv = NM_OBJECT_GET_PRIVATE (object);
|
|
if (!priv->notify_id)
|
|
priv->notify_id = g_idle_add_full (G_PRIORITY_LOW, deferred_notify_cb, object, NULL);
|
|
|
|
for (iter = priv->notify_props; iter; iter = g_slist_next (iter)) {
|
|
if (!strcmp ((char *) iter->data, property)) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
priv->notify_props = g_slist_prepend (priv->notify_props, g_strdup (property));
|
|
}
|
|
|
|
void
|
|
_nm_object_register_type_func (GType base_type, NMObjectTypeFunc type_func,
|
|
NMObjectTypeAsyncFunc type_async_func)
|
|
{
|
|
g_hash_table_insert (type_funcs,
|
|
GSIZE_TO_POINTER (base_type),
|
|
type_func);
|
|
g_hash_table_insert (type_async_funcs,
|
|
GSIZE_TO_POINTER (base_type),
|
|
type_async_func);
|
|
}
|
|
|
|
static GObject *
|
|
_nm_object_create (GType type, DBusGConnection *connection, const char *path)
|
|
{
|
|
NMObjectTypeFunc type_func;
|
|
|
|
type_func = g_hash_table_lookup (type_funcs, GSIZE_TO_POINTER (type));
|
|
if (type_func)
|
|
type = type_func (connection, path);
|
|
|
|
return g_object_new (type,
|
|
NM_OBJECT_DBUS_CONNECTION, connection,
|
|
NM_OBJECT_DBUS_PATH, path,
|
|
NULL);
|
|
}
|
|
|
|
typedef void (*NMObjectCreateCallbackFunc) (GObject *, gpointer);
|
|
typedef struct {
|
|
DBusGConnection *connection;
|
|
char *path;
|
|
NMObjectCreateCallbackFunc callback;
|
|
gpointer user_data;
|
|
} NMObjectTypeAsyncData;
|
|
|
|
static void
|
|
async_got_type (GType type, gpointer user_data)
|
|
{
|
|
NMObjectTypeAsyncData *async_data = user_data;
|
|
GObject *object;
|
|
|
|
if (type != G_TYPE_INVALID) {
|
|
object = g_object_new (type,
|
|
NM_OBJECT_DBUS_CONNECTION, async_data->connection,
|
|
NM_OBJECT_DBUS_PATH, async_data->path,
|
|
NULL);
|
|
} else
|
|
object = NULL;
|
|
|
|
async_data->callback (object, async_data->user_data);
|
|
|
|
g_free (async_data->path);
|
|
g_slice_free (NMObjectTypeAsyncData, async_data);
|
|
}
|
|
|
|
static void
|
|
_nm_object_create_async (GType type, DBusGConnection *connection, const char *path,
|
|
NMObjectCreateCallbackFunc callback, gpointer user_data)
|
|
{
|
|
NMObjectTypeAsyncFunc type_async_func;
|
|
NMObjectTypeAsyncData *async_data;
|
|
|
|
async_data = g_slice_new (NMObjectTypeAsyncData);
|
|
async_data->connection = connection;
|
|
async_data->path = g_strdup (path);
|
|
async_data->callback = callback;
|
|
async_data->user_data = user_data;
|
|
|
|
type_async_func = g_hash_table_lookup (type_async_funcs, GSIZE_TO_POINTER (type));
|
|
if (type_async_func)
|
|
type_async_func (connection, path, async_got_type, async_data);
|
|
else
|
|
async_got_type (type, async_data);
|
|
}
|
|
|
|
/* Stolen from dbus-glib */
|
|
static char*
|
|
wincaps_to_dash (const char *caps)
|
|
{
|
|
const char *p;
|
|
GString *str;
|
|
|
|
str = g_string_new (NULL);
|
|
p = caps;
|
|
while (*p) {
|
|
if (g_ascii_isupper (*p)) {
|
|
if (str->len > 0 && (str->len < 2 || str->str[str->len-2] != '-'))
|
|
g_string_append_c (str, '-');
|
|
g_string_append_c (str, g_ascii_tolower (*p));
|
|
} else
|
|
g_string_append_c (str, *p);
|
|
++p;
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
typedef struct {
|
|
NMObject *self;
|
|
PropertyInfo *pi;
|
|
|
|
GObject **objects;
|
|
int length, remaining;
|
|
|
|
gboolean array;
|
|
const char *property_name;
|
|
} ObjectCreatedData;
|
|
|
|
static void
|
|
object_created (GObject *obj, gpointer user_data)
|
|
{
|
|
ObjectCreatedData *odata = user_data;
|
|
NMObject *self = odata->self;
|
|
PropertyInfo *pi = odata->pi;
|
|
|
|
/* We assume that on error, the creator_func printed something */
|
|
|
|
odata->objects[--odata->remaining] = obj;
|
|
if (odata->remaining)
|
|
return;
|
|
|
|
if (odata->array) {
|
|
GPtrArray **array = pi->field;
|
|
int i;
|
|
|
|
if (*array)
|
|
g_boxed_free (NM_TYPE_OBJECT_ARRAY, *array);
|
|
*array = g_ptr_array_sized_new (odata->length);
|
|
for (i = 0; i < odata->length; i++) {
|
|
if (odata->objects[i])
|
|
g_ptr_array_add (*array, odata->objects[i]);
|
|
}
|
|
} else {
|
|
GObject **obj_p = pi->field;
|
|
|
|
g_clear_object (obj_p);
|
|
*obj_p = odata->objects[0];
|
|
}
|
|
|
|
if (odata->property_name)
|
|
_nm_object_queue_notify (self, odata->property_name);
|
|
|
|
g_object_unref (self);
|
|
g_free (odata->objects);
|
|
g_slice_free (ObjectCreatedData, odata);
|
|
}
|
|
|
|
static gboolean
|
|
handle_object_property (NMObject *self, const char *property_name, GValue *value,
|
|
PropertyInfo *pi, gboolean synchronously)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
|
|
GObject *obj;
|
|
const char *path;
|
|
ObjectCreatedData *odata;
|
|
|
|
odata = g_slice_new (ObjectCreatedData);
|
|
odata->self = g_object_ref (self);
|
|
odata->pi = pi;
|
|
odata->objects = g_new (GObject *, 1);
|
|
odata->length = odata->remaining = 1;
|
|
odata->array = FALSE;
|
|
odata->property_name = property_name;
|
|
|
|
path = g_value_get_boxed (value);
|
|
if (!strcmp (path, "/")) {
|
|
object_created (NULL, odata);
|
|
return TRUE;
|
|
}
|
|
|
|
obj = G_OBJECT (_nm_object_cache_get (path));
|
|
if (obj) {
|
|
object_created (obj, odata);
|
|
return TRUE;
|
|
} else if (synchronously) {
|
|
obj = _nm_object_create (pi->object_type, priv->connection, path);
|
|
object_created (obj, odata);
|
|
return obj != NULL;
|
|
} else {
|
|
_nm_object_create_async (pi->object_type, priv->connection, path,
|
|
object_created, odata);
|
|
/* Assume success */
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
handle_object_array_property (NMObject *self, const char *property_name, GValue *value,
|
|
PropertyInfo *pi, gboolean synchronously)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
|
|
GObject *obj;
|
|
GPtrArray *paths;
|
|
GPtrArray **array = pi->field;
|
|
const char *path;
|
|
ObjectCreatedData *odata;
|
|
int i;
|
|
|
|
paths = g_value_get_boxed (value);
|
|
|
|
odata = g_slice_new (ObjectCreatedData);
|
|
odata->self = g_object_ref (self);
|
|
odata->pi = pi;
|
|
odata->objects = g_new0 (GObject *, paths->len);
|
|
odata->length = odata->remaining = paths->len;
|
|
odata->array = TRUE;
|
|
odata->property_name = property_name;
|
|
|
|
for (i = 0; i < paths->len; i++) {
|
|
path = paths->pdata[i];
|
|
if (!strcmp (path, "/")) {
|
|
/* FIXME: can't happen? */
|
|
continue;
|
|
}
|
|
|
|
obj = G_OBJECT (_nm_object_cache_get (path));
|
|
if (obj) {
|
|
object_created (obj, odata);
|
|
} else if (synchronously) {
|
|
obj = _nm_object_create (pi->object_type, priv->connection, path);
|
|
object_created (obj, odata);
|
|
} else {
|
|
_nm_object_create_async (pi->object_type, priv->connection, path,
|
|
object_created, odata);
|
|
}
|
|
}
|
|
|
|
if (!synchronously) {
|
|
/* Assume success */
|
|
return TRUE;
|
|
}
|
|
|
|
return *array && ((*array)->len == paths->len);
|
|
}
|
|
|
|
static void
|
|
handle_property_changed (NMObject *self, const char *dbus_name, GValue *value, gboolean synchronously)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
|
|
char *prop_name;
|
|
PropertyInfo *pi;
|
|
GParamSpec *pspec;
|
|
gboolean success = FALSE, found = FALSE;
|
|
GSList *iter;
|
|
|
|
prop_name = wincaps_to_dash (dbus_name);
|
|
|
|
/* Iterate through the object and its parents to find the property */
|
|
for (iter = priv->property_tables; iter; iter = g_slist_next (iter)) {
|
|
pi = g_hash_table_lookup ((GHashTable *) iter->data, prop_name);
|
|
if (pi) {
|
|
if (!pi->field) {
|
|
/* We know about this property but aren't tracking changes on it. */
|
|
goto out;
|
|
}
|
|
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
#if DEBUG
|
|
g_warning ("Property '%s' unhandled.", prop_name);
|
|
#endif
|
|
goto out;
|
|
}
|
|
|
|
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (self)), prop_name);
|
|
if (!pspec) {
|
|
g_warning ("%s: property '%s' changed but wasn't defined by object type %s.",
|
|
__func__,
|
|
prop_name,
|
|
G_OBJECT_TYPE_NAME (self));
|
|
goto out;
|
|
}
|
|
|
|
if (pi->object_type) {
|
|
if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
|
|
success = handle_object_property (self, pspec->name, value, pi, synchronously);
|
|
else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH))
|
|
success = handle_object_array_property (self, pspec->name, value, pi, synchronously);
|
|
else {
|
|
g_warn_if_reached ();
|
|
goto out;
|
|
}
|
|
} else
|
|
success = (*(pi->func)) (self, pspec, value, pi->field);
|
|
|
|
if (!success) {
|
|
g_warning ("%s: failed to update property '%s' of object type %s.",
|
|
__func__,
|
|
prop_name,
|
|
G_OBJECT_TYPE_NAME (self));
|
|
}
|
|
|
|
out:
|
|
g_free (prop_name);
|
|
}
|
|
|
|
void
|
|
_nm_object_process_properties_changed (NMObject *self, GHashTable *properties)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer name, value;
|
|
|
|
g_hash_table_iter_init (&iter, properties);
|
|
while (g_hash_table_iter_next (&iter, &name, &value))
|
|
handle_property_changed (self, name, value, FALSE);
|
|
}
|
|
|
|
static void
|
|
properties_changed_proxy (DBusGProxy *proxy,
|
|
GHashTable *properties,
|
|
gpointer user_data)
|
|
{
|
|
_nm_object_process_properties_changed (NM_OBJECT (user_data), properties);
|
|
}
|
|
|
|
#define HANDLE_TYPE(ucase, lcase, getter) \
|
|
} else if (pspec->value_type == G_TYPE_##ucase) { \
|
|
if (G_VALUE_HOLDS_##ucase (value)) { \
|
|
g##lcase *param = (g##lcase *) field; \
|
|
*param = g_value_get_##getter (value); \
|
|
} else { \
|
|
success = FALSE; \
|
|
goto done; \
|
|
}
|
|
|
|
static gboolean
|
|
demarshal_generic (NMObject *object,
|
|
GParamSpec *pspec,
|
|
GValue *value,
|
|
gpointer field)
|
|
{
|
|
gboolean success = TRUE;
|
|
|
|
if (pspec->value_type == G_TYPE_STRING) {
|
|
if (G_VALUE_HOLDS_STRING (value)) {
|
|
char **param = (char **) field;
|
|
g_free (*param);
|
|
*param = g_value_dup_string (value);
|
|
} else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH)) {
|
|
char **param = (char **) field;
|
|
g_free (*param);
|
|
*param = g_strdup (g_value_get_boxed (value));
|
|
/* Handle "NULL" object paths */
|
|
if (g_strcmp0 (*param, "/") == 0) {
|
|
g_free (*param);
|
|
*param = NULL;
|
|
}
|
|
} else {
|
|
success = FALSE;
|
|
goto done;
|
|
}
|
|
HANDLE_TYPE(BOOLEAN, boolean, boolean)
|
|
HANDLE_TYPE(CHAR, char, schar)
|
|
HANDLE_TYPE(UCHAR, uchar, uchar)
|
|
HANDLE_TYPE(DOUBLE, double, double)
|
|
HANDLE_TYPE(INT, int, int)
|
|
HANDLE_TYPE(UINT, uint, uint)
|
|
HANDLE_TYPE(INT64, int, int)
|
|
HANDLE_TYPE(UINT64, uint, uint)
|
|
HANDLE_TYPE(LONG, long, long)
|
|
HANDLE_TYPE(ULONG, ulong, ulong)
|
|
} else {
|
|
g_warning ("%s: %s/%s unhandled type %s.",
|
|
__func__, G_OBJECT_TYPE_NAME (object), pspec->name,
|
|
g_type_name (pspec->value_type));
|
|
success = FALSE;
|
|
}
|
|
|
|
done:
|
|
if (success) {
|
|
_nm_object_queue_notify (object, pspec->name);
|
|
} else {
|
|
g_warning ("%s: %s/%s (type %s) couldn't be set with type %s.",
|
|
__func__, G_OBJECT_TYPE_NAME (object), pspec->name,
|
|
g_type_name (pspec->value_type), G_VALUE_TYPE_NAME (value));
|
|
}
|
|
return success;
|
|
}
|
|
|
|
void
|
|
_nm_object_register_properties (NMObject *object,
|
|
DBusGProxy *proxy,
|
|
const NMPropertiesInfo *info)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
|
NMPropertiesInfo *tmp;
|
|
GHashTable *instance;
|
|
|
|
g_return_if_fail (NM_IS_OBJECT (object));
|
|
g_return_if_fail (proxy != NULL);
|
|
g_return_if_fail (info != NULL);
|
|
|
|
priv->property_interfaces = g_slist_prepend (priv->property_interfaces,
|
|
g_strdup (dbus_g_proxy_get_interface (proxy)));
|
|
|
|
dbus_g_proxy_add_signal (proxy, "PropertiesChanged", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
|
|
dbus_g_proxy_connect_signal (proxy,
|
|
"PropertiesChanged",
|
|
G_CALLBACK (properties_changed_proxy),
|
|
object,
|
|
NULL);
|
|
|
|
instance = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
priv->property_tables = g_slist_prepend (priv->property_tables, instance);
|
|
|
|
for (tmp = (NMPropertiesInfo *) info; tmp->name; tmp++) {
|
|
PropertyInfo *pi;
|
|
|
|
if (!tmp->name || (tmp->func && !tmp->field)) {
|
|
g_warning ("%s: missing field in NMPropertiesInfo", __func__);
|
|
continue;
|
|
}
|
|
|
|
pi = g_malloc0 (sizeof (PropertyInfo));
|
|
pi->func = tmp->func ? tmp->func : demarshal_generic;
|
|
pi->object_type = tmp->object_type;
|
|
pi->field = tmp->field;
|
|
g_hash_table_insert (instance, g_strdup (tmp->name), pi);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
_nm_object_reload_properties (NMObject *object, GError **error)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
|
GHashTable *props = NULL;
|
|
GSList *p;
|
|
|
|
if (!priv->property_interfaces)
|
|
return TRUE;
|
|
|
|
priv->inited = TRUE;
|
|
|
|
for (p = priv->property_interfaces; p; p = p->next) {
|
|
if (!dbus_g_proxy_call (priv->properties_proxy, "GetAll", error,
|
|
G_TYPE_STRING, p->data,
|
|
G_TYPE_INVALID,
|
|
DBUS_TYPE_G_MAP_OF_VARIANT, &props,
|
|
G_TYPE_INVALID))
|
|
return FALSE;
|
|
|
|
_nm_object_process_properties_changed (object, props);
|
|
g_hash_table_destroy (props);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
_nm_object_ensure_inited (NMObject *object)
|
|
{
|
|
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
|
|
GError *error = NULL;
|
|
|
|
if (!priv->inited) {
|
|
if (!_nm_object_reload_properties (object, &error)) {
|
|
g_warning ("Could not initialize %s %s: %s",
|
|
G_OBJECT_TYPE_NAME (object), priv->path,
|
|
error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
_nm_object_reload_property (NMObject *object,
|
|
const char *interface,
|
|
const char *prop_name)
|
|
{
|
|
GValue value = { 0, };
|
|
GError *err = NULL;
|
|
|
|
g_return_if_fail (NM_IS_OBJECT (object));
|
|
g_return_if_fail (interface != NULL);
|
|
g_return_if_fail (prop_name != NULL);
|
|
|
|
if (!dbus_g_proxy_call_with_timeout (NM_OBJECT_GET_PRIVATE (object)->properties_proxy,
|
|
"Get", 15000, &err,
|
|
G_TYPE_STRING, interface,
|
|
G_TYPE_STRING, prop_name,
|
|
G_TYPE_INVALID,
|
|
G_TYPE_VALUE, &value,
|
|
G_TYPE_INVALID)) {
|
|
/* Don't warn about D-Bus no reply/timeout errors; it's mostly noise and
|
|
* happens for example when NM quits and the applet is still running.
|
|
*/
|
|
if (!g_error_matches (err, DBUS_GERROR, DBUS_GERROR_NO_REPLY)) {
|
|
g_warning ("%s: Error getting '%s' for %s: (%d) %s\n",
|
|
__func__,
|
|
prop_name,
|
|
nm_object_get_path (object),
|
|
err->code,
|
|
err->message);
|
|
}
|
|
g_clear_error (&err);
|
|
return;
|
|
}
|
|
|
|
handle_property_changed (object, prop_name, &value, TRUE);
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
void
|
|
_nm_object_set_property (NMObject *object,
|
|
const char *interface,
|
|
const char *prop_name,
|
|
GValue *value)
|
|
{
|
|
g_return_if_fail (NM_IS_OBJECT (object));
|
|
g_return_if_fail (interface != NULL);
|
|
g_return_if_fail (prop_name != NULL);
|
|
g_return_if_fail (G_IS_VALUE (value));
|
|
|
|
if (!dbus_g_proxy_call_with_timeout (NM_OBJECT_GET_PRIVATE (object)->properties_proxy,
|
|
"Set", 2000, NULL,
|
|
G_TYPE_STRING, interface,
|
|
G_TYPE_STRING, prop_name,
|
|
G_TYPE_VALUE, value,
|
|
G_TYPE_INVALID)) {
|
|
|
|
/* Ignore errors. dbus_g_proxy_call_with_timeout() is called instead of
|
|
* dbus_g_proxy_call_no_reply() to give NM chance to authenticate the caller.
|
|
*/
|
|
}
|
|
}
|