
GNOME Settings 3.26 is crashing every time a VPN connection changed its state. After some digging, a debug message was put on dispose, and this issue was found: libnm-Message: Object 0x55555633c070 disposed libnm-Message: Object 0x55555633c730 disposed libnm-Message: Object 0x55555633eae0 disposed libnm-Message: Object 0x555556340a80 disposed Thread 1 "gnome-control-c" received signal SIGSEGV, Segmentation fault. g_type_check_instance_cast (type_instance=type_instance@entry=0x55555633c070, iface_type=93825006537856) at /.../glib/gobject/gtype.c:4057 4057 node = lookup_type_node_I (type_instance->g_class->g_type); (gdb) bt NetworkManager is calling callbacks on disposed objects, which leads to crashes in clients (e.g. GNOME Settings). Fix this issue by disconnecting signal handlers when the objects are disposed. Patch originally by Georges Basile Stavracas Neto <georges.stavracas@gmail.com> https://bugzilla.gnome.org/show_bug.cgi?id=787893
850 lines
29 KiB
C
850 lines
29 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
/*
|
|
* 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 2007 - 2008 Novell, Inc.
|
|
* Copyright 2007 - 2011 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "nm-dbus-interface.h"
|
|
#include "nm-utils.h"
|
|
#include "nm-setting-connection.h"
|
|
#include "nm-core-internal.h"
|
|
|
|
#include "nm-remote-connection.h"
|
|
#include "nm-remote-connection-private.h"
|
|
#include "nm-object-private.h"
|
|
#include "nm-dbus-helpers.h"
|
|
|
|
#include "introspection/org.freedesktop.NetworkManager.Settings.Connection.h"
|
|
|
|
static void nm_remote_connection_connection_iface_init (NMConnectionInterface *iface);
|
|
static void nm_remote_connection_initable_iface_init (GInitableIface *iface);
|
|
static void nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface);
|
|
static GInitableIface *nm_remote_connection_parent_initable_iface;
|
|
static GAsyncInitableIface *nm_remote_connection_parent_async_initable_iface;
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (NMRemoteConnection, nm_remote_connection, NM_TYPE_OBJECT,
|
|
G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION, nm_remote_connection_connection_iface_init);
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_remote_connection_initable_iface_init);
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_remote_connection_async_initable_iface_init);
|
|
)
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_UNSAVED,
|
|
PROP_VISIBLE,
|
|
|
|
LAST_PROP
|
|
};
|
|
|
|
typedef struct {
|
|
NMDBusSettingsConnection *proxy;
|
|
|
|
gboolean unsaved;
|
|
|
|
gboolean visible;
|
|
} NMRemoteConnectionPrivate;
|
|
|
|
#define NM_REMOTE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnectionPrivate))
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_remote_connection_commit_changes:
|
|
* @connection: the #NMRemoteConnection
|
|
* @save_to_disk: whether to persist the changes to disk
|
|
* @cancellable: a #GCancellable, or %NULL
|
|
* @error: location for a #GError, or %NULL
|
|
*
|
|
* Send any local changes to the settings and properties of @connection to
|
|
* NetworkManager. If @save_to_disk is %TRUE, the updated connection will be saved to
|
|
* disk; if %FALSE, then only the in-memory representation will be changed.
|
|
*
|
|
* Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
|
|
**/
|
|
gboolean
|
|
nm_remote_connection_commit_changes (NMRemoteConnection *connection,
|
|
gboolean save_to_disk,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
NMRemoteConnectionPrivate *priv;
|
|
GVariant *settings;
|
|
gboolean ret;
|
|
|
|
g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
|
|
|
|
priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
settings = nm_connection_to_dbus (NM_CONNECTION (connection), NM_CONNECTION_SERIALIZE_ALL);
|
|
if (save_to_disk) {
|
|
ret = nmdbus_settings_connection_call_update_sync (priv->proxy,
|
|
settings,
|
|
cancellable, error);
|
|
} else {
|
|
ret = nmdbus_settings_connection_call_update_unsaved_sync (priv->proxy,
|
|
settings,
|
|
cancellable, error);
|
|
}
|
|
if (error && *error)
|
|
g_dbus_error_strip_remote_error (*error);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
update_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *simple = user_data;
|
|
gboolean (*finish_func) (NMDBusSettingsConnection *, GAsyncResult *, GError **);
|
|
GError *error = NULL;
|
|
|
|
finish_func = g_object_get_data (G_OBJECT (simple), "finish_func");
|
|
if (finish_func (NMDBUS_SETTINGS_CONNECTION (proxy), result, &error))
|
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
|
else {
|
|
g_dbus_error_strip_remote_error (error);
|
|
g_simple_async_result_take_error (simple, error);
|
|
}
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_commit_changes_async:
|
|
* @connection: the #NMRemoteConnection
|
|
* @save_to_disk: whether to save the changes to persistent storage
|
|
* @cancellable: a #GCancellable, or %NULL
|
|
* @callback: callback to be called when the commit operation completes
|
|
* @user_data: caller-specific data passed to @callback
|
|
*
|
|
* Asynchronously sends any local changes to the settings and properties of
|
|
* @connection to NetworkManager. If @save is %TRUE, the updated connection will
|
|
* be saved to disk; if %FALSE, then only the in-memory representation will be
|
|
* changed.
|
|
**/
|
|
void
|
|
nm_remote_connection_commit_changes_async (NMRemoteConnection *connection,
|
|
gboolean save_to_disk,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
NMRemoteConnectionPrivate *priv;
|
|
GSimpleAsyncResult *simple;
|
|
GVariant *settings;
|
|
|
|
g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
|
|
|
|
priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data,
|
|
nm_remote_connection_commit_changes_async);
|
|
|
|
settings = nm_connection_to_dbus (NM_CONNECTION (connection), NM_CONNECTION_SERIALIZE_ALL);
|
|
if (save_to_disk) {
|
|
g_object_set_data (G_OBJECT (simple), "finish_func",
|
|
nmdbus_settings_connection_call_update_finish);
|
|
nmdbus_settings_connection_call_update (priv->proxy,
|
|
settings,
|
|
cancellable,
|
|
update_cb, simple);
|
|
} else {
|
|
g_object_set_data (G_OBJECT (simple), "finish_func",
|
|
nmdbus_settings_connection_call_update_unsaved_finish);
|
|
nmdbus_settings_connection_call_update_unsaved (priv->proxy,
|
|
settings,
|
|
cancellable,
|
|
update_cb, simple);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_commit_changes_finish:
|
|
* @connection: the #NMRemoteConnection
|
|
* @result: the result passed to the #GAsyncReadyCallback
|
|
* @error: location for a #GError, or %NULL
|
|
*
|
|
* Gets the result of a call to nm_remote_connection_commit_changes_async().
|
|
*
|
|
* Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
|
|
**/
|
|
gboolean
|
|
nm_remote_connection_commit_changes_finish (NMRemoteConnection *connection,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
GSimpleAsyncResult *simple;
|
|
|
|
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), nm_remote_connection_commit_changes_async), FALSE);
|
|
|
|
simple = G_SIMPLE_ASYNC_RESULT (result);
|
|
if (g_simple_async_result_propagate_error (simple, error))
|
|
return FALSE;
|
|
else
|
|
return g_simple_async_result_get_op_res_gboolean (simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_save:
|
|
* @connection: the #NMRemoteConnection
|
|
* @cancellable: a #GCancellable, or %NULL
|
|
* @error: location for a #GError, or %NULL
|
|
*
|
|
* Saves the connection to disk if the connection has changes that have not yet
|
|
* been written to disk, or if the connection has never been saved.
|
|
*
|
|
* Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
|
|
**/
|
|
gboolean
|
|
nm_remote_connection_save (NMRemoteConnection *connection,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
NMRemoteConnectionPrivate *priv;
|
|
gboolean ret;
|
|
|
|
g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
|
|
|
|
priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
ret = nmdbus_settings_connection_call_save_sync (priv->proxy, cancellable, error);
|
|
if (error && *error)
|
|
g_dbus_error_strip_remote_error (*error);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
save_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *simple = user_data;
|
|
GError *error = NULL;
|
|
|
|
if (nmdbus_settings_connection_call_save_finish (NMDBUS_SETTINGS_CONNECTION (proxy),
|
|
result, &error))
|
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
|
else {
|
|
g_dbus_error_strip_remote_error (error);
|
|
g_simple_async_result_take_error (simple, error);
|
|
}
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_save_async:
|
|
* @connection: the #NMRemoteConnection
|
|
* @cancellable: a #GCancellable, or %NULL
|
|
* @callback: callback to be called when the save operation completes
|
|
* @user_data: caller-specific data passed to @callback
|
|
*
|
|
* Saves the connection to disk if the connection has changes that have not yet
|
|
* been written to disk, or if the connection has never been saved.
|
|
**/
|
|
void
|
|
nm_remote_connection_save_async (NMRemoteConnection *connection,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
NMRemoteConnectionPrivate *priv;
|
|
GSimpleAsyncResult *simple;
|
|
|
|
g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
|
|
|
|
priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data,
|
|
nm_remote_connection_save_async);
|
|
nmdbus_settings_connection_call_save (priv->proxy, cancellable, save_cb, simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_save_finish:
|
|
* @connection: the #NMRemoteConnection
|
|
* @result: the result passed to the #GAsyncReadyCallback
|
|
* @error: location for a #GError, or %NULL
|
|
*
|
|
* Gets the result of a call to nm_remote_connection_save_async().
|
|
*
|
|
* Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
|
|
**/
|
|
gboolean
|
|
nm_remote_connection_save_finish (NMRemoteConnection *connection,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
GSimpleAsyncResult *simple;
|
|
|
|
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), nm_remote_connection_save_async), FALSE);
|
|
|
|
simple = G_SIMPLE_ASYNC_RESULT (result);
|
|
if (g_simple_async_result_propagate_error (simple, error))
|
|
return FALSE;
|
|
else
|
|
return g_simple_async_result_get_op_res_gboolean (simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_delete:
|
|
* @connection: the #NMRemoteConnection
|
|
* @cancellable: a #GCancellable, or %NULL
|
|
* @error: location for a #GError, or %NULL
|
|
*
|
|
* Deletes the connection.
|
|
*
|
|
* Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
|
|
**/
|
|
gboolean
|
|
nm_remote_connection_delete (NMRemoteConnection *connection,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
NMRemoteConnectionPrivate *priv;
|
|
gboolean ret;
|
|
|
|
g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
|
|
|
|
priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
ret = nmdbus_settings_connection_call_delete_sync (priv->proxy, cancellable, error);
|
|
if (error && *error)
|
|
g_dbus_error_strip_remote_error (*error);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
delete_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *simple = user_data;
|
|
GError *error = NULL;
|
|
|
|
if (nmdbus_settings_connection_call_delete_finish (NMDBUS_SETTINGS_CONNECTION (proxy),
|
|
result, &error))
|
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
|
else {
|
|
g_dbus_error_strip_remote_error (error);
|
|
g_simple_async_result_take_error (simple, error);
|
|
}
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_delete_async:
|
|
* @connection: the #NMRemoteConnection
|
|
* @cancellable: a #GCancellable, or %NULL
|
|
* @callback: callback to be called when the delete operation completes
|
|
* @user_data: caller-specific data passed to @callback
|
|
*
|
|
* Asynchronously deletes the connection.
|
|
**/
|
|
void
|
|
nm_remote_connection_delete_async (NMRemoteConnection *connection,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
NMRemoteConnectionPrivate *priv;
|
|
GSimpleAsyncResult *simple;
|
|
|
|
g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
|
|
|
|
priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data,
|
|
nm_remote_connection_delete_async);
|
|
nmdbus_settings_connection_call_delete (priv->proxy, cancellable, delete_cb, simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_delete_finish:
|
|
* @connection: the #NMRemoteConnection
|
|
* @result: the result passed to the #GAsyncReadyCallback
|
|
* @error: location for a #GError, or %NULL
|
|
*
|
|
* Gets the result of a call to nm_remote_connection_delete_async().
|
|
*
|
|
* Returns: %TRUE on success, %FALSE on error, in which case @error will be set.
|
|
**/
|
|
gboolean
|
|
nm_remote_connection_delete_finish (NMRemoteConnection *connection,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
GSimpleAsyncResult *simple;
|
|
|
|
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), nm_remote_connection_delete_async), FALSE);
|
|
|
|
simple = G_SIMPLE_ASYNC_RESULT (result);
|
|
if (g_simple_async_result_propagate_error (simple, error))
|
|
return FALSE;
|
|
else
|
|
return g_simple_async_result_get_op_res_gboolean (simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_get_secrets:
|
|
* @connection: the #NMRemoteConnection
|
|
* @setting_name: the #NMSetting object name to get secrets for
|
|
* @cancellable: a #GCancellable, or %NULL
|
|
* @error: location for a #GError, or %NULL
|
|
*
|
|
* Request the connection's secrets. Note that this is a blocking D-Bus call,
|
|
* not a simple property accessor.
|
|
*
|
|
* Returns: a #GVariant of type %NM_VARIANT_TYPE_CONNECTION containing
|
|
* @connection's secrets, or %NULL on error.
|
|
**/
|
|
GVariant *
|
|
nm_remote_connection_get_secrets (NMRemoteConnection *connection,
|
|
const char *setting_name,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
NMRemoteConnectionPrivate *priv;
|
|
GVariant *secrets;
|
|
|
|
g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), NULL);
|
|
|
|
priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
if (nmdbus_settings_connection_call_get_secrets_sync (priv->proxy,
|
|
setting_name,
|
|
&secrets,
|
|
cancellable, error))
|
|
return secrets;
|
|
else {
|
|
if (error && *error)
|
|
g_dbus_error_strip_remote_error (*error);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_secrets_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *simple = user_data;
|
|
GVariant *secrets = NULL;
|
|
GError *error = NULL;
|
|
|
|
if (nmdbus_settings_connection_call_get_secrets_finish (NMDBUS_SETTINGS_CONNECTION (proxy),
|
|
&secrets, result, &error))
|
|
g_simple_async_result_set_op_res_gpointer (simple, secrets, (GDestroyNotify) g_variant_unref);
|
|
else {
|
|
g_dbus_error_strip_remote_error (error);
|
|
g_simple_async_result_take_error (simple, error);
|
|
}
|
|
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_get_secrets_async:
|
|
* @connection: the #NMRemoteConnection
|
|
* @setting_name: the #NMSetting object name to get secrets for
|
|
* @cancellable: a #GCancellable, or %NULL
|
|
* @callback: callback to be called when the secret request completes
|
|
* @user_data: caller-specific data passed to @callback
|
|
*
|
|
* Asynchronously requests the connection's secrets.
|
|
**/
|
|
void
|
|
nm_remote_connection_get_secrets_async (NMRemoteConnection *connection,
|
|
const char *setting_name,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
NMRemoteConnectionPrivate *priv;
|
|
GSimpleAsyncResult *simple;
|
|
|
|
g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
|
|
|
|
priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data,
|
|
nm_remote_connection_get_secrets_async);
|
|
|
|
nmdbus_settings_connection_call_get_secrets (priv->proxy,
|
|
setting_name,
|
|
cancellable,
|
|
get_secrets_cb, simple);
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_get_secrets_finish:
|
|
* @connection: the #NMRemoteConnection
|
|
* @result: the result passed to the #GAsyncReadyCallback
|
|
* @error: location for a #GError, or %NULL
|
|
*
|
|
* Gets the result of a call to nm_remote_connection_get_secrets_async().
|
|
*
|
|
* Returns: (transfer full): a #GVariant of type %NM_VARIANT_TYPE_CONNECTION
|
|
* containing @connection's secrets, or %NULL on error.
|
|
**/
|
|
GVariant *
|
|
nm_remote_connection_get_secrets_finish (NMRemoteConnection *connection,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
GSimpleAsyncResult *simple;
|
|
|
|
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), nm_remote_connection_get_secrets_async), FALSE);
|
|
|
|
simple = G_SIMPLE_ASYNC_RESULT (result);
|
|
if (g_simple_async_result_propagate_error (simple, error))
|
|
return NULL;
|
|
else
|
|
return g_variant_ref (g_simple_async_result_get_op_res_gpointer (simple));
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_get_unsaved:
|
|
* @connection: the #NMRemoteConnection
|
|
*
|
|
* Returns: %TRUE if the remote connection contains changes that have not
|
|
* been saved to disk, %FALSE if the connection is the same as its on-disk
|
|
* representation.
|
|
**/
|
|
gboolean
|
|
nm_remote_connection_get_unsaved (NMRemoteConnection *connection)
|
|
{
|
|
g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
|
|
|
|
return NM_REMOTE_CONNECTION_GET_PRIVATE (connection)->unsaved;
|
|
}
|
|
|
|
/**
|
|
* nm_remote_connection_get_visible:
|
|
* @connection: the #NMRemoteConnection
|
|
*
|
|
* Checks if the connection is visible to the current user. If the
|
|
* connection is not visible then it is essentially useless; it will
|
|
* not contain any settings, and operations such as
|
|
* nm_remote_connection_save() and nm_remote_connection_delete() will
|
|
* always fail. (#NMRemoteSettings will not normally return
|
|
* non-visible connections to callers, but it is possible for a
|
|
* connection's visibility to change after you already have a
|
|
* reference to it.)
|
|
*
|
|
* Returns: %TRUE if the remote connection is visible to the current
|
|
* user, %FALSE if not.
|
|
**/
|
|
gboolean
|
|
nm_remote_connection_get_visible (NMRemoteConnection *connection)
|
|
{
|
|
g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
|
|
|
|
return NM_REMOTE_CONNECTION_GET_PRIVATE (connection)->visible;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
replace_settings (NMRemoteConnection *self, GVariant *new_settings)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!_nm_connection_replace_settings ((NMConnection *) self,
|
|
new_settings,
|
|
NM_SETTING_PARSE_FLAGS_BEST_EFFORT,
|
|
&error))
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
static void
|
|
updated_get_settings_cb (GObject *proxy,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
NMRemoteConnection *self = user_data;
|
|
NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
|
|
GVariant *new_settings;
|
|
gboolean visible;
|
|
|
|
if (!nmdbus_settings_connection_call_get_settings_finish (priv->proxy, &new_settings,
|
|
result, NULL)) {
|
|
/* Connection is no longer visible to this user. */
|
|
nm_connection_clear_settings (NM_CONNECTION (self));
|
|
|
|
visible = FALSE;
|
|
} else {
|
|
replace_settings (self, new_settings);
|
|
g_variant_unref (new_settings);
|
|
|
|
visible = TRUE;
|
|
}
|
|
|
|
if (visible != priv->visible) {
|
|
priv->visible = visible;
|
|
g_object_notify (G_OBJECT (self), NM_REMOTE_CONNECTION_VISIBLE);
|
|
}
|
|
|
|
g_object_unref (self);
|
|
}
|
|
|
|
static void
|
|
updated_cb (NMDBusSettingsConnection *proxy, gpointer user_data)
|
|
{
|
|
NMRemoteConnection *self = NM_REMOTE_CONNECTION (user_data);
|
|
NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
|
|
|
|
/* The connection got updated; request the replacement settings */
|
|
nmdbus_settings_connection_call_get_settings (priv->proxy,
|
|
NULL,
|
|
updated_get_settings_cb,
|
|
g_object_ref (self));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
init_dbus (NMObject *object)
|
|
{
|
|
NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (object);
|
|
const NMPropertiesInfo property_info[] = {
|
|
{ NM_REMOTE_CONNECTION_UNSAVED, &priv->unsaved },
|
|
{ NULL },
|
|
};
|
|
|
|
NM_OBJECT_CLASS (nm_remote_connection_parent_class)->init_dbus (object);
|
|
|
|
_nm_object_register_properties (object,
|
|
NM_DBUS_INTERFACE_SETTINGS_CONNECTION,
|
|
property_info);
|
|
}
|
|
|
|
static gboolean
|
|
init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
|
|
{
|
|
NMRemoteConnection *self = NM_REMOTE_CONNECTION (initable);
|
|
NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (initable);
|
|
GVariant *settings;
|
|
|
|
priv->proxy = NMDBUS_SETTINGS_CONNECTION (_nm_object_get_proxy (NM_OBJECT (initable), NM_DBUS_INTERFACE_SETTINGS_CONNECTION));
|
|
g_signal_connect_object (priv->proxy, "updated", G_CALLBACK (updated_cb), initable, 0);
|
|
|
|
if (nmdbus_settings_connection_call_get_settings_sync (priv->proxy,
|
|
&settings,
|
|
cancellable,
|
|
NULL)) {
|
|
priv->visible = TRUE;
|
|
replace_settings (self, settings);
|
|
g_variant_unref (settings);
|
|
}
|
|
|
|
if (!nm_remote_connection_parent_initable_iface->init (initable, cancellable, error))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
typedef struct {
|
|
NMRemoteConnection *connection;
|
|
GCancellable *cancellable;
|
|
GSimpleAsyncResult *result;
|
|
GAsyncInitable *initable;
|
|
int io_priority;
|
|
} NMRemoteConnectionInitData;
|
|
|
|
static void
|
|
init_async_complete (NMRemoteConnectionInitData *init_data, GError *error)
|
|
{
|
|
if (error)
|
|
g_simple_async_result_take_error (init_data->result, error);
|
|
else
|
|
g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE);
|
|
|
|
g_simple_async_result_complete (init_data->result);
|
|
g_object_unref (init_data->result);
|
|
g_clear_object (&init_data->cancellable);
|
|
g_slice_free (NMRemoteConnectionInitData, init_data);
|
|
}
|
|
|
|
static void
|
|
init_async_parent_inited (GObject *source, GAsyncResult *result, gpointer user_data)
|
|
{
|
|
NMRemoteConnectionInitData *init_data = user_data;
|
|
GError *error = NULL;
|
|
|
|
init_async_complete (init_data, error);
|
|
}
|
|
|
|
static void
|
|
init_get_settings_cb (GObject *proxy,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
NMRemoteConnectionInitData *init_data = user_data;
|
|
NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->initable);
|
|
GVariant *settings;
|
|
GError *error = NULL;
|
|
|
|
if (!nmdbus_settings_connection_call_get_settings_finish (priv->proxy, &settings,
|
|
result, &error)) {
|
|
g_error_free (error);
|
|
} else {
|
|
priv->visible = TRUE;
|
|
replace_settings (NM_REMOTE_CONNECTION (init_data->initable), settings);
|
|
g_variant_unref (settings);
|
|
}
|
|
|
|
nm_remote_connection_parent_async_initable_iface->
|
|
init_async (init_data->initable, init_data->io_priority, init_data->cancellable, init_async_parent_inited, init_data);
|
|
}
|
|
|
|
static void
|
|
init_async (GAsyncInitable *initable, int io_priority,
|
|
GCancellable *cancellable, GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
NMRemoteConnectionInitData *init_data;
|
|
NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (initable);
|
|
|
|
init_data = g_slice_new0 (NMRemoteConnectionInitData);
|
|
init_data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
|
|
init_data->result = g_simple_async_result_new (G_OBJECT (initable), callback,
|
|
user_data, init_async);
|
|
init_data->initable = initable;
|
|
init_data->io_priority = io_priority;
|
|
|
|
priv->proxy = NMDBUS_SETTINGS_CONNECTION (_nm_object_get_proxy (NM_OBJECT (initable),
|
|
NM_DBUS_INTERFACE_SETTINGS_CONNECTION));
|
|
|
|
g_signal_connect_object (priv->proxy, "updated",
|
|
G_CALLBACK (updated_cb), initable, 0);
|
|
|
|
nmdbus_settings_connection_call_get_settings (NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->initable)->proxy,
|
|
init_data->cancellable,
|
|
init_get_settings_cb, init_data);
|
|
}
|
|
|
|
static void
|
|
nm_remote_connection_init (NMRemoteConnection *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
get_property (GObject *object, guint prop_id,
|
|
GValue *value, GParamSpec *pspec)
|
|
{
|
|
switch (prop_id) {
|
|
case PROP_UNSAVED:
|
|
g_value_set_boolean (value, NM_REMOTE_CONNECTION_GET_PRIVATE (object)->unsaved);
|
|
break;
|
|
case PROP_VISIBLE:
|
|
g_value_set_boolean (value, NM_REMOTE_CONNECTION_GET_PRIVATE (object)->visible);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
constructed (GObject *object)
|
|
{
|
|
G_OBJECT_CLASS (nm_remote_connection_parent_class)->constructed (object);
|
|
|
|
nm_connection_set_path (NM_CONNECTION (object),
|
|
nm_object_get_path (NM_OBJECT (object)));
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (object);
|
|
|
|
g_clear_object (&priv->proxy);
|
|
|
|
G_OBJECT_CLASS (nm_remote_connection_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
nm_remote_connection_class_init (NMRemoteConnectionClass *remote_class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (remote_class);
|
|
NMObjectClass *nm_object_class = NM_OBJECT_CLASS (remote_class);
|
|
|
|
g_type_class_add_private (object_class, sizeof (NMRemoteConnectionPrivate));
|
|
|
|
/* virtual methods */
|
|
object_class->constructed = constructed;
|
|
object_class->get_property = get_property;
|
|
object_class->dispose = dispose;
|
|
|
|
nm_object_class->init_dbus = init_dbus;
|
|
|
|
/* Properties */
|
|
/**
|
|
* NMRemoteConnection:unsaved:
|
|
*
|
|
* %TRUE if the remote connection contains changes that have not been saved
|
|
* to disk, %FALSE if the connection is the same as its on-disk representation.
|
|
**/
|
|
g_object_class_install_property
|
|
(object_class, PROP_UNSAVED,
|
|
g_param_spec_boolean (NM_REMOTE_CONNECTION_UNSAVED, "", "",
|
|
FALSE,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* NMRemoteConnection:visible:
|
|
*
|
|
* %TRUE if the remote connection is visible to the current user, %FALSE if
|
|
* not. If the connection is not visible then it is essentially useless; it
|
|
* will not contain any settings, and operations such as
|
|
* nm_remote_connection_save() and nm_remote_connection_delete() will always
|
|
* fail. (#NMRemoteSettings will not normally return non-visible connections
|
|
* to callers, but it is possible for a connection's visibility to change
|
|
* after you already have a reference to it.)
|
|
**/
|
|
g_object_class_install_property
|
|
(object_class, PROP_VISIBLE,
|
|
g_param_spec_boolean (NM_REMOTE_CONNECTION_VISIBLE, "", "",
|
|
FALSE,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
}
|
|
|
|
static void
|
|
nm_remote_connection_connection_iface_init (NMConnectionInterface *iface)
|
|
{
|
|
}
|
|
|
|
static void
|
|
nm_remote_connection_initable_iface_init (GInitableIface *iface)
|
|
{
|
|
nm_remote_connection_parent_initable_iface = g_type_interface_peek_parent (iface);
|
|
|
|
iface->init = init_sync;
|
|
}
|
|
|
|
static void
|
|
nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface)
|
|
{
|
|
nm_remote_connection_parent_async_initable_iface = g_type_interface_peek_parent (iface);
|
|
|
|
iface->init_async = init_async;
|
|
}
|