diff options
Diffstat (limited to 'src/nm-bus-manager.c')
-rw-r--r-- | src/nm-bus-manager.c | 411 |
1 files changed, 181 insertions, 230 deletions
diff --git a/src/nm-bus-manager.c b/src/nm-bus-manager.c index 1c09a7b68a..571573833f 100644 --- a/src/nm-bus-manager.c +++ b/src/nm-bus-manager.c @@ -57,7 +57,7 @@ typedef struct _PrivateServer PrivateServer; typedef struct { GDBusConnection *connection; - GHashTable *exported; + GDBusObjectManagerServer *obj_manager; gboolean started; GSList *private_servers; @@ -73,6 +73,13 @@ static gboolean nm_bus_manager_init_bus (NMBusManager *self); static void nm_bus_manager_cleanup (NMBusManager *self); static void start_reconnection_timeout (NMBusManager *self); +/* The base path for our GDBusObjectManagerServers. They do not contain + * "NetworkManager" because GDBusObjectManagerServer requires that all + * exported objects be *below* the base path, and eg the Manager object + * is the base path already. + */ +#define OBJECT_MANAGER_SERVER_BASE_PATH "/org/freedesktop" + NM_DEFINE_SINGLETON_REGISTER (NMBusManager); NMBusManager * @@ -103,66 +110,23 @@ nm_bus_manager_setup (NMBusManager *instance) /**************************************************************/ -static void -nm_assert_exported (NMBusManager *self, const char *path, NMExportedObject *object) -{ -#if NM_MORE_ASSERTS - NMBusManagerPrivate *priv; - const char *p2, *po; - NMExportedObject *o2; - - /* NMBusManager and NMExportedObject are tied closely together. For example, while - * being registered, NMBusManager uses the path from nm_exported_object_get_path() - * as index. It relies on the path being stable. - * - * The alternative would be that NMBusManager copies the path upon registration - * to support diversion of NMExportedObject's path while being registered. But such - * a inconsistency would already indicate a bug, or at least a strange situation. - * - * So instead require some close cooperation between the two classes and add an - * assert here... */ - - nm_assert (NM_IS_BUS_MANAGER (self)); - nm_assert (!path || *path); - nm_assert (!object || NM_IS_EXPORTED_OBJECT (object)); - nm_assert (!!path || !!object); - - priv = NM_BUS_MANAGER_GET_PRIVATE (self); - nm_assert (priv->exported); - - if (!path) { - nm_assert (NM_IS_EXPORTED_OBJECT (object)); - - po = nm_exported_object_get_path (object); - nm_assert (po && *po); - - if (!g_hash_table_lookup_extended (priv->exported, po, (gpointer *) &p2, (gpointer *) &o2)) - nm_assert (FALSE); - - nm_assert (object == o2); - nm_assert (po == p2); - } else { - nm_assert (path && *path); - - if (!g_hash_table_lookup_extended (priv->exported, path, (gpointer *) &p2, (gpointer *) &o2)) - nm_assert (FALSE); - - nm_assert (NM_IS_EXPORTED_OBJECT (o2)); - nm_assert (!object || object == o2); - nm_assert (!g_strcmp0 (path, p2)); - nm_assert (p2 == nm_exported_object_get_path (o2)); - } -#endif -} - -/**************************************************************/ - struct _PrivateServer { const char *tag; GQuark detail; char *address; GDBusServer *server; - GHashTable *connections; + + /* With peer bus connections, we'll get a new connection for each + * client. For each connection we create an ObjectManager for + * that connection to handle exporting our objects. This table + * maps GDBusObjectManager :: 'fake sender'. + * + * Note that even for connections that don't export any objects + * we'll still create GDBusObjectManager since that's where we store + * the pointer to the GDBusConnection. + */ + GHashTable *obj_managers; + NMBusManager *manager; }; @@ -177,6 +141,8 @@ close_connection_in_idle (gpointer user_data) { CloseConnectionInfo *info = user_data; PrivateServer *server = info->server; + GHashTableIter iter; + GDBusObjectManagerServer *manager; /* Emit this for the manager */ g_signal_emit (server->manager, @@ -191,7 +157,14 @@ close_connection_in_idle (gpointer user_data) if (info->remote_peer_vanished) g_dbus_connection_close (info->connection, NULL, NULL, NULL); - g_hash_table_remove (server->connections, info->connection); + g_hash_table_iter_init (&iter, server->obj_managers); + while (g_hash_table_iter_next (&iter, (gpointer) &manager, NULL)) { + if (g_dbus_object_manager_server_get_connection (manager) == info->connection) { + g_hash_table_iter_remove (&iter); + break; + } + } + g_object_unref (server->manager); g_slice_free (CloseConnectionInfo, info); @@ -199,10 +172,10 @@ close_connection_in_idle (gpointer user_data) } static void -private_server_closed (GDBusConnection *conn, - gboolean remote_peer_vanished, - GError *error, - gpointer user_data) +private_server_closed_connection (GDBusConnection *conn, + gboolean remote_peer_vanished, + GError *error, + gpointer user_data) { PrivateServer *s = user_data; CloseConnectionInfo *info; @@ -230,13 +203,17 @@ private_server_new_connection (GDBusServer *server, { PrivateServer *s = user_data; static guint32 counter = 0; + GDBusObjectManagerServer *manager; char *sender; - g_signal_connect (conn, "closed", G_CALLBACK (private_server_closed), s); + g_signal_connect (conn, "closed", G_CALLBACK (private_server_closed_connection), s); /* Fake a sender since private connections don't have one */ sender = g_strdup_printf ("x:y:%d", counter++); - g_hash_table_insert (s->connections, g_object_ref (conn), sender); + + manager = g_dbus_object_manager_server_new (OBJECT_MANAGER_SERVER_BASE_PATH); + g_dbus_object_manager_server_set_connection (manager, conn); + g_hash_table_insert (s->obj_managers, manager, sender); nm_log_dbg (LOGD_CORE, "(%s) accepted connection %p on private socket.", s->tag, conn); @@ -245,16 +222,20 @@ private_server_new_connection (GDBusServer *server, g_signal_emit (s->manager, signals[PRIVATE_CONNECTION_NEW], s->detail, - conn); + conn, + manager); return TRUE; } static void -private_server_dbus_connection_destroy (GDBusConnection *conn) +private_server_manager_destroy (GDBusObjectManagerServer *manager) { - if (!g_dbus_connection_is_closed (conn)) - g_dbus_connection_close (conn, NULL, NULL, NULL); - g_object_unref (conn); + GDBusConnection *connection = g_dbus_object_manager_server_get_connection (manager); + + if (!g_dbus_connection_is_closed (connection)) + g_dbus_connection_close (connection, NULL, NULL, NULL); + g_dbus_object_manager_server_set_connection (manager, NULL); + g_object_unref (manager); } static gboolean @@ -308,9 +289,9 @@ private_server_new (const char *path, g_signal_connect (server, "new-connection", G_CALLBACK (private_server_new_connection), s); - s->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, - (GDestroyNotify) private_server_dbus_connection_destroy, - g_free); + s->obj_managers = g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) private_server_manager_destroy, + g_free); s->manager = manager; s->detail = g_quark_from_string (tag); s->tag = g_quark_to_string (s->detail); @@ -327,7 +308,7 @@ private_server_free (gpointer ptr) unlink (s->address); g_free (s->address); - g_hash_table_destroy (s->connections); + g_hash_table_destroy (s->obj_managers); g_dbus_server_stop (s->server); g_object_unref (s->server); @@ -364,10 +345,56 @@ nm_bus_manager_private_server_register (NMBusManager *self, static const char * private_server_get_connection_owner (PrivateServer *s, GDBusConnection *connection) { + GHashTableIter iter; + GDBusObjectManagerServer *manager; + const char *owner; + g_return_val_if_fail (s != NULL, NULL); g_return_val_if_fail (connection != NULL, NULL); - return g_hash_table_lookup (s->connections, connection); + g_hash_table_iter_init (&iter, s->obj_managers); + while (g_hash_table_iter_next (&iter, (gpointer) &manager, (gpointer) &owner)) { + if (g_dbus_object_manager_server_get_connection (manager) == connection) + return owner; + } + return NULL; +} + +static GDBusConnection * +private_server_get_connection_by_owner (PrivateServer *s, const char *owner) +{ + GHashTableIter iter; + GDBusObjectManagerServer *manager; + const char *priv_sender; + + g_hash_table_iter_init (&iter, s->obj_managers); + while (g_hash_table_iter_next (&iter, (gpointer) &manager, (gpointer) &priv_sender)) { + if (g_strcmp0 (owner, priv_sender) == 0) + return g_dbus_object_manager_server_get_connection (manager); + } + return NULL; +} + +static void +private_server_register_object (PrivateServer *s, GDBusObjectSkeleton *object) +{ + GHashTableIter iter; + GDBusObjectManagerServer *manager; + + g_hash_table_iter_init (&iter, s->obj_managers); + while (g_hash_table_iter_next (&iter, (gpointer) &manager, NULL)) + g_dbus_object_manager_server_export (manager, object); +} + +static void +private_server_unregister_object (PrivateServer *s, const char *path) +{ + GHashTableIter iter; + GDBusObjectManagerServer *manager; + + g_hash_table_iter_init (&iter, s->obj_managers); + while (g_hash_table_iter_next (&iter, (gpointer) &manager, NULL)) + g_dbus_object_manager_server_unexport (manager, path); } /**************************************************************/ @@ -455,7 +482,7 @@ _get_caller_info (NMBusManager *self, for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) { PrivateServer *s = iter->data; - sender = g_hash_table_lookup (s->connections, connection); + sender = private_server_get_connection_owner (s, connection); if (sender) { if (out_uid) *out_uid = 0; @@ -538,17 +565,10 @@ nm_bus_manager_get_unix_user (NMBusManager *self, g_return_val_if_fail (out_uid != NULL, FALSE); /* Check if it's a private connection sender, which we fake */ - for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) { - PrivateServer *s = iter->data; - GHashTableIter hiter; - const char *priv_sender; - - g_hash_table_iter_init (&hiter, s->connections); - while (g_hash_table_iter_next (&hiter, NULL, (gpointer) &priv_sender)) { - if (g_strcmp0 (sender, priv_sender) == 0) { - *out_uid = 0; - return TRUE; - } + for (iter = priv->private_servers; iter; iter = iter->next) { + if (private_server_get_connection_by_owner (iter->data, sender)) { + *out_uid = 0; + return TRUE; } } @@ -566,42 +586,30 @@ nm_bus_manager_get_unix_user (NMBusManager *self, /**************************************************************/ static void -private_connection_new (NMBusManager *self, GDBusConnection *connection) +manager_private_connection_new (NMBusManager *self, + GDBusConnection *connection, + GDBusObjectManagerServer *manager, + gpointer user_data) { NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - GHashTableIter iter; - NMExportedObject *object; - const char *path; - GError *error = NULL; - - /* Register all exported objects on this private connection */ - g_hash_table_iter_init (&iter, priv->exported); - while (g_hash_table_iter_next (&iter, (gpointer *) &path, (gpointer *) &object)) { - GSList *interfaces = nm_exported_object_get_interfaces (object); - - nm_assert_exported (self, path, object); + GList *objects, *iter; - for (; interfaces; interfaces = interfaces->next) { - GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (interfaces->data); - - if (g_dbus_interface_skeleton_export (interface, connection, path, &error)) { - nm_log_trace (LOGD_CORE, "(%s) registered %p (%s) at '%s' on private socket.", - PRIV_SOCK_TAG, object, G_OBJECT_TYPE_NAME (interface), path); - } else { - nm_log_warn (LOGD_CORE, "(%s) could not register %p (%s) at '%s' on private socket: %s.", - PRIV_SOCK_TAG, object, G_OBJECT_TYPE_NAME (interface), path, - error->message); - g_clear_error (&error); - } - } + /* Register all exported objects on the new private connection */ + objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (priv->obj_manager)); + for (iter = objects; iter; iter = iter->next) { + g_dbus_object_manager_server_export (manager, iter->data); + g_object_unref (iter->data); } + g_list_free (objects); } static void -private_server_setup (NMBusManager *self) +nm_bus_manager_init (NMBusManager *self) { NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); + priv->obj_manager = g_dbus_object_manager_server_new (OBJECT_MANAGER_SERVER_BASE_PATH); + /* Skip this step if this is just a test program */ if (nm_utils_get_testing ()) return; @@ -616,37 +624,17 @@ private_server_setup (NMBusManager *self) priv->private_servers = g_slist_append (priv->private_servers, priv->priv_server); g_signal_connect (self, NM_BUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG, - (GCallback) private_connection_new, + (GCallback) manager_private_connection_new, NULL); } } static void -nm_bus_manager_init (NMBusManager *self) -{ - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - - priv->exported = g_hash_table_new (g_str_hash, g_str_equal); - - private_server_setup (self); -} - -static void nm_bus_manager_dispose (GObject *object) { NMBusManager *self = NM_BUS_MANAGER (object); NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - - if (priv->exported) { - /* We don't take references to the registered objects. - * We rely on the objects to properly unregister. - * Especially, they must unregister before destroying the - * NMBusManager instance. */ - g_assert (g_hash_table_size (priv->exported) == 0); - - g_hash_table_destroy (priv->exported); - priv->exported = NULL; - } + GList *exported, *iter; g_slist_free_full (priv->private_servers, private_server_free); priv->private_servers = NULL; @@ -654,11 +642,24 @@ nm_bus_manager_dispose (GObject *object) nm_bus_manager_cleanup (self); - if (priv->reconnect_id) { - g_source_remove (priv->reconnect_id); - priv->reconnect_id = 0; + if (priv->obj_manager) { + /* The ObjectManager owns the last reference to many exported + * objects, and when that reference is dropped the objects unregister + * themselves via nm_bus_manager_unregister_object(). By that time + * priv->obj_manager is already NULL and that prints warnings. Unregister + * them before clearing the ObjectManager instead. + */ + exported = g_dbus_object_manager_get_objects ((GDBusObjectManager *) priv->obj_manager); + for (iter = exported; iter; iter = iter->next) { + nm_bus_manager_unregister_object (self, iter->data); + g_object_unref (iter->data); + } + g_list_free (exported); + g_clear_object (&priv->obj_manager); } + nm_clear_g_source (&priv->reconnect_id); + G_OBJECT_CLASS (nm_bus_manager_parent_class)->dispose (object); } @@ -683,9 +684,8 @@ nm_bus_manager_class_init (NMBusManagerClass *klass) g_signal_new (NM_BUS_MANAGER_PRIVATE_CONNECTION_NEW, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - G_STRUCT_OFFSET (NMBusManagerClass, private_connection_new), - NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_POINTER); + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_DBUS_CONNECTION, G_TYPE_DBUS_OBJECT_MANAGER_SERVER); signals[PRIVATE_CONNECTION_DISCONNECTED] = g_signal_new (NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, @@ -711,6 +711,7 @@ nm_bus_manager_cleanup (NMBusManager *self) g_clear_object (&priv->connection); } + g_dbus_object_manager_server_set_connection (priv->obj_manager, NULL); priv->started = FALSE; } @@ -812,6 +813,7 @@ nm_bus_manager_init_bus (NMBusManager *self) return FALSE; } + g_dbus_object_manager_server_set_connection (priv->obj_manager, priv->connection); return TRUE; } @@ -876,131 +878,87 @@ nm_bus_manager_get_connection (NMBusManager *self) void nm_bus_manager_register_object (NMBusManager *self, - NMExportedObject *object) + GDBusObjectSkeleton *object) { NMBusManagerPrivate *priv; - GDBusConnection *connection; - GHashTableIter iter; - const char *path; - GSList *interfaces, *ifs; g_return_if_fail (NM_IS_BUS_MANAGER (self)); g_return_if_fail (NM_IS_EXPORTED_OBJECT (object)); - path = nm_exported_object_get_path (object); - g_return_if_fail (path && *path); - priv = NM_BUS_MANAGER_GET_PRIVATE (self); - /* We hold a direct reference to the @path of the @object. Note that - * this requires the object not to modify the path as long as the object - * is registered. Especially, it must not free the path. - * - * This is a reasonable requirement, because having the object change - * the path while being registered is an awkward situation in the first - * place. While being registered, the @path and @interfaces must stay - * stable -- because the path is the identifier for the object in this - * situation. */ - - if (!nm_g_hash_table_insert (priv->exported, (gpointer) path, object)) +#if NM_MORE_ASSERTS >= 1 +#if GLIB_CHECK_VERSION(2,34,0) + if (g_dbus_object_manager_server_is_exported (priv->obj_manager, object)) g_return_if_reached (); +#endif +#endif - nm_assert_exported (self, path, object); - - interfaces = nm_exported_object_get_interfaces (object); - - if (priv->connection) { - for (ifs = interfaces; ifs; ifs = ifs->next) { - g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (ifs->data), - priv->connection, path, NULL); - } - } - - if (priv->priv_server) { - g_hash_table_iter_init (&iter, priv->priv_server->connections); - while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) { - for (ifs = interfaces; ifs; ifs = ifs->next) { - g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (ifs->data), - connection, path, NULL); - } - } - } + g_dbus_object_manager_server_export (priv->obj_manager, object); + if (priv->priv_server) + private_server_register_object (priv->priv_server, object); } -NMExportedObject * +GDBusObjectSkeleton * nm_bus_manager_get_registered_object (NMBusManager *self, const char *path) { - NMBusManagerPrivate *priv; - NMExportedObject *object; - - g_return_val_if_fail (NM_IS_BUS_MANAGER (self), NULL); - g_return_val_if_fail (path && *path, NULL); - - priv = NM_BUS_MANAGER_GET_PRIVATE (self); - - object = g_hash_table_lookup (priv->exported, path); - - if (object) - nm_assert_exported (self, path, object); + NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - return object; + return G_DBUS_OBJECT_SKELETON (g_dbus_object_manager_get_object ((GDBusObjectManager *) priv->obj_manager, path)); } void -nm_bus_manager_unregister_object (NMBusManager *self, NMExportedObject *object) +nm_bus_manager_unregister_object (NMBusManager *self, + GDBusObjectSkeleton *object) { NMBusManagerPrivate *priv; - GSList *interfaces; - const char *path; + gs_free char *path = NULL; g_return_if_fail (NM_IS_BUS_MANAGER (self)); g_return_if_fail (NM_IS_EXPORTED_OBJECT (object)); - path = nm_exported_object_get_path (object); - g_return_if_fail (path && *path); - - nm_assert_exported (self, NULL, object); - priv = NM_BUS_MANAGER_GET_PRIVATE (self); - if (!g_hash_table_remove (priv->exported, path)) +#if NM_MORE_ASSERTS >= 1 +#if GLIB_CHECK_VERSION(2,34,0) + if (!g_dbus_object_manager_server_is_exported (priv->obj_manager, object)) g_return_if_reached (); +#endif +#endif - for (interfaces = nm_exported_object_get_interfaces (object); interfaces; interfaces = interfaces->next) { - GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (interfaces->data); + g_object_get (G_OBJECT (object), "g-object-path", &path, NULL); + g_return_if_fail (path != NULL); - if (g_dbus_interface_skeleton_get_object_path (interface)) - g_dbus_interface_skeleton_unexport (interface); - } + g_dbus_object_manager_server_unexport (priv->obj_manager, path); + if (priv->priv_server) + private_server_unregister_object (priv->priv_server, path); } -gboolean -nm_bus_manager_connection_is_private (NMBusManager *self, - GDBusConnection *connection) +const char * +nm_bus_manager_connection_get_private_name (NMBusManager *self, + GDBusConnection *connection) { NMBusManagerPrivate *priv; GSList *iter; + const char *owner; g_return_val_if_fail (NM_IS_BUS_MANAGER (self), FALSE); g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); - if (g_dbus_connection_get_unique_name (connection)) - return FALSE; + if (g_dbus_connection_get_unique_name (connection)) { + /* Shortcut. The connection is not a private connection. */ + return NULL; + } - /* Assert that we still track the private connection. The caller - * of nm_bus_manager_connection_is_private() want's to subscribe - * to NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, thus the signal - * never comes if we don't track the connection. */ priv = NM_BUS_MANAGER_GET_PRIVATE (self); for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) { PrivateServer *s = iter->data; - if (g_hash_table_contains (s->connections, - connection)) - return TRUE; + if ((owner = private_server_get_connection_owner (s, connection))) + return owner; } - g_return_val_if_reached (TRUE); + g_return_val_if_reached (NULL); } /** @@ -1027,8 +985,6 @@ nm_bus_manager_new_proxy (NMBusManager *self, const char *path, const char *iface) { - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - GSList *iter; const char *owner; GDBusProxy *proxy; GError *error = NULL; @@ -1037,15 +993,10 @@ nm_bus_manager_new_proxy (NMBusManager *self, g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); /* Might be a private connection, for which @name is fake */ - for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) { - PrivateServer *s = iter->data; - - owner = private_server_get_connection_owner (s, connection); - if (owner) { - g_assert_cmpstr (owner, ==, name); - name = NULL; - break; - } + owner = nm_bus_manager_connection_get_private_name (self, connection); + if (owner) { + g_return_val_if_fail (!g_strcmp0 (owner, name), NULL); + name = NULL; } proxy = g_initable_new (proxy_type, NULL, &error, |