Commit 8a79fb1d authored by Dan Williams's avatar Dan Williams

settings: implement ability to add connections without saving them to disk

We don't always want to immediately write new connections to disk, to
facilitate "runtime" or "temporary" connections where an interface's
runtime config isn't backed by on-disk config.  Also, just because
an interface's configuration is changed doesn't necessarily mean
that new configuration should be written to disk either.

Add D-Bus methods for adding new connections and for updating existing
connections that don't immediately save the connection to disk.

Also add infrastructure to indicate to plugins that the new connection
shouldn't be immediately saved if the connection was added with the
new method.
parent f73d0663
...@@ -9,10 +9,11 @@ ...@@ -9,10 +9,11 @@
<method name="Update"> <method name="Update">
<tp:docstring> <tp:docstring>
Update the connection with new settings and properties, replacing Update the connection with new settings and properties (replacing
all previous settings and properties. Secrets may be part of the all previous settings and properties) and save the connection to
update request, and will be either stored in persistent storage or disk. Secrets may be part of the update request, and will be either
given to a Secret Agent for storage, depending on the request. stored in persistent storage or sent to a Secret Agent for storage,
depending on the flags associated with each secret.
</tp:docstring> </tp:docstring>
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_settings_connection_update"/> <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_settings_connection_update"/>
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/> <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
...@@ -23,6 +24,23 @@ ...@@ -23,6 +24,23 @@
</arg> </arg>
</method> </method>
<method name="UpdateUnsaved">
<tp:docstring>
Update the connection with new settings and properties (replacing
all previous settings and properties) but do not immediately save
the connection to disk. Secrets may be part of the update request
and may sent to a Secret Agent for storage, depending on the the
flags associated with each secret.
</tp:docstring>
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_settings_connection_update_unsaved"/>
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<arg name="properties" type="a{sa{sv}}" direction="in">
<tp:docstring>
New connection settings, properties, and (optionally) secrets.
</tp:docstring>
</arg>
</method>
<method name="Delete"> <method name="Delete">
<tp:docstring> <tp:docstring>
Delete the connection. Delete the connection.
......
...@@ -56,6 +56,27 @@ ...@@ -56,6 +56,27 @@
</arg> </arg>
</method> </method>
<method name="AddConnectionUnsaved">
<tp:docstring>
Add new connection but do not save it to disk immediately. This
operation does not start the network connection unless (1) device is
idle and able to connect to the network described by the new connection,
and (2) the connection is allowed to be started automatically.
</tp:docstring>
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_settings_add_connection_unsaved"/>
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<arg name="connection" type="a{sa{sv}}" direction="in">
<tp:docstring>
Connection settings and properties.
</tp:docstring>
</arg>
<arg name="path" type="o" direction="out">
<tp:docstring>
Object path of the new connection that was just added.
</tp:docstring>
</arg>
</method>
<method name="SaveHostname"> <method name="SaveHostname">
<tp:docstring> <tp:docstring>
Save the hostname to persistent configuration. Save the hostname to persistent configuration.
......
...@@ -3089,6 +3089,7 @@ add_and_activate_auth_done (PendingActivation *pending, GError *error) ...@@ -3089,6 +3089,7 @@ add_and_activate_auth_done (PendingActivation *pending, GError *error)
/* Basic sender auth checks performed; try to add the connection */ /* Basic sender auth checks performed; try to add the connection */
nm_settings_add_connection (priv->settings, nm_settings_add_connection (priv->settings,
pending->connection, pending->connection,
TRUE,
pending->context, pending->context,
activation_add_done, activation_add_done,
pending); pending);
......
...@@ -52,6 +52,10 @@ static void impl_settings_connection_update (NMSettingsConnection *connection, ...@@ -52,6 +52,10 @@ static void impl_settings_connection_update (NMSettingsConnection *connection,
GHashTable *new_settings, GHashTable *new_settings,
DBusGMethodInvocation *context); DBusGMethodInvocation *context);
static void impl_settings_connection_update_unsaved (NMSettingsConnection *connection,
GHashTable *new_settings,
DBusGMethodInvocation *context);
static void impl_settings_connection_delete (NMSettingsConnection *connection, static void impl_settings_connection_delete (NMSettingsConnection *connection,
DBusGMethodInvocation *context); DBusGMethodInvocation *context);
...@@ -1159,8 +1163,26 @@ typedef struct { ...@@ -1159,8 +1163,26 @@ typedef struct {
DBusGMethodInvocation *context; DBusGMethodInvocation *context;
NMAgentManager *agent_mgr; NMAgentManager *agent_mgr;
gulong sender_uid; gulong sender_uid;
NMConnection *new_settings;
gboolean save_to_disk;
} UpdateInfo; } UpdateInfo;
static void
update_complete (NMSettingsConnection *self,
UpdateInfo *info,
GError *error)
{
if (error)
dbus_g_method_return_error (info->context, error);
else
dbus_g_method_return (info->context);
g_clear_object (&info->agent_mgr);
g_clear_object (&info->new_settings);
memset (info, 0, sizeof (*info));
g_free (info);
}
static void static void
con_update_cb (NMSettingsConnection *self, con_update_cb (NMSettingsConnection *self,
GError *error, GError *error,
...@@ -1169,9 +1191,7 @@ con_update_cb (NMSettingsConnection *self, ...@@ -1169,9 +1191,7 @@ con_update_cb (NMSettingsConnection *self,
UpdateInfo *info = user_data; UpdateInfo *info = user_data;
NMConnection *for_agent; NMConnection *for_agent;
if (error) if (!error) {
dbus_g_method_return_error (info->context, error);
else {
/* Dupe the connection so we can clear out non-agent-owned secrets, /* Dupe the connection so we can clear out non-agent-owned secrets,
* as agent-owned secrets are the only ones we send back be saved. * as agent-owned secrets are the only ones we send back be saved.
* Only send secrets to agents of the same UID that called update too. * Only send secrets to agents of the same UID that called update too.
...@@ -1186,9 +1206,7 @@ con_update_cb (NMSettingsConnection *self, ...@@ -1186,9 +1206,7 @@ con_update_cb (NMSettingsConnection *self,
dbus_g_method_return (info->context); dbus_g_method_return (info->context);
} }
g_object_unref (info->agent_mgr); update_complete (self, info, error);
memset (info, 0, sizeof (*info));
g_free (info);
} }
static void static void
...@@ -1198,32 +1216,36 @@ update_auth_cb (NMSettingsConnection *self, ...@@ -1198,32 +1216,36 @@ update_auth_cb (NMSettingsConnection *self,
GError *error, GError *error,
gpointer data) gpointer data)
{ {
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); UpdateInfo *info = data;
NMConnection *new_settings = data; GError *local = NULL;
UpdateInfo *info;
if (error) if (error) {
dbus_g_method_return_error (context, error); update_complete (self, info, error);
else { return;
info = g_malloc0 (sizeof (*info)); }
info->context = context;
info->agent_mgr = g_object_ref (priv->agent_mgr);
info->sender_uid = sender_uid;
/* Cache the new secrets from the agent, as stuff like inotify-triggered
* changes to connection's backing config files will blow them away if
* they're in the main connection.
*/
update_agent_secrets_cache (self, new_settings);
/* Update and commit our settings. */ info->sender_uid = sender_uid;
/* Cache the new secrets from the agent, as stuff like inotify-triggered
* changes to connection's backing config files will blow them away if
* they're in the main connection.
*/
update_agent_secrets_cache (self, info->new_settings);
if (info->save_to_disk) {
nm_settings_connection_replace_and_commit (self, nm_settings_connection_replace_and_commit (self,
new_settings, info->new_settings,
con_update_cb, con_update_cb,
info); info);
} else {
/* Do nothing if there's nothing to update */
if (!nm_connection_compare (NM_CONNECTION (self), info->new_settings, NM_SETTING_COMPARE_FLAG_EXACT)) {
if (!nm_settings_connection_replace_settings (self, info->new_settings, TRUE, &local))
g_assert (local);
}
con_update_cb (self, local, info);
g_clear_error (&local);
} }
g_object_unref (new_settings);
} }
static const char * static const char *
...@@ -1253,13 +1275,15 @@ get_modify_permission_update (NMConnection *old, NMConnection *new) ...@@ -1253,13 +1275,15 @@ get_modify_permission_update (NMConnection *old, NMConnection *new)
} }
static void static void
impl_settings_connection_update (NMSettingsConnection *self, impl_settings_connection_update_helper (NMSettingsConnection *self,
GHashTable *new_settings, GHashTable *new_settings,
DBusGMethodInvocation *context) DBusGMethodInvocation *context,
gboolean save_to_disk)
{ {
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
NMConnection *tmp; NMConnection *tmp;
GError *error = NULL; GError *error = NULL;
UpdateInfo *info;
/* If the connection is read-only, that has to be changed at the source of /* If the connection is read-only, that has to be changed at the source of
* the problem (ex a system settings plugin that can't write connections out) * the problem (ex a system settings plugin that can't write connections out)
...@@ -1295,11 +1319,34 @@ impl_settings_connection_update (NMSettingsConnection *self, ...@@ -1295,11 +1319,34 @@ impl_settings_connection_update (NMSettingsConnection *self,
return; return;
} }
info = g_malloc0 (sizeof (*info));
info->context = context;
info->agent_mgr = g_object_ref (priv->agent_mgr);
info->sender_uid = G_MAXULONG;
info->save_to_disk = save_to_disk;
info->new_settings = tmp;
auth_start (self, auth_start (self,
context, context,
get_modify_permission_update (NM_CONNECTION (self), tmp), get_modify_permission_update (NM_CONNECTION (self), info->new_settings),
update_auth_cb, update_auth_cb,
tmp); info);
}
static void
impl_settings_connection_update (NMSettingsConnection *self,
GHashTable *new_settings,
DBusGMethodInvocation *context)
{
impl_settings_connection_update_helper (self, new_settings, context, TRUE);
}
static void
impl_settings_connection_update_unsaved (NMSettingsConnection *self,
GHashTable *new_settings,
DBusGMethodInvocation *context)
{
impl_settings_connection_update_helper (self, new_settings, context, FALSE);
} }
static void static void
......
...@@ -100,6 +100,10 @@ static void impl_settings_add_connection (NMSettings *self, ...@@ -100,6 +100,10 @@ static void impl_settings_add_connection (NMSettings *self,
GHashTable *settings, GHashTable *settings,
DBusGMethodInvocation *context); DBusGMethodInvocation *context);
static void impl_settings_add_connection_unsaved (NMSettings *self,
GHashTable *settings,
DBusGMethodInvocation *context);
static void impl_settings_save_hostname (NMSettings *self, static void impl_settings_save_hostname (NMSettings *self,
const char *hostname, const char *hostname,
DBusGMethodInvocation *context); DBusGMethodInvocation *context);
...@@ -894,6 +898,7 @@ remove_default_wired_connection (NMSettings *self, ...@@ -894,6 +898,7 @@ remove_default_wired_connection (NMSettings *self,
static NMSettingsConnection * static NMSettingsConnection *
add_new_connection (NMSettings *self, add_new_connection (NMSettings *self,
NMConnection *connection, NMConnection *connection,
gboolean save_to_disk,
GError **error) GError **error)
{ {
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
...@@ -927,14 +932,20 @@ add_new_connection (NMSettings *self, ...@@ -927,14 +932,20 @@ add_new_connection (NMSettings *self,
NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data); NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
GError *add_error = NULL; GError *add_error = NULL;
g_clear_error (error); added = nm_system_config_interface_add_connection (plugin, connection, save_to_disk, &add_error);
added = nm_system_config_interface_add_connection (plugin, connection, &add_error);
if (added) { if (added) {
claim_connection (self, added, TRUE); claim_connection (self, added, TRUE);
return added; return added;
} }
g_propagate_error (error, add_error); nm_log_dbg (LOGD_SETTINGS, "Failed to add %s/'%s': %s",
nm_connection_get_uuid (connection),
nm_connection_get_id (connection),
add_error ? add_error->message : "(unknown)");
g_clear_error (&add_error);
} }
g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_ADD_FAILED,
"No plugin supported adding this connection");
return NULL; return NULL;
} }
...@@ -986,13 +997,14 @@ pk_add_cb (NMAuthChain *chain, ...@@ -986,13 +997,14 @@ pk_add_cb (NMAuthChain *chain,
NMSettings *self = NM_SETTINGS (user_data); NMSettings *self = NM_SETTINGS (user_data);
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
NMAuthCallResult result; NMAuthCallResult result;
GError *error = NULL, *add_error = NULL; GError *error = NULL;
NMConnection *connection; NMConnection *connection;
NMSettingsConnection *added = NULL; NMSettingsConnection *added = NULL;
NMSettingsAddCallback callback; NMSettingsAddCallback callback;
gpointer callback_data; gpointer callback_data;
gulong caller_uid; gulong caller_uid;
const char *perm; const char *perm;
gboolean save_to_disk;
priv->auths = g_slist_remove (priv->auths, chain); priv->auths = g_slist_remove (priv->auths, chain);
...@@ -1013,15 +1025,8 @@ pk_add_cb (NMAuthChain *chain, ...@@ -1013,15 +1025,8 @@ pk_add_cb (NMAuthChain *chain,
/* Authorized */ /* Authorized */
connection = nm_auth_chain_get_data (chain, "connection"); connection = nm_auth_chain_get_data (chain, "connection");
g_assert (connection); g_assert (connection);
added = add_new_connection (self, connection, &add_error); save_to_disk = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "save-to-disk"));
if (!added) { added = add_new_connection (self, connection, save_to_disk, &error);
error = g_error_new (NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_ADD_FAILED,
"Saving connection failed: (%d) %s",
add_error ? add_error->code : -1,
(add_error && add_error->message) ? add_error->message : "(unknown)");
g_clear_error (&add_error);
}
} }
callback = nm_auth_chain_get_data (chain, "callback"); callback = nm_auth_chain_get_data (chain, "callback");
...@@ -1038,19 +1043,6 @@ pk_add_cb (NMAuthChain *chain, ...@@ -1038,19 +1043,6 @@ pk_add_cb (NMAuthChain *chain,
nm_auth_chain_unref (chain); nm_auth_chain_unref (chain);
} }
static void
add_cb (NMSettings *self,
NMSettingsConnection *connection,
GError *error,
DBusGMethodInvocation *context,
gpointer user_data)
{
if (error)
dbus_g_method_return_error (context, error);
else
dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (connection)));
}
/* FIXME: remove if/when kernel supports adhoc wpa */ /* FIXME: remove if/when kernel supports adhoc wpa */
static gboolean static gboolean
is_adhoc_wpa (NMConnection *connection) is_adhoc_wpa (NMConnection *connection)
...@@ -1086,6 +1078,7 @@ is_adhoc_wpa (NMConnection *connection) ...@@ -1086,6 +1078,7 @@ is_adhoc_wpa (NMConnection *connection)
void void
nm_settings_add_connection (NMSettings *self, nm_settings_add_connection (NMSettings *self,
NMConnection *connection, NMConnection *connection,
gboolean save_to_disk,
DBusGMethodInvocation *context, DBusGMethodInvocation *context,
NMSettingsAddCallback callback, NMSettingsAddCallback callback,
gpointer user_data) gpointer user_data)
...@@ -1178,6 +1171,7 @@ nm_settings_add_connection (NMSettings *self, ...@@ -1178,6 +1171,7 @@ nm_settings_add_connection (NMSettings *self,
nm_auth_chain_set_data (chain, "callback", callback, NULL); nm_auth_chain_set_data (chain, "callback", callback, NULL);
nm_auth_chain_set_data (chain, "callback-data", user_data, NULL); nm_auth_chain_set_data (chain, "callback-data", user_data, NULL);
nm_auth_chain_set_data_ulong (chain, "caller-uid", caller_uid); nm_auth_chain_set_data_ulong (chain, "caller-uid", caller_uid);
nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL);
} else { } else {
error = g_error_new_literal (NM_SETTINGS_ERROR, error = g_error_new_literal (NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_PERMISSION_DENIED, NM_SETTINGS_ERROR_PERMISSION_DENIED,
...@@ -1188,16 +1182,35 @@ nm_settings_add_connection (NMSettings *self, ...@@ -1188,16 +1182,35 @@ nm_settings_add_connection (NMSettings *self,
} }
static void static void
impl_settings_add_connection (NMSettings *self, impl_settings_add_connection_add_cb (NMSettings *self,
GHashTable *settings, NMSettingsConnection *connection,
DBusGMethodInvocation *context) GError *error,
DBusGMethodInvocation *context,
gpointer user_data)
{
if (error)
dbus_g_method_return_error (context, error);
else
dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (connection)));
}
static void
impl_settings_add_connection_helper (NMSettings *self,
GHashTable *settings,
gboolean save_to_disk,
DBusGMethodInvocation *context)
{ {
NMConnection *connection; NMConnection *connection;
GError *error = NULL; GError *error = NULL;
connection = nm_connection_new_from_hash (settings, &error); connection = nm_connection_new_from_hash (settings, &error);
if (connection) { if (connection) {
nm_settings_add_connection (self, connection, context, add_cb, NULL); nm_settings_add_connection (self,
connection,
save_to_disk,
context,
impl_settings_add_connection_add_cb,
NULL);
g_object_unref (connection); g_object_unref (connection);
} else { } else {
g_assert (error); g_assert (error);
...@@ -1206,6 +1219,22 @@ impl_settings_add_connection (NMSettings *self, ...@@ -1206,6 +1219,22 @@ impl_settings_add_connection (NMSettings *self,
} }
} }
static void
impl_settings_add_connection (NMSettings *self,
GHashTable *settings,
DBusGMethodInvocation *context)
{
impl_settings_add_connection_helper (self, settings, TRUE, context);
}
static void
impl_settings_add_connection_unsaved (NMSettings *self,
GHashTable *settings,
DBusGMethodInvocation *context)
{
impl_settings_add_connection_helper (self, settings, FALSE, context);
}
static void static void
pk_hostname_cb (NMAuthChain *chain, pk_hostname_cb (NMAuthChain *chain,
GError *chain_error, GError *chain_error,
...@@ -1406,7 +1435,7 @@ default_wired_try_update (NMDefaultWiredConnection *wired, ...@@ -1406,7 +1435,7 @@ default_wired_try_update (NMDefaultWiredConnection *wired,
g_assert (id); g_assert (id);
remove_default_wired_connection (self, NM_SETTINGS_CONNECTION (wired), FALSE); remove_default_wired_connection (self, NM_SETTINGS_CONNECTION (wired), FALSE);
added = add_new_connection (self, NM_CONNECTION (wired), &error); added = add_new_connection (self, NM_CONNECTION (wired), TRUE, &error);
if (added) { if (added) {
nm_settings_connection_delete (NM_SETTINGS_CONNECTION (wired), delete_cb, NULL); nm_settings_connection_delete (NM_SETTINGS_CONNECTION (wired), delete_cb, NULL);
......
...@@ -94,6 +94,7 @@ typedef void (*NMSettingsAddCallback) (NMSettings *settings, ...@@ -94,6 +94,7 @@ typedef void (*NMSettingsAddCallback) (NMSettings *settings,
void nm_settings_add_connection (NMSettings *self, void nm_settings_add_connection (NMSettings *self,
NMConnection *connection, NMConnection *connection,
gboolean save_to_disk,
DBusGMethodInvocation *context, DBusGMethodInvocation *context,
NMSettingsAddCallback callback, NMSettingsAddCallback callback,
gpointer user_data); gpointer user_data);
......
...@@ -150,13 +150,14 @@ nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface *config) ...@@ -150,13 +150,14 @@ nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface *config)
NMSettingsConnection * NMSettingsConnection *
nm_system_config_interface_add_connection (NMSystemConfigInterface *config, nm_system_config_interface_add_connection (NMSystemConfigInterface *config,
NMConnection *connection, NMConnection *connection,
gboolean save_to_disk,
GError **error) GError **error)
{ {
g_return_val_if_fail (config != NULL, NULL); g_return_val_if_fail (config != NULL, NULL);
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->add_connection) if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->add_connection)
return NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->add_connection (config, connection, error); return NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->add_connection (config, connection, save_to_disk, error);
return NULL; return NULL;
} }
...@@ -110,12 +110,14 @@ struct _NMSystemConfigInterface { ...@@ -110,12 +110,14 @@ struct _NMSystemConfigInterface {
GSList * (*get_unmanaged_specs) (NMSystemConfigInterface *config); GSList * (*get_unmanaged_specs) (NMSystemConfigInterface *config);
/* /*
* Save the given connection to backing storage, and return a new * Initialize the plugin-specific connection and return a new
* NMSettingsConnection subclass that contains the same settings as the * NMSettingsConnection subclass that contains the same settings as the
* original connection. * original connection. The connection should only be saved to backing
* storage if @save_to_disk is TRUE.
*/ */
NMSettingsConnection * (*add_connection) (NMSystemConfigInterface *config, NMSettingsConnection * (*add_connection) (NMSystemConfigInterface *config,
NMConnection *connection, NMConnection *connection,
gboolean save_to_disk,
GError **error); GError **error);
/* Signals */ /* Signals */
...@@ -139,6 +141,7 @@ GSList *nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface ...@@ -139,6 +141,7 @@ GSList *nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface
NMSettingsConnection *nm_system_config_interface_add_connection (NMSystemConfigInterface *config, NMSettingsConnection *nm_system_config_interface_add_connection (NMSystemConfigInterface *config,
NMConnection *connection, NMConnection *connection,
gboolean save_to_disk,
GError **error); GError **error);
G_END_DECLS G_END_DECLS
......
...@@ -503,6 +503,7 @@ get_connections (NMSystemConfigInterface *config) ...@@ -503,6 +503,7 @@ get_connections (NMSystemConfigInterface *config)