summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c14
-rw-r--r--src/devices/nm-device.c26
-rw-r--r--src/devices/wifi/nm-device-wifi.c7
-rw-r--r--src/devices/wifi/nm-iwd-manager.c15
-rw-r--r--src/meson.build3
-rw-r--r--src/nm-active-connection.c7
-rw-r--r--src/nm-checkpoint.c33
-rw-r--r--src/nm-manager.c114
-rw-r--r--src/nm-policy.c15
-rw-r--r--src/settings/nm-settings-connection.c1079
-rw-r--r--src/settings/nm-settings-connection.h222
-rw-r--r--src/settings/nm-settings-plugin.c202
-rw-r--r--src/settings/nm-settings-plugin.h171
-rw-r--r--src/settings/nm-settings-storage.c242
-rw-r--r--src/settings/nm-settings-storage.h117
-rw-r--r--src/settings/nm-settings.c1955
-rw-r--r--src/settings/nm-settings.h35
-rw-r--r--src/settings/plugins/ifcfg-rh/meson.build2
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c400
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h53
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c1745
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h24
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c17
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h8
-rw-r--r--src/settings/plugins/ifupdown/meson.build1
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-connection.c91
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-connection.h43
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.c37
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.h13
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c217
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.h24
-rw-r--r--src/settings/plugins/ifupdown/tests/test-ifupdown.c76
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.c187
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.h43
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c1587
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.h22
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c18
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.h5
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.c101
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.h118
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.c27
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.h16
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c38
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.h6
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile-settings.c3
45 files changed, 5983 insertions, 3196 deletions
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index 5360992282..e8ca6dd159 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -235,15 +235,17 @@ pan_connection_check_create (NMBluezDevice *self)
* which then already finds the suitable connection in priv->connections. This is confusing,
* so block the signal. check_emit_usable will succeed after this function call returns. */
g_signal_handlers_block_by_func (priv->settings, cp_connection_added, self);
- added = nm_settings_add_connection (priv->settings, connection, FALSE, &error);
+ nm_settings_add_connection (priv->settings,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &added,
+ &error);
g_signal_handlers_unblock_by_func (priv->settings, cp_connection_added, self);
if (added) {
nm_assert (!g_slist_find (priv->connections, added));
nm_assert (connection_compatible (self, added));
-
- nm_settings_connection_set_flags (added, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, TRUE);
-
priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
priv->pan_connection = added;
nm_log_dbg (LOGD_BT, "bluez[%s] added new Bluetooth connection for NAP device: '%s' (%s)", priv->path, id, uuid);
@@ -392,7 +394,7 @@ cp_connection_removed (NMSettings *settings,
static void
cp_connection_updated (NMSettings *settings,
NMSettingsConnection *sett_conn,
- gboolean by_user,
+ guint update_reason_u,
NMBluezDevice *self)
{
if (_internal_track_connection (self, sett_conn,
@@ -1225,7 +1227,7 @@ dispose (GObject *object)
if (to_delete) {
nm_log_dbg (LOGD_BT, "bluez[%s] removing Bluetooth connection for NAP device: '%s' (%s)", priv->path,
nm_settings_connection_get_id (to_delete), nm_settings_connection_get_uuid (to_delete));
- nm_settings_connection_delete (to_delete, NULL);
+ nm_settings_connection_delete (to_delete, FALSE);
g_object_unref (to_delete);
}
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 6a27469d41..5f7027d82a 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -12399,16 +12399,28 @@ nm_device_set_ip_config (NMDevice *self,
if ( nm_device_sys_iface_state_is_external (self)
&& (settings_connection = nm_device_get_settings_connection (self))
- && NM_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection),
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
+ && NM_FLAGS_ALL (nm_settings_connection_get_flags (settings_connection),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
&& nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request.obj)) == NM_ACTIVATION_TYPE_EXTERNAL) {
- g_object_freeze_notify (G_OBJECT (settings_connection));
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_add_setting (nm_settings_connection_get_connection (settings_connection),
+ gs_unref_object NMConnection *new_connection = NULL;
+
+ new_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (settings_connection));
+
+ nm_connection_add_setting (new_connection,
IS_IPv4
? nm_ip4_config_create_setting (priv->ip_config_4)
: nm_ip6_config_create_setting (priv->ip_config_6));
- g_object_thaw_notify (G_OBJECT (settings_connection));
+
+ nm_settings_connection_update (settings_connection,
+ new_connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE,
+ "update-external",
+ NULL);
}
nm_device_queue_recheck_assume (self);
@@ -14360,7 +14372,7 @@ cp_connection_added (NMSettings *settings, NMSettingsConnection *sett_conn, gpoi
}
static void
-cp_connection_updated (NMSettings *settings, NMSettingsConnection *sett_conn, gboolean by_user, gpointer user_data)
+cp_connection_updated (NMSettings *settings, NMSettingsConnection *sett_conn, guint update_reason_u, gpointer user_data)
{
cp_connection_added_or_updated (user_data, sett_conn);
}
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index aff631f889..a1fa96be9f 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -1760,9 +1760,10 @@ supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface,
}
if (secrets) {
if (nm_settings_connection_new_secrets (nm_act_request_get_settings_connection (req),
- nm_act_request_get_applied_connection (req),
- NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
- secrets, &error)) {
+ nm_act_request_get_applied_connection (req),
+ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+ secrets,
+ &error)) {
wifi_secrets_cancel (self);
nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
} else {
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index a2057a51d6..8889bb204a 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -469,19 +469,18 @@ mirror_8021x_connection (NMIwdManager *self,
if (!nm_connection_normalize (connection, NULL, NULL, NULL))
return NULL;
- settings_connection = nm_settings_add_connection (priv->settings, connection,
- FALSE, &error);
- if (!settings_connection) {
+ if (!nm_settings_add_connection (priv->settings,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &settings_connection,
+ &error)) {
_LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s",
name, error->message);
g_error_free (error);
return NULL;
}
- nm_settings_connection_set_flags (settings_connection,
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED |
- NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED,
- TRUE);
return settings_connection;
}
@@ -498,7 +497,7 @@ mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn)
/* If connection has not been saved since we created it
* in interface_added it too can be removed now. */
if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
- nm_settings_connection_delete (sett_conn, NULL);
+ nm_settings_connection_delete (sett_conn, FALSE);
g_object_unref (sett_conn);
}
diff --git a/src/meson.build b/src/meson.build
index aec6eceb44..698e05acd1 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -103,7 +103,7 @@ sources = files(
'dnsmasq/nm-dnsmasq-manager.c',
'dnsmasq/nm-dnsmasq-utils.c',
'ppp/nm-ppp-manager-call.c',
- 'settings/plugins/keyfile/nms-keyfile-connection.c',
+ 'settings/plugins/keyfile/nms-keyfile-storage.c',
'settings/plugins/keyfile/nms-keyfile-plugin.c',
'settings/plugins/keyfile/nms-keyfile-reader.c',
'settings/plugins/keyfile/nms-keyfile-utils.c',
@@ -113,6 +113,7 @@ sources = files(
'settings/nm-settings.c',
'settings/nm-settings-connection.c',
'settings/nm-settings-plugin.c',
+ 'settings/nm-settings-storage.c',
'supplicant/nm-supplicant-config.c',
'supplicant/nm-supplicant-interface.c',
'supplicant/nm-supplicant-manager.c',
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index 06932b5f15..b937f7329e 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -181,7 +181,7 @@ NM_UTILS_FLAGS2STR_DEFINE_STATIC (_state_flags_to_string, NMActivationStateFlags
static void
_settings_connection_updated (NMSettingsConnection *sett_conn,
- gboolean by_user,
+ guint update_reason_u,
gpointer user_data)
{
NMActiveConnection *self = user_data;
@@ -522,8 +522,9 @@ nm_active_connection_clear_secrets (NMActiveConnection *self)
if (nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection.obj,
priv->applied_connection,
NM_SETTING_COMPARE_FLAG_NONE)) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (priv->settings_connection.obj));
+ nm_settings_connection_clear_secrets (priv->settings_connection.obj,
+ FALSE,
+ FALSE);
}
nm_connection_clear_secrets (priv->applied_connection);
}
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index 000edee5b7..b8277539e3 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -217,19 +217,35 @@ restore_and_activate_connection (NMCheckpoint *self,
gs_unref_object NMAuthSubject *subject = NULL;
GError *local_error = NULL;
gboolean need_update, need_activation;
+ NMSettingsConnectionPersistMode persist_mode;
+ NMSettingsConnectionIntFlags sett_flags;
+ NMSettingsConnectionIntFlags sett_mask;
connection = find_settings_connection (self,
dev_checkpoint,
&need_update,
&need_activation);
+
+ /* FIXME(settings-rework): we need to ensure that we re-create/update the profile
+ * for the same settings plugin. E.g. if it was a keyfile in /run or /etc,
+ * it must be again. If it was previously handled by a certain settings plugin,
+ * so it must again.
+ *
+ * FIXME: set the right storage flags for the setting (volatile, nm-generated). */
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
+ sett_flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
+ sett_mask = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
+
if (connection) {
if (need_update) {
_LOGD ("rollback: updating connection %s",
nm_settings_connection_get_uuid (connection));
nm_settings_connection_update (connection,
dev_checkpoint->settings_connection,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+ persist_mode,
+ sett_flags,
+ sett_mask,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE,
"checkpoint-rollback",
NULL);
}
@@ -238,11 +254,12 @@ restore_and_activate_connection (NMCheckpoint *self,
_LOGD ("rollback: adding connection %s again",
nm_connection_get_uuid (dev_checkpoint->settings_connection));
- connection = nm_settings_add_connection (NM_SETTINGS_GET,
- dev_checkpoint->settings_connection,
- TRUE,
- &local_error);
- if (!connection) {
+ if (!nm_settings_add_connection (NM_SETTINGS_GET,
+ dev_checkpoint->settings_connection,
+ persist_mode,
+ sett_flags,
+ &connection,
+ &local_error)) {
_LOGD ("rollback: connection add failure: %s", local_error->message);
g_clear_error (&local_error);
return FALSE;
@@ -428,7 +445,7 @@ next_dev:
nm_settings_connection_get_uuid (con))) {
_LOGD ("rollback: deleting new connection %s",
nm_settings_connection_get_uuid (con));
- nm_settings_connection_delete (con, NULL);
+ nm_settings_connection_delete (con, FALSE);
}
}
}
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 52433049fa..605e04e238 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -94,7 +94,8 @@ typedef struct {
struct {
GDBusMethodInvocation *invocation;
NMConnection *connection;
- NMSettingsConnectionPersistMode persist;
+ NMSettingsConnectionPersistMode persist_mode;
+ bool is_volatile:1;
} add_and_activate;
};
} ac_auth;
@@ -378,7 +379,8 @@ static void _add_and_activate_auth_done (NMManager *self,
NMActiveConnection *active,
NMConnection *connection,
GDBusMethodInvocation *invocation,
- NMSettingsConnectionPersistMode persist,
+ NMSettingsConnectionPersistMode persist_mode,
+ gboolean is_volatile,
gboolean success,
const char *error_desc);
static void _activation_auth_done (NMManager *self,
@@ -495,7 +497,8 @@ _async_op_data_new_ac_auth_add_and_activate (NMManager *self,
NMActiveConnection *active_take,
GDBusMethodInvocation *invocation_take,
NMConnection *connection_take,
- NMSettingsConnectionPersistMode persist)
+ NMSettingsConnectionPersistMode persist_mode,
+ gboolean is_volatile)
{
AsyncOpData *async_op_data;
@@ -508,7 +511,8 @@ _async_op_data_new_ac_auth_add_and_activate (NMManager *self,
async_op_data->ac_auth.active = active_take;
async_op_data->ac_auth.add_and_activate.invocation = invocation_take;
async_op_data->ac_auth.add_and_activate.connection = connection_take;
- async_op_data->ac_auth.add_and_activate.persist = persist;
+ async_op_data->ac_auth.add_and_activate.persist_mode = persist_mode;
+ async_op_data->ac_auth.add_and_activate.is_volatile = is_volatile;
c_list_link_tail (&NM_MANAGER_GET_PRIVATE (self)->async_op_lst_head, &async_op_data->async_op_lst);
return async_op_data;
}
@@ -550,7 +554,8 @@ _async_op_complete_ac_auth_cb (NMActiveConnection *active,
async_op_data->ac_auth.active,
async_op_data->ac_auth.add_and_activate.connection,
async_op_data->ac_auth.add_and_activate.invocation,
- async_op_data->ac_auth.add_and_activate.persist,
+ async_op_data->ac_auth.add_and_activate.persist_mode,
+ async_op_data->ac_auth.add_and_activate.is_volatile,
success,
error_desc);
g_object_unref (async_op_data->ac_auth.add_and_activate.connection);
@@ -831,7 +836,7 @@ _delete_volatile_connection_do (NMManager *self,
_LOGD (LOGD_DEVICE, "volatile connection disconnected. Deleting connection '%s' (%s)",
nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection));
- nm_settings_connection_delete (connection, NULL);
+ nm_settings_connection_delete (connection, FALSE);
}
/* Returns: whether to notify D-Bus of the removal or not */
@@ -2140,7 +2145,7 @@ connection_added_cb (NMSettings *settings,
static void
connection_updated_cb (NMSettings *settings,
NMSettingsConnection *sett_conn,
- gboolean by_user,
+ guint update_reason_u,
NMManager *self)
{
connection_changed_on_idle (self, sett_conn);
@@ -2660,8 +2665,13 @@ get_existing_connection (NMManager *self,
nm_device_assume_state_reset (device);
- added = nm_settings_add_connection (priv->settings, connection, FALSE, &error);
- if (!added) {
+ if (!nm_settings_add_connection (priv->settings,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &added,
+ &error)) {
_LOG2W (LOGD_SETTINGS, device, "assume: failure to save generated connection '%s': %s",
nm_connection_get_id (connection),
error->message);
@@ -2669,10 +2679,6 @@ get_existing_connection (NMManager *self,
return NULL;
}
- nm_settings_connection_set_flags (added,
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED |
- NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
- TRUE);
NM_SET_OUT (out_generated, TRUE);
return added;
}
@@ -2774,7 +2780,7 @@ recheck_assume_connection (NMManager *self,
if (generated) {
_LOG2D (LOGD_DEVICE, device, "assume: deleting generated connection after assuming failed");
- nm_settings_connection_delete (sett_conn, NULL);
+ nm_settings_connection_delete (sett_conn, FALSE);
} else {
if (nm_device_sys_iface_state_get (device) == NM_DEVICE_SYS_IFACE_STATE_ASSUME)
nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL);
@@ -5315,14 +5321,11 @@ activation_add_done (NMSettings *settings,
NMManager *self;
gs_unref_object NMActiveConnection *active = NULL;
gs_free_error GError *local = NULL;
- gpointer persist_ptr;
- NMSettingsConnectionPersistMode persist;
gpointer async_op_type_ptr;
AsyncOpType async_op_type;
GVariant *result_floating;
- nm_utils_user_data_unpack (user_data, &self, &active, &persist_ptr, &async_op_type_ptr);
- persist = GPOINTER_TO_INT (persist_ptr);
+ nm_utils_user_data_unpack (user_data, &self, &active, &async_op_type_ptr);
async_op_type = GPOINTER_TO_INT (async_op_type_ptr);
if (error)
@@ -5330,17 +5333,8 @@ activation_add_done (NMSettings *settings,
nm_active_connection_set_settings_connection (active, new_connection);
- if (!_internal_activate_generic (self, active, &local)) {
- error = local;
+ if (!_internal_activate_generic (self, active, &local))
goto fail;
- }
-
- nm_settings_connection_update (new_connection,
- NULL,
- persist,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED,
- "add-and-activate",
- NULL);
if (async_op_type == ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE) {
result_floating = g_variant_new ("(oo)",
@@ -5363,13 +5357,17 @@ activation_add_done (NMSettings *settings,
return;
fail:
- nm_assert (error);
+ if (local) {
+ nm_assert (!error);
+ error = local;
+ } else
+ nm_assert (error);
nm_active_connection_set_state_fail (active,
NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN,
error->message);
if (new_connection)
- nm_settings_connection_delete (new_connection, NULL);
+ nm_settings_connection_delete (new_connection, FALSE);
g_dbus_method_invocation_return_gerror (context, error);
nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
NULL,
@@ -5385,7 +5383,8 @@ _add_and_activate_auth_done (NMManager *self,
NMActiveConnection *active,
NMConnection *connection,
GDBusMethodInvocation *invocation,
- NMSettingsConnectionPersistMode persist,
+ NMSettingsConnectionPersistMode persist_mode,
+ gboolean is_volatile,
gboolean success,
const char *error_desc)
{
@@ -5413,13 +5412,15 @@ _add_and_activate_auth_done (NMManager *self,
* shutdown. */
nm_settings_add_connection_dbus (priv->settings,
connection,
- FALSE,
+ persist_mode,
+ ( is_volatile
+ ? NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE),
nm_active_connection_get_subject (active),
invocation,
activation_add_done,
nm_utils_user_data_pack (self,
g_object_ref (active),
- GINT_TO_POINTER (persist),
GINT_TO_POINTER (async_op_type)));
}
@@ -5445,7 +5446,8 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
const char *device_path;
const char *specific_object_path;
gs_free NMConnection **conns = NULL;
- NMSettingsConnectionPersistMode persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ NMSettingsConnectionPersistMode persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ gboolean is_volatile = FALSE;
gboolean bind_dbus_client = FALSE;
AsyncOpType async_op_type;
@@ -5474,13 +5476,17 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
&& g_variant_is_of_type (option_value, G_VARIANT_TYPE_STRING)) {
s = g_variant_get_string (option_value, NULL);
- if (nm_streq (s, "volatile"))
- persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY;
- else if (nm_streq (s, "memory"))
- persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
- else if (nm_streq (s, "disk"))
- persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
- else {
+ is_volatile = FALSE;
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+
+ if (nm_streq (s, "volatile")) {
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+ is_volatile = TRUE;
+ } else if (nm_streq (s, "memory"))
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+ else if (nm_streq (s, "disk")) {
+ /* pass */
+ } else {
error = g_error_new_literal (NM_MANAGER_ERROR,
NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"Option \"persist\" must be one of \"volatile\", \"memory\" or \"disk\"");
@@ -5599,7 +5605,8 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
active,
invocation,
incompl_conn,
- persist));
+ persist_mode,
+ is_volatile));
/* we passed the pointers on to _async_op_data_new_ac_auth_add_and_activate() */
g_steal_pointer (&incompl_conn);
@@ -6549,8 +6556,9 @@ nm_manager_start (NMManager *self, GError **error)
gs_free NMSettingsConnection **connections = NULL;
guint i;
- if (!nm_settings_start (priv->settings, error))
- return FALSE;
+ nm_device_factory_manager_load_factories (_register_device_factory, self);
+
+ nm_device_factory_manager_for_each_factory (start_factory, NULL);
/* Set initial radio enabled/disabled state */
for (i = 0; i < RFKILL_TYPE_MAX; i++) {
@@ -6573,16 +6581,15 @@ nm_manager_start (NMManager *self, GError **error)
manager_update_radio_enabled (self, rstate, enabled);
}
- /* Log overall networking status - enabled/disabled */
_LOGI (LOGD_CORE, "Networking is %s by state file",
priv->net_enabled ? "enabled" : "disabled");
system_unmanaged_devices_changed_cb (priv->settings, NULL, self);
+
hostname_changed_cb (priv->hostname_manager, NULL, self);
- /* Start device factories */
- nm_device_factory_manager_load_factories (_register_device_factory, self);
- nm_device_factory_manager_for_each_factory (start_factory, NULL);
+ if (!nm_settings_start (priv->settings, error))
+ return FALSE;
nm_platform_process_events (priv->platform);
@@ -6596,10 +6603,11 @@ nm_manager_start (NMManager *self, GError **error)
/* Load VPN plugins */
priv->vpn_manager = g_object_ref (nm_vpn_manager_get ());
- /* Connections added before the manager is started do not emit
- * connection-added signals thus devices have to be created manually.
- */
_LOGD (LOGD_CORE, "creating virtual devices...");
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
+ G_CALLBACK (connection_added_cb), self);
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
+ G_CALLBACK (connection_updated_cb), self);
connections = nm_settings_get_connections_clone (priv->settings, NULL,
NULL, NULL,
nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
@@ -7334,10 +7342,6 @@ constructed (GObject *object)
G_CALLBACK (settings_startup_complete_changed), self);
g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS,
G_CALLBACK (system_unmanaged_devices_changed_cb), self);
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
- G_CALLBACK (connection_added_cb), self);
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
- G_CALLBACK (connection_updated_cb), self);
g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_FLAGS_CHANGED, G_CALLBACK (connection_flags_changed), self);
priv->hostname_manager = g_object_ref (nm_hostname_manager_get ());
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 9d5b027c28..04c855ef53 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -1846,8 +1846,7 @@ device_state_changed (NMDevice *device,
}
}
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (sett_conn));
+ nm_settings_connection_clear_secrets (sett_conn, FALSE, FALSE);
}
break;
case NM_DEVICE_STATE_ACTIVATED:
@@ -1858,9 +1857,7 @@ device_state_changed (NMDevice *device,
/* And clear secrets so they will always be requested from the
* settings service when the next connection is made.
*/
-
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (sett_conn));
+ nm_settings_connection_clear_secrets (sett_conn, FALSE, FALSE);
}
/* Add device's new IPv4 and IPv6 configs to DNS */
@@ -2399,16 +2396,17 @@ dns_config_changed (NMDnsManager *dns_manager, gpointer user_data)
static void
connection_updated (NMSettings *settings,
NMSettingsConnection *connection,
- gboolean by_user,
+ guint update_reason_u,
gpointer user_data)
{
NMPolicyPrivate *priv = user_data;
NMPolicy *self = _PRIV_TO_SELF (priv);
+ NMSettingsConnectionUpdateReason update_reason = update_reason_u;
const CList *tmp_lst;
NMDevice *device = NULL;
NMDevice *dev;
- if (by_user) {
+ if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL)) {
/* find device with given connection */
nm_manager_for_each_device (priv->manager, dev, tmp_lst) {
if (nm_device_get_settings_connection (dev) == connection) {
@@ -2419,9 +2417,6 @@ connection_updated (NMSettings *settings,
if (device)
nm_device_reapply_settings_immediately (device);
-
- /* Reset auto retries back to default since connection was updated */
- nm_settings_connection_autoconnect_retries_reset (connection);
}
schedule_activate_all (self);
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 6368b3d5a0..937b9b5eb1 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -37,6 +37,8 @@
#include "NetworkManagerUtils.h"
#include "nm-core-internal.h"
#include "nm-audit-manager.h"
+#include "nm-settings.h"
+#include "settings/plugins/keyfile/nms-keyfile-storage.h"
#define AUTOCONNECT_RETRIES_UNSET -2
#define AUTOCONNECT_RETRIES_FOREVER -1
@@ -67,13 +69,11 @@ nm_settings_connections_array_to_connections (NMSettingsConnection *const*connec
NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsConnection,
PROP_UNSAVED,
- PROP_READY,
PROP_FLAGS,
PROP_FILENAME,
);
enum {
- REMOVED,
UPDATED_INTERNAL,
FLAGS_CHANGED,
LAST_SIGNAL
@@ -83,21 +83,12 @@ static guint signals[LAST_SIGNAL] = { 0 };
typedef struct _NMSettingsConnectionPrivate {
+ NMSettings *settings;
+
NMKeyFileDB *kf_db_timestamps;
NMKeyFileDB *kf_db_seen_bssids;
NMAgentManager *agent_mgr;
- NMSessionMonitor *session_monitor;
- gulong session_changed_id;
-
- NMSettingsConnectionIntFlags flags:5;
-
- bool removed:1;
- bool ready:1;
-
- bool timestamp_set:1;
-
- NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4;
/* List of pending authentication requests */
CList auth_lst_head;
@@ -106,6 +97,12 @@ typedef struct _NMSettingsConnectionPrivate {
NMConnection *connection;
+ NMSettingsStorage *storage;
+
+ char *filename;
+
+ NMDevice *default_wired_device;
+
/* Caches secrets from on-disk connections; were they not cached any
* call to nm_connection_clear_secrets() wipes them out and we'd have
* to re-read them from disk which defeats the purpose of having the
@@ -121,8 +118,6 @@ typedef struct _NMSettingsConnectionPrivate {
*/
GVariant *agent_secrets;
- char *filename;
-
GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
guint64 timestamp; /* Up-to-date timestamp of connection use */
@@ -130,10 +125,21 @@ typedef struct _NMSettingsConnectionPrivate {
guint64 last_secret_agent_version_id;
int autoconnect_retries;
+
gint32 autoconnect_retries_blocked_until;
+ bool timestamp_set:1;
+
+ NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4;
+
+ NMSettingsConnectionIntFlags flags:5;
+
} NMSettingsConnectionPrivate;
+struct _NMSettingsConnectionClass {
+ NMDBusObjectClass parent;
+};
+
G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT)
#define NM_SETTINGS_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMSettingsConnection, NM_IS_SETTINGS_CONNECTION)
@@ -167,6 +173,68 @@ static const GDBusSignalInfo signal_info_updated;
static const GDBusSignalInfo signal_info_removed;
static const NMDBusInterfaceInfoExtended interface_info_settings_connection;
+static void update_system_secrets_cache (NMSettingsConnection *self, NMConnection *new);
+static void update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new);
+
+/*****************************************************************************/
+
+NMDevice *
+nm_settings_connection_default_wired_get_device (NMSettingsConnection *self)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+ nm_assert (!priv->default_wired_device || NM_IS_DEVICE (priv->default_wired_device));
+
+ return priv->default_wired_device;
+}
+
+void
+nm_settings_connection_default_wired_set_device (NMSettingsConnection *self,
+ NMDevice *device)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+ nm_assert (!priv->default_wired_device || NM_IS_DEVICE (priv->default_wired_device));
+ nm_assert (!device || NM_IS_DEVICE (device));
+
+ nm_assert ((!!priv->default_wired_device) != (!!device));
+
+ priv->default_wired_device = device;
+}
+
+/*****************************************************************************/
+
+NMSettingsStorage *
+nm_settings_connection_get_storage (NMSettingsConnection *self)
+{
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
+
+ return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->storage;
+}
+
+void
+_nm_settings_connection_set_storage (NMSettingsConnection *self,
+ NMSettingsStorage *storage)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ const char *filename;
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (storage));
+ nm_assert ( !priv->storage
+ || nm_streq (nm_settings_storage_get_uuid (storage),
+ nm_settings_storage_get_uuid (priv->storage)));
+
+ nm_g_object_ref_set (&priv->storage, storage);
+
+ filename = nm_settings_storage_get_filename (priv->storage);
+
+ if (!nm_streq0 (priv->filename, filename)) {
+ g_free (priv->filename);
+ priv->filename = g_strdup (filename);
+ _notify (self, PROP_FILENAME);
+ }
+}
+
/*****************************************************************************/
static GHashTable *
@@ -185,6 +253,50 @@ nm_settings_connection_get_connection (NMSettingsConnection *self)
return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->connection;
}
+void
+_nm_settings_connection_set_connection (NMSettingsConnection *self,
+ NMConnection *new_connection,
+ NMConnection **out_connection_old,
+ NMSettingsConnectionUpdateReason update_reason)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ gs_unref_object NMConnection *connection_old = NULL;
+
+ nm_assert (NM_IS_CONNECTION (new_connection));
+ nm_assert (NM_IS_SETTINGS_STORAGE (priv->storage));
+ nm_assert (nm_streq0 (nm_settings_storage_get_uuid (priv->storage), nm_connection_get_uuid (new_connection)));
+ nm_assert (!out_connection_old || !*out_connection_old);
+
+ if ( !priv->connection
+ || !nm_connection_compare (priv->connection,
+ new_connection,
+ NM_SETTING_COMPARE_FLAG_EXACT)) {
+ connection_old = priv->connection;
+ priv->connection = g_object_ref (new_connection);
+ nmtst_connection_assert_unchanging (priv->connection);
+
+ /* note that we only return @connection_old if the new connection actually differs from
+ * before.
+ *
+ * So, there are three cases:
+ *
+ * - return %NULL when setting the connection the first time.
+ * - return %NULL if setting a profile with the same content that we already have.
+ * - return the previous pointer if the connection changed. */
+ NM_SET_OUT (out_connection_old, g_steal_pointer (&connection_old));
+ }
+
+ if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS))
+ update_system_secrets_cache (self, NULL);
+ else if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS))
+ update_system_secrets_cache (self, priv->connection);
+
+ if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS))
+ update_agent_secrets_cache (self, NULL);
+ else if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS))
+ update_agent_secrets_cache (self, priv->connection);
+}
+
/*****************************************************************************/
gboolean
@@ -214,34 +326,23 @@ nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *s
/*****************************************************************************/
-static void
-set_visible (NMSettingsConnection *self, gboolean new_visible)
-{
- nm_settings_connection_set_flags (self,
- NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE,
- new_visible);
-}
-
-void
-nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
+gboolean
+nm_settings_connection_check_visibility (NMSettingsConnection *self,
+ NMSessionMonitor *session_monitor)
{
- NMSettingsConnectionPrivate *priv;
NMSettingConnection *s_con;
guint32 num, i;
- g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
- priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ nm_assert (NM_IS_SESSION_MONITOR (session_monitor));
s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (self));
/* Check every user in the ACL for a session */
num = nm_setting_connection_get_num_permissions (s_con);
- if (num == 0) {
- /* Visible to all */
- set_visible (self, TRUE);
- return;
- }
+ if (num == 0)
+ return TRUE;
for (i = 0; i < num; i++) {
const char *user;
@@ -251,20 +352,13 @@ nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
continue;
if (!nm_session_monitor_user_to_uid (user, &uid))
continue;
- if (!nm_session_monitor_session_exists (priv->session_monitor, uid, FALSE))
+ if (!nm_session_monitor_session_exists (session_monitor, uid, FALSE))
continue;
- set_visible (self, TRUE);
- return;
+ return TRUE;
}
- set_visible (self, FALSE);
-}
-
-static void
-session_changed_cb (NMSessionMonitor *self, NMSettingsConnection *sett_conn)
-{
- nm_settings_connection_recheck_visibility (sett_conn);
+ return FALSE;
}
/*****************************************************************************/
@@ -310,7 +404,8 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL)) {
NMSecretAgent *agent = nm_agent_manager_get_agent_by_user (priv->agent_mgr, puser);
- if (agent && nm_secret_agent_has_permission (agent, permission))
+ if ( agent
+ && nm_secret_agent_has_permission (agent, permission))
return TRUE;
}
}
@@ -321,20 +416,37 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
/*****************************************************************************/
static void
-update_system_secrets_cache (NMSettingsConnection *self)
+update_system_secrets_cache (NMSettingsConnection *self, NMConnection *new)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
gs_unref_object NMConnection *connection_cloned = NULL;
+ gs_unref_variant GVariant *old_secrets = NULL;
- nm_clear_pointer (&priv->system_secrets, g_variant_unref);
+ old_secrets = g_steal_pointer (&priv->system_secrets);
- connection_cloned = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
+ if (!new)
+ goto out;
+
+ /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
+ * in particular if there are no secrets to begin with. */
+
+ connection_cloned = nm_simple_connection_new_clone (new);
/* Clear out non-system-owned and not-saved secrets */
_nm_connection_clear_secrets_by_secret_flags (connection_cloned,
NM_SETTING_SECRET_FLAG_NONE);
priv->system_secrets = nm_g_variant_ref_sink (nm_connection_to_dbus (connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
+
+out:
+ if (_LOGT_ENABLED ()) {
+ if ((!!old_secrets) != (!!priv->system_secrets)) {
+ _LOGT ("update system secrets: secrets %s",
+ old_secrets ? "cleared" : "set");
+ } else if ( priv->system_secrets
+ && !g_variant_equal (old_secrets, priv->system_secrets))
+ _LOGT ("update system secrets: secrets updated");
+ }
}
static void
@@ -342,11 +454,17 @@ update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
gs_unref_object NMConnection *connection_cloned = NULL;
+ gs_unref_variant GVariant *old_secrets = NULL;
- nm_clear_pointer (&priv->agent_secrets, g_variant_unref);
+ old_secrets = g_steal_pointer (&priv->agent_secrets);
- connection_cloned = nm_simple_connection_new_clone ( new
- ?: nm_settings_connection_get_connection (self));
+ if (!new)
+ goto out;
+
+ /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
+ * in particular if there are no secrets to begin with. */
+
+ connection_cloned = nm_simple_connection_new_clone (new);
/* Clear out non-system-owned secrets */
_nm_connection_clear_secrets_by_secret_flags (connection_cloned,
@@ -354,328 +472,137 @@ update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
| NM_SETTING_SECRET_FLAG_AGENT_OWNED);
priv->agent_secrets = nm_g_variant_ref_sink (nm_connection_to_dbus (connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
-}
-
-static void
-secrets_cleared_cb (NMConnection *connection, NMSettingsConnection *self)
-{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- /* Clear agent secrets when connection's secrets are cleared since agent
- * secrets are transient.
- */
- nm_clear_pointer (&priv->agent_secrets, g_variant_unref);
-}
-
-static void
-set_persist_mode (NMSettingsConnection *self, NMSettingsConnectionPersistMode persist_mode)
-{
- NMSettingsConnectionIntFlags flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
- const NMSettingsConnectionIntFlags ALL = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED
- | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
- | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
- switch (persist_mode) {
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK:
- flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
- break;
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY:
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED:
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY:
- flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED;
- break;
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED:
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY:
- flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED |
- NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
- break;
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED:
- /* only set the connection as unsaved, but preserve the nm-generated
- * and volatile flag. */
- nm_settings_connection_set_flags (self,
- NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED,
- TRUE);
- return;
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP:
- case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED:
- /* Nothing to do */
- return;
+out:
+ if (_LOGT_ENABLED ()) {
+ if ((!!old_secrets) != (!!priv->agent_secrets)) {
+ _LOGT ("update agent secrets: secrets %s",
+ old_secrets ? "cleared" : "set");
+ } else if ( priv->agent_secrets
+ && !g_variant_equal (old_secrets, priv->agent_secrets))
+ _LOGT ("update agent secrets: secrets updated");
}
-
- nm_settings_connection_set_flags_full (self, ALL, flags);
}
-static void
-_emit_updated (NMSettingsConnection *self, gboolean by_user)
+void
+nm_settings_connection_clear_secrets (NMSettingsConnection *self,
+ gboolean clear_cached_system_secrets,
+ gboolean persist)
{
- nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
- &interface_info_settings_connection,
- &signal_info_updated,
- "()");
- g_signal_emit (self, signals[UPDATED_INTERNAL], 0, by_user);
-}
+ gs_unref_object NMConnection *connection_cloned = NULL;
-static void
-connection_changed_cb (NMConnection *connection, NMSettingsConnection *self)
-{
- set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
- _emit_updated (self, FALSE);
-}
+ /* FIXME: add API to NMConnection so that we can clone a profile without secrets. */
-static gboolean
-_delete (NMSettingsConnection *self, GError **error)
-{
- NMSettingsConnectionClass *klass;
- GError *local = NULL;
- const char *filename;
+ connection_cloned = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
- nm_assert (NM_IS_SETTINGS_CONNECTION (self));
+ nm_connection_clear_secrets (connection_cloned);
- klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
- if (!klass->delete) {
- g_set_error (&local,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_FAILED,
- "delete not supported");
- goto fail;
- }
- if (!klass->delete (self,
- &local))
- goto fail;
-
- filename = nm_settings_connection_get_filename (self);
- if (filename) {
- _LOGD ("delete: success deleting connection (\"%s\")", filename);
- nm_settings_connection_set_filename (self, NULL);
- } else
- _LOGT ("delete: success deleting connection (no-file)");
- return TRUE;
-fail:
- _LOGD ("delete: failure deleting connection: %s", local->message);
- g_propagate_error (error, local);
- return FALSE;
+ if (!nm_settings_connection_update (self,
+ connection_cloned,
+ persist
+ ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
+ : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+ | (clear_cached_system_secrets ? NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE)
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS,
+ "clear-secrets",
+ NULL))
+ nm_assert_not_reached ();
}
static gboolean
-_update_prepare (NMSettingsConnection *self,
- NMConnection *new_connection,
+_secrets_update (NMConnection *connection,
+ const char *setting_name,
+ GVariant *secrets,
+ NMConnection **out_new_connection,
GError **error)
{
- g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
- g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE);
+ gs_unref_variant GVariant *secrets_setting = NULL;
- if (!nm_connection_normalize (new_connection, NULL, NULL, error))
- return FALSE;
+ nm_assert (NM_IS_CONNECTION (connection));
- if ( nm_dbus_object_get_path (NM_DBUS_OBJECT (self))
- && g_strcmp0 (nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection)) != 0) {
- /* Updating the UUID is not allowed once the path is exported. */
- g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "connection %s cannot change the UUID from %s to %s", nm_settings_connection_get_id (self),
- nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection));
+ if ( setting_name
+ && !nm_connection_get_setting_by_name (connection, setting_name)) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
+ setting_name);
return FALSE;
}
- return TRUE;
-}
-
-gboolean
-nm_settings_connection_update (NMSettingsConnection *self,
- NMConnection *new_connection,
- NMSettingsConnectionPersistMode persist_mode,
- NMSettingsConnectionCommitReason commit_reason,
- const char *log_diff_name,
- GError **error)
-{
- NMSettingsConnectionPrivate *priv;
- NMSettingsConnectionClass *klass = NULL;
- gs_unref_object NMConnection *reread_connection = NULL;
- NMConnection *replace_connection;
- gboolean replaced = FALSE;
- gs_free char *logmsg_change = NULL;
- GError *local = NULL;
- gs_unref_variant GVariant *con_agent_secrets = NULL;
- gs_unref_variant GVariant *new_agent_secrets = NULL;
-
- g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
-
- priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
- klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
- if (!klass->commit_changes) {
- g_set_error (&local,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_FAILED,
- "writing settings not supported");
- goto out;
- }
- }
-
- if ( new_connection
- && !_update_prepare (self,
- new_connection,
- &local))
- goto out;
-
- if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
- if (!klass->commit_changes (self,
- new_connection ?: nm_settings_connection_get_connection (self),
- commit_reason,
- &reread_connection,
- &logmsg_change,
- &local))
- goto out;
-
- if ( reread_connection
- && !_update_prepare (self,
- reread_connection,
- &local))
- goto out;
- }
-
- replace_connection = reread_connection ?: new_connection;
-
- /* Save agent-owned secrets from the new connection for later use */
- if (new_connection) {
- new_agent_secrets = nm_connection_to_dbus (new_connection, NM_CONNECTION_SERIALIZE_ONLY_SECRETS
- | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
- }
-
- /* Disconnect the changed signal to ensure we don't set Unsaved when
- * it's not required.
- */
- g_signal_handlers_block_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
-
- /* Do nothing if there's nothing to update */
- if ( replace_connection
- && !nm_connection_compare (nm_settings_connection_get_connection (self),
- replace_connection,
- NM_SETTING_COMPARE_FLAG_EXACT)) {
-
- if (log_diff_name) {
- nm_utils_log_connection_diff (replace_connection, nm_settings_connection_get_connection (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ",
- nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
- }
-
- /* Make a copy of agent-owned secrets because they won't be present in
- * the connection returned by plugins, as plugins return only what was
- * reread from the file. */
- con_agent_secrets = nm_connection_to_dbus (nm_settings_connection_get_connection (self),
- NM_CONNECTION_SERIALIZE_ONLY_SECRETS
- | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
-
- nm_connection_replace_settings_from_connection (nm_settings_connection_get_connection (self), replace_connection);
-
- replaced = TRUE;
- }
+ if (!secrets)
+ return TRUE;
- nm_settings_connection_set_flags (self,
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
- FALSE);
+ nm_assert ( g_variant_is_of_type (secrets, NM_VARIANT_TYPE_SETTING)
+ || g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION));
- if (replaced) {
- /* Cache the just-updated system secrets in case something calls
- * nm_connection_clear_secrets() and clears them.
- */
- update_system_secrets_cache (self);
+ if (g_variant_n_children (secrets) == 0)
+ return TRUE;
- /* Add agent and always-ask secrets back; they won't necessarily be
- * in the replacement connection data if it was eg reread from disk.
- */
- if (priv->agent_secrets) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, priv->agent_secrets, NULL);
- }
- if (con_agent_secrets) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, con_agent_secrets, NULL);
+ if ( setting_name
+ && g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION)) {
+ secrets_setting = g_variant_lookup_value (secrets, setting_name, NM_VARIANT_TYPE_SETTING);
+ if (!secrets_setting) {
+ /* The connection dictionary didn't contain any secrets for
+ * @setting_name; just return success.
+ */
+ return TRUE;
}
+ secrets = secrets_setting;
}
- /* Apply agent-owned secrets from the new connection so that
- * they can be sent to agents */
- if (new_agent_secrets) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_update_secrets (nm_settings_connection_get_connection (self),
- NULL,
- new_agent_secrets,
- NULL);
+ /* if @out_new_connection is provided, we don't modify @connection but clone
+ * and return it. Otherwise, we update @connection inplace. */
+ if (out_new_connection) {
+ nm_assert (!*out_new_connection);
+ connection = nm_simple_connection_new_clone (connection);
+ *out_new_connection = connection;
}
- nm_settings_connection_recheck_visibility (self);
-
- if ( replaced
- && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP)
- set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
- else
- set_persist_mode (self, persist_mode);
-
- if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY))
- _delete (self, NULL);
- else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED))
- nm_settings_connection_set_filename (self, NULL);
-
- g_signal_handlers_unblock_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
-
- _emit_updated (self, TRUE);
-
-out:
- if (local) {
- _LOGI ("write: failure to update connection: %s", local->message);
- g_propagate_error (error, local);
+ if (!nm_connection_update_secrets (connection,
+ setting_name,
+ secrets,
+ error))
return FALSE;
- }
- if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
- if (reread_connection)
- _LOGI ("write: successfully updated (%s), connection was modified in the process", logmsg_change);
- else if (new_connection)
- _LOGI ("write: successfully updated (%s)", logmsg_change);
- else
- _LOGI ("write: successfully committed (%s)", logmsg_change);
- }
return TRUE;
}
gboolean
-nm_settings_connection_delete (NMSettingsConnection *self,
+nm_settings_connection_update (NMSettingsConnection *self,
+ NMConnection *new_connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason,
+ const char *log_context_name,
GError **error)
{
- gs_unref_object NMSettingsConnection *self_keep_alive = NULL;
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
- NMConnection *for_agents;
- const char *connection_uuid;
-
g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
- self_keep_alive = g_object_ref (self);
-
- if (!_delete (self, error))
- return FALSE;
-
- set_visible (self, FALSE);
-
- /* Tell agents to remove secrets for this connection */
- for_agents = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
- nm_connection_clear_secrets (for_agents);
- nm_agent_manager_delete_secrets (priv->agent_mgr,
- nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
- for_agents);
- g_object_unref (for_agents);
-
- connection_uuid = nm_settings_connection_get_uuid (self);
-
- if (priv->kf_db_timestamps)
- nm_key_file_db_remove_key (priv->kf_db_timestamps, connection_uuid);
+ return nm_settings_update_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings,
+ self,
+ new_connection,
+ persist_mode,
+ sett_flags,
+ sett_mask,
+ update_reason,
+ log_context_name,
+ error);
+}
- if (priv->kf_db_seen_bssids)
- nm_key_file_db_remove_key (priv->kf_db_seen_bssids, connection_uuid);
+void
+nm_settings_connection_delete (NMSettingsConnection *self,
+ gboolean allow_add_to_no_auto_default)
+{
+ g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
- nm_settings_connection_signal_remove (self);
- return TRUE;
+ nm_settings_delete_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings,
+ self,
+ TRUE,
+ allow_add_to_no_auto_default);
}
/*****************************************************************************/
@@ -860,26 +787,37 @@ nm_settings_connection_new_secrets (NMSettingsConnection *self,
GVariant *secrets,
GError **error)
{
- if (!nm_settings_connection_has_unmodified_applied_connection (self, applied_connection,
- NM_SETTING_COMPARE_FLAG_NONE)) {
+ gs_unref_object NMConnection *new_connection = NULL;
+ NMConnection *connection;
+
+ if (!nm_settings_connection_has_unmodified_applied_connection (self,
+ applied_connection,
+ NM_SETTING_COMPARE_FLAG_NONE)) {
g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
"The connection was modified since activation");
return FALSE;
}
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- if (!nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, secrets, error))
- return FALSE;
+ connection = nm_settings_connection_get_connection (self);
- update_system_secrets_cache (self);
- update_agent_secrets_cache (self, NULL);
+ if (!_secrets_update (connection,
+ setting_name,
+ secrets,
+ &new_connection,
+ error))
+ return FALSE;
- nm_settings_connection_update (self,
- NULL,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "new-secrets",
- NULL);
+ if (!nm_settings_connection_update (self,
+ new_connection ?: connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
+ "new-secrets",
+ NULL))
+ nm_assert_not_reached ();
return TRUE;
}
@@ -901,8 +839,10 @@ get_secrets_done_cb (NMAgentManager *manager,
NMConnection *applied_connection;
gs_free_error GError *local = NULL;
gs_unref_variant GVariant *system_secrets = NULL;
+ gs_unref_object NMConnection *new_connection = NULL;
gboolean agent_had_system = FALSE;
ForEachSecretFlags cmp_flags = { NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_SECRET_FLAG_NONE };
+ gs_unref_variant GVariant *filtered_secrets = NULL;
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
@@ -965,66 +905,67 @@ get_secrets_done_cb (NMAgentManager *manager,
system_secrets = nm_g_variant_ref (priv->system_secrets);
- /* Update the connection with our existing secrets from backing storage */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (self));
-
- if ( !system_secrets
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- || nm_connection_update_secrets (nm_settings_connection_get_connection (self),
- setting_name,
- system_secrets,
- &local)) {
- gs_unref_variant GVariant *filtered_secrets = NULL;
-
- /* Update the connection with the agent's secrets; by this point if any
- * system-owned secrets exist in 'secrets' the agent that provided them
- * will have been authenticated, so those secrets can replace the existing
- * system secrets.
- */
- filtered_secrets = validate_secret_flags (nm_settings_connection_get_connection (self), secrets, &cmp_flags);
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- if (nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, filtered_secrets, &local)) {
- /* Now that all secrets are updated, copy and cache new secrets,
- * then save them to backing storage.
- */
- update_system_secrets_cache (self);
- update_agent_secrets_cache (self, NULL);
+ new_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
- /* Only save secrets to backing storage if the agent returned any
- * new system secrets. If it didn't, then the secrets are agent-
- * owned and there's no point to writing out the connection when
- * nothing has changed, since agent-owned secrets don't get saved here.
- */
- if (agent_had_system) {
- _LOGD ("(%s:%p) saving new secrets to backing storage",
- setting_name,
- call_id);
-
- nm_settings_connection_update (self,
- NULL,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "get-new-secrets",
- NULL);
- } else {
- _LOGD ("(%s:%p) new agent secrets processed",
- setting_name,
- call_id);
- }
+ nm_connection_clear_secrets (new_connection);
- } else {
- _LOGD ("(%s:%p) failed to update with agent secrets: %s",
- setting_name,
- call_id,
- local->message);
- }
- } else {
+ if (!_secrets_update (new_connection,
+ setting_name,
+ system_secrets,
+ NULL,
+ &local)) {
_LOGD ("(%s:%p) failed to update with existing secrets: %s",
setting_name,
call_id,
local->message);
}
+ /* Update the connection with the agent's secrets; by this point if any
+ * system-owned secrets exist in 'secrets' the agent that provided them
+ * will have been authenticated, so those secrets can replace the existing
+ * system secrets.
+ */
+ filtered_secrets = validate_secret_flags (new_connection, secrets, &cmp_flags);
+
+ if (!_secrets_update (new_connection,
+ setting_name,
+ filtered_secrets,
+ NULL,
+ &local)) {
+ _LOGD ("(%s:%p) failed to update with agent secrets: %s",
+ setting_name,
+ call_id,
+ local->message);
+ }
+
+ /* Only save secrets to backing storage if the agent returned any
+ * new system secrets. If it didn't, then the secrets are agent-
+ * owned and there's no point to writing out the connection when
+ * nothing has changed, since agent-owned secrets don't get saved here.
+ */
+ if (agent_had_system) {
+ _LOGD ("(%s:%p) saving new secrets to backing storage",
+ setting_name,
+ call_id);
+ } else {
+ _LOGD ("(%s:%p) new agent secrets processed",
+ setting_name,
+ call_id);
+ }
+ if (!nm_settings_connection_update (self,
+ new_connection,
+ agent_had_system
+ ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
+ : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
+ "get-new-secrets",
+ NULL))
+ nm_assert_not_reached ();
+
applied_connection = call_id->applied_connection;
if (applied_connection) {
get_cmp_flags (self,
@@ -1042,10 +983,10 @@ get_secrets_done_cb (NMAgentManager *manager,
if ( !system_secrets
|| nm_connection_update_secrets (applied_connection, setting_name, system_secrets, NULL)) {
- gs_unref_variant GVariant *filtered_secrets = NULL;
+ gs_unref_variant GVariant *filtered_secrets2 = NULL;
- filtered_secrets = validate_secret_flags (applied_connection, secrets, &cmp_flags);
- nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets, NULL);
+ filtered_secrets2 = validate_secret_flags (applied_connection, secrets, &cmp_flags);
+ nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets2, NULL);
}
}
@@ -1502,10 +1443,8 @@ update_auth_cb (NMSettingsConnection *self,
{
NMSettingsConnectionPrivate *priv;
UpdateInfo *info = data;
- NMSettingsConnectionCommitReason commit_reason;
gs_free_error GError *local = NULL;
NMSettingsConnectionPersistMode persist_mode;
- const char *log_diff_name;
if (error) {
update_complete (self, info, error);
@@ -1547,34 +1486,16 @@ update_auth_cb (NMSettingsConnection *self,
}
}
- commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION;
- if ( info->new_settings
- && !nm_streq0 (nm_connection_get_id (nm_settings_connection_get_connection (self)),
- nm_connection_get_id (info->new_settings)))
- commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED;
-
if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_TO_DISK))
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
- else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY))
- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY;
- else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED)) {
- persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
- ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED
- : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
- } else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) {
- persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
- ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY
- : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+ else if (NM_FLAGS_ANY (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY
+ | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED))
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
+ else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) {
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
} else
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
- if ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK
- || ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
- && !nm_settings_connection_get_unsaved (self)))
- log_diff_name = info->new_settings ? "update-settings" : "write-out-to-disk";
- else
- log_diff_name = info->new_settings ? "update-unsaved" : "make-unsaved";
-
if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)) {
nm_settings_connection_autoconnect_blocked_reason_set (self,
NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
@@ -1584,8 +1505,16 @@ update_auth_cb (NMSettingsConnection *self,
nm_settings_connection_update (self,
info->new_settings,
persist_mode,
- commit_reason,
- log_diff_name,
+ ( NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
+ ? NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
+ "update-from-dbus",
&local);
if (!local) {
@@ -1604,6 +1533,9 @@ update_auth_cb (NMSettingsConnection *self,
info->subject);
}
+ /* Reset auto retries back to default since connection was updated */
+ nm_settings_connection_autoconnect_retries_reset (self);
+
update_complete (self, info, local);
}
@@ -1839,7 +1771,6 @@ delete_auth_cb (NMSettingsConnection *self,
gpointer data)
{
gs_unref_object NMSettingsConnection *self_keep_alive = NULL;
- gs_free_error GError *local = NULL;
self_keep_alive = g_object_ref (self);
@@ -1850,15 +1781,11 @@ delete_auth_cb (NMSettingsConnection *self,
return;
}
- nm_settings_connection_delete (self, &local);
+ nm_settings_connection_delete (self, TRUE);
nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self,
- !local, NULL, subject, local ? local->message : NULL);
-
- if (local)
- g_dbus_method_invocation_return_gerror (context, local);
- else
- g_dbus_method_invocation_return_value (context, NULL);
+ TRUE, NULL, subject, NULL);
+ g_dbus_method_invocation_return_value (context, NULL);
}
static const char *
@@ -2006,26 +1933,13 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
return;
}
- /* Clear secrets in connection and caches */
-
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (nm_settings_connection_get_connection (self));
-
- nm_clear_pointer (&priv->system_secrets, g_variant_unref);
- nm_clear_pointer (&priv->agent_secrets, g_variant_unref);
+ nm_settings_connection_clear_secrets (self, TRUE, TRUE);
/* Tell agents to remove secrets for this connection */
nm_agent_manager_delete_secrets (priv->agent_mgr,
nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
nm_settings_connection_get_connection (self));
- nm_settings_connection_update (self,
- NULL,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "clear-secrets",
- &local);
-
nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
!local, NULL, subject, local ? local->message : NULL);
@@ -2066,40 +1980,28 @@ impl_settings_connection_clear_secrets (NMDBusObject *obj,
/*****************************************************************************/
void
-nm_settings_connection_added (NMSettingsConnection *self)
+_nm_settings_connection_emit_dbus_signal_updated (NMSettingsConnection *self)
{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- /* FIXME: we should always dispose connections that are removed
- * and not reuse them, but currently plugins keep alive unmanaged
- * (e.g. NM_CONTROLLED=no) connections. */
- priv->removed = FALSE;
+ nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+ &interface_info_settings_connection,
+ &signal_info_updated,
+ "()");
}
void
-nm_settings_connection_signal_remove (NMSettingsConnection *self)
+_nm_settings_connection_emit_dbus_signal_removed (NMSettingsConnection *self)
{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
- AuthData *auth_data;
-
- if (priv->removed)
- return;
- priv->removed = TRUE;
-
- while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst)))
- nm_auth_manager_check_authorization_cancel (auth_data->call_id);
-
nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
&interface_info_settings_connection,
&signal_info_removed,
"()");
- g_signal_emit (self, signals[REMOVED], 0);
}
-gboolean
-nm_settings_connection_get_unsaved (NMSettingsConnection *self)
+void
+_nm_settings_connection_emit_signal_updated_internal (NMSettingsConnection *self,
+ NMSettingsConnectionUpdateReason update_reason)
{
- return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED);
+ g_signal_emit (self, signals[UPDATED_INTERNAL], 0, (guint) update_reason);
}
/*****************************************************************************/
@@ -2121,14 +2023,6 @@ nm_settings_connection_get_flags (NMSettingsConnection *self)
}
NMSettingsConnectionIntFlags
-nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set)
-{
- return nm_settings_connection_set_flags_full (self,
- flags,
- set ? flags : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
-}
-
-NMSettingsConnectionIntFlags
nm_settings_connection_set_flags_full (NMSettingsConnection *self,
NMSettingsConnectionIntFlags mask,
NMSettingsConnectionIntFlags value)
@@ -2137,7 +2031,8 @@ nm_settings_connection_set_flags_full (NMSettingsConnection *self,
NMSettingsConnectionIntFlags old_flags;
g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
- nm_assert (mask && !NM_FLAGS_ANY (mask, ~NM_SETTINGS_CONNECTION_INT_FLAGS_ALL));
+
+ nm_assert (!NM_FLAGS_ANY (mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_ALL));
nm_assert (!NM_FLAGS_ANY (value, ~mask));
priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
@@ -2298,17 +2193,10 @@ nm_settings_connection_update_timestamp (NMSettingsConnection *self,
}
}
-/**
- * nm_settings_connection_read_and_fill_timestamp:
- * @self: the #NMSettingsConnection
- *
- * Retrieves timestamp of the connection's last usage from database file and
- * stores it into the connection private data.
- **/
void
-nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
- NMKeyFileDB *kf_db_timestamps,
- NMKeyFileDB *kf_db_seen_bssids)
+_nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
+ NMKeyFileDB *kf_db_timestamps,
+ NMKeyFileDB *kf_db_seen_bssids)
{
NMSettingsConnectionPrivate *priv;
const char *connection_uuid;
@@ -2625,46 +2513,6 @@ nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self)
/*****************************************************************************/
-gboolean
-nm_settings_connection_get_ready (NMSettingsConnection *self)
-{
- return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->ready;
-}
-
-void
-nm_settings_connection_set_ready (NMSettingsConnection *self,
- gboolean ready)
-{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- ready = !!ready;
- if (priv->ready != ready) {
- priv->ready = ready;
- _notify (self, PROP_READY);
- }
-}
-
-/**
- * nm_settings_connection_set_filename:
- * @self: an #NMSettingsConnection
- * @filename: @self's filename
- *
- * Called by a backend to sets the filename that @self is read
- * from/written to.
- */
-void
-nm_settings_connection_set_filename (NMSettingsConnection *self,
- const char *filename)
-{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
- if (g_strcmp0 (filename, priv->filename) != 0) {
- g_free (priv->filename);
- priv->filename = g_strdup (filename);
- _notify (self, PROP_FILENAME);
- }
-}
-
/**
* nm_settings_connection_get_filename:
* @self: an #NMSettingsConnection
@@ -2678,9 +2526,9 @@ nm_settings_connection_set_filename (NMSettingsConnection *self,
const char *
nm_settings_connection_get_filename (NMSettingsConnection *self)
{
- NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
- return priv->filename;
+ return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->filename;
}
const char *
@@ -2692,7 +2540,18 @@ nm_settings_connection_get_id (NMSettingsConnection *self)
const char *
nm_settings_connection_get_uuid (NMSettingsConnection *self)
{
- return nm_connection_get_uuid (nm_settings_connection_get_connection (self));
+ NMSettingsConnectionPrivate *priv;
+ const char *uuid;
+
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
+
+ priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+ uuid = nm_settings_storage_get_uuid (priv->storage);
+
+ nm_assert (uuid && nm_streq0 (uuid, nm_connection_get_uuid (nm_settings_connection_get_connection (self))));
+
+ return uuid;
}
const char *
@@ -2703,6 +2562,43 @@ nm_settings_connection_get_connection_type (NMSettingsConnection *self)
/*****************************************************************************/
+void
+_nm_settings_connection_cleanup_after_remove (NMSettingsConnection *self)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+ AuthData *auth_data;
+
+ while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst)))
+ nm_auth_manager_check_authorization_cancel (auth_data->call_id);
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+
+ switch (prop_id) {
+ case PROP_UNSAVED:
+ g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
+ break;
+ case PROP_FLAGS:
+ g_value_set_uint (value,
+ nm_settings_connection_get_flags (self) & _NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK);
+ break;
+ case PROP_FILENAME:
+ g_value_set_string (value, nm_settings_connection_get_filename (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+
static void
nm_settings_connection_init (NMSettingsConnection *self)
{
@@ -2713,33 +2609,19 @@ nm_settings_connection_init (NMSettingsConnection *self)
c_list_init (&self->_connections_lst);
- priv->ready = TRUE;
c_list_init (&priv->call_ids_lst_head);
c_list_init (&priv->auth_lst_head);
- priv->session_monitor = g_object_ref (nm_session_monitor_get ());
- priv->session_changed_id = g_signal_connect (priv->session_monitor,
- NM_SESSION_MONITOR_CHANGED,
- G_CALLBACK (session_changed_cb), self);
-
priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
+ priv->settings = g_object_ref (nm_settings_get ());
priv->autoconnect_retries = AUTOCONNECT_RETRIES_UNSET;
-
- priv->connection = nm_simple_connection_new ();
-
- g_signal_connect (priv->connection, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), self);
- g_signal_connect (priv->connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed_cb), self);
}
-static void
-constructed (GObject *object)
+NMSettingsConnection *
+nm_settings_connection_new (void)
{
- NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
-
- _LOGD ("constructed (%s)", G_OBJECT_TYPE_NAME (self));
-
- G_OBJECT_CLASS (nm_settings_connection_parent_class)->constructed (object);
+ return g_object_new (NM_TYPE_SETTINGS_CONNECTION, NULL);
}
static void
@@ -2751,6 +2633,8 @@ dispose (GObject *object)
_LOGD ("disposing");
+ nm_assert (!priv->default_wired_device);
+
nm_assert (c_list_is_empty (&self->_connections_lst));
nm_assert (c_list_is_empty (&priv->auth_lst_head));
@@ -2760,83 +2644,29 @@ dispose (GObject *object)
_get_secrets_cancel (self, call_id, TRUE);
}
- set_visible (self, FALSE);
-
- if (priv->connection) {
- /* Disconnect handlers.
- * connection_changed_cb() has to be disconnected *before* nm_connection_clear_secrets(),
- * because nm_connection_clear_secrets() emits NM_CONNECTION_CHANGED signal.
- */
- g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (secrets_cleared_cb), self);
- g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
-
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_clear_secrets (priv->connection);
- }
-
nm_clear_pointer (&priv->system_secrets, g_variant_unref);
nm_clear_pointer (&priv->agent_secrets, g_variant_unref);
g_clear_pointer (&priv->seen_bssids, g_hash_table_destroy);
- nm_clear_g_signal_handler (priv->session_monitor, &priv->session_changed_id);
- g_clear_object (&priv->session_monitor);
-
g_clear_object (&priv->agent_mgr);
g_clear_object (&priv->connection);
- g_clear_pointer (&priv->filename, g_free);
-
g_clear_pointer (&priv->kf_db_timestamps, nm_key_file_db_unref);
g_clear_pointer (&priv->kf_db_seen_bssids, nm_key_file_db_unref);
G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);
-}
-
-static void
-get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
-{
- NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
- switch (prop_id) {
- case PROP_UNSAVED:
- g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
- break;
- case PROP_READY:
- g_value_set_boolean (value, nm_settings_connection_get_ready (self));
- break;
- case PROP_FLAGS:
- g_value_set_uint (value,
- nm_settings_connection_get_flags (self) & NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK);
- break;
- case PROP_FILENAME:
- g_value_set_string (value, nm_settings_connection_get_filename (self));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
+ g_clear_object (&priv->storage);
-static void
-set_property (GObject *object, guint prop_id,
- const GValue *value, GParamSpec *pspec)
-{
- NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+ nm_clear_g_free (&priv->filename);
- switch (prop_id) {
- case PROP_FILENAME:
- /* construct-only */
- nm_settings_connection_set_filename (self, g_value_get_string (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ g_clear_object (&priv->settings);
}
+/*****************************************************************************/
+
static const GDBusSignalInfo signal_info_updated = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
"Updated",
);
@@ -2946,10 +2776,8 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_SETTINGS);
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings_connection);
- object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->get_property = get_property;
- object_class->set_property = set_property;
obj_properties[PROP_UNSAVED] =
g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "",
@@ -2957,12 +2785,6 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
- obj_properties[PROP_READY] =
- g_param_spec_boolean (NM_SETTINGS_CONNECTION_READY, "", "",
- TRUE,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
obj_properties[PROP_FLAGS] =
g_param_spec_uint (NM_SETTINGS_CONNECTION_FLAGS, "", "",
0, G_MAXUINT32, 0,
@@ -2972,29 +2794,20 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
obj_properties[PROP_FILENAME] =
g_param_spec_string (NM_SETTINGS_CONNECTION_FILENAME, "", "",
NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
- /* internal signal, with an argument (gboolean by_user). */
+ /* internal signal, with an argument (NMSettingsConnectionUpdateReason update_reason) as
+ * guint. */
signals[UPDATED_INTERNAL] =
g_signal_new (NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0, NULL, NULL,
- g_cclosure_marshal_VOID__BOOLEAN,
- G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
- signals[REMOVED] =
- g_signal_new (NM_SETTINGS_CONNECTION_REMOVED,
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
signals[FLAGS_CHANGED] =
g_signal_new (NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index 38e34d8fa5..80950513f8 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -21,11 +21,78 @@
#ifndef __NETWORKMANAGER_SETTINGS_CONNECTION_H__
#define __NETWORKMANAGER_SETTINGS_CONNECTION_H__
-#include <net/ethernet.h>
-
#include "nm-dbus-object.h"
#include "nm-connection.h"
+#include "nm-settings-storage.h"
+
+/*****************************************************************************/
+
+typedef enum {
+
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE = 0,
+
+ /* with persist-mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, and
+ * update tries to update the profile on disk (which can always fail).
+ * In some cases we want to ignore such failure and proceed. For example,
+ * when we receive secrets from a secret-agent, we want to update the connection
+ * at all cost and ignore failures to write them to disk. */
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE = (1u << 0),
+
+ /* When updating the profile, force renaming the file on disk. That matters
+ * only for keyfile plugin. Keyfile prefers a filename based on connection.id.
+ * When the connection.id changes we might want to rename the file on disk
+ * (that is, don't overwrite the existing file, but delete it and write it
+ * with the new name).
+ * This flag forces such rename. */
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME = (1u << 1),
+
+ /* Usually, changing a profile that is currently active does not immediately
+ * reapply the changes. The exception are connection.zone and connection.metered
+ * properties. When this flag is set, then these two properties are reapplied
+ * right away. */
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL = (1u << 2),
+
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS = (1u << 3),
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS = (1u << 4),
+
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS = (1u << 5),
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS = (1u << 6),
+
+ /* if a profile was greated as default-wired connection for a device, then
+ * when the user modifies it via D-Bus, the profile should become persisted
+ * to disk and it the purpose why the profile was created should be forgotten. */
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_DEFAULT_WIRED = (1u << 7),
+
+} NMSettingsConnectionUpdateReason;
+
+typedef enum {
+
+ /* if the profile is in-memory, update it in-memory and keep it.
+ * if the profile is on-disk, update it on-disk, and keep it. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+
+ /* persist to disk. If the profile is currenly in-memory, remove
+ * it from /run. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+
+ /* persist to /run (in-memory). If the profile is currently on disk,
+ * delete it from disk. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+
+ /* persist to /run (in-memory). If the profile is currently on disk,
+ * forget about it, but don't delete it from disk. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+
+ /* this only updates the connection in-memory. Note that "in-memory" above
+ * means to write to keyfile in /run. This really means to not notify the
+ * settings plugin about the change. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+
+} NMSettingsConnectionPersistMode;
+
+/*****************************************************************************/
+
#define NM_TYPE_SETTINGS_CONNECTION (nm_settings_connection_get_type ())
#define NM_SETTINGS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnection))
#define NM_SETTINGS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass))
@@ -33,7 +100,6 @@
#define NM_IS_SETTINGS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_CONNECTION))
#define NM_SETTINGS_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass))
-#define NM_SETTINGS_CONNECTION_REMOVED "removed"
#define NM_SETTINGS_CONNECTION_GET_SECRETS "get-secrets"
#define NM_SETTINGS_CONNECTION_CANCEL_SECRETS "cancel-secrets"
#define NM_SETTINGS_CONNECTION_UPDATED_INTERNAL "updated-internal"
@@ -44,9 +110,6 @@
#define NM_SETTINGS_CONNECTION_FLAGS "flags"
#define NM_SETTINGS_CONNECTION_FILENAME "filename"
-/* Internal properties */
-#define NM_SETTINGS_CONNECTION_READY "ready"
-
/**
* NMSettingsConnectionIntFlags:
* @NM_SETTINGS_CONNECTION_INT_FLAGS_NONE: no flag set
@@ -61,38 +124,37 @@
* currently active but cleanup on disconnect.
* See also #NM_SETTINGS_CONNECTION_FLAG_VOLATILE.
* @NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE: The connection is visible
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK: the entire enum is
+ * @_NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK: the entire enum is
* internal, however, parts of it is public API as #NMSettingsConnectionFlags.
* This mask, are the public flags.
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_ALL: special mask, for all known flags
+ * @_NM_SETTINGS_CONNECTION_INT_FLAGS_ALL: special mask, for all known flags
*
* #NMSettingsConnection flags.
**/
-typedef enum {
+typedef enum _NMSettingsConnectionIntFlags {
NM_SETTINGS_CONNECTION_INT_FLAGS_NONE = 0,
NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED = NM_SETTINGS_CONNECTION_FLAG_UNSAVED,
NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED = NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED,
NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE = NM_SETTINGS_CONNECTION_FLAG_VOLATILE,
- NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE = (1LL << 3),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE = 0x08,
- __NM_SETTINGS_CONNECTION_INT_FLAGS_LAST,
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_LAST,
- NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK = 0
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK = 0
| NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED
| NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
| NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
| 0,
- NM_SETTINGS_CONNECTION_INT_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_INT_FLAGS_LAST - 1) << 1) - 1,
-} NMSettingsConnectionIntFlags;
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK = 0
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ | 0,
-typedef enum {
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE = 0,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION = (1LL << 0),
- NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED = (1LL << 1),
-} NMSettingsConnectionCommitReason;
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_ALL = ((_NM_SETTINGS_CONNECTION_INT_FLAGS_LAST - 1) << 1) - 1,
+} NMSettingsConnectionIntFlags;
typedef enum {
NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE = 0,
@@ -106,7 +168,6 @@ typedef enum {
| NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS),
} NMSettingsAutoconnectBlockedReason;
-struct _NMSettingsConnectionCallId;
typedef struct _NMSettingsConnectionCallId NMSettingsConnectionCallId;
typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass;
@@ -115,27 +176,27 @@ struct _NMSettingsConnectionPrivate;
struct _NMSettingsConnection {
NMDBusObject parent;
- struct _NMSettingsConnectionPrivate *_priv;
CList _connections_lst;
+ struct _NMSettingsConnectionPrivate *_priv;
};
-struct _NMSettingsConnectionClass {
- NMDBusObjectClass parent;
+GType nm_settings_connection_get_type (void);
+
+NMSettingsConnection *nm_settings_connection_new (void);
- gboolean (*commit_changes) (NMSettingsConnection *self,
- NMConnection *new_connection,
- NMSettingsConnectionCommitReason commit_reason,
- NMConnection **out_reread_connection,
- char **out_logmsg_change,
- GError **error);
+NMConnection *nm_settings_connection_get_connection (NMSettingsConnection *self);
- gboolean (*delete) (NMSettingsConnection *self,
- GError **error);
-};
+void _nm_settings_connection_set_connection (NMSettingsConnection *self,
+ NMConnection *new_connection,
+ NMConnection **out_old_connection,
+ NMSettingsConnectionUpdateReason update_reason);
-GType nm_settings_connection_get_type (void);
+NMSettingsStorage *nm_settings_connection_get_storage (NMSettingsConnection *self);
-NMConnection *nm_settings_connection_get_connection (NMSettingsConnection *self);
+void _nm_settings_connection_set_storage (NMSettingsConnection *self,
+ NMSettingsStorage *storage);
+
+const char *nm_settings_connection_get_filename (NMSettingsConnection *self);
guint64 nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *self);
@@ -143,34 +204,18 @@ gboolean nm_settings_connection_has_unmodified_applied_connection (NMSettingsCon
NMConnection *applied_connection,
NMSettingCompareFlags compare_flage);
-typedef enum {
- NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
-
- /* like KEEP, but always clears the UNSAVED flag */
- NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
-
- /* unsaved, only sets the unsaved flag, but it doesn't touch
- * the NM_GENERATED nor VOLATILE flag. */
- NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED,
-
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY,
-} NMSettingsConnectionPersistMode;
-
-gboolean nm_settings_connection_update (NMSettingsConnection *self,
- NMConnection *new_connection,
- NMSettingsConnectionPersistMode persist_mode,
- NMSettingsConnectionCommitReason commit_reason,
- const char *log_diff_name,
- GError **error);
-
-gboolean nm_settings_connection_delete (NMSettingsConnection *self,
+gboolean nm_settings_connection_update (NMSettingsConnection *self,
+ NMConnection *new_connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason,
+ const char *log_context_name,
GError **error);
+void nm_settings_connection_delete (NMSettingsConnection *self,
+ gboolean allow_add_to_no_auto_default);
+
typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *self,
NMSettingsConnectionCallId *call_id,
const char *agent_username,
@@ -196,21 +241,44 @@ NMSettingsConnectionCallId *nm_settings_connection_get_secrets (NMSettingsConnec
void nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
NMSettingsConnectionCallId *call_id);
-void nm_settings_connection_recheck_visibility (NMSettingsConnection *self);
+void nm_settings_connection_clear_secrets (NMSettingsConnection *self,
+ gboolean clear_cached_system_secrets,
+ gboolean persist);
+
+gboolean nm_settings_connection_check_visibility (NMSettingsConnection *self,
+ NMSessionMonitor *session_monitor);
gboolean nm_settings_connection_check_permission (NMSettingsConnection *self,
const char *permission);
-void nm_settings_connection_added (NMSettingsConnection *self);
+/*****************************************************************************/
-void nm_settings_connection_signal_remove (NMSettingsConnection *self);
+NMDevice *nm_settings_connection_default_wired_get_device (NMSettingsConnection *self);
+void nm_settings_connection_default_wired_set_device (NMSettingsConnection *self,
+ NMDevice *device);
-gboolean nm_settings_connection_get_unsaved (NMSettingsConnection *self);
+/*****************************************************************************/
NMSettingsConnectionIntFlags nm_settings_connection_get_flags (NMSettingsConnection *self);
-NMSettingsConnectionIntFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set);
+
+static inline gboolean
+nm_settings_connection_get_unsaved (NMSettingsConnection *self)
+{
+ return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED);
+}
+
NMSettingsConnectionIntFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionIntFlags mask, NMSettingsConnectionIntFlags value);
+static inline NMSettingsConnectionIntFlags
+nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set)
+{
+ return nm_settings_connection_set_flags_full (self,
+ flags,
+ set ? flags : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
+}
+
+/*****************************************************************************/
+
int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab);
int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data);
int nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b);
@@ -218,9 +286,9 @@ int nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer p
struct _NMKeyFileDB;
-void nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
- struct _NMKeyFileDB *kf_db_timestamps,
- struct _NMKeyFileDB *kf_db_seen_bssids);
+void _nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
+ struct _NMKeyFileDB *kf_db_timestamps,
+ struct _NMKeyFileDB *kf_db_seen_bssids);
gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self,
guint64 *out_timestamp);
@@ -259,14 +327,6 @@ nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *sel
gboolean nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self);
-gboolean nm_settings_connection_get_ready (NMSettingsConnection *self);
-void nm_settings_connection_set_ready (NMSettingsConnection *self,
- gboolean ready);
-
-void nm_settings_connection_set_filename (NMSettingsConnection *self,
- const char *filename);
-const char *nm_settings_connection_get_filename (NMSettingsConnection *self);
-
const char *nm_settings_connection_get_id (NMSettingsConnection *connection);
const char *nm_settings_connection_get_uuid (NMSettingsConnection *connection);
const char *nm_settings_connection_get_connection_type (NMSettingsConnection *connection);
@@ -276,4 +336,14 @@ const char *nm_settings_connection_get_connection_type (NMSettingsConnection *co
NMConnection **nm_settings_connections_array_to_connections (NMSettingsConnection *const*connections,
gssize n_connections);
+/*****************************************************************************/
+
+void _nm_settings_connection_emit_dbus_signal_updated (NMSettingsConnection *self);
+void _nm_settings_connection_emit_dbus_signal_removed (NMSettingsConnection *self);
+
+void _nm_settings_connection_emit_signal_updated_internal (NMSettingsConnection *self,
+ NMSettingsConnectionUpdateReason update_reason);
+
+void _nm_settings_connection_cleanup_after_remove (NMSettingsConnection *self);
+
#endif /* __NETWORKMANAGER_SETTINGS_CONNECTION_H__ */
diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c
index 81a168b24d..e64cc84f64 100644
--- a/src/settings/nm-settings-plugin.c
+++ b/src/settings/nm-settings-plugin.c
@@ -22,12 +22,14 @@
#include "nm-settings-plugin.h"
+#include "nm-utils.h"
+#include "nm-core-internal.h"
+
#include "nm-settings-connection.h"
/*****************************************************************************/
enum {
- CONNECTION_ADDED,
UNMANAGED_SPECS_CHANGED,
UNRECOGNIZED_SPECS_CHANGED,
@@ -41,104 +43,173 @@ G_DEFINE_TYPE (NMSettingsPlugin, nm_settings_plugin, G_TYPE_OBJECT)
/*****************************************************************************/
GSList *
-nm_settings_plugin_get_connections (NMSettingsPlugin *self)
+nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self)
{
+ NMSettingsPluginClass *klass;
+
g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_connections)
- return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_connections (self);
- return NULL;
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+ if (!klass->get_unmanaged_specs)
+ return NULL;
+ return klass->get_unmanaged_specs (self);
}
-gboolean
-nm_settings_plugin_load_connection (NMSettingsPlugin *self,
- const char *filename)
+GSList *
+nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self)
{
- g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
+ NMSettingsPluginClass *klass;
+
+ g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection)
- return NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection (self, filename);
- return FALSE;
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+ if (!klass->get_unrecognized_specs)
+ return NULL;
+ return klass->get_unrecognized_specs (self);
}
void
-nm_settings_plugin_reload_connections (NMSettingsPlugin *self)
+nm_settings_plugin_reload_connections (NMSettingsPlugin *self,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data)
{
+ NMSettingsPluginClass *klass;
+
g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self));
+ g_return_if_fail (callback);
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->reload_connections)
- NM_SETTINGS_PLUGIN_GET_CLASS (self)->reload_connections (self);
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+ if (klass->reload_connections)
+ klass->reload_connections (self, callback, user_data);
}
-GSList *
-nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self)
+gboolean
+nm_settings_plugin_load_connection (NMSettingsPlugin *self,
+ const char *filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ GError **error)
{
- g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
-
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs)
- return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs (self);
- return NULL;
-}
+ NMSettingsPluginClass *klass;
-GSList *
-nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self)
-{
- g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
+ g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
+ g_return_val_if_fail (filename && filename[0] == '/', FALSE);
+ g_return_val_if_fail (out_storage && !*out_storage, FALSE);
+ g_return_val_if_fail (out_connection && !*out_connection, FALSE);
+ g_return_val_if_fail (out_storage_replaced && !*out_storage_replaced, FALSE);
- if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs)
- return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs (self);
- return NULL;
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+ if (!klass->load_connection) {
+ g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
+ "settings plugin does not support loading connection");
+ return FALSE;
+ }
+ return klass->load_connection (self, filename, out_storage, out_connection, out_storage_replaced, error);
}
-/**
- * nm_settings_plugin_add_connection:
- * @self: the #NMSettingsPlugin
- * @connection: the source connection to create a plugin-specific
- * #NMSettingsConnection from
- * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to
- * not save to disk
- * @error: on return, a location to store any errors that may occur
- *
- * Creates a new #NMSettingsConnection for the given source @connection. If the
- * plugin cannot handle the given connection type, it should return %NULL and
- * set @error. The plugin owns the returned object and the caller must reference
- * the object if it wishes to continue using it.
- *
- * Returns: the new #NMSettingsConnection or %NULL
- */
-NMSettingsConnection *
+gboolean
nm_settings_plugin_add_connection (NMSettingsPlugin *self,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
GError **error)
{
NMSettingsPluginClass *klass;
- g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
- g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+#if NM_MORE_ASSERTS > 5
+ nm_assert (nm_connection_verify (connection, NULL));
+#endif
+
+ NM_SET_OUT (out_storage, NULL);
+ NM_SET_OUT (out_connection, NULL);
klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
if (!klass->add_connection) {
g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
- "Plugin does not support adding connections");
- return NULL;
+ "settings plugin does not support adding connections");
+ return FALSE;
}
-
- return klass->add_connection (self, connection, save_to_disk, error);
+ return klass->add_connection (self,
+ connection,
+ out_storage,
+ out_connection,
+ error);
}
-/*****************************************************************************/
+gboolean
+nm_settings_plugin_update_connection (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
+{
+ NMSettingsPluginClass *klass = NULL;
-void
-_nm_settings_plugin_emit_signal_connection_added (NMSettingsPlugin *self,
- NMSettingsConnection *sett_conn)
+ g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), FALSE);
+ g_return_val_if_fail (nm_settings_storage_get_plugin (storage) == self, FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+#if NM_MORE_ASSERTS > 5
+ nm_assert (nm_connection_verify (connection, NULL));
+ nm_assert (nm_streq (nm_connection_get_uuid (connection), nm_settings_storage_get_uuid (storage)));
+#endif
+
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+
+ NM_SET_OUT (out_storage, NULL);
+ NM_SET_OUT (out_connection, NULL);
+
+ if (!klass->update_connection) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_NOT_SUPPORTED,
+ "settings plugin does not support modifying connections");
+ return FALSE;
+ }
+ return klass->update_connection (self,
+ storage,
+ connection,
+ out_storage,
+ out_connection,
+ error);
+}
+
+gboolean
+nm_settings_plugin_delete_connection (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ gboolean remove_from_disk,
+ GError **error)
{
- nm_assert (NM_IS_SETTINGS_PLUGIN (self));
- nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+ NMSettingsPluginClass *klass = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), FALSE);
+ g_return_val_if_fail (nm_settings_storage_get_plugin (storage) == self, FALSE);
+
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+
+ if (!klass->delete_connection) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_NOT_SUPPORTED,
+ "settings plugin does not support deleting connections");
+ return FALSE;
+ }
- g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn);
+ return klass->delete_connection (self,
+ storage,
+ remove_from_disk,
+ error);
}
+/*****************************************************************************/
+
void
_nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self)
{
@@ -167,15 +238,6 @@ nm_settings_plugin_class_init (NMSettingsPluginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- signals[CONNECTION_ADDED] =
- g_signal_new (NM_SETTINGS_PLUGIN_CONNECTION_ADDED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE, 1,
- NM_TYPE_SETTINGS_CONNECTION);
-
signals[UNMANAGED_SPECS_CHANGED] =
g_signal_new (NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
G_OBJECT_CLASS_TYPE (object_class),
diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h
index 11b859978a..a214d7c026 100644
--- a/src/settings/nm-settings-plugin.h
+++ b/src/settings/nm-settings-plugin.h
@@ -23,6 +23,15 @@
#include "nm-connection.h"
+#include "nm-settings-storage.h"
+
+typedef struct _NMSettingsPlugin NMSettingsPlugin;
+
+typedef void (*NMSettingsPluginConnectionReloadCallback) (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ gpointer user_data);
+
#define NM_TYPE_SETTINGS_PLUGIN (nm_settings_plugin_get_type ())
#define NM_SETTINGS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPlugin))
#define NM_SETTINGS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPluginClass))
@@ -32,32 +41,14 @@
#define NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED "unmanaged-specs-changed"
#define NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED "unrecognized-specs-changed"
-#define NM_SETTINGS_PLUGIN_CONNECTION_ADDED "connection-added"
-typedef struct {
+struct _NMSettingsPlugin {
GObject parent;
-} NMSettingsPlugin;
+};
typedef struct {
GObjectClass parent;
- /* Returns a GSList of NMSettingsConnection objects that represent
- * connections the plugin knows about. The returned list is freed by the
- * system settings service.
- */
- GSList * (*get_connections) (NMSettingsPlugin *plugin);
-
- /* Requests that the plugin load/reload a single connection, if it
- * recognizes the filename. Returns success or failure.
- */
- gboolean (*load_connection) (NMSettingsPlugin *plugin,
- const char *filename);
-
- /* Requests that the plugin reload all connection files from disk,
- * and emit signals reflecting new, changed, and removed connections.
- */
- void (*reload_connections) (NMSettingsPlugin *plugin);
-
/*
* Return a string list of specifications of devices which NetworkManager
* should not manage. Returned list will be freed by the system settings
@@ -67,7 +58,7 @@ typedef struct {
* Each string in the list must be in one of the formats recognized by
* nm_device_spec_match_list().
*/
- GSList * (*get_unmanaged_specs) (NMSettingsPlugin *plugin);
+ GSList * (*get_unmanaged_specs) (NMSettingsPlugin *self);
/*
* Return a string list of specifications of devices for which at least
@@ -79,49 +70,129 @@ typedef struct {
* Each string in the list must be in one of the formats recognized by
* nm_device_spec_match_list().
*/
- GSList * (*get_unrecognized_specs) (NMSettingsPlugin *plugin);
+ GSList * (*get_unrecognized_specs) (NMSettingsPlugin *self);
- /*
- * Initialize the plugin-specific connection and return a new
- * NMSettingsConnection subclass that contains the same settings as the
- * original connection. The connection should only be saved to backing
- * storage if @save_to_disk is TRUE. The returned object is owned by the
- * plugin and must be referenced by the owner if necessary.
+ /* Requests that the plugin load/reload a single connection, if it
+ * recognizes the filename. Returns success or failure.
+ */
+ gboolean (*load_connection) (NMSettingsPlugin *self,
+ const char *filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ GError **error);
+
+ /* Requests that the plugin reload all connection files from disk,
+ * and emit signals reflecting new, changed, and removed connections.
*/
- NMSettingsConnection * (*add_connection) (NMSettingsPlugin *plugin,
- NMConnection *connection,
- gboolean save_to_disk,
- GError **error);
+ void (*reload_connections) (NMSettingsPlugin *self,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data);
+
+ gboolean (*add_connection) (NMSettingsPlugin *self,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+ gboolean (*update_connection) (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+ gboolean (*delete_connection) (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ gboolean remove_from_disk,
+ GError **error);
+
+ const char *plugin_name;
+
} NMSettingsPluginClass;
+/*****************************************************************************/
+
GType nm_settings_plugin_get_type (void);
-typedef NMSettingsPlugin *(*NMSettingsPluginFactoryFunc) (void);
+/*****************************************************************************/
-/* Plugin's factory function that returns a #NMSettingsPlugin */
-NMSettingsPlugin *nm_settings_plugin_factory (void);
+#define NM_SETTINGS_STORAGE_PRINT_FMT \
+ NM_HASH_OBFUSCATE_PTR_FMT"/%s"
+
+#define NM_SETTINGS_STORAGE_PRINT_ARG(storage) \
+ NM_HASH_OBFUSCATE_PTR (storage), \
+ nm_settings_plugin_get_plugin_name (nm_settings_storage_get_plugin (storage))
+
+static inline const char *
+nm_settings_plugin_get_plugin_name (NMSettingsPlugin *self)
+{
+ NMSettingsPluginClass *klass;
+
+ nm_assert (NM_SETTINGS_PLUGIN (self));
+
+ klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+
+ nm_assert (klass && klass->plugin_name && strlen (klass->plugin_name) > 0);
+
+ return klass->plugin_name;
+}
-GSList *nm_settings_plugin_get_connections (NMSettingsPlugin *plugin);
+/*****************************************************************************/
-gboolean nm_settings_plugin_load_connection (NMSettingsPlugin *plugin,
- const char *filename);
-void nm_settings_plugin_reload_connections (NMSettingsPlugin *plugin);
+GSList *nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self);
+GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self);
+
+void nm_settings_plugin_reload_connections (NMSettingsPlugin *self,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data);
+
+gboolean nm_settings_plugin_load_connection (NMSettingsPlugin *self,
+ const char *filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ GError **error);
+
+gboolean nm_settings_plugin_add_connection (NMSettingsPlugin *self,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+gboolean nm_settings_plugin_update_connection (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+gboolean nm_settings_plugin_delete_connection (NMSettingsPlugin *self,
+ NMSettingsStorage *storage,
+ gboolean remove_from_disk,
+ GError **error);
+
+/*****************************************************************************/
+
+typedef NMSettingsPlugin *(*NMSettingsPluginFactoryFunc) (void);
+
+NMSettingsPlugin *nm_settings_plugin_factory (void);
-GSList *nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *plugin);
-GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *plugin);
+/*****************************************************************************
+ * Internal API
+ *****************************************************************************/
-NMSettingsConnection *nm_settings_plugin_add_connection (NMSettingsPlugin *plugin,
- NMConnection *connection,
- gboolean save_to_disk,
- GError **error);
+void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self);
-/* internal API */
+void _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *self);
-void _nm_settings_plugin_emit_signal_connection_added (NMSettingsPlugin *plugin,
- NMSettingsConnection *sett_conn);
+/*****************************************************************************/
-void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *plugin);
+/* forward declare this function from NMSettings. It's used by the ifcfg-rh plugin,
+ * but that shouldn't include all "nm-settings.h" header. */
+NMSettings *nm_settings_get (void);
-void _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *plugin);
+const char *nm_settings_get_dbus_path_for_uuid (NMSettings *self,
+ const char *uuid);
#endif /* __NM_SETTINGS_PLUGIN_H__ */
diff --git a/src/settings/nm-settings-storage.c b/src/settings/nm-settings-storage.c
new file mode 100644
index 0000000000..d07da2ca39
--- /dev/null
+++ b/src/settings/nm-settings-storage.c
@@ -0,0 +1,242 @@
+/*
+ * 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 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-settings-storage.h"
+
+#include "nm-utils.h"
+#include "nm-settings-plugin.h"
+
+#include "settings/plugins/keyfile/nms-keyfile-storage.h"
+
+/*****************************************************************************/
+
+int
+nm_settings_storage_cmp (NMSettingsStorage *a,
+ NMSettingsStorage *b,
+ const GSList *plugin_list)
+{
+ NMSettingsStorageClass *klass;
+ NMSettingsPlugin *plugin_a;
+ NMSettingsPlugin *plugin_b;
+
+ /* Sort by priority.
+ *
+ * If a > b (by priority), we return a positive number (as one
+ * would expect by a cmp() function). */
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (a));
+ nm_assert (NM_IS_SETTINGS_STORAGE (b));
+ nm_assert (a != b);
+ nm_assert (nm_streq (nm_settings_storage_get_uuid (a), nm_settings_storage_get_uuid (b)));
+
+ /* in-memory has always higher priority */
+ NM_CMP_DIRECT (nm_settings_storage_is_in_memory (a),
+ nm_settings_storage_is_in_memory (b));
+
+ plugin_a = nm_settings_storage_get_plugin (a);
+ plugin_b = nm_settings_storage_get_plugin (b);
+
+ if (plugin_a != plugin_b) {
+ int idx_a = g_slist_index ((GSList *) plugin_list, plugin_a);
+ int idx_b = g_slist_index ((GSList *) plugin_list, plugin_b);
+
+ nm_assert (idx_a >= 0);
+ nm_assert (idx_b >= 0);
+ nm_assert (idx_a != idx_b);
+
+ /* plugins that appear first in @plugin_list have higher priority.
+ * That means: smaller index -> higher priority. Reverse sort. */
+ NM_CMP_DIRECT (idx_b, idx_a);
+
+ /* undecided. We really don't expect unknown plugins here. */
+ return 0;
+ }
+
+ /* a hidden storage (tombstone) has higher priority... */
+ NM_CMP_DIRECT (nm_settings_storage_is_tombstone (a),
+ nm_settings_storage_is_tombstone (b));
+
+ klass = NM_SETTINGS_STORAGE_GET_CLASS (a);
+
+ if (klass != NM_SETTINGS_STORAGE_GET_CLASS (b)) {
+ nm_assert_not_reached ();
+ return 0;
+ }
+
+ if (klass->storage_cmp)
+ NM_CMP_RETURN (klass->storage_cmp (a, b));
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsStorage,
+ PROP_PLUGIN,
+ PROP_UUID,
+ PROP_FILENAME,
+);
+
+G_DEFINE_TYPE (NMSettingsStorage, nm_settings_storage, G_TYPE_OBJECT)
+
+/*****************************************************************************/
+
+void
+_nm_settings_storage_set_filename (NMSettingsStorage *self,
+ const char *filename)
+{
+ g_return_if_fail (NM_IS_SETTINGS_STORAGE (self));
+
+ if (nm_streq0 (self->_filename, filename))
+ return;
+
+ g_free (self->_filename);
+ self->_filename = g_strdup (filename);
+ _notify (self, PROP_FILENAME);
+}
+
+void
+_nm_settings_storage_set_filename_take (NMSettingsStorage *self,
+ char *filename)
+{
+ g_return_if_fail (NM_IS_SETTINGS_STORAGE (self));
+
+ if (nm_streq0 (self->_filename, filename)) {
+ g_free (filename);
+ return;
+ }
+
+ g_free (self->_filename);
+ self->_filename = filename;
+ _notify (self, PROP_FILENAME);
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingsStorage *self = NM_SETTINGS_STORAGE (object);
+
+ switch (prop_id) {
+ case PROP_FILENAME:
+ g_value_set_string (value, nm_settings_storage_get_filename (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingsStorage *self = NM_SETTINGS_STORAGE (object);
+
+ switch (prop_id) {
+ case PROP_PLUGIN:
+ /* construct-only */
+ self->_plugin = g_object_ref (g_value_get_object (value));
+ nm_assert (NM_IS_SETTINGS_PLUGIN (self->_plugin));
+ break;
+ case PROP_UUID:
+ /* construct-only */
+ self->_uuid = g_value_dup_string (value);
+ nm_assert (nm_utils_is_uuid (self->_uuid));
+ break;
+ case PROP_FILENAME:
+ /* construct-only */
+ self->_filename = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+
+static void
+nm_settings_storage_init (NMSettingsStorage *self)
+{
+}
+
+NMSettingsStorage *
+nm_settings_storage_new (NMSettingsPlugin *plugin,
+ const char *uuid,
+ const char *filename)
+{
+ nm_assert (NM_IS_SETTINGS_PLUGIN (plugin));
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ return g_object_new (NM_TYPE_SETTINGS_STORAGE,
+ NM_SETTINGS_STORAGE_PLUGIN, plugin,
+ NM_SETTINGS_STORAGE_UUID, uuid,
+ NM_SETTINGS_STORAGE_FILENAME, filename,
+ NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingsStorage *self = NM_SETTINGS_STORAGE (object);
+
+ g_object_unref (self->_plugin);
+ g_free (self->_uuid);
+ g_free (self->_filename);
+
+ G_OBJECT_CLASS (nm_settings_storage_parent_class)->finalize (object);
+}
+
+static void
+nm_settings_storage_class_init (NMSettingsStorageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->finalize = finalize;
+
+ obj_properties[PROP_PLUGIN] =
+ g_param_spec_object (NM_SETTINGS_STORAGE_PLUGIN, "", "",
+ NM_TYPE_SETTINGS_PLUGIN,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_UUID] =
+ g_param_spec_string (NM_SETTINGS_STORAGE_UUID, "", "",
+ NULL,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_FILENAME] =
+ g_param_spec_string (NM_SETTINGS_STORAGE_FILENAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+}
diff --git a/src/settings/nm-settings-storage.h b/src/settings/nm-settings-storage.h
new file mode 100644
index 0000000000..7164a15753
--- /dev/null
+++ b/src/settings/nm-settings-storage.h
@@ -0,0 +1,117 @@
+/*
+ * 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 2018 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTINGS_STORAGE_H__
+#define __NM_SETTINGS_STORAGE_H__
+
+/*****************************************************************************/
+
+#define NM_TYPE_SETTINGS_STORAGE (nm_settings_storage_get_type ())
+#define NM_SETTINGS_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorage))
+#define NM_SETTINGS_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorageClass))
+#define NM_IS_SETTINGS_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTINGS_STORAGE))
+#define NM_IS_SETTINGS_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_STORAGE))
+#define NM_SETTINGS_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorageClass))
+
+#define NM_SETTINGS_STORAGE_PLUGIN "plugin"
+#define NM_SETTINGS_STORAGE_UUID "uuid"
+#define NM_SETTINGS_STORAGE_FILENAME "filename"
+
+struct _NMSettingsPlugin;
+
+typedef struct NMSettingsStorage {
+ GObject parent;
+ struct _NMSettingsPlugin *_plugin;
+ char *_uuid;
+ char *_filename;
+} NMSettingsStorage;
+
+typedef struct {
+ GObjectClass parent;
+
+ int (*storage_cmp) (NMSettingsStorage *a,
+ NMSettingsStorage *b);
+
+} NMSettingsStorageClass;
+
+GType nm_settings_storage_get_type (void);
+
+NMSettingsStorage *nm_settings_storage_new (struct _NMSettingsPlugin *plugin,
+ const char *uuid,
+ const char *filename);
+
+static inline struct _NMSettingsPlugin *
+nm_settings_storage_get_plugin (NMSettingsStorage *self)
+{
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL);
+
+ return self->_plugin;
+}
+
+static inline const char *
+nm_settings_storage_get_uuid (NMSettingsStorage *self)
+{
+ gboolean nm_utils_is_uuid (const char *str);
+
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL);
+
+ nm_assert (nm_utils_is_uuid (self->_uuid));
+ return self->_uuid;
+}
+
+static inline const char *
+nm_settings_storage_get_filename (NMSettingsStorage *self)
+{
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL);
+
+ return self->_filename;
+}
+
+void _nm_settings_storage_set_filename (NMSettingsStorage *self,
+ const char *filename);
+void _nm_settings_storage_set_filename_take (NMSettingsStorage *self,
+ char *filename);
+
+static inline gboolean
+nm_settings_storage_is_tombstone (NMSettingsStorage *self)
+{
+ /* Tombstones are intended to hide a UUID. This is not yet implemented,
+ * nor used ATM. Maybe TODO, maybe this concept can be dropped... */
+ return FALSE;
+}
+
+/*****************************************************************************/
+
+#define nm_assert_valid_settings_storage(plugin, storage) \
+ G_STMT_START { \
+ NMSettingsPlugin *const _plugin = (plugin); \
+ NMSettingsStorage *const _storage = (storage); \
+ \
+ nm_assert (!_plugin || NM_IS_SETTINGS_PLUGIN (_plugin)); \
+ nm_assert (NM_IS_SETTINGS_STORAGE (_storage)); \
+ nm_assert (!_plugin || nm_settings_storage_get_plugin (_storage) == _plugin); \
+ } G_STMT_END
+
+/*****************************************************************************/
+
+int nm_settings_storage_cmp (NMSettingsStorage *sd_a,
+ NMSettingsStorage *sd_b,
+ const GSList *plugin_list);
+
+#endif /* __NM_SETTINGS_STORAGE_H__ */
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 6fbda5274b..3086601fad 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -60,6 +60,7 @@
#include "nm-utils.h"
#include "nm-core-internal.h"
+#include "nm-std-aux/c-list-util.h"
#include "nm-glib-aux/nm-c-list.h"
#include "nm-dbus-object.h"
#include "devices/nm-device-ethernet.h"
@@ -70,6 +71,7 @@
#include "nm-auth-subject.h"
#include "nm-session-monitor.h"
#include "plugins/keyfile/nms-keyfile-plugin.h"
+#include "plugins/keyfile/nms-keyfile-storage.h"
#include "nm-agent-manager.h"
#include "nm-config.h"
#include "nm-audit-manager.h"
@@ -79,15 +81,154 @@
/*****************************************************************************/
-#define EXPORT(sym) void * __export_##sym = &sym;
+static NM_CACHED_QUARK_FCN ("default-wired-connection", _default_wired_connection_quark)
+
+/*****************************************************************************/
+
+typedef struct _StorageData {
+ CList sd_lst;
+ NMSettingsStorage *storage;
+ NMConnection *connection;
+ bool prioritize:1;
+} StorageData;
+
+static StorageData *
+_storage_data_new_stale (NMSettingsStorage *storage,
+ NMConnection *connection)
+{
+ StorageData *sd;
+
+ sd = g_slice_new (StorageData);
+ sd->storage = g_object_ref (storage);
+ sd->connection = nm_g_object_ref (connection);
+ return sd;
+}
+
+static void
+_storage_data_destroy (StorageData *sd)
+{
+ c_list_unlink_stale (&sd->sd_lst);
+ g_object_unref (sd->storage);
+ nm_g_object_unref (sd->connection);
+ g_slice_free (StorageData, sd);
+}
+
+static StorageData *
+_storage_data_find_in_lst (CList *head,
+ NMSettingsStorage *storage)
+{
+ StorageData *sd;
+
+ nm_assert (head);
+ nm_assert (NM_IS_SETTINGS_STORAGE (storage));
+
+ c_list_for_each_entry (sd, head, sd_lst) {
+ if (sd->storage == storage)
+ return sd;
+ }
+ return NULL;
+}
+
+static void
+nm_assert_storage_data_lst (CList *head)
+{
+#if NM_MORE_ASSERTS > 5
+ const char *uuid = NULL;
+ StorageData *sd;
+ CList *iter;
+
+ nm_assert (head);
+
+ if (c_list_is_empty (head))
+ return;
+
+ c_list_for_each_entry (sd, head, sd_lst) {
+ const char *u;
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (sd->storage));
+ nm_assert (!sd->connection || NM_IS_CONNECTION (sd->connection));
+ u = nm_settings_storage_get_uuid (sd->storage);
+ if (!uuid) {
+ uuid = u;
+ nm_assert (nm_utils_is_uuid (uuid));
+ } else
+ nm_assert (nm_streq0 (uuid, u));
+ }
+
+ /* assert that all storages are unique. */
+ c_list_for_each_entry (sd, head, sd_lst) {
+ for (iter = sd->sd_lst.next; iter != head; iter = iter->next)
+ nm_assert (c_list_entry (iter, StorageData, sd_lst)->storage != sd->storage);
+ }
+#endif
+}
-EXPORT(nm_settings_connection_get_type)
-EXPORT(nm_settings_connection_update)
+static gboolean
+_storage_data_is_alive (StorageData *sd)
+{
+ if (sd->connection)
+ return TRUE;
+
+ if (nm_settings_storage_is_tombstone (sd->storage)) {
+ /* entry does not have a profile, but it's here as a tombstone to
+ * hide/shadow other connections. That's also relevant. */
+ return TRUE;
+ }
+
+ return FALSE;
+}
/*****************************************************************************/
-static NM_CACHED_QUARK_FCN ("default-wired-connection", _default_wired_connection_quark)
-static NM_CACHED_QUARK_FCN ("default-wired-device", _default_wired_device_quark)
+typedef struct {
+ const char *uuid;
+ NMSettingsConnection *sett_conn;
+ NMSettingsStorage *storage;
+ CList sd_lst_head;
+ CList dirty_sd_lst_head;
+
+ CList sce_dirty_lst;
+
+ char _uuid_data[];
+} SettConnEntry;
+
+static SettConnEntry *
+_sett_conn_entry_new (const char *uuid)
+{
+ SettConnEntry *sett_conn_entry;
+ gsize l_p_1;
+
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ l_p_1 = strlen (uuid) + 1;
+
+ sett_conn_entry = g_malloc (sizeof (SettConnEntry) + l_p_1);
+ sett_conn_entry->uuid = sett_conn_entry->_uuid_data;
+ sett_conn_entry->sett_conn = NULL;
+ sett_conn_entry->storage = NULL;
+ c_list_init (&sett_conn_entry->sd_lst_head);
+ c_list_init (&sett_conn_entry->dirty_sd_lst_head);
+ c_list_init (&sett_conn_entry->sce_dirty_lst);
+ memcpy (sett_conn_entry->_uuid_data, uuid, l_p_1);
+ return sett_conn_entry;
+}
+
+static void
+_sett_conn_entry_free (SettConnEntry *sett_conn_entry)
+{
+ c_list_unlink_stale (&sett_conn_entry->sce_dirty_lst);
+ nm_c_list_free_all (&sett_conn_entry->sd_lst_head, StorageData, sd_lst, _storage_data_destroy);
+ nm_c_list_free_all (&sett_conn_entry->dirty_sd_lst_head, StorageData, sd_lst, _storage_data_destroy);
+ nm_g_object_unref (sett_conn_entry->sett_conn);
+ nm_g_object_unref (sett_conn_entry->storage);
+ g_free (sett_conn_entry);
+}
+
+static NMSettingsConnection *
+_sett_conn_entry_get_conn (SettConnEntry *sett_conn_entry)
+{
+ return sett_conn_entry ? sett_conn_entry->sett_conn : NULL;
+}
/*****************************************************************************/
@@ -114,8 +255,12 @@ typedef struct {
NMConfig *config;
+ NMPlatform *platform;
+
NMHostnameManager *hostname_manager;
+ NMSessionMonitor *session_monitor;
+
CList auth_lst_head;
NMSKeyfilePlugin *keyfile_plugin;
@@ -125,6 +270,10 @@ typedef struct {
NMKeyFileDB *kf_db_timestamps;
NMKeyFileDB *kf_db_seen_bssids;
+ GHashTable *sce_idx;
+
+ CList sce_dirty_lst_head;
+
CList connections_lst_head;
NMSettingsConnection **connections_cached_list;
@@ -132,16 +281,19 @@ typedef struct {
GSList *unmanaged_specs;
GSList *unrecognized_specs;
+ GHashTable *startup_complete_idx;
NMSettingsConnection *startup_complete_blocked_by;
+ gulong startup_complete_platform_change_id;
+ guint startup_complete_timeout_id;
guint connections_len;
+ guint connections_generation;
+
guint kf_db_flush_idle_id_timestamps;
guint kf_db_flush_idle_id_seen_bssids;
bool started:1;
- bool startup_complete:1;
- bool connections_loaded:1;
} NMSettingsPrivate;
@@ -160,6 +312,9 @@ G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_DBUS_OBJECT);
/*****************************************************************************/
+/* FIXME: a lot of logging lines are directly connected to a profile. Set the @con_uuid
+ * argument for structured logging. */
+
#define _NMLOG_DOMAIN LOGD_SETTINGS
#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "settings", __VA_ARGS__)
@@ -169,57 +324,267 @@ static const NMDBusInterfaceInfoExtended interface_info_settings;
static const GDBusSignalInfo signal_info_new_connection;
static const GDBusSignalInfo signal_info_connection_removed;
-static void claim_connection (NMSettings *self,
- NMSettingsConnection *connection);
-
-static void connection_ready_changed (NMSettingsConnection *conn,
- GParamSpec *pspec,
- gpointer user_data);
-
static void default_wired_clear_tag (NMSettings *self,
NMDevice *device,
- NMSettingsConnection *connection,
+ NMSettingsConnection *sett_conn,
gboolean add_to_no_auto_default);
static void _clear_connections_cached_list (NMSettingsPrivate *priv);
+static void _startup_complete_check (NMSettings *self,
+ gint64 now_us);
+
/*****************************************************************************/
static void
-check_startup_complete (NMSettings *self)
+_emit_connection_added (NMSettings *self,
+ NMSettingsConnection *sett_conn)
{
- NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn);
+}
+
+static void
+_emit_connection_updated (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ NMSettingsConnectionUpdateReason update_reason)
+{
+ _nm_settings_connection_emit_signal_updated_internal (sett_conn, update_reason);
+ g_signal_emit (self, signals[CONNECTION_UPDATED], 0, sett_conn, (guint) update_reason);
+}
+
+static void
+_emit_connection_removed (NMSettings *self,
+ NMSettingsConnection *sett_conn)
+{
+ g_signal_emit (self, signals[CONNECTION_REMOVED], 0, sett_conn);
+}
+
+static void
+_emit_connection_flags_changed (NMSettings *self,
+ NMSettingsConnection *sett_conn)
+{
+ g_signal_emit (self, signals[CONNECTION_FLAGS_CHANGED], 0, sett_conn);
+}
+
+/*****************************************************************************/
+
+typedef struct {
NMSettingsConnection *sett_conn;
+ gint64 start_at;
+ gint64 timeout;
+} StartupCompleteData;
+
+static void
+_startup_complete_data_destroy (StartupCompleteData *scd)
+{
+ g_object_unref (scd->sett_conn);
+ g_slice_free (StartupCompleteData, scd);
+}
+
+static gboolean
+_startup_complete_check_is_ready (NMPlatform *platform,
+ NMSettingsConnection *sett_conn)
+{
+ const NMPlatformLink *plink;
+ const char *ifname;
+
+ /* FIXME: instead of just looking for the interface name, it would be better
+ * to wait for a device that is compatible with the profile. */
+
+ ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (sett_conn));
+
+ if (!ifname)
+ return TRUE;
+
+ plink = nm_platform_link_get_by_ifname (platform, ifname);
+ return plink && plink->initialized;
+}
- if (priv->startup_complete)
+static gboolean
+_startup_complete_timeout_cb (gpointer user_data)
+{
+ NMSettings *self = user_data;
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+
+ priv->startup_complete_timeout_id = 0;
+ _startup_complete_check (self, 0);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+_startup_complete_platform_change_cb (NMPlatform *platform,
+ int obj_type_i,
+ int ifindex,
+ const NMPlatformLink *link,
+ int change_type_i,
+ NMSettings *self)
+{
+ const NMPlatformSignalChangeType change_type = change_type_i;
+ NMSettingsPrivate *priv;
+ const char *ifname;
+
+ if (change_type == NM_PLATFORM_SIGNAL_REMOVED)
return;
- c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst) {
- if (!nm_settings_connection_get_ready (sett_conn)) {
- nm_g_object_ref_set (&priv->startup_complete_blocked_by, sett_conn);
- return;
+ if (!link->initialized)
+ return;
+
+ priv = NM_SETTINGS_GET_PRIVATE (self);
+
+ ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by));
+ if ( ifname
+ && !nm_streq (ifname, link->name))
+ return;
+
+ nm_assert (priv->startup_complete_timeout_id > 0);
+
+ nm_clear_g_source (&priv->startup_complete_timeout_id);
+ priv->startup_complete_timeout_id = g_idle_add (_startup_complete_timeout_cb, self);
+}
+
+static void
+_startup_complete_check (NMSettings *self,
+ gint64 now_us)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ gint64 next_expiry;
+ StartupCompleteData *scd;
+ NMSettingsConnection *next_sett_conn = NULL;
+ GHashTableIter iter;
+
+ if (!priv->started) {
+ /* before we are started, we don't setup the timers... */
+ return;
+ }
+
+ if (!priv->startup_complete_idx)
+ goto ready;
+
+ if (!now_us)
+ now_us = nm_utils_get_monotonic_timestamp_us ();
+
+ next_expiry = 0;
+
+ g_hash_table_iter_init (&iter, priv->startup_complete_idx);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &scd, NULL)) {
+ gint64 expiry;
+
+ if (scd->start_at == 0) {
+ /* once ready, the decision is remembered and there is nothing
+ * left to check. */
+ continue;
+ }
+
+ expiry = scd->start_at + scd->timeout;
+ if (expiry <= now_us) {
+ scd->start_at = 0;
+ continue;
+ }
+
+ if (_startup_complete_check_is_ready (priv->platform, scd->sett_conn)) {
+ scd->start_at = 0;
+ continue;
}
+
+ next_expiry = expiry;
+ next_sett_conn = scd->sett_conn;
+ /* we found one timeout for which to wait. that's good enough. */
+ break;
}
- g_clear_object (&priv->startup_complete_blocked_by);
+ nm_clear_g_source (&priv->startup_complete_timeout_id);
+ nm_g_object_ref_set (&priv->startup_complete_blocked_by, next_sett_conn);
+ if (next_expiry > 0) {
+ nm_assert (priv->startup_complete_blocked_by);
+ if (priv->startup_complete_platform_change_id == 0) {
+ priv->startup_complete_platform_change_id = g_signal_connect (priv->platform,
+ NM_PLATFORM_SIGNAL_LINK_CHANGED,
+ G_CALLBACK (_startup_complete_platform_change_cb),
+ self);
+ }
+ priv->startup_complete_timeout_id = g_timeout_add (NM_MIN (3600u*1000u, (next_expiry - now_us) / 1000u),
+ _startup_complete_timeout_cb,
+ self);
+ _LOGT ("startup-complete: wait for device \"%s\" due to connection %s (%s)",
+ nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by)),
+ nm_settings_connection_get_uuid (priv->startup_complete_blocked_by),
+ nm_settings_connection_get_id (priv->startup_complete_blocked_by));
+ return;
+ }
- /* the connection_ready_changed signal handler is no longer needed. */
- c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst)
- g_signal_handlers_disconnect_by_func (sett_conn, G_CALLBACK (connection_ready_changed), self);
+ nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy);
+ nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id);
- priv->startup_complete = TRUE;
+ready:
+ _LOGT ("startup-complete: ready, no profiles to wait for");
+ nm_assert (priv->started);
+ nm_assert (!priv->startup_complete_blocked_by);
+ nm_assert (!priv->startup_complete_idx);
+ nm_assert (priv->startup_complete_timeout_id == 0);
+ nm_assert (priv->startup_complete_platform_change_id == 0);
_notify (self, PROP_STARTUP_COMPLETE);
}
static void
-connection_ready_changed (NMSettingsConnection *conn,
- GParamSpec *pspec,
- gpointer user_data)
+_startup_complete_notify_connection (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ gboolean forget)
{
- NMSettings *self = NM_SETTINGS (user_data);
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ gint64 timeout;
+ gint64 now_us = 0;
+
+ nm_assert ( !priv->started
+ || priv->startup_complete_idx);
+
+ timeout = 0;
+ if (!forget) {
+ NMSettingConnection *s_con;
+ gint32 v;
+
+ s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (sett_conn));
+ v = nm_setting_connection_get_wait_device_timeout (s_con);
+ if (v > 0) {
+ nm_assert (nm_setting_connection_get_interface_name (s_con));
+ timeout = ((gint64) v) * 1000;
+ }
+ }
+
+ if (timeout == 0) {
+ if ( !priv->startup_complete_idx
+ || !g_hash_table_remove (priv->startup_complete_idx, &sett_conn))
+ return;
+ } else {
+ StartupCompleteData *scd;
+
+ if (!priv->startup_complete_idx) {
+ nm_assert (!priv->started);
+ priv->startup_complete_idx = g_hash_table_new_full (nm_pdirect_hash,
+ nm_pdirect_equal,
+ NULL,
+ (GDestroyNotify) _startup_complete_data_destroy);
+ scd = NULL;
+ } else
+ scd = g_hash_table_lookup (priv->startup_complete_idx, &sett_conn);
+ if (!scd) {
+ now_us = nm_utils_get_monotonic_timestamp_us ();
+ scd = g_slice_new (StartupCompleteData);
+ *scd = (StartupCompleteData) {
+ .sett_conn = g_object_ref (sett_conn),
+ .start_at = now_us,
+ .timeout = timeout,
+ };
+ g_hash_table_add (priv->startup_complete_idx, scd);
+ } else {
+ if (scd->start_at == 0) {
+ /* the entry already is ready and no longer relevant. Ignore it. */
+ return;
+ }
+ scd->timeout = timeout;
+ }
+ }
- if (nm_settings_connection_get_ready (conn))
- check_startup_complete (self);
+ _startup_complete_check (self, now_us);
}
const char *
@@ -228,10 +593,12 @@ nm_settings_get_startup_complete_blocked_reason (NMSettings *self)
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
const char *uuid = NULL;
- if (priv->startup_complete)
- return NULL;
- if (priv->startup_complete_blocked_by)
- uuid = nm_settings_connection_get_uuid (priv->startup_complete_blocked_by);
+ if (priv->started) {
+ if (!priv->startup_complete_idx)
+ return NULL;
+ if (priv->startup_complete_blocked_by)
+ uuid = nm_settings_connection_get_uuid (priv->startup_complete_blocked_by);
+ }
return uuid ?: "unknown";
}
@@ -283,8 +650,8 @@ update_specs (NMSettings *self, GSList **specs_ptr,
}
static void
-unmanaged_specs_changed (NMSettingsPlugin *config,
- gpointer user_data)
+_plugin_unmanaged_specs_changed (NMSettingsPlugin *config,
+ gpointer user_data)
{
NMSettings *self = NM_SETTINGS (user_data);
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
@@ -295,8 +662,8 @@ unmanaged_specs_changed (NMSettingsPlugin *config,
}
static void
-unrecognized_specs_changed (NMSettingsPlugin *config,
- gpointer user_data)
+_plugin_unrecognized_specs_changed (NMSettingsPlugin *config,
+ gpointer user_data)
{
NMSettings *self = NM_SETTINGS (user_data);
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
@@ -308,320 +675,1103 @@ unrecognized_specs_changed (NMSettingsPlugin *config,
/*****************************************************************************/
static void
-plugin_connection_added (NMSettingsPlugin *config,
- NMSettingsConnection *connection,
- NMSettings *self)
+connection_flags_changed (NMSettingsConnection *sett_conn,
+ gpointer user_data)
+{
+ _emit_connection_flags_changed (NM_SETTINGS (user_data), sett_conn);
+}
+
+/*****************************************************************************/
+
+static SettConnEntry *
+_sett_conn_entries_get (NMSettings *self,
+ const char *uuid)
{
- claim_connection (self, connection);
+ nm_assert (nm_utils_is_uuid (uuid));
+ return g_hash_table_lookup (NM_SETTINGS_GET_PRIVATE (self)->sce_idx, &uuid);
+}
+
+static SettConnEntry *
+_sett_conn_entries_create_and_add (NMSettings *self,
+ const char *uuid)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ SettConnEntry *sett_conn_entry;
+
+ sett_conn_entry = _sett_conn_entry_new (uuid);
+
+ if (!g_hash_table_add (priv->sce_idx, sett_conn_entry))
+ nm_assert_not_reached ();
+ else if (g_hash_table_size (priv->sce_idx) == 1)
+ g_object_ref (self);
+
+ return sett_conn_entry;
}
static void
-load_connections (NMSettings *self)
+_sett_conn_entries_remove_and_destroy (NMSettings *self,
+ SettConnEntry *sett_conn_entry)
{
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
- GSList *iter;
- for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
- NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
- GSList *plugin_connections;
- GSList *elt;
+ if (!g_hash_table_remove (priv->sce_idx, sett_conn_entry))
+ nm_assert_not_reached ();
+ else if (g_hash_table_size (priv->sce_idx) == 0)
+ g_object_unref (self);
+}
- plugin_connections = nm_settings_plugin_get_connections (plugin);
+/*****************************************************************************/
- // FIXME: ensure connections from plugins loaded with a lower priority
- // get rejected when they conflict with connections from a higher
- // priority plugin.
+static int
+_sett_conn_entry_sds_update_cmp (const CList *ls_a,
+ const CList *ls_b,
+ gconstpointer user_data)
+{
+ StorageData *sd_a = c_list_entry (ls_a, StorageData, sd_lst);
+ StorageData *sd_b = c_list_entry (ls_b, StorageData, sd_lst);
+
+ /* prioritized entries are sorted first (higher priority). */
+ NM_CMP_FIELD_UNSAFE (sd_b, sd_a, prioritize);
+
+ /* nm_settings_storage_cmp() compares in ascending order. Meaning,
+ * if the storage has higher priority, it gives a positive number (as one
+ * would expect).
+ *
+ * We want to sort the list in reverse though, with highest priority first. */
+ return nm_settings_storage_cmp (sd_b->storage, sd_a->storage, user_data);
+}
- for (elt = plugin_connections; elt; elt = g_slist_next (elt))
- claim_connection (self, elt->data);
+static void
+_sett_conn_entry_sds_update (NMSettings *self,
+ SettConnEntry *sett_conn_entry)
+{
+ StorageData *sd;
+ StorageData *sd_safe;
+ StorageData *sd_dirty;
+ gboolean reprioritize;
+
+ nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head);
+ nm_assert_storage_data_lst (&sett_conn_entry->dirty_sd_lst_head);
+
+ /* we merge the dirty list with the previous list.
+ *
+ * The idea is:
+ *
+ * - _connection_changed_track() appends events for the same UUID. Meaning:
+ * if the storage is new, it get appended (having lower priority).
+ * If it already exist and is an update for an event that we already
+ * track it, it keeps the list position in @dirty_sd_lst_head unchanged.
+ *
+ * - during merge, we want to preserve the previous order (with higher
+ * priority first in the list).
+ */
- g_slist_free (plugin_connections);
+ /* first go through all storages that we track and check whether they
+ * got an update...*/
- g_signal_connect (plugin, NM_SETTINGS_PLUGIN_CONNECTION_ADDED,
- G_CALLBACK (plugin_connection_added), self);
- g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
- G_CALLBACK (unmanaged_specs_changed), self);
- g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED,
- G_CALLBACK (unrecognized_specs_changed), self);
+ reprioritize = FALSE;
+ c_list_for_each_entry (sd, &sett_conn_entry->dirty_sd_lst_head, sd_lst) {
+ if (sd->prioritize) {
+ reprioritize = TRUE;
+ break;
+ }
}
- priv->connections_loaded = TRUE;
- _notify (self, PROP_CONNECTIONS);
+ c_list_for_each_entry_safe (sd, sd_safe, &sett_conn_entry->sd_lst_head, sd_lst) {
- unmanaged_specs_changed (NULL, self);
- unrecognized_specs_changed (NULL, self);
+ sd_dirty = _storage_data_find_in_lst (&sett_conn_entry->dirty_sd_lst_head, sd->storage);
+ if (!sd_dirty) {
+ /* there is no update for this storage. */
+ if (reprioritize)
+ sd->prioritize = FALSE;
+ continue;
+ }
+
+ nm_g_object_ref_set (&sd->connection, sd_dirty->connection);
+ sd->prioritize = sd_dirty->prioritize;
+
+ _storage_data_destroy (sd_dirty);
+ }
+
+ /* all remaining (so far unseen) dirty entries are appended to the merged list.
+ * (append means lower priority). */
+
+ c_list_splice (&sett_conn_entry->sd_lst_head, &sett_conn_entry->dirty_sd_lst_head);
+
+ /* we drop the entries that are no longer "alive" (meaning, they no longer
+ * indicate a connection and are not a tombstone). */
+ c_list_for_each_entry_safe (sd, sd_safe, &sett_conn_entry->sd_lst_head, sd_lst) {
+ if (!_storage_data_is_alive (sd))
+ _storage_data_destroy (sd);
+ }
+
+ /* as last, we sort the entries. Note that this is a stable-sort... */
+ c_list_sort (&sett_conn_entry->sd_lst_head,
+ _sett_conn_entry_sds_update_cmp,
+ NM_SETTINGS_GET_PRIVATE (self)->plugins);
+
+ nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head);
+ nm_assert (c_list_is_empty (&sett_conn_entry->dirty_sd_lst_head));
}
/*****************************************************************************/
-static void
-connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer user_data)
+static NMConnection *
+_connection_changed_normalize_connection (NMSettingsStorage *storage,
+ NMConnection *connection,
+ GVariant *secrets_to_merge,
+ NMConnection **out_connection_cloned)
{
- g_signal_emit (NM_SETTINGS (user_data),
- signals[CONNECTION_UPDATED],
- 0,
- connection,
- by_user);
+ gs_unref_object NMConnection *connection_cloned = NULL;
+ gs_free_error GError *error = NULL;
+ const char *uuid;
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (storage));
+ nm_assert (out_connection_cloned && !*out_connection_cloned);
+
+ if (!connection)
+ return NULL;
+
+ nm_assert (NM_IS_CONNECTION (connection));
+
+ uuid = nm_settings_storage_get_uuid (storage);
+
+ if (secrets_to_merge) {
+ connection_cloned = nm_simple_connection_new_clone (connection);
+ connection = connection_cloned;
+ nm_connection_update_secrets (connection,
+ NULL,
+ secrets_to_merge,
+ NULL);
+ }
+
+ if (!_nm_connection_ensure_normalized (connection,
+ !!connection_cloned,
+ uuid,
+ FALSE,
+ connection_cloned ? NULL : &connection_cloned,
+ &error)) {
+ /* this is most likely a bug in the plugin. It provided a connection that no longer verifies.
+ * Well, I guess it could also happen when we merge @secrets_to_merge above. In any case
+ * somewhere is a bug. */
+ _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: plugin provided an invalid connection: %s",
+ uuid,
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage),
+ error->message);
+ return NULL;
+ }
+ if (connection_cloned)
+ connection = connection_cloned;
+
+ *out_connection_cloned = g_steal_pointer (&connection_cloned);
+ return connection;
}
+/*****************************************************************************/
+
static void
-connection_flags_changed (NMSettingsConnection *connection,
- gpointer user_data)
+_connection_changed_update (NMSettings *self,
+ SettConnEntry *sett_conn_entry,
+ NMConnection *connection,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason)
{
- g_signal_emit (NM_SETTINGS (user_data),
- signals[CONNECTION_FLAGS_CHANGED],
- 0,
- connection);
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ gs_unref_object NMConnection *connection_old = NULL;
+ NMSettingsStorage *storage = sett_conn_entry->storage;
+ gs_unref_object NMSettingsConnection *sett_conn = g_object_ref (sett_conn_entry->sett_conn);
+ const char *path;
+ gboolean is_new;
+
+ nm_assert (!NM_FLAGS_ANY (sett_mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
+ nm_assert (!NM_FLAGS_ANY (sett_flags, ~sett_mask));
+
+ is_new = c_list_is_empty (&sett_conn->_connections_lst);
+
+ _LOGT ("update[%s]: %s connection \"%s\" ("NM_SETTINGS_STORAGE_PRINT_FMT")",
+ nm_settings_storage_get_uuid (storage),
+ is_new ? "adding" : "updating",
+ nm_connection_get_id (connection),
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage));
+
+ _nm_settings_connection_set_storage (sett_conn, storage);
+
+ _nm_settings_connection_set_connection (sett_conn, connection, &connection_old, update_reason);
+
+
+ if (is_new) {
+ _nm_settings_connection_register_kf_dbs (sett_conn,
+ priv->kf_db_timestamps,
+ priv->kf_db_seen_bssids);
+
+ _clear_connections_cached_list (priv);
+ c_list_link_tail (&priv->connections_lst_head, &sett_conn->_connections_lst);
+ priv->connections_len++;
+ priv->connections_generation++;
+
+ g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, G_CALLBACK (connection_flags_changed), self);
+
+ }
+
+ sett_mask |= NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE;
+ if (nm_settings_connection_check_visibility (sett_conn, priv->session_monitor))
+ sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE;
+ else
+ nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE));
+
+ sett_mask |= NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED;
+ if (nm_settings_storage_is_in_memory (storage))
+ sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED;
+ else {
+ nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED));
+
+ /* Profiles that don't reside in /run, are never nm-generated
+ * and never volatile. */
+ sett_mask |= ( NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE);
+ sett_flags &= ~( NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE);
+ }
+
+ nm_settings_connection_set_flags_full (sett_conn,
+ sett_mask,
+ sett_flags);
+
+ if (is_new) {
+ /* FIXME(shutdown): The NMSettings instance can't be disposed
+ * while there is any exported connection. Ideally we should
+ * unexport all connections on NMSettings' disposal, but for now
+ * leak @self on termination when there are connections alive. */
+ path = nm_dbus_object_export (NM_DBUS_OBJECT (sett_conn));
+ } else
+ path = nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn));
+
+ if ( is_new
+ || connection_old) {
+ nm_utils_log_connection_diff (nm_settings_connection_get_connection (sett_conn),
+ connection_old,
+ LOGL_DEBUG,
+ LOGD_CORE,
+ is_new ? "new connection" : "update connection",
+ "++ ",
+ path);
+ }
+
+ if (is_new) {
+ nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+ &interface_info_settings,
+ &signal_info_new_connection,
+ "(o)",
+ path);
+ _notify (self, PROP_CONNECTIONS);
+ _emit_connection_added (self, sett_conn);
+ } else {
+ _nm_settings_connection_emit_dbus_signal_updated (sett_conn);
+ _emit_connection_updated (self, sett_conn, update_reason);
+ }
+
+ if ( !priv->started
+ || priv->startup_complete_idx) {
+ if (nm_settings_has_connection (self, sett_conn))
+ _startup_complete_notify_connection (self, sett_conn, FALSE);
+ }
}
static void
-connection_removed (NMSettingsConnection *connection, gpointer user_data)
+_connection_changed_delete (NMSettings *self,
+ NMSettingsStorage *storage,
+ NMSettingsConnection *sett_conn,
+ gboolean allow_add_to_no_auto_default)
{
- NMSettings *self = NM_SETTINGS (user_data);
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ gs_unref_object NMConnection *connection_for_agents = NULL;
NMDevice *device;
+ const char *uuid;
- g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
- g_return_if_fail (!c_list_is_empty (&connection->_connections_lst));
- nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
+ nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+ nm_assert (c_list_contains (&priv->connections_lst_head, &sett_conn->_connections_lst));
+ nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (sett_conn)));
+
+ uuid = nm_settings_storage_get_uuid (storage);
+
+ _LOGT ("update[%s]: delete connection \"%s\" ("NM_SETTINGS_STORAGE_PRINT_FMT")",
+ uuid,
+ nm_settings_connection_get_id (sett_conn),
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage));
- /* When the default wired connection is removed (either deleted or saved to
- * a new persistent connection by a plugin), write the MAC address of the
+ /* When the default wired sett_conn is removed (either deleted or saved to
+ * a new persistent sett_conn by a plugin), write the MAC address of the
* wired device to the config file and don't create a new default wired
- * connection for that device again.
+ * sett_conn for that device again.
*/
- device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
+ device = nm_settings_connection_default_wired_get_device (sett_conn);
if (device)
- default_wired_clear_tag (self, device, connection, TRUE);
+ default_wired_clear_tag (self, device, sett_conn, allow_add_to_no_auto_default);
- /* Disconnect signal handlers, as plugins might still keep references
- * to the connection (and thus the signal handlers would still be live)
- * even after NMSettings has dropped all its references.
- */
-
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self);
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self);
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_flags_changed), self);
- if (!priv->startup_complete)
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self);
+ g_signal_handlers_disconnect_by_func (sett_conn, G_CALLBACK (connection_flags_changed), self);
- /* Forget about the connection internally */
_clear_connections_cached_list (priv);
+ c_list_unlink (&sett_conn->_connections_lst);
priv->connections_len--;
- c_list_unlink (&connection->_connections_lst);
+ priv->connections_generation++;
- if (priv->connections_loaded) {
- _notify (self, PROP_CONNECTIONS);
+ /* Tell agents to remove secrets for this connection */
+ connection_for_agents = nm_simple_connection_new_clone (nm_settings_connection_get_connection (sett_conn));
+ nm_connection_clear_secrets (connection_for_agents);
+ nm_agent_manager_delete_secrets (priv->agent_mgr,
+ nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
+ connection_for_agents);
- nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
- &interface_info_settings,
- &signal_info_connection_removed,
- "(o)",
- nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)));
- }
+ _notify (self, PROP_CONNECTIONS);
+ _nm_settings_connection_emit_dbus_signal_removed (sett_conn);
+ nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+ &interface_info_settings,
+ &signal_info_connection_removed,
+ "(o)",
+ nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)));
- nm_dbus_object_unexport (NM_DBUS_OBJECT (connection));
+ nm_dbus_object_unexport (NM_DBUS_OBJECT (sett_conn));
- if (priv->connections_loaded)
- g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
+ nm_settings_connection_set_flags (sett_conn,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
+ FALSE);
- check_startup_complete (self);
+ _emit_connection_removed (self, sett_conn);
- g_object_unref (connection);
+ _nm_settings_connection_cleanup_after_remove (sett_conn);
- g_object_unref (self); /* Balanced by a ref in claim_connection() */
-}
+ nm_key_file_db_remove_key (priv->kf_db_timestamps, uuid);
+ nm_key_file_db_remove_key (priv->kf_db_seen_bssids, uuid);
-/*****************************************************************************/
+ if ( !priv->started
+ || priv->startup_complete_idx)
+ _startup_complete_notify_connection (self, sett_conn, TRUE);
+}
static void
-claim_connection (NMSettings *self, NMSettingsConnection *sett_conn)
+_connection_changed_process_one (NMSettings *self,
+ SettConnEntry *sett_conn_entry,
+ gboolean allow_add_to_no_auto_default,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ gboolean override_sett_flags,
+ NMSettingsConnectionUpdateReason update_reason)
{
- NMSettingsPrivate *priv;
- GError *error = NULL;
- const char *path;
- NMSettingsConnection *existing;
+ StorageData *sd_best;
- g_return_if_fail (NM_IS_SETTINGS (self));
- g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn));
- g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (sett_conn)));
+ c_list_unlink (&sett_conn_entry->sce_dirty_lst);
- priv = NM_SETTINGS_GET_PRIVATE (self);
+ _sett_conn_entry_sds_update (self, sett_conn_entry);
- /* prevent duplicates */
- if (!c_list_is_empty (&sett_conn->_connections_lst)) {
- nm_assert (c_list_contains (&priv->connections_lst_head, &sett_conn->_connections_lst));
+ sd_best = c_list_first_entry (&sett_conn_entry->sd_lst_head, StorageData, sd_lst);;
+
+ if ( !sd_best
+ || !sd_best->connection) {
+ gs_unref_object NMSettingsConnection *sett_conn = NULL;
+ gs_unref_object NMSettingsStorage *storage = NULL;
+
+ if (!sett_conn_entry->sett_conn) {
+
+ if (!sd_best) {
+ _sett_conn_entries_remove_and_destroy (self, sett_conn_entry);
+ return;
+ }
+
+ if (sett_conn_entry->storage != sd_best->storage) {
+ _LOGT ("update[%s]: shadow UUID ("NM_SETTINGS_STORAGE_PRINT_FMT")",
+ sett_conn_entry->uuid,
+ NM_SETTINGS_STORAGE_PRINT_ARG (sd_best->storage));
+ }
+
+ nm_g_object_ref_set (&sett_conn_entry->storage, sd_best->storage);
+ return;
+ }
+
+ sett_conn = g_steal_pointer (&sett_conn_entry->sett_conn);
+ if (sd_best) {
+ storage = g_object_ref (sd_best->storage);
+ nm_g_object_ref_set (&sett_conn_entry->storage, storage);
+ nm_assert_valid_settings_storage (NULL, storage);
+ } else {
+ storage = g_object_ref (sett_conn_entry->storage);
+ _sett_conn_entries_remove_and_destroy (self, sett_conn_entry);
+ }
+
+ _connection_changed_delete (self, storage, sett_conn, allow_add_to_no_auto_default);
return;
}
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- if (!nm_connection_normalize (nm_settings_connection_get_connection (sett_conn), NULL, NULL, &error)) {
- _LOGW ("plugin provided invalid connection: %s", error->message);
- g_error_free (error);
- return;
+ if (override_sett_flags) {
+ NMSettingsConnectionIntFlags s_f, s_m;
+
+ nm_settings_storage_load_sett_flags (sd_best->storage, &s_f, &s_m);
+
+ nm_assert (!NM_FLAGS_ANY (s_f, ~s_m));
+
+ sett_mask |= s_m;
+ sett_flags = (sett_flags & ~s_m) | (s_f & s_m);
}
- existing = nm_settings_get_connection_by_uuid (self, nm_settings_connection_get_uuid (sett_conn));
- if (existing) {
- /* Cannot add duplicate connections per UUID. Just return without action and
- * log a warning.
- *
- * This means, that plugins must not provide duplicate connections (UUID).
- * In fact, none of the plugins currently would do that.
- *
- * But globaly, over different setting plugins, there could be duplicates
- * without the individual plugins being aware. Don't handle that at all, just
- * error out. That should not happen unless the admin misconfigured the system
- * to create conflicting connections. */
- _LOGW ("plugin provided duplicate connection with UUID %s",
- nm_settings_connection_get_uuid (sett_conn));
- return;
+ nm_g_object_ref_set (&sett_conn_entry->storage, sd_best->storage);
+
+ if (!sett_conn_entry->sett_conn)
+ sett_conn_entry->sett_conn = nm_settings_connection_new ();
+
+ _connection_changed_update (self,
+ sett_conn_entry,
+ sd_best->connection,
+ sett_flags,
+ sett_mask,
+ update_reason);
+}
+
+static void
+_connection_changed_process_all_dirty (NMSettings *self,
+ gboolean allow_add_to_no_auto_default,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ gboolean override_sett_flags,
+ NMSettingsConnectionUpdateReason update_reason)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ SettConnEntry *sett_conn_entry;
+
+ while ((sett_conn_entry = c_list_first_entry (&priv->sce_dirty_lst_head, SettConnEntry, sce_dirty_lst))) {
+ _connection_changed_process_one (self,
+ sett_conn_entry,
+ allow_add_to_no_auto_default,
+ sett_flags,
+ sett_mask,
+ override_sett_flags,
+ update_reason);
}
+}
+
+static SettConnEntry *
+_connection_changed_track (NMSettings *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ gboolean prioritize)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ SettConnEntry *sett_conn_entry;
+ StorageData *sd;
+ const char *uuid;
- nm_settings_connection_register_kf_dbs (sett_conn,
- priv->kf_db_timestamps,
- priv->kf_db_seen_bssids);
+ nm_assert_valid_settings_storage (NULL, storage);
- /* Ensure its initial visibility is up-to-date */
- nm_settings_connection_recheck_visibility (sett_conn);
+ uuid = nm_settings_storage_get_uuid (storage);
- /* This one unexports the connection, it needs to run late to give the active
- * connection a chance to deal with its reference to this settings connection. */
- g_signal_connect_after (sett_conn, NM_SETTINGS_CONNECTION_REMOVED,
- G_CALLBACK (connection_removed), self);
- g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
- G_CALLBACK (connection_updated), self);
- g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
- G_CALLBACK (connection_flags_changed),
- self);
- if (!priv->startup_complete) {
- g_signal_connect (sett_conn, "notify::" NM_SETTINGS_CONNECTION_READY,
- G_CALLBACK (connection_ready_changed),
- self);
+ nm_assert (!connection || NM_IS_CONNECTION (connection));
+ nm_assert (!connection || (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS));
+ nm_assert (!connection || nm_streq0 (uuid, nm_connection_get_uuid (connection)));
+
+ nmtst_connection_assert_unchanging (connection);
+
+ sett_conn_entry = _sett_conn_entries_get (self, uuid)
+ ?: _sett_conn_entries_create_and_add (self, uuid);
+
+ if (_LOGT_ENABLED ()) {
+ const char *filename;
+
+ filename = nm_settings_storage_get_filename (storage);
+ if (connection) {
+ _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event with connection \"%s\"%s%s%s",
+ sett_conn_entry->uuid,
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage),
+ nm_connection_get_id (connection),
+ NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""));
+ } else if (nm_settings_storage_is_tombstone (storage)) {
+ _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for hiding profile%s%s%s",
+ sett_conn_entry->uuid,
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage),
+ NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""));
+ } else {
+ _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for dropping profile%s%s%s",
+ sett_conn_entry->uuid,
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage),
+ NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""));
+ }
}
- _clear_connections_cached_list (priv);
+ /* see _sett_conn_entry_sds_update() for why we append the new events
+ * and leave existing ones at their position. */
+ sd = _storage_data_find_in_lst (&sett_conn_entry->dirty_sd_lst_head, storage);
+ if (sd)
+ nm_g_object_ref_set (&sd->connection, connection);
+ else {
+ sd = _storage_data_new_stale (storage, connection);
+ c_list_link_tail (&sett_conn_entry->dirty_sd_lst_head, &sd->sd_lst);
+ }
- g_object_ref (sett_conn);
- /* FIXME(shutdown): The NMSettings instance can't be disposed
- * while there is any exported connection. Ideally we should
- * unexport all connections on NMSettings' disposal, but for now
- * leak @self on termination when there are connections alive. */
- g_object_ref (self);
- priv->connections_len++;
- c_list_link_tail (&priv->connections_lst_head, &sett_conn->_connections_lst);
-
- path = nm_dbus_object_export (NM_DBUS_OBJECT (sett_conn));
-
- nm_utils_log_connection_diff (nm_settings_connection_get_connection (sett_conn),
- NULL,
- LOGL_DEBUG,
- LOGD_CORE,
- "new connection", "++ ",
- path);
-
- /* Only emit the individual connection-added signal after connections
- * have been initially loaded.
- */
- if (priv->connections_loaded) {
- nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
- &interface_info_settings,
- &signal_info_new_connection,
- "(o)",
- nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)));
+ if (prioritize) {
+ StorageData *sd2;
- g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn);
- _notify (self, PROP_CONNECTIONS);
+ /* only one entry can be prioritized. */
+ c_list_for_each_entry (sd2, &sett_conn_entry->dirty_sd_lst_head, sd_lst)
+ sd2->prioritize = FALSE;
+ sd->prioritize = TRUE;
+ }
+
+ nm_c_list_move_tail (&priv->sce_dirty_lst_head, &sett_conn_entry->sce_dirty_lst);
+
+ return sett_conn_entry;
+}
+
+/*****************************************************************************/
+
+static void
+_plugin_connections_reload_cb (NMSettingsPlugin *plugin,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ gpointer user_data)
+{
+ _connection_changed_track (user_data, storage, connection, FALSE);
+}
+
+static void
+_plugin_connections_reload (NMSettings *self)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ GSList *iter;
+
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ nm_settings_plugin_reload_connections (iter->data,
+ _plugin_connections_reload_cb,
+ self);
}
- nm_settings_connection_added (sett_conn);
+ _connection_changed_process_all_dirty (self,
+ FALSE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ TRUE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
}
/*****************************************************************************/
+static gboolean
+_add_connection_to_first_plugin (NMSettings *self,
+ NMConnection *new_connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean in_memory,
+ NMSettingsStorage **out_new_storage,
+ NMConnection **out_new_connection,
+ GError **error)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ GError *first_error = NULL;
+ GSList *iter;
+ const char *uuid;
+
+ uuid = nm_connection_get_uuid (new_connection);
+
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
+ gs_unref_object NMSettingsStorage *storage = NULL;
+ gs_unref_object NMConnection *connection_to_add = NULL;
+ gs_unref_object NMConnection *connection_to_add_cloned = NULL;
+ NMConnection *connection_to_add_real = NULL;
+ gs_unref_variant GVariant *agent_owned_secrets = NULL;
+ gs_free_error GError *add_error = NULL;
+ gboolean success;
+ const char *filename;
+
+ if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) {
+ success = nms_keyfile_plugin_add_connection (priv->keyfile_plugin,
+ new_connection,
+ is_nm_generated,
+ is_volatile,
+ in_memory,
+ &storage,
+ &connection_to_add,
+ &add_error);
+ } else {
+ if (in_memory)
+ continue;
+ nm_assert (!is_nm_generated);
+ nm_assert (!is_volatile);
+ success = nm_settings_plugin_add_connection (plugin,
+ new_connection,
+ &storage,
+ &connection_to_add,
+ &add_error);
+ }
+ if (!success) {
+ _LOGT ("add-connection: failed to add %s/'%s': %s",
+ nm_connection_get_uuid (new_connection),
+ nm_connection_get_id (new_connection),
+ add_error->message);
+ if (!first_error)
+ first_error = g_steal_pointer (&add_error);
+ continue;
+ }
+
+ if (!nm_streq0 (nm_settings_storage_get_uuid (storage), uuid)) {
+ nm_assert_not_reached ();
+ continue;
+ }
+
+ agent_owned_secrets = nm_connection_to_dbus (new_connection,
+ NM_CONNECTION_SERIALIZE_ONLY_SECRETS
+ | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+ connection_to_add_real = _connection_changed_normalize_connection (storage,
+ connection_to_add,
+ agent_owned_secrets,
+ &connection_to_add_cloned);
+ if (!connection_to_add_real) {
+ nm_assert_not_reached ();
+ continue;
+ }
+
+ filename = nm_settings_storage_get_filename (storage);
+ _LOGT ("add-connection: successfully added connection %s,'%s' ("NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s",
+ nm_settings_storage_get_uuid (storage),
+ nm_connection_get_id (new_connection),
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage),
+ NM_PRINT_FMT_QUOTED (filename, ", \"", filename, "\")", ")"));
+
+ *out_new_storage = g_steal_pointer (&storage);
+ *out_new_connection = g_steal_pointer (&connection_to_add_cloned)
+ ?: g_steal_pointer (&connection_to_add);
+ nm_assert (NM_IS_CONNECTION (*out_new_connection));
+ return TRUE;
+ }
+
+ nm_assert (first_error);
+ g_propagate_error (error, first_error);
+ return FALSE;
+}
+
/**
* nm_settings_add_connection:
* @self: the #NMSettings object
* @connection: the source connection to create a new #NMSettingsConnection from
- * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to
- * not save to disk
+ * @persist_mode: the persist-mode for this profile.
+ * @sett_flags: the settings flags to set.
+ * @out_sett_conn: (allow-none) (transfer none): the added settings connection on success.
* @error: on return, a location to store any errors that may occur
*
* Creates a new #NMSettingsConnection for the given source @connection.
* The returned object is owned by @self and the caller must reference
* the object to continue using it.
*
- * Returns: the new #NMSettingsConnection or %NULL
+ * Returns: TRUE on success.
*/
-NMSettingsConnection *
+gboolean
nm_settings_add_connection (NMSettings *self,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnection **out_sett_conn,
GError **error)
{
- NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
- GSList *iter;
- NMSettingsConnection *added = NULL;
- NMSettingsConnection *candidate = NULL;
+ gs_unref_object NMConnection *connection_cloned_1 = NULL;
+ gs_unref_object NMConnection *new_connection = NULL;
+ gs_unref_object NMSettingsStorage *storage = NULL;
+ gs_free_error GError *local = NULL;
+ SettConnEntry *sett_conn_entry;
const char *uuid;
+ nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY));
+
+ nm_assert (!NM_FLAGS_ANY (sett_flags, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
+
+ nm_assert ( !NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
+ || persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY);
+
+ nm_assert ( !NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)
+ || persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY);
+
+ NM_SET_OUT (out_sett_conn, NULL);
+
uuid = nm_connection_get_uuid (connection);
/* Make sure a connection with this UUID doesn't already exist */
- c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
- if (nm_streq0 (uuid, nm_settings_connection_get_uuid (candidate))) {
- g_set_error_literal (error,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_UUID_EXISTS,
- "A connection with this UUID already exists.");
- return NULL;
+ if (_sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid))) {
+ g_set_error_literal (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_UUID_EXISTS,
+ "a connection with this UUID already exists");
+ return FALSE;
+ }
+
+ if (!_nm_connection_ensure_normalized (connection,
+ FALSE,
+ NULL,
+ FALSE,
+ &connection_cloned_1,
+ &local)) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
+ "connection is invalid: %s",
+ local->message);
+ return FALSE;
+ }
+ if (connection_cloned_1)
+ connection = connection_cloned_1;
+
+ if (!_add_connection_to_first_plugin (self,
+ connection,
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED),
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE),
+ ( persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK
+ || NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)),
+ &storage,
+ &new_connection,
+ &local)) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_FAILED,
+ "failure adding connection: %s",
+ local->message);
+ return FALSE;
+ }
+
+ sett_conn_entry = _connection_changed_track (self, storage, new_connection, TRUE);
+
+ _connection_changed_process_all_dirty (self,
+ FALSE,
+ sett_flags,
+ _NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK,
+ FALSE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
+
+ nm_assert (sett_conn_entry == _sett_conn_entries_get (self, sett_conn_entry->uuid));
+ nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn_entry->sett_conn));
+
+ NM_SET_OUT (out_sett_conn, _sett_conn_entry_get_conn (sett_conn_entry));
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+nm_settings_update_connection (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ NMConnection *connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason,
+ const char *log_context_name,
+ GError **error)
+{
+ NMSettingsPrivate *priv;
+ gs_unref_object NMConnection *connection_cloned_1 = NULL;
+ gs_unref_object NMConnection *new_connection_cloned = NULL;
+ gs_unref_object NMConnection *new_connection = NULL;
+ NMConnection *new_connection_real;
+ gs_free_error GError *local = NULL;
+ gs_unref_object NMSettingsStorage *cur_storage = NULL;
+ gs_unref_object NMSettingsStorage *new_storage = NULL;
+ gboolean cur_in_memory;
+ gboolean new_in_memory;
+ gboolean remove_from_disk;
+ const char *uuid;
+ gboolean success;
+
+ g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn), FALSE);
+ g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), FALSE);
+
+ nm_assert (!NM_FLAGS_ANY (sett_mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
+ nm_assert (!NM_FLAGS_ANY (sett_flags, ~sett_mask));
+ nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY));
+
+ priv = NM_SETTINGS_GET_PRIVATE (self);
+
+ cur_storage = g_object_ref (nm_settings_connection_get_storage (sett_conn));
+
+ uuid = nm_settings_storage_get_uuid (cur_storage);
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (cur_storage));
+ nm_assert (_sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid)) == sett_conn);
+
+ if (connection) {
+ if (!_nm_connection_ensure_normalized (connection,
+ FALSE,
+ uuid,
+ TRUE,
+ &connection_cloned_1,
+ &local)) {
+ _LOGT ("update[%s]: %s: failed because profile is invalid: %s",
+ nm_settings_storage_get_uuid (cur_storage),
+ log_context_name,
+ local->message);
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
+ "connection is invalid: %s",
+ local->message);
+ return FALSE;
}
+ if (connection_cloned_1)
+ connection = connection_cloned_1;
+ } else
+ connection = nm_settings_connection_get_connection (sett_conn);
+
+ cur_in_memory = !!nm_settings_storage_is_in_memory (cur_storage);
+
+ if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP) {
+ persist_mode = cur_in_memory
+ ? NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED
+ : NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
}
- /* 1) plugin writes the NMConnection to disk
- * 2) plugin creates a new NMSettingsConnection subclass with the settings
- * from the NMConnection and returns it to the settings service
- * 3) settings service exports the new NMSettingsConnection subclass
- * 4) plugin notices that something on the filesystem has changed
- * 5) plugin reads the changes and ignores them because they will
- * contain the same data as the connection it already knows about
- */
- for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
- NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
- GError *add_error = NULL;
- gs_unref_variant GVariant *secrets = NULL;
-
- /* Make a copy of agent-owned secrets because they won't be present in
- * the connection returned by plugins, as plugins return only what was
- * reread from the file. */
- secrets = nm_connection_to_dbus (connection,
- NM_CONNECTION_SERIALIZE_ONLY_SECRETS
- | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
-
- added = nm_settings_plugin_add_connection (plugin, connection, save_to_disk, &add_error);
- if (added) {
- if (secrets) {
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- nm_connection_update_secrets (nm_settings_connection_get_connection (added),
- NULL,
- secrets,
- NULL);
+ if ( NM_FLAGS_HAS (sett_mask, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
+ && !NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) {
+ NMDevice *device;
+
+ /* The connection has been changed by the user, it should no longer be
+ * considered a default wired connection, and should no longer affect
+ * the no-auto-default configuration option.
+ */
+ device = nm_settings_connection_default_wired_get_device (sett_conn);
+ if (device) {
+ nm_assert (cur_in_memory);
+ nm_assert (!NM_FLAGS_ANY (nm_settings_connection_get_flags (sett_conn),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE));
+
+ default_wired_clear_tag (self, device, sett_conn, FALSE);
+
+ if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST)) {
+ /* making a default-wired-connection a regulard connection implies persisting
+ * it to disk (unless specified differently). */
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
}
- claim_connection (self, added);
- return added;
}
- _LOGD ("Failed to add %s/'%s': %s",
- nm_connection_get_uuid (connection),
+ }
+
+ if ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST
+ && NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)
+ && NM_FLAGS_ANY ((sett_flags ^ nm_settings_connection_get_flags (sett_conn)) & sett_flags,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) {
+ /* we update the nm-generated/volatile setting of a profile (which is inherrently
+ * in-memory. The caller did not request to persis this to disk, however we need
+ * to store the flags in run. */
+ nm_assert (cur_in_memory);
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
+ }
+
+ if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK)
+ new_in_memory = FALSE;
+ else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY))
+ new_in_memory = TRUE;
+ else {
+ nm_assert (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST);
+ new_in_memory = cur_in_memory;
+ }
+
+ if (!new_in_memory) {
+ /* Persistent connections cannot be volatile nor nm-generated.
+ *
+ * That is obviously true for volatile, as it is enforced by Update2() API.
+ *
+ * For nm-generated profiles also, because the nm-generated flag is only stored
+ * for in-memory profiles. If we would persist the profile to /etc it would loose
+ * the nm-generated flag after restart/reload, and that cannot be right. If a profile
+ * ends up on disk, the information who created it get lost. */
+ nm_assert (!NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE));
+ sett_mask |= NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
+ sett_flags &= ~( NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE);
+ }
+
+ remove_from_disk = (persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED);
+
+ if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST) {
+ success = TRUE;
+ new_storage = g_object_ref (cur_storage);
+ new_connection = g_object_ref (connection);
+ } else if (new_in_memory != cur_in_memory) {
+ success = _add_connection_to_first_plugin (self,
+ connection,
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED),
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE),
+ new_in_memory,
+ &new_storage,
+ &new_connection,
+ &local);
+ } else {
+ NMSettingsPlugin *plugin;
+
+ plugin = nm_settings_storage_get_plugin (cur_storage);
+ if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) {
+ success = nms_keyfile_plugin_update_connection (priv->keyfile_plugin,
+ cur_storage,
+ connection,
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED),
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE),
+ NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME),
+ &new_storage,
+ &new_connection,
+ &local);
+ } else {
+ success = nm_settings_plugin_update_connection (nm_settings_storage_get_plugin (cur_storage),
+ cur_storage,
+ connection,
+ &new_storage,
+ &new_connection,
+ &local);
+ }
+ }
+ if (!success) {
+ gboolean ignore_failure;
+
+ ignore_failure = NM_FLAGS_ANY (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE);
+
+ _LOGT ("update[%s]: %s: %sfailure to %s connection \"%s\" on storage: %s",
+ nm_settings_storage_get_uuid (cur_storage),
+ log_context_name,
+ ignore_failure ? "ignore " : "",
+ (new_in_memory != cur_in_memory) ? "write" : "update",
nm_connection_get_id (connection),
- add_error->message);
- g_clear_error (&add_error);
+ local->message);
+ if (!ignore_failure) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
+ "failed to %s connection: %s",
+ (new_in_memory != cur_in_memory) ? "write" : "update",
+ local->message);
+ return FALSE;
+ }
+ g_clear_error (&local);
+ new_storage = g_object_ref (cur_storage);
+ new_connection = g_object_ref (connection);
+ } else {
+ _LOGT ("update[%s]: %s: update profile \"%s\"%s",
+ nm_settings_storage_get_uuid (cur_storage),
+ log_context_name,
+ nm_connection_get_id (connection),
+ persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST ? " (not persisted)" : "");
}
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "No plugin supported adding this connection");
- return NULL;
+ nm_assert_valid_settings_storage (NULL, new_storage);
+ nm_assert (NM_IS_CONNECTION (new_connection));
+ nm_assert (nm_streq (uuid, nm_settings_storage_get_uuid (new_storage)));
+
+ if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST)
+ new_connection_real = new_connection;
+ else {
+ gs_unref_variant GVariant *agent_owned_secrets = NULL;
+
+ agent_owned_secrets = nm_connection_to_dbus (connection,
+ NM_CONNECTION_SERIALIZE_ONLY_SECRETS
+ | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+ new_connection_real = _connection_changed_normalize_connection (new_storage,
+ new_connection,
+ agent_owned_secrets,
+ &new_connection_cloned);
+ }
+
+ nm_assert (NM_IS_CONNECTION (new_connection_real));
+
+ _connection_changed_track (self, new_storage, new_connection_real, TRUE);
+
+ if (new_storage != cur_storage) {
+ if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (cur_storage),
+ cur_storage,
+ remove_from_disk,
+ &local)) {
+ const char *filename;
+
+ filename = nm_settings_storage_get_filename (cur_storage);
+ _LOGW ("update[%s]: failed to delete moved storage "NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s: %s",
+ nm_settings_storage_get_uuid (cur_storage),
+ NM_SETTINGS_STORAGE_PRINT_ARG (cur_storage),
+ local->message,
+ NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""));
+
+ g_clear_error (&local);
+ }
+
+ _connection_changed_track (self, cur_storage, NULL, FALSE);
+ }
+
+ _connection_changed_process_all_dirty (self,
+ FALSE,
+ sett_flags,
+ sett_mask,
+ FALSE,
+ update_reason);
+
+ return TRUE;
}
+void
+nm_settings_delete_connection (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ gboolean remove_from_disk,
+ gboolean allow_add_to_no_auto_default)
+{
+ NMSettingsStorage *cur_storage;
+ gs_free_error GError *local = NULL;
+ SettConnEntry *sett_conn_entry = NULL;
+ const char *uuid;
+
+ g_return_if_fail (NM_IS_SETTINGS (self));
+ g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn));
+
+ cur_storage = nm_settings_connection_get_storage (sett_conn);
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (cur_storage));
+
+ uuid = nm_settings_storage_get_uuid (cur_storage);
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ sett_conn_entry = _sett_conn_entries_get (self, uuid);
+
+ g_return_if_fail (sett_conn_entry);
+ nm_assert (sett_conn_entry->sett_conn == sett_conn);
+ nm_assert (sett_conn_entry->storage == cur_storage);
+
+ if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (cur_storage),
+ cur_storage,
+ remove_from_disk,
+ &local)) {
+ _LOGW ("add-connection: failed to delete storage "NM_SETTINGS_STORAGE_PRINT_FMT": %s",
+ NM_SETTINGS_STORAGE_PRINT_ARG (cur_storage),
+ local->message);
+ g_clear_error (&local);
+ /* there is no aborting back form this. We must get rid of the connection and
+ * cannot do better than warn. Proceed... */
+ }
+
+ _connection_changed_track (self, cur_storage, NULL, FALSE);
+ _connection_changed_process_all_dirty (self,
+ allow_add_to_no_auto_default,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ FALSE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE);
+}
+
+/*****************************************************************************/
+
static void
send_agent_owned_secrets (NMSettings *self,
NMSettingsConnection *sett_conn,
@@ -657,7 +1807,6 @@ pk_add_cb (NMAuthChain *chain,
gpointer callback_data;
NMAuthSubject *subject;
const char *perm;
- gboolean save_to_disk;
nm_assert (G_IS_DBUS_METHOD_INVOCATION (context));
@@ -675,10 +1824,14 @@ pk_add_cb (NMAuthChain *chain,
} else {
/* Authorized */
connection = nm_auth_chain_get_data (chain, "connection");
- nm_assert (connection);
+ nm_assert (NM_IS_CONNECTION (connection));
- save_to_disk = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "save-to-disk"));
- added = nm_settings_add_connection (self, connection, save_to_disk, &error);
+ nm_settings_add_connection (self,
+ connection,
+ GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "persist-mode")),
+ GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "sett-flags")),
+ &added,
+ &error);
/* The callback may remove the connection from the settings manager (e.g.
* because it's found to be incompatible with the device on AddAndActivate).
@@ -694,8 +1847,7 @@ pk_add_cb (NMAuthChain *chain,
callback (self, added, error, context, subject, callback_data);
/* Send agent-owned secrets to the agents */
- if ( !error
- && added
+ if ( added
&& nm_settings_has_connection (self, added))
send_agent_owned_secrets (self, added, subject);
}
@@ -703,7 +1855,8 @@ pk_add_cb (NMAuthChain *chain,
void
nm_settings_add_connection_dbus (NMSettings *self,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
NMAuthSubject *subject,
GDBusMethodInvocation *context,
NMSettingsAddCallback callback,
@@ -719,6 +1872,8 @@ nm_settings_add_connection_dbus (NMSettings *self,
g_return_if_fail (NM_IS_AUTH_SUBJECT (subject));
g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (context));
+ nm_assert (!NM_FLAGS_ANY (sett_flags, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
+
/* Connection must be valid, of course */
if (_nm_connection_verify (connection, &tmp_error) != NM_SETTING_VERIFY_SUCCESS) {
error = g_error_new (NM_SETTINGS_ERROR,
@@ -774,7 +1929,8 @@ nm_settings_add_connection_dbus (NMSettings *self,
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, "subject", g_object_ref (subject), g_object_unref);
- nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL);
+ nm_auth_chain_set_data (chain, "persist-mode", GUINT_TO_POINTER (persist_mode), NULL);
+ nm_auth_chain_set_data (chain, "sett-flags", GUINT_TO_POINTER (sett_flags), NULL);
nm_auth_chain_add_call_unsafe (chain, perm, TRUE);
return;
@@ -808,7 +1964,7 @@ static void
settings_add_connection_helper (NMSettings *self,
GDBusMethodInvocation *context,
GVariant *settings,
- gboolean save_to_disk)
+ NMSettingsConnectionPersistMode persist_mode)
{
gs_unref_object NMConnection *connection = NULL;
GError *error = NULL;
@@ -836,7 +1992,8 @@ settings_add_connection_helper (NMSettings *self,
nm_settings_add_connection_dbus (self,
connection,
- save_to_disk,
+ persist_mode,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
subject,
context,
settings_add_connection_add_cb,
@@ -856,7 +2013,7 @@ impl_settings_add_connection (NMDBusObject *obj,
gs_unref_variant GVariant *settings = NULL;
g_variant_get (parameters, "(@a{sa{sv}})", &settings);
- settings_add_connection_helper (self, invocation, settings, TRUE);
+ settings_add_connection_helper (self, invocation, settings, NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK);
}
static void
@@ -872,14 +2029,16 @@ impl_settings_add_connection_unsaved (NMDBusObject *obj,
gs_unref_variant GVariant *settings = NULL;
g_variant_get (parameters, "(@a{sa{sv}})", &settings);
- settings_add_connection_helper (self, invocation, settings, FALSE);
+ settings_add_connection_helper (self, invocation, settings, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY);
}
+/*****************************************************************************/
+
static void
impl_settings_load_connections (NMDBusObject *obj,
const NMDBusInterfaceInfoExtended *interface_info,
const NMDBusMethodInfoExtended *method_info,
- GDBusConnection *connection,
+ GDBusConnection *dbus_connection,
const char *sender,
GDBusMethodInvocation *invocation,
GVariant *parameters)
@@ -906,26 +2065,54 @@ impl_settings_load_connections (NMDBusObject *obj,
gsize i;
for (i = 0; filenames[i]; i++) {
- GSList *iter;
+ gboolean handled = FALSE;
if (filenames[i][0] != '/')
_LOGW ("load: connection filename '%s' is not an absolute path", filenames[i]);
else {
- for (iter = priv->plugins; iter; iter = iter->next) {
- NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
+ GSList *iter;
- if (nm_settings_plugin_load_connection (plugin, filenames[i]))
- goto next_filename;
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ NMSettingsPlugin *plugin = iter->data;
+ gs_unref_object NMSettingsStorage *storage = NULL;
+ gs_unref_object NMSettingsStorage *storage_replaced = NULL;
+ gs_unref_object NMConnection *connection_added = NULL;
+
+ if (!nm_settings_plugin_load_connection (plugin,
+ filenames[i],
+ &storage,
+ &connection_added,
+ &storage_replaced,
+ NULL))
+ continue;
+
+ nm_assert ((!storage && !storage_replaced) || (storage != storage_replaced));
+
+ if (storage) {
+ nm_assert (connection_added);
+ _connection_changed_track (self, storage, connection_added, TRUE);
+ }
+ if (storage_replaced)
+ _connection_changed_track (self, storage_replaced, NULL, FALSE);
+
+ handled = TRUE;
}
}
- if (!failures)
- failures = g_ptr_array_new ();
- g_ptr_array_add (failures, (char *) filenames[i]);
-
-next_filename:
- ;
+ if (!handled) {
+ if (!failures)
+ failures = g_ptr_array_new ();
+ g_ptr_array_add (failures, (char *) filenames[i]);
+ }
}
+
+ _connection_changed_process_all_dirty (self,
+ TRUE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+ TRUE,
+ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+ | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
}
if (failures)
@@ -937,6 +2124,8 @@ next_filename:
failures
? (const char **) failures->pdata
: NM_PTRARRAY_EMPTY (const char *)));
+
+ /* FIXME: add audit-logging. */
}
static void
@@ -949,8 +2138,6 @@ impl_settings_reload_connections (NMDBusObject *obj,
GVariant *parameters)
{
NMSettings *self = NM_SETTINGS (obj);
- NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
- GSList *iter;
/* The permission is already enforced by the D-Bus daemon, but we ensure
* that the caller is still alive so that clients are forced to wait and
@@ -963,13 +2150,11 @@ impl_settings_reload_connections (NMDBusObject *obj,
NM_SETTINGS_ERROR_PERMISSION_DENIED))
return;
- for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
- NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
-
- nm_settings_plugin_reload_connections (plugin);
- }
+ _plugin_connections_reload (self);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
+
+ /* FIXME: add audit-logging. */
}
/*****************************************************************************/
@@ -1019,20 +2204,24 @@ impl_settings_list_connections (NMDBusObject *obj,
NMSettingsConnection *
nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
{
- NMSettingsPrivate *priv;
- NMSettingsConnection *candidate;
-
g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
g_return_val_if_fail (uuid != NULL, NULL);
- priv = NM_SETTINGS_GET_PRIVATE (self);
+ return _sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid));
+}
- c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
- if (nm_streq (uuid, nm_settings_connection_get_uuid (candidate)))
- return candidate;
- }
+const char *
+nm_settings_get_dbus_path_for_uuid (NMSettings *self,
+ const char *uuid)
+{
+ NMSettingsConnection *sett_conn;
- return NULL;
+ sett_conn = nm_settings_get_connection_by_uuid (self, uuid);
+
+ if (!sett_conn)
+ return NULL;
+
+ return nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn));
}
static void
@@ -1249,6 +2438,9 @@ add_plugin (NMSettings *self,
nm_assert (NM_IS_SETTINGS (self));
nm_assert (NM_IS_SETTINGS_PLUGIN (plugin));
+ nm_assert (pname);
+ nm_assert (nm_streq0 (pname, nm_settings_plugin_get_plugin_name (plugin)));
+
priv = NM_SETTINGS_GET_PRIVATE (self);
nm_assert (!g_slist_find (priv->plugins, plugin));
@@ -1504,6 +2696,13 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
if (!nm_device_check_connection_compatible (device, connection, NULL))
continue;
+ if (nm_settings_connection_default_wired_get_device (sett_conn))
+ continue;
+
+ if (NM_FLAGS_ANY (nm_settings_connection_get_flags (sett_conn),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
+ continue;
+
iface = nm_setting_connection_get_interface_name (s_con);
if (nm_streq0 (iface, nm_device_get_iface (device)))
continue;
@@ -1535,38 +2734,20 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
}
static void
-default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, gboolean by_user, NMSettings *self)
-{
- NMDevice *device;
-
- if (!by_user)
- return;
-
- /* The connection has been changed by the user, it should no longer be
- * considered a default wired connection, and should no longer affect
- * the no-auto-default configuration option.
- */
- device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
- if (device)
- default_wired_clear_tag (self, device, connection, FALSE);
-}
-
-static void
default_wired_clear_tag (NMSettings *self,
NMDevice *device,
- NMSettingsConnection *connection,
+ NMSettingsConnection *sett_conn,
gboolean add_to_no_auto_default)
{
nm_assert (NM_IS_SETTINGS (self));
nm_assert (NM_IS_DEVICE (device));
- nm_assert (NM_IS_SETTINGS_CONNECTION (connection));
- nm_assert (device == g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ()));
- nm_assert (connection == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()));
+ nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+ nm_assert (device == nm_settings_connection_default_wired_get_device (sett_conn));
+ nm_assert (sett_conn == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()));
- g_object_set_qdata (G_OBJECT (connection), _default_wired_device_quark (), NULL);
- g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL);
+ nm_settings_connection_default_wired_set_device (sett_conn, NULL);
- g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
+ g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL);
if (add_to_no_auto_default)
nm_config_set_no_auto_default_for_device (NM_SETTINGS_GET_PRIVATE (self)->config, device);
@@ -1575,7 +2756,7 @@ default_wired_clear_tag (NMSettings *self,
static void
device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
{
- NMConnection *connection;
+ gs_unref_object NMConnection *connection = NULL;
NMSettingsConnection *added;
GError *error = NULL;
@@ -1598,10 +2779,12 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
if (!connection)
return;
- /* Add the connection */
- added = nm_settings_add_connection (self, connection, FALSE, &error);
- g_object_unref (connection);
-
+ nm_settings_add_connection (self,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &added,
+ &error);
if (!added) {
if (!g_error_matches (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_UUID_EXISTS)) {
_LOGW ("(%s) couldn't create default wired connection: %s",
@@ -1612,11 +2795,9 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
return;
}
- g_object_set_qdata (G_OBJECT (added), _default_wired_device_quark (), device);
- g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), added);
+ nm_settings_connection_default_wired_set_device (added, device);
- g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
- G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
+ g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), added);
_LOGI ("(%s): created default wired connection '%s'",
nm_device_get_iface (device),
@@ -1629,6 +2810,8 @@ nm_settings_device_added (NMSettings *self, NMDevice *device)
if (nm_device_is_real (device))
device_realized (device, NULL, self);
else {
+ /* FIXME(shutdown): we need to disconnect this signal handler during
+ * shutdown. */
g_signal_connect_after (device, "notify::" NM_DEVICE_REAL,
G_CALLBACK (device_realized),
self);
@@ -1652,7 +2835,43 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin
* remains up and can be assumed if NM starts again.
*/
if (quitting == FALSE)
- nm_settings_connection_delete (connection, NULL);
+ nm_settings_connection_delete (connection, TRUE);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+session_monitor_changed_cb (NMSessionMonitor *session_monitor,
+ NMSettings *self)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ NMSettingsConnection *const*list;
+ guint i, len;
+ guint generation;
+
+again:
+ list = nm_settings_get_connections (self, &len);
+ generation = priv->connections_generation;
+ for (i = 0; i < len; i++) {
+ gboolean is_visible;
+
+ is_visible = nm_settings_connection_check_visibility (list[i],
+ session_monitor);
+ nm_settings_connection_set_flags (list[i],
+ NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE,
+ is_visible);
+ if (generation != priv->connections_generation) {
+ /* the cached list was invalidated. Start again.
+ *
+ * Note that nm_settings_connection_recheck_visibility() will do nothing
+ * if the visibility didn't change (including emitting no signals,
+ * and not invalidating the list).
+ *
+ * Hence, for this to be an endless loop, the settings would have
+ * to constantly change the visibility flag and also invalidate the list. */
+ goto again;
+ }
}
}
@@ -1785,9 +3004,14 @@ nm_settings_start (NMSettings *self, GError **error)
{
NMSettingsPrivate *priv;
gs_strfreev char **plugins = NULL;
+ GSList *iter;
priv = NM_SETTINGS_GET_PRIVATE (self);
+ nm_assert (!priv->started);
+
+ priv->hostname_manager = g_object_ref (nm_hostname_manager_get ());
+
priv->kf_db_timestamps = nm_key_file_db_new (NMSTATEDIR "/timestamps",
"timestamps",
_kf_db_log_fcn,
@@ -1807,11 +3031,20 @@ nm_settings_start (NMSettings *self, GError **error)
if (!load_plugins (self, (const char *const*) plugins, error))
return FALSE;
- load_connections (self);
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
+
+ g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
+ G_CALLBACK (_plugin_unmanaged_specs_changed), self);
+ g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED,
+ G_CALLBACK (_plugin_unrecognized_specs_changed), self);
+ }
- check_startup_complete (self);
+ _plugin_unmanaged_specs_changed (NULL, self);
+ _plugin_unrecognized_specs_changed (NULL, self);
+
+ _plugin_connections_reload (self);
- priv->hostname_manager = g_object_ref (nm_hostname_manager_get ());
g_signal_connect (priv->hostname_manager,
"notify::"NM_HOSTNAME_MANAGER_HOSTNAME,
G_CALLBACK (_hostname_changed_cb),
@@ -1819,6 +3052,14 @@ nm_settings_start (NMSettings *self, GError **error)
if (nm_hostname_manager_get_hostname (priv->hostname_manager))
_notify (self, PROP_HOSTNAME);
+ priv->started = TRUE;
+ _startup_complete_check (self, 0);
+
+ /* FIXME(shutdown): we also need a nm_settings_stop() during shutdown.
+ *
+ * In particular, we need to remove all in-memory keyfiles from /run that are nm-generated.
+ * alternatively, the nm-generated flag must also be persisted and loaded to /run. */
+
return TRUE;
}
@@ -1848,14 +3089,11 @@ get_property (GObject *object, guint prop_id,
g_value_set_boolean (value, TRUE);
break;
case PROP_CONNECTIONS:
- if (priv->connections_loaded) {
- strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head,
- priv->connections_len,
- G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst),
- TRUE);
- g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv));
- } else
- g_value_set_boxed (value, NULL);
+ strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head,
+ priv->connections_len,
+ G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst),
+ TRUE);
+ g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv));
break;
case PROP_STARTUP_COMPLETE:
g_value_set_boolean (value, !nm_settings_get_startup_complete_blocked_reason (self));
@@ -1876,8 +3114,21 @@ nm_settings_init (NMSettings *self)
c_list_init (&priv->auth_lst_head);
c_list_init (&priv->connections_lst_head);
- priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
+ c_list_init (&priv->sce_dirty_lst_head);
+ priv->sce_idx = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal,
+ NULL, (GDestroyNotify) _sett_conn_entry_free);
+
priv->config = g_object_ref (nm_config_get ());
+
+ priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
+
+ priv->platform = g_object_ref (NM_PLATFORM_GET);
+
+ priv->session_monitor = g_object_ref (nm_session_monitor_get ());
+ g_signal_connect (priv->session_monitor,
+ NM_SESSION_MONITOR_CHANGED,
+ G_CALLBACK (session_monitor_changed_cb),
+ self);
}
NMSettings *
@@ -1893,6 +3144,12 @@ dispose (GObject *object)
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
CList *iter;
+ nm_assert (c_list_is_empty (&priv->sce_dirty_lst_head));
+ nm_assert (g_hash_table_size (priv->sce_idx) == 0);
+
+ nm_clear_g_source (&priv->startup_complete_timeout_id);
+ nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id);
+ nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy);
g_clear_object (&priv->startup_complete_blocked_by);
while ((iter = c_list_first (&priv->auth_lst_head)))
@@ -1905,6 +3162,13 @@ dispose (GObject *object)
g_clear_object (&priv->hostname_manager);
}
+ if (priv->session_monitor) {
+ g_signal_handlers_disconnect_by_func (priv->session_monitor,
+ G_CALLBACK (session_monitor_changed_cb),
+ self);
+ g_clear_object (&priv->session_monitor);
+ }
+
G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object);
}
@@ -1919,6 +3183,11 @@ finalize (GObject *object)
nm_assert (c_list_is_empty (&priv->connections_lst_head));
+ nm_assert (c_list_is_empty (&priv->sce_dirty_lst_head));
+ nm_assert (g_hash_table_size (priv->sce_idx) == 0);
+
+ nm_clear_pointer (&priv->sce_idx, g_hash_table_destroy);
+
g_slist_free_full (priv->unmanaged_specs, g_free);
g_slist_free_full (priv->unrecognized_specs, g_free);
@@ -1933,8 +3202,6 @@ finalize (GObject *object)
g_clear_object (&priv->agent_mgr);
- g_clear_object (&priv->config);
-
nm_clear_g_source (&priv->kf_db_flush_idle_id_timestamps);
nm_clear_g_source (&priv->kf_db_flush_idle_id_seen_bssids);
nm_key_file_db_to_file (priv->kf_db_timestamps, FALSE);
@@ -1943,6 +3210,10 @@ finalize (GObject *object)
nm_key_file_db_destroy (priv->kf_db_seen_bssids);
G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object);
+
+ g_clear_object (&priv->config);
+
+ g_clear_object (&priv->platform);
}
static const GDBusSignalInfo signal_info_new_connection = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
@@ -2113,7 +3384,7 @@ nm_settings_class_init (NMSettingsClass *class)
G_SIGNAL_RUN_FIRST,
0, NULL, NULL,
NULL,
- G_TYPE_NONE, 2, NM_TYPE_SETTINGS_CONNECTION, G_TYPE_BOOLEAN);
+ G_TYPE_NONE, 2, NM_TYPE_SETTINGS_CONNECTION, G_TYPE_UINT);
signals[CONNECTION_REMOVED] =
g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index 02d65377e0..737e917908 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -27,6 +27,8 @@
#include "nm-connection.h"
+#include "nm-settings-connection.h"
+
#define NM_TYPE_SETTINGS (nm_settings_get_type ())
#define NM_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS, NMSettings))
#define NM_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS, NMSettingsClass))
@@ -67,6 +69,7 @@ NMSettings *nm_settings_get (void);
#define NM_SETTINGS_GET (nm_settings_get ())
NMSettings *nm_settings_new (void);
+
gboolean nm_settings_start (NMSettings *self, GError **error);
typedef void (*NMSettingsAddCallback) (NMSettings *settings,
@@ -78,7 +81,8 @@ typedef void (*NMSettingsAddCallback) (NMSettings *settings,
void nm_settings_add_connection_dbus (NMSettings *self,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
NMAuthSubject *subject,
GDBusMethodInvocation *context,
NMSettingsAddCallback callback,
@@ -93,16 +97,37 @@ NMSettingsConnection **nm_settings_get_connections_clone (NMSettings *self,
GCompareDataFunc sort_compare_func,
gpointer sort_data);
-NMSettingsConnection *nm_settings_add_connection (NMSettings *settings,
- NMConnection *connection,
- gboolean save_to_disk,
- GError **error);
+gboolean nm_settings_add_connection (NMSettings *settings,
+ NMConnection *connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnection **out_sett_conn,
+ GError **error);
+
+gboolean nm_settings_update_connection (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ NMConnection *new_connection,
+ NMSettingsConnectionPersistMode persist_mode,
+ NMSettingsConnectionIntFlags sett_flags,
+ NMSettingsConnectionIntFlags sett_mask,
+ NMSettingsConnectionUpdateReason update_reason,
+ const char *log_context_name,
+ GError **error);
+
+void nm_settings_delete_connection (NMSettings *self,
+ NMSettingsConnection *sett_conn,
+ gboolean remove_from_disk,
+ gboolean allow_add_to_no_auto_default);
+
NMSettingsConnection *nm_settings_get_connection_by_path (NMSettings *settings,
const char *path);
NMSettingsConnection *nm_settings_get_connection_by_uuid (NMSettings *settings,
const char *uuid);
+const char *nm_settings_get_dbus_path_for_uuid (NMSettings *self,
+ const char *uuid);
+
gboolean nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection);
const GSList *nm_settings_get_unmanaged_specs (NMSettings *self);
diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build
index a238db940e..5f8268e7a4 100644
--- a/src/settings/plugins/ifcfg-rh/meson.build
+++ b/src/settings/plugins/ifcfg-rh/meson.build
@@ -35,7 +35,7 @@ libnms_ifcfg_rh_core = static_library(
dependencies: deps,
)
-sources = [dbus_sources] + core_sources + files('nms-ifcfg-rh-connection.c', 'nms-ifcfg-rh-plugin.c')
+sources = [dbus_sources] + core_sources + files('nms-ifcfg-rh-plugin.c')
libnm_settings_plugin_ifcfg_rh = shared_module(
'nm-settings-plugin-ifcfg-rh',
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
deleted file mode 100644
index 5caa861de9..0000000000
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
+++ /dev/null
@@ -1,400 +0,0 @@
-/* NetworkManager system settings service
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2008 - 2011 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nms-ifcfg-rh-connection.h"
-
-#include <sys/inotify.h>
-#include <glib/gstdio.h>
-
-#include "nm-dbus-interface.h"
-#include "nm-setting-connection.h"
-#include "nm-setting-wired.h"
-#include "nm-setting-wireless.h"
-#include "nm-setting-gsm.h"
-#include "nm-setting-cdma.h"
-#include "nm-setting-pppoe.h"
-#include "nm-setting-wireless-security.h"
-#include "nm-setting-8021x.h"
-#include "platform/nm-platform.h"
-#include "nm-config.h"
-
-#include "nms-ifcfg-rh-common.h"
-#include "nms-ifcfg-rh-reader.h"
-#include "nms-ifcfg-rh-writer.h"
-#include "nms-ifcfg-rh-utils.h"
-
-/*****************************************************************************/
-
-NM_GOBJECT_PROPERTIES_DEFINE_BASE (
- PROP_UNMANAGED_SPEC,
- PROP_UNRECOGNIZED_SPEC,
-);
-
-typedef struct {
- char *unmanaged_spec;
- char *unrecognized_spec;
-
- gulong devtimeout_link_changed_handler;
- guint devtimeout_timeout_id;
-} NMIfcfgConnectionPrivate;
-
-struct _NMIfcfgConnection {
- NMSettingsConnection parent;
- NMIfcfgConnectionPrivate _priv;
-};
-
-struct _NMIfcfgConnectionClass {
- NMSettingsConnectionClass parent;
-};
-
-G_DEFINE_TYPE (NMIfcfgConnection, nm_ifcfg_connection, NM_TYPE_SETTINGS_CONNECTION)
-
-#define NM_IFCFG_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIfcfgConnection, NM_IS_IFCFG_CONNECTION)
-
-/*****************************************************************************/
-
-static gboolean
-devtimeout_ready (gpointer user_data)
-{
- NMIfcfgConnection *self = user_data;
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
-
- priv->devtimeout_timeout_id = 0;
- nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), TRUE);
- return FALSE;
-}
-
-static void
-link_changed (NMPlatform *platform, int obj_type_i, int ifindex, const NMPlatformLink *link,
- int change_type_i,
- NMConnection *self)
-{
- const NMPlatformSignalChangeType change_type = change_type_i;
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) self);
- const char *ifname;
-
- ifname = nm_connection_get_interface_name (self);
- if (g_strcmp0 (link->name, ifname) != 0)
- return;
-
- if (change_type == NM_PLATFORM_SIGNAL_REMOVED)
- return;
-
- nm_log_info (LOGD_SETTINGS, "Device %s appeared; connection '%s' now ready",
- ifname, nm_connection_get_id (self));
-
- g_signal_handler_disconnect (platform, priv->devtimeout_link_changed_handler);
- priv->devtimeout_link_changed_handler = 0;
- g_source_remove (priv->devtimeout_timeout_id);
-
- /* Don't declare the connection ready right away, since NMManager may not have
- * started processing the device yet.
- */
- priv->devtimeout_timeout_id = g_idle_add (devtimeout_ready, self);
-}
-
-static gboolean
-devtimeout_expired (gpointer user_data)
-{
- NMIfcfgConnection *self = user_data;
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
-
- nm_log_info (LOGD_SETTINGS, "Device for connection '%s' did not appear before timeout",
- nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self)));
-
- g_signal_handler_disconnect (NM_PLATFORM_GET, priv->devtimeout_link_changed_handler);
- priv->devtimeout_link_changed_handler = 0;
- priv->devtimeout_timeout_id = 0;
-
- nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), TRUE);
- return FALSE;
-}
-
-static void
-nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self)
-{
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
- NMSettingConnection *s_con;
- const char *ifname;
- const char *filename;
- guint devtimeout;
- const NMPlatformLink *pllink;
-
- s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (self)));
-
- if (!nm_setting_connection_get_autoconnect (s_con))
- return;
- ifname = nm_setting_connection_get_interface_name (s_con);
- if (!ifname)
- return;
- filename = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (self));
- if (!filename)
- return;
-
- pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname);
- if (pllink && pllink->initialized)
- return;
-
- devtimeout = devtimeout_from_file (filename);
- if (!devtimeout)
- return;
-
- /* ONBOOT=yes, DEVICE and DEVTIMEOUT are set, but device is not present */
- nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), FALSE);
-
- nm_log_info (LOGD_SETTINGS, "Waiting %u seconds for %s to appear for connection '%s'",
- devtimeout, ifname, nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self)));
-
- priv->devtimeout_link_changed_handler =
- g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED,
- G_CALLBACK (link_changed), self);
- priv->devtimeout_timeout_id = g_timeout_add_seconds (devtimeout, devtimeout_expired, self);
-}
-
-const char *
-nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self)
-{
- g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), NULL);
-
- return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unmanaged_spec;
-}
-
-const char *
-nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self)
-{
- g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), NULL);
-
- return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unrecognized_spec;
-}
-
-static gboolean
-commit_changes (NMSettingsConnection *connection,
- NMConnection *new_connection,
- NMSettingsConnectionCommitReason commit_reason,
- NMConnection **out_reread_connection,
- char **out_logmsg_change,
- GError **error)
-{
- const char *filename;
- gs_unref_object NMConnection *reread = NULL;
- gboolean reread_same = TRUE;
- const char *operation_message;
- gs_free char *ifcfg_path = NULL;
-
- nm_assert (out_reread_connection && !*out_reread_connection);
- nm_assert (!out_logmsg_change || !*out_logmsg_change);
-
- filename = nm_settings_connection_get_filename (connection);
- if (!nms_ifcfg_rh_writer_write_connection (new_connection,
- IFCFG_DIR,
- filename,
- NULL,
- NULL,
- &ifcfg_path,
- &reread,
- &reread_same,
- error))
- return FALSE;
-
- nm_assert ((!filename && ifcfg_path) || (filename && !ifcfg_path));
- if (ifcfg_path) {
- nm_settings_connection_set_filename (connection, ifcfg_path);
- operation_message = "persist";
- } else
- operation_message = "update";
-
- if (reread && !reread_same)
- *out_reread_connection = g_steal_pointer (&reread);
-
- NM_SET_OUT (out_logmsg_change,
- g_strdup_printf ("ifcfg-rh: %s %s",
- operation_message, filename));
- return TRUE;
-}
-
-static gboolean
-delete (NMSettingsConnection *connection,
- GError **error)
-{
- const char *filename;
-
- filename = nm_settings_connection_get_filename (connection);
- if (filename) {
- gs_free char *keyfile = utils_get_keys_path (filename);
- gs_free char *routefile = utils_get_route_path (filename);
- gs_free char *route6file = utils_get_route6_path (filename);
-
- g_unlink (filename);
- if (keyfile)
- g_unlink (keyfile);
- if (routefile)
- g_unlink (routefile);
- if (route6file)
- g_unlink (route6file);
- }
-
- return TRUE;
-}
-
-/*****************************************************************************/
-
-static void
-get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
-{
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object);
-
- switch (prop_id) {
- case PROP_UNMANAGED_SPEC:
- g_value_set_string (value, priv->unmanaged_spec);
- break;
- case PROP_UNRECOGNIZED_SPEC:
- g_value_set_string (value, priv->unrecognized_spec);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-set_property (GObject *object, guint prop_id,
- const GValue *value, GParamSpec *pspec)
-{
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object);
-
- switch (prop_id) {
- case PROP_UNMANAGED_SPEC:
- priv->unmanaged_spec = g_value_dup_string (value);
- break;
- case PROP_UNRECOGNIZED_SPEC:
- priv->unrecognized_spec = g_value_dup_string (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-/*****************************************************************************/
-
-static void
-nm_ifcfg_connection_init (NMIfcfgConnection *self)
-{
-}
-
-NMIfcfgConnection *
-nm_ifcfg_connection_new (NMConnection *source,
- const char *full_path,
- GError **error,
- gboolean *out_ignore_error)
-{
- GObject *object;
- NMConnection *tmp;
- char *unhandled_spec = NULL;
- const char *unmanaged_spec = NULL, *unrecognized_spec = NULL;
-
- g_assert (source || full_path);
-
- if (out_ignore_error)
- *out_ignore_error = FALSE;
-
- /* If we're given a connection already, prefer that instead of re-reading */
- if (source)
- tmp = g_object_ref (source);
- else {
- tmp = connection_from_file (full_path,
- &unhandled_spec,
- error,
- out_ignore_error);
- if (!tmp)
- return NULL;
- }
-
- if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unmanaged:"))
- unmanaged_spec = unhandled_spec + strlen ("unmanaged:");
- else if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unrecognized:"))
- unrecognized_spec = unhandled_spec + strlen ("unrecognized:");
-
- object = (GObject *) g_object_new (NM_TYPE_IFCFG_CONNECTION,
- NM_SETTINGS_CONNECTION_FILENAME, full_path,
- NM_IFCFG_CONNECTION_UNMANAGED_SPEC, unmanaged_spec,
- NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, unrecognized_spec,
- NULL);
- /* Update our settings with what was read from the file */
- if (nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
- tmp,
- full_path
- ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED
- : NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- NULL,
- error))
- nm_ifcfg_connection_check_devtimeout (NM_IFCFG_CONNECTION (object));
- else
- g_clear_object (&object);
-
- g_object_unref (tmp);
- g_free (unhandled_spec);
- return (NMIfcfgConnection *) object;
-}
-
-static void
-dispose (GObject *object)
-{
- NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object);
-
- nm_clear_g_signal_handler (NM_PLATFORM_GET, &priv->devtimeout_link_changed_handler);
- nm_clear_g_source (&priv->devtimeout_timeout_id);
-
- g_clear_pointer (&priv->unmanaged_spec, g_free);
- g_clear_pointer (&priv->unrecognized_spec, g_free);
-
- G_OBJECT_CLASS (nm_ifcfg_connection_parent_class)->dispose (object);
-}
-
-static void
-nm_ifcfg_connection_class_init (NMIfcfgConnectionClass *ifcfg_connection_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (ifcfg_connection_class);
- NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (ifcfg_connection_class);
-
- object_class->set_property = set_property;
- object_class->get_property = get_property;
- object_class->dispose = dispose;
-
- settings_class->delete = delete;
- settings_class->commit_changes = commit_changes;
-
- obj_properties[PROP_UNMANAGED_SPEC] =
- g_param_spec_string (NM_IFCFG_CONNECTION_UNMANAGED_SPEC, "", "",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_UNRECOGNIZED_SPEC] =
- g_param_spec_string (NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, "", "",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-}
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h
deleted file mode 100644
index 8f5e77ef33..0000000000
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* NetworkManager system settings service
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2008 - 2011 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_IFCFG_CONNECTION_H__
-#define __NETWORKMANAGER_IFCFG_CONNECTION_H__
-
-#include "nm-dbus-interface.h"
-#include "settings/nm-settings-connection.h"
-
-#define NM_TYPE_IFCFG_CONNECTION (nm_ifcfg_connection_get_type ())
-#define NM_IFCFG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnection))
-#define NM_IFCFG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass))
-#define NM_IS_IFCFG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFCFG_CONNECTION))
-#define NM_IS_IFCFG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFCFG_CONNECTION))
-#define NM_IFCFG_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass))
-
-#define NM_IFCFG_CONNECTION_UNMANAGED_SPEC "unmanaged-spec"
-#define NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC "unrecognized-spec"
-
-typedef struct _NMIfcfgConnection NMIfcfgConnection;
-typedef struct _NMIfcfgConnectionClass NMIfcfgConnectionClass;
-
-GType nm_ifcfg_connection_get_type (void);
-
-NMIfcfgConnection *nm_ifcfg_connection_new (NMConnection *source,
- const char *full_path,
- GError **error,
- gboolean *out_ignore_error);
-
-const char *nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self);
-const char *nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self);
-
-gboolean nm_ifcfg_connection_update (NMIfcfgConnection *self,
- GHashTable *new_settings,
- GError **error);
-
-#endif /* __NETWORKMANAGER_IFCFG_CONNECTION_H__ */
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
index 6375b6cc14..b260b3f114 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -24,23 +24,24 @@
#include "nms-ifcfg-rh-plugin.h"
-#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <gmodule.h>
+#include <unistd.h>
+#include "nm-std-aux/c-list-util.h"
+#include "nm-glib-aux/nm-c-list.h"
+#include "nm-glib-aux/nm-io-utils.h"
#include "nm-std-aux/nm-dbus-compat.h"
-#include "nm-setting-connection.h"
-#include "settings/nm-settings-plugin.h"
+#include "nm-utils.h"
+#include "nm-core-internal.h"
#include "nm-config.h"
+#include "settings/nm-settings-plugin.h"
#include "NetworkManagerUtils.h"
-#include "nms-ifcfg-rh-connection.h"
#include "nms-ifcfg-rh-common.h"
+#include "nms-ifcfg-rh-utils.h"
#include "nms-ifcfg-rh-reader.h"
#include "nms-ifcfg-rh-writer.h"
-#include "nms-ifcfg-rh-utils.h"
-#include "shvar.h"
#define IFCFGRH1_BUS_NAME "com.redhat.ifcfgrh1"
#define IFCFGRH1_OBJECT_PATH "/com/redhat/ifcfgrh1"
@@ -49,6 +50,38 @@
/*****************************************************************************/
+struct _StorageData;
+
+typedef struct {
+ char *filename;
+ char *uuid;
+ CList rd_lst;
+ NMConnection *connection;
+ char *unmanaged_spec;
+ char *unrecognized_spec;
+ struct timespec stat_mtime;
+} ReadData;
+
+typedef struct _StorageData {
+ const char *filename;
+
+ CList sd_lst;
+
+ NMSettingsStorage *storage;
+ NMConnection *connection;
+
+ char *unmanaged_spec;
+ char *unrecognized_spec;
+
+ char filename_data[];
+} StorageData;
+
+typedef struct {
+ CList sd_lst_head;
+ GHashTable *uuid_idx;
+ GHashTable *filename_idx;
+} Storages;
+
typedef struct {
NMConfig *config;
@@ -59,23 +92,25 @@ typedef struct {
guint regist_id;
} dbus;
- GHashTable *connections; /* uuid::connection */
+ Storages storages;
+
+ GHashTable *unmanaged_specs;
+ GHashTable *unrecognized_specs;
- bool initialized:1;
-} SettingsPluginIfcfgPrivate;
+} NMSIfcfgRHPluginPrivate;
-struct _SettingsPluginIfcfg {
+struct _NMSIfcfgRHPlugin {
NMSettingsPlugin parent;
- SettingsPluginIfcfgPrivate _priv;
+ NMSIfcfgRHPluginPrivate _priv;
};
-struct _SettingsPluginIfcfgClass {
+struct _NMSIfcfgRHPluginClass {
NMSettingsPluginClass parent;
};
-G_DEFINE_TYPE (SettingsPluginIfcfg, settings_plugin_ifcfg, NM_TYPE_SETTINGS_PLUGIN)
+G_DEFINE_TYPE (NMSIfcfgRHPlugin, nms_ifcfg_rh_plugin, NM_TYPE_SETTINGS_PLUGIN)
-#define SETTINGS_PLUGIN_IFCFG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfcfg, SETTINGS_IS_PLUGIN_IFCFG)
+#define NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfcfgRHPlugin, NMS_IS_IFCFG_RH_PLUGIN, NMSettingsPlugin)
/*****************************************************************************/
@@ -90,531 +125,1238 @@ G_DEFINE_TYPE (SettingsPluginIfcfg, settings_plugin_ifcfg, NM_TYPE_SETTINGS_PLUG
/*****************************************************************************/
-static NMIfcfgConnection *update_connection (SettingsPluginIfcfg *plugin,
- NMConnection *source,
- const char *full_path,
- NMIfcfgConnection *connection,
- gboolean protect_existing_connection,
- GHashTable *protected_connections,
- GError **error);
+static void _unhandled_specs_reset (NMSIfcfgRHPlugin *self);
+
+static void _unhandled_specs_merge_read_data (NMSIfcfgRHPlugin *self,
+ CList *rd_lst_head);
/*****************************************************************************/
-static void
-connection_removed_cb (NMSettingsConnection *obj, gpointer user_data)
+static gboolean
+_connection_equals (NMConnection *a,
+ NMConnection *b)
+{
+ return nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT);
+}
+
+static gboolean
+_parse_unhandled_spec (const char *unhandled_spec,
+ char **out_unmanaged_spec,
+ char **out_unrecognized_spec)
{
- g_hash_table_remove (SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) user_data)->connections,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (obj)));
+ nm_assert (unhandled_spec);
+ nm_assert (out_unmanaged_spec && !*out_unmanaged_spec);
+ nm_assert (out_unrecognized_spec && !*out_unrecognized_spec);
+
+ if (NM_STR_HAS_PREFIX (unhandled_spec, "unmanaged:"))
+ *out_unmanaged_spec = g_strdup (&unhandled_spec[NM_STRLEN ("unmanaged:")]);
+ if (NM_STR_HAS_PREFIX (unhandled_spec, "unrecognized:"))
+ *out_unrecognized_spec = g_strdup (&unhandled_spec[NM_STRLEN ("unrecognized:")]);
+ else {
+ nm_assert_not_reached ();
+ return FALSE;
+ }
+
+ return TRUE;
}
+/*****************************************************************************/
+
static void
-remove_connection (SettingsPluginIfcfg *self, NMIfcfgConnection *connection)
+_read_data_destroy (ReadData *rd)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
- gboolean unmanaged, unrecognized;
+ c_list_unlink_stale (&rd->rd_lst);
+
+ g_free (rd->filename);
+ g_free (rd->uuid);
+ g_free (rd->unmanaged_spec);
+ g_free (rd->unrecognized_spec);
+ nm_g_object_unref (rd->connection);
- g_return_if_fail (self != NULL);
- g_return_if_fail (connection != NULL);
+ g_slice_free (ReadData, rd);
+}
- _LOGI ("remove "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection));
+static int
+_read_data_sort_by_mtime (const CList *p_a,
+ const CList *p_b,
+ const void *user_data)
+{
+ ReadData *a = c_list_entry (p_a, ReadData, rd_lst);
+ ReadData *b = c_list_entry (p_b, ReadData, rd_lst);
- unmanaged = !!nm_ifcfg_connection_get_unmanaged_spec (connection);
- unrecognized = !!nm_ifcfg_connection_get_unrecognized_spec (connection);
+ NM_CMP_FIELD (b, a, stat_mtime.tv_sec);
+ NM_CMP_FIELD (b, a, stat_mtime.tv_nsec);
+ NM_CMP_FIELD_STR (b, a, filename);
+ return 0;
+}
- g_object_ref (connection);
- g_hash_table_remove (priv->connections, nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection)));
- if (!unmanaged && !unrecognized)
- nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
- g_object_unref (connection);
+/*****************************************************************************/
- /* Emit changes _after_ removing the connection */
- if (unmanaged)
- _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
- if (unrecognized)
- _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
+static StorageData *
+_storage_data_new (Storages *storages,
+ const char *filename)
+{
+ StorageData *sd;
+ gsize l = strlen (filename) + 1;
+
+ sd = g_malloc (sizeof (StorageData) + l);
+ sd->filename = sd->filename_data;
+ memcpy (sd->filename_data, filename, l);
+ c_list_init (&sd->sd_lst);
+ sd->unmanaged_spec = NULL;
+ sd->unrecognized_spec = NULL;
+ sd->connection = NULL;
+ sd->storage = NULL;
+
+ c_list_link_tail (&storages->sd_lst_head, &sd->sd_lst);
+ if (!g_hash_table_add (storages->filename_idx, sd))
+ nm_assert_not_reached ();
+
+ return sd;
}
-static NMIfcfgConnection *
-find_by_path (SettingsPluginIfcfg *self, const char *path)
+static void
+_storage_data_destroy (StorageData *sd)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
- GHashTableIter iter;
- NMSettingsConnection *candidate = NULL;
+ c_list_unlink_stale (&sd->sd_lst);
+ nm_g_object_unref (sd->storage);
+ g_free (sd->unmanaged_spec);
+ g_free (sd->unrecognized_spec);
+ nm_g_object_unref (sd->connection);
- g_return_val_if_fail (path != NULL, NULL);
+ g_free (sd);
+}
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
- if (g_strcmp0 (path, nm_settings_connection_get_filename (candidate)) == 0)
- return NM_IFCFG_CONNECTION (candidate);
- }
- return NULL;
+static StorageData *
+_storages_by_filename (Storages *storages,
+ const char *filename)
+{
+ return g_hash_table_lookup (storages->filename_idx, &filename);
}
-static NMIfcfgConnection *
-update_connection (SettingsPluginIfcfg *self,
- NMConnection *source,
- const char *full_path,
- NMIfcfgConnection *connection,
- gboolean protect_existing_connection,
- GHashTable *protected_connections,
- GError **error)
+static StorageData *
+_storages_by_uuid (Storages *storages,
+ const char *uuid)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
- NMIfcfgConnection *connection_new;
- NMIfcfgConnection *connection_by_uuid;
- GError *local = NULL;
- const char *new_unmanaged = NULL, *old_unmanaged = NULL;
- const char *new_unrecognized = NULL, *old_unrecognized = NULL;
- gboolean unmanaged_changed = FALSE, unrecognized_changed = FALSE;
- const char *uuid;
- gboolean ignore_error = FALSE;
-
- g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL);
- g_return_val_if_fail (full_path || source, NULL);
-
- if (full_path)
- _LOGD ("loading from file \"%s\"...", full_path);
-
- /* Create a NMIfcfgConnection instance, either by reading from @full_path or
- * based on @source. */
- connection_new = nm_ifcfg_connection_new (source, full_path, &local, &ignore_error);
- if (!connection_new) {
- /* Unexpected failure. Probably the file is invalid? */
- if ( connection
- && !protect_existing_connection
- && (!protected_connections || !g_hash_table_contains (protected_connections, connection)))
- remove_connection (self, connection);
- if (!source) {
- _NMLOG (ignore_error ? LOGL_DEBUG : LOGL_WARN,
- "loading \"%s\" fails: %s", full_path, local ? local->message : "(unknown reason)");
- }
- g_propagate_error (error, local);
- return NULL;
- }
+ return g_hash_table_lookup (storages->uuid_idx, uuid);
+}
+
+static void
+_storages_init (Storages *storages)
+{
+ c_list_init (&storages->sd_lst_head);
+ storages->filename_idx = g_hash_table_new_full (nm_pstr_hash,
+ nm_pstr_equal,
+ (GDestroyNotify) _storage_data_destroy,
+ NULL);
+ storages->uuid_idx = g_hash_table_new (nm_str_hash,
+ g_str_equal);
+}
+
+/*****************************************************************************/
+
+static void
+nm_assert_self (NMSIfcfgRHPlugin *self)
+{
+ nm_assert (NMS_IS_IFCFG_RH_PLUGIN (self));
+
+#if NM_MORE_ASSERTS > 5
+ {
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ StorageData *sd_f;
+ StorageData *sd_u;
+ gsize n_uuid;
+ gs_unref_hashtable GHashTable *h_unmanaged = NULL;
+ gs_unref_hashtable GHashTable *h_unrecognized = NULL;
+
+ nm_assert (g_hash_table_size (priv->storages.filename_idx) == c_list_length (&priv->storages.sd_lst_head));
+
+ h_unmanaged = g_hash_table_new (nm_str_hash, g_str_equal);
+ h_unrecognized = g_hash_table_new (nm_str_hash, g_str_equal);
+
+ n_uuid = 0;
+
+ c_list_for_each_entry (sd_f, &priv->storages.sd_lst_head, sd_lst) {
+
+ nm_assert (!sd_f->unmanaged_spec || strlen (sd_f->unmanaged_spec) > 0);
+ nm_assert (!sd_f->unrecognized_spec || strlen (sd_f->unrecognized_spec) > 0);
+ nm_assert (!sd_f->connection || NM_IS_CONNECTION (sd_f->connection));
+ nm_assert (!sd_f->storage || NM_IS_SETTINGS_STORAGE (sd_f->storage));
+ nm_assert ((!!sd_f->connection) == (!!sd_f->storage));
+
+ nm_assert (NM_STR_HAS_PREFIX (sd_f->filename, IFCFG_DIR));
- uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new));
- connection_by_uuid = g_hash_table_lookup (priv->connections, uuid);
+ nm_assert (sd_f == _storages_by_filename (&priv->storages, sd_f->filename));
- if ( connection
- && connection != connection_by_uuid) {
+ nm_assert ((!!sd_f->connection) + (!!sd_f->unmanaged_spec) + (!!sd_f->unrecognized_spec) == 1);
- if ( (protect_existing_connection && connection_by_uuid != NULL)
- || (protected_connections && g_hash_table_contains (protected_connections, connection))) {
- NMIfcfgConnection *conflicting = (protect_existing_connection && connection_by_uuid != NULL) ? connection_by_uuid : connection;
+ if (sd_f->connection) {
+ const char *uuid = nm_settings_storage_get_uuid (sd_f->storage);
+ const char *k;
- if (source)
- _LOGW ("cannot update protected connection "NM_IFCFG_CONNECTION_LOG_FMT" due to conflicting UUID %s", NM_IFCFG_CONNECTION_LOG_ARG (conflicting), uuid);
+ nm_assert (nm_streq0 (nm_connection_get_uuid (sd_f->connection), uuid));
+
+ if (!g_hash_table_lookup_extended (priv->storages.uuid_idx, uuid, (gpointer *) &k, (gpointer *) &sd_u))
+ nm_assert_not_reached ();
+
+ nm_assert (uuid == k);
+ nm_assert (sd_f == sd_u);
+
+ nm_assert (_nm_connection_verify (sd_f->connection, NULL) == NM_SETTING_VERIFY_SUCCESS);
+
+ nm_assert (nm_streq (sd_f->filename, nm_settings_storage_get_filename (sd_f->storage)));
+
+ n_uuid++;
+ } else if (sd_f->unmanaged_spec)
+ g_hash_table_add (h_unmanaged, sd_f->unmanaged_spec);
else
- _LOGW ("cannot load %s due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, full_path, NM_IFCFG_CONNECTION_LOG_ARG (conflicting));
- g_object_unref (connection_new);
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Cannot update protected connection due to conflicting UUID");
- return NULL;
+ g_hash_table_add (h_unrecognized, sd_f->unrecognized_spec);
}
- /* The new connection has a different UUID then the original one that we
- * are about to update. Remove @connection. */
- remove_connection (self, connection);
+ nm_assert (g_hash_table_size (priv->storages.uuid_idx) == n_uuid);
+
+ nm_assert (nm_utils_hashtable_same_keys (h_unmanaged, priv->unmanaged_specs));
+ nm_assert (nm_utils_hashtable_same_keys (h_unrecognized, priv->unrecognized_specs));
}
+#endif
+}
- /* Check if the found connection with the same UUID is not protected from updating. */
- if ( connection_by_uuid
- && ( (!connection && protect_existing_connection)
- || (protected_connections && g_hash_table_contains (protected_connections, connection_by_uuid)))) {
- if (source)
- _LOGW ("cannot update connection due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_by_uuid));
- else
- _LOGW ("cannot load %s due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, full_path, NM_IFCFG_CONNECTION_LOG_ARG (connection_by_uuid));
- g_object_unref (connection_new);
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Skip updating protected connection during reload");
- return NULL;
- }
-
- /* Evaluate unmanaged/unrecognized flags. */
- if (connection_by_uuid)
- old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (connection_by_uuid);
- new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (connection_new);
- unmanaged_changed = g_strcmp0 (old_unmanaged, new_unmanaged);
-
- if (connection_by_uuid)
- old_unrecognized = nm_ifcfg_connection_get_unrecognized_spec (connection_by_uuid);
- new_unrecognized = nm_ifcfg_connection_get_unrecognized_spec (connection_new);
- unrecognized_changed = g_strcmp0 (old_unrecognized, new_unrecognized);
-
- if (connection_by_uuid) {
- const char *old_path;
-
- old_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid));
-
- if ( !unmanaged_changed
- && !unrecognized_changed
- && nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)),
- nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
- NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
- NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
- if ( old_path
- && !nm_streq0 (old_path, full_path)) {
- _LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT" without other changes",
- nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)),
- NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- }
- } else {
+/*****************************************************************************/
- /*******************************************************
- * UPDATE
- *******************************************************/
+static gboolean
+load_connection_impl (NMSIfcfgRHPlugin *self,
+ const char *filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ GError **error)
+{
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ gs_free char *ifcfg_path = NULL;
+ gs_unref_object NMConnection *connection = NULL;
+ gs_free char *unhandled_spec = NULL;
+ gs_free char *unmanaged_spec = NULL;
+ gs_free char *unrecognized_spec = NULL;
+ gs_free_error GError *local = NULL;
+ gboolean load_error_ignore;
+ StorageData *sd_f;
+ StorageData *sd_u;
+ const char *uuid;
+ gboolean connection_changed;
- if (source)
- _LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT" from %s", NM_IFCFG_CONNECTION_LOG_ARG (connection_new), NM_IFCFG_CONNECTION_LOG_PATH (old_path));
- else if (nm_streq0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new))))
- _LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- else if (old_path)
- _LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT, old_path, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- else
- _LOGI ("update and persist "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
-
- g_object_set (connection_by_uuid,
- NM_IFCFG_CONNECTION_UNMANAGED_SPEC, new_unmanaged,
- NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, new_unrecognized,
- NULL);
-
- if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid),
- nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
- NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "ifcfg-update",
- &local)) {
- /* Shouldn't ever get here as 'connection_new' was verified by the reader already
- * and the UUID did not change. */
- g_assert_not_reached ();
- }
- g_assert_no_error (local);
-
- if (new_unmanaged || new_unrecognized) {
- if (!old_unmanaged && !old_unrecognized) {
- /* ref connection first, because we put it into priv->connections below.
- * Emitting signal-removed might otherwise delete it. */
- g_object_ref (connection_by_uuid);
-
- /* Unexport the connection by telling the settings service it's
- * been removed.
- */
- nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection_by_uuid));
-
- /* signal_remove() will end up removing the connection from our hash,
- * so add it back now.
- */
- g_hash_table_insert (priv->connections,
- g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_by_uuid))),
- connection_by_uuid /* we took reference above and pass it on */);
- }
+ if (nm_utils_file_is_in_path (filename, IFCFG_DIR)) {
+ /* get the real ifcfg-path. This allows us to properly
+ * handle load command using a route-* file etc. */
+ ifcfg_path = utils_detect_ifcfg_path (filename, FALSE);
+ }
+
+ if (!ifcfg_path) {
+ g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "path name does not name an ifcfg file");
+ return FALSE;
+ }
+
+ sd_f = _storages_by_filename (&priv->storages, ifcfg_path);
+
+ connection = connection_from_file (ifcfg_path,
+ &unhandled_spec,
+ &local,
+ &load_error_ignore);
+ if (local) {
+
+ if ( sd_f
+ && nm_utils_file_stat (ifcfg_path, NULL) == -ENOENT) {
+
+ if (sd_f->connection) {
+ _LOGT ("load[%s]: %s (%s) deleted (file no longer present)",
+ sd_f->filename,
+ nm_settings_storage_get_uuid (sd_f->storage),
+ nm_connection_get_id (sd_f->connection));
+ *out_storage_replaced = g_object_ref (sd_f->storage);
+ if (!g_hash_table_remove (priv->storages.uuid_idx, nm_settings_storage_get_uuid (sd_f->storage)))
+ nm_assert_not_reached ();
} else {
- if (old_unmanaged /* && !new_unmanaged */) {
- _LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" and its device because NM_CONTROLLED was true.",
- NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (connection_by_uuid));
- } else if (old_unrecognized /* && !new_unrecognized */) {
- _LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" because it is now a recognized type.",
- NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (connection_by_uuid));
- }
+ _LOGT ("load[%s]: %s spec \"%s\" deleted (file no longer present)",
+ sd_f->filename,
+ sd_f->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd_f->unmanaged_spec ?: sd_f->unrecognized_spec);
}
+ if (!g_hash_table_remove (priv->storages.filename_idx, sd_f))
+ nm_assert_not_reached ();
+ return TRUE;
+ }
+
+ _LOGT ("load[\"%s\"]: failed to load connection: %s", ifcfg_path, local->message);
+ g_propagate_error (error, g_steal_pointer (&local));
+ return FALSE;
+ }
+
+ if (unhandled_spec) {
+ g_clear_object (&connection);
+ if (!_parse_unhandled_spec (unhandled_spec,
+ &unmanaged_spec,
+ &unrecognized_spec))
+ g_return_val_if_reached (FALSE);
+ } else
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+ uuid = connection ? nm_connection_get_uuid (connection) : NULL;
+ sd_u = connection ? _storages_by_uuid (&priv->storages, uuid) : NULL;
+
+ /* There is a limitation of the load-API of NMSettingsPlugin. nm_settings_plugin_load_connection()
+ * can only load one file at at time. That means, on the first file the plugin might notice
+ * that a profile got moved/deleted, only to notice at the next file that the profile ss still
+ * present.
+ *
+ * Note that while NMSettings loads the file names one by one, it only keeps track
+ * of the result and processes them all at. That means, NMSettings corrects this
+ * limitation.
+ *
+ * The only problem here is our debug logging might indicate a removal, when we would later
+ * find out that it was a rename. But detecting complex renames/moves is anyway so complicated
+ * that it's not worth to fix this only for the purpose of a coherent logging output.
+ */
- if (unmanaged_changed)
- _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
- if (unrecognized_changed)
- _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
+ if (!sd_f) {
+ sd_f = _storage_data_new (&priv->storages, ifcfg_path);
+
+ if (!connection) {
+ sd_f->unmanaged_spec = g_steal_pointer (&unmanaged_spec);
+ sd_f->unrecognized_spec = g_steal_pointer (&unrecognized_spec);
+ _LOGT ("load[%s]: %s spec \"%s\" added",
+ sd_f->filename,
+ sd_f->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd_f->unmanaged_spec ?: sd_f->unrecognized_spec);
+ _unhandled_specs_reset (self);
+ return TRUE;
}
- nm_settings_connection_set_filename (NM_SETTINGS_CONNECTION (connection_by_uuid), full_path);
- g_object_unref (connection_new);
- return connection_by_uuid;
- } else {
- /*******************************************************
- * ADD
- *******************************************************/
+ if (!sd_u) {
+ sd_f->storage = nm_settings_storage_new (NM_SETTINGS_PLUGIN (self),
+ uuid,
+ sd_f->filename);
+ sd_f->connection = g_object_ref (connection);
+ _LOGT ("load[%s]: add %s (%s)",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (connection));
+ if (!g_hash_table_replace (priv->storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd_f->storage), sd_f))
+ nm_assert_not_reached ();
+ } else {
- if (source)
- _LOGI ("add connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- else
- _LOGI ("new connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
- g_hash_table_insert (priv->connections,
- g_strdup (uuid),
- connection_new /* take reference */);
-
- g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED,
- G_CALLBACK (connection_removed_cb),
- self);
-
- if (nm_ifcfg_connection_get_unmanaged_spec (connection_new)) {
- _LOGI ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" due to NM_CONTROLLED=no. Unmanaged: %s.",
- NM_IFCFG_CONNECTION_LOG_ARG (connection_new),
- nm_ifcfg_connection_get_unmanaged_spec (connection_new));
- } else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new))
- _LOGW ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" of unrecognized type.", NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
-
- if (!source) {
- /* Only raise the signal if we were called without source, i.e. if we read the connection from file.
- * Otherwise, we were called by add_connection() which does not expect the signal. */
- if (nm_ifcfg_connection_get_unmanaged_spec (connection_new))
- _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
- else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new))
- _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
+ sd_f->storage = g_object_ref (sd_u->storage);
+ connection_changed = !_connection_equals (connection, sd_u->connection);
+ if (!connection_changed)
+ sd_f->connection = g_object_ref (sd_u->connection);
else {
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (connection_new));
+ nmtst_connection_assert_unchanging (connection);
+ sd_f->connection = g_object_ref (connection);
}
+
+ _LOGT ("load[%s]: update %s (%s) (%s%s \"%s\")",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (sd_f->connection),
+ connection_changed ? "modified, " : "",
+ nm_utils_file_stat (sd_u->filename, NULL) == -ENOENT ? "renamed from" : "shadows previous file",
+ sd_u->filename);
+
+ if (g_hash_table_replace (priv->storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd_f->storage), sd_f))
+ nm_assert_not_reached ();
+
+ g_hash_table_remove (priv->storages.filename_idx, sd_u);
+ _nm_settings_storage_set_filename (sd_f->storage, sd_f->filename);
}
- return connection_new;
+
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+ return TRUE;
}
-}
-static GHashTable *
-_paths_from_connections (GHashTable *connections)
-{
- GHashTableIter iter;
- NMIfcfgConnection *connection;
- GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal);
+ if (!connection) {
+
+ if ( nm_streq0 (unmanaged_spec, sd_f->unmanaged_spec)
+ && nm_streq0 (unrecognized_spec, sd_f->unrecognized_spec)) {
+ _LOGT ("load[%s]: %s spec \"%s\" unchanged",
+ sd_f->filename,
+ sd_f->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd_f->unmanaged_spec ?: sd_f->unrecognized_spec);
+ return TRUE;
+ }
- g_hash_table_iter_init (&iter, connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
- const char *path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection));
+ if (!sd_f->connection) {
+ _LOGT ("load[%s]: %s spec \"%s\" changed (was %s spec \"%s\" before)",
+ sd_f->filename,
+ unmanaged_spec ? "unmanaged" : "unrecognized",
+ unmanaged_spec ?: unrecognized_spec,
+ sd_f->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd_f->unmanaged_spec ?: sd_f->unrecognized_spec);
+ } else {
+ _LOGT ("load[%s]: %s spec \"%s\" changed (was %s (%s) before)",
+ sd_f->filename,
+ unmanaged_spec ? "unmanaged" : "unrecognized",
+ unmanaged_spec ?: unrecognized_spec,
+ nm_settings_storage_get_uuid (sd_f->storage),
+ nm_connection_get_id (sd_f->connection));
+ }
+ g_free (sd_f->unmanaged_spec);
+ g_free (sd_f->unrecognized_spec);
+ sd_f->unmanaged_spec = g_steal_pointer (&unmanaged_spec);
+ sd_f->unrecognized_spec = g_steal_pointer (&unrecognized_spec);
+ g_clear_object (&sd_f->connection);
+ *out_storage_replaced = g_steal_pointer (&sd_f->storage);
+ return TRUE;
+ }
- if (path)
- g_hash_table_add (paths, (void *) path);
+ if (!sd_u) {
+ _LOGT ("load[%s]: add %s (%s) (was %s spec \"%s\")",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (connection),
+ sd_f->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd_f->unmanaged_spec ?: sd_f->unrecognized_spec);
+
+ nm_clear_g_free (&sd_f->unmanaged_spec);
+ nm_clear_g_free (&sd_f->unrecognized_spec);
+ sd_f->connection = g_steal_pointer (&connection);
+ sd_f->storage = nm_settings_storage_new (NM_SETTINGS_PLUGIN (self), uuid, sd_f->filename);
+
+ if (!g_hash_table_replace (priv->storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd_f->storage), sd_f))
+ nm_assert_not_reached ();
+
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+ return TRUE;
}
- return paths;
-}
-static int
-_sort_paths (const char **f1, const char **f2, GHashTable *paths)
-{
- struct stat st;
- gboolean c1, c2;
- gint64 m1, m2;
+ if (sd_f == sd_u) {
+
+ connection_changed = !_connection_equals (connection, sd_f->connection);
+
+ if (!connection_changed) {
+ _LOGT ("load[%s]: unchanged %s (%s)",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (sd_f->connection));
+ } else {
+ _LOGT ("load[%s]: update %s (%s) (modified)",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (connection));
+ nm_g_object_ref_set (&sd_f->connection, connection);
+ }
- c1 = !!g_hash_table_contains (paths, *f1);
- c2 = !!g_hash_table_contains (paths, *f2);
- if (c1 != c2)
- return c1 ? -1 : 1;
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+ return TRUE;
+ }
+
+ connection_changed = !_connection_equals (connection, sd_u->connection);
+
+ _LOGT ("load[%s]: update %s (%s) (%sfile %s \"%s\" replacing %s (%s))",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (connection),
+ connection_changed ? "modified, " : "",
+ nm_utils_file_stat (sd_u->filename, NULL) == -ENOENT ? "renamed from" : "shadows previous file",
+ sd_u->filename,
+ nm_settings_storage_get_uuid (sd_f->storage),
+ nm_connection_get_id (sd_f->connection));
+
+ if (connection_changed)
+ nm_g_object_ref_set (&sd_f->connection, sd_u->connection);
+ else
+ nm_g_object_ref_set (&sd_f->connection, connection);
+
+ *out_storage_replaced = g_steal_pointer (&sd_f->storage);
+
+ sd_f->storage = g_object_ref (sd_u->storage);
+
+ _nm_settings_storage_set_filename (sd_f->storage, sd_f->filename);
+
+ if (g_hash_table_replace (priv->storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd_f->storage), sd_f))
+ nm_assert_not_reached ();
+
+ g_hash_table_remove (priv->storages.filename_idx, sd_u);
+
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+ return TRUE;
+}
- m1 = stat (*f1, &st) == 0 ? (gint64) st.st_mtime : G_MININT64;
- m2 = stat (*f2, &st) == 0 ? (gint64) st.st_mtime : G_MININT64;
- if (m1 != m2)
- return m1 > m2 ? -1 : 1;
+static gboolean
+load_connection (NMSettingsPlugin *plugin,
+ const char *filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ GError **error)
+{
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
+ gboolean success;
- return strcmp (*f1, *f2);
+ nm_assert_self (self);
+ success = load_connection_impl (self, filename, out_storage, out_connection, out_storage_replaced, error);
+ nm_assert_self (self);
+ return success;
}
static void
-read_connections (SettingsPluginIfcfg *plugin)
+reload_connections (NMSettingsPlugin *plugin,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ gs_unref_hashtable GHashTable *rd_idx_by_uuid = NULL;
+ gs_unref_hashtable GHashTable *rd_idx_by_filename = NULL;
+ gs_free_error GError *local = NULL;
+ CList rd_lst_head;
+ StorageData *sd_safe;
+ StorageData *sd;
+ StorageData *sd2;
+ ReadData *rd_safe;
+ ReadData *rd;
GDir *dir;
- GError *err = NULL;
- const char *item;
- GHashTable *alive_connections;
- GHashTableIter iter;
- NMIfcfgConnection *connection;
- GPtrArray *dead_connections = NULL;
- guint i;
- GPtrArray *filenames;
- GHashTable *paths;
-
- dir = g_dir_open (IFCFG_DIR, 0, &err);
+ gs_unref_ptrarray GPtrArray *storages_changed = NULL;
+ Storages new_storages = { };
+
+ nm_assert_self (self);
+
+ storages_changed = g_ptr_array_new_with_free_func (g_object_unref);
+
+ c_list_init (&rd_lst_head);
+
+ rd_idx_by_uuid = g_hash_table_new (nm_str_hash, g_str_equal);
+ rd_idx_by_filename = g_hash_table_new (nm_pstr_hash, nm_pstr_equal);
+
+ /* load the list of filenames... */
+
+ dir = g_dir_open (IFCFG_DIR, 0, &local);
if (!dir) {
- _LOGW ("Could not read directory '%s': %s", IFCFG_DIR, err->message);
- g_error_free (err);
- return;
+ _LOGW ("Could not read directory '%s': %s", IFCFG_DIR, local->message);
+ g_clear_error (&local);
+ } else {
+ const char *item;
+
+ while ((item = g_dir_read_name (dir))) {
+ gs_free char *full_path = NULL;
+ gs_free char *real_path = NULL;
+ struct stat st;
+
+ full_path = g_build_filename (IFCFG_DIR, item, NULL);
+ real_path = utils_detect_ifcfg_path (full_path, TRUE);
+ if (!real_path)
+ continue;
+
+ rd = g_hash_table_lookup (rd_idx_by_filename, &real_path);
+ if (rd)
+ continue;
+
+ if (stat (real_path, &st) != 0) {
+ int errsv = errno;
+
+ _LOGT ("load[%s]: failure to stat file: %s", real_path, nm_strerror_native (errsv));
+ continue;
+ }
+
+ rd = g_slice_new (ReadData);
+ *rd = (ReadData) {
+ .filename = g_steal_pointer (&real_path),
+ .stat_mtime = st.st_mtim,
+ };
+ c_list_link_tail (&rd_lst_head, &rd->rd_lst);
+ g_hash_table_add (rd_idx_by_filename, rd);
+ }
+ g_dir_close (dir);
}
- alive_connections = g_hash_table_new (nm_direct_hash, NULL);
+ /* load the connections/unhandled-spec from the filenames... */
+
+ c_list_sort (&rd_lst_head, _read_data_sort_by_mtime, NULL);
+
+ c_list_for_each_entry_safe (rd, rd_safe, &rd_lst_head, rd_lst) {
+ gs_free_error GError *load_error = NULL;
+ gs_free char *unhandled_spec = NULL;
+ gboolean load_error_ignore;
+ const char *uuid;
+ ReadData *rd_dup;
+
+ rd->connection = connection_from_file (rd->filename,
+ &unhandled_spec,
+ &load_error,
+ &load_error_ignore);
+ if (load_error) {
+ _NMLOG (load_error_ignore ? LOGL_TRACE : LOGL_WARN,
+ "load[%s]: failure to read file: %s", rd->filename, load_error->message);
+ goto destroy_rd_1;
+ }
+
+ if (unhandled_spec) {
+ g_clear_object (&rd->connection);
+ if (!_parse_unhandled_spec (unhandled_spec,
+ &rd->unmanaged_spec,
+ &rd->unrecognized_spec))
+ goto destroy_rd_1;
+ continue;
+ }
+
+ nm_assert (NM_IS_CONNECTION (rd->connection));
+ nm_assert (_nm_connection_verify (rd->connection, NULL) == NM_SETTING_VERIFY_SUCCESS);
- filenames = g_ptr_array_new_with_free_func (g_free);
- while ((item = g_dir_read_name (dir))) {
- char *full_path, *real_path;
+ nmtst_connection_assert_unchanging (rd->connection);
- full_path = g_build_filename (IFCFG_DIR, item, NULL);
- real_path = utils_detect_ifcfg_path (full_path, TRUE);
+ uuid = nm_connection_get_uuid (rd->connection);
+ nm_assert (nm_utils_is_uuid (uuid));
- if (real_path)
- g_ptr_array_add (filenames, real_path);
- g_free (full_path);
+ rd_dup = g_hash_table_lookup (rd_idx_by_uuid, uuid);
+ if (rd_dup) {
+ _LOGW ("load[%s]: file (uuid %s) shadowed by file \"%s\"",
+ uuid,
+ rd->filename,
+ rd_dup->filename);
+ goto destroy_rd_1;
+ }
+
+ rd->uuid = g_strdup (uuid);
+ g_hash_table_insert (rd_idx_by_uuid, rd->uuid, rd);
+ continue;
+
+destroy_rd_1:
+ g_hash_table_remove (rd_idx_by_filename, rd);
+ _read_data_destroy (rd);
}
- g_dir_close (dir);
- /* While reloading, we don't replace connections that we already loaded while
- * iterating over the files.
+ /* when we reload all files, we must signal add/update/modify of profiles one-by-one.
+ * NMSettings then goes ahead and emits further signals and a lot of things happen.
*
- * To have sensible, reproducible behavior, sort the paths by last modification
- * time preferring older files.
- */
- paths = _paths_from_connections (priv->connections);
- g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths);
- g_hash_table_destroy (paths);
-
- for (i = 0; i < filenames->len; i++) {
- connection = update_connection (plugin, NULL, filenames->pdata[i], NULL, FALSE, alive_connections, NULL);
- if (connection)
- g_hash_table_add (alive_connections, connection);
- }
- g_ptr_array_free (filenames, TRUE);
-
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
- if ( !g_hash_table_contains (alive_connections, connection)
- && nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection))) {
- if (!dead_connections)
- dead_connections = g_ptr_array_new ();
- g_ptr_array_add (dead_connections, connection);
+ * So, first, emit an update of the unmanaged/unrecognized specs that contains *all*
+ * the unmanaged/unrecognized devices from before and after. Since both unmanaged/unrecognized
+ * specs have the meaning of "not doing something", it makes sense that we temporarily
+ * disable that action for the sum of before and after. */
+ _unhandled_specs_merge_read_data (self, &rd_lst_head);
+
+
+ _storages_init (&new_storages);
+
+
+ /* first find and remove the storage-data that is gone entirely. */
+
+ c_list_for_each_entry_safe (sd, sd_safe, &priv->storages.sd_lst_head, sd_lst) {
+
+ rd = g_hash_table_lookup (rd_idx_by_filename, &sd->filename);
+
+ if (!sd->connection) {
+ if (!rd) {
+ _LOGT ("load[%s]: %s spec \"%s\" deleted (file no longer present)",
+ sd->filename,
+ sd->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd->unmanaged_spec ?: sd->unrecognized_spec);
+ g_hash_table_remove (priv->storages.filename_idx, sd);
+ }
+ continue;
+ }
+
+ if ( !rd
+ && !g_hash_table_lookup (rd_idx_by_uuid, nm_settings_storage_get_uuid (sd->storage))) {
+ _LOGT ("load[%s]: %s (%s) deleted (file no longer present)",
+ sd->filename,
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection));
+ g_ptr_array_add (storages_changed, g_object_ref (sd->storage));
+ g_hash_table_remove (rd_idx_by_uuid, nm_settings_storage_get_uuid (sd->storage));
+ g_hash_table_remove (priv->storages.uuid_idx, nm_settings_storage_get_uuid (sd->storage));
+ g_hash_table_remove (priv->storages.filename_idx, sd);
}
}
- g_hash_table_destroy (alive_connections);
- if (dead_connections) {
- for (i = 0; i < dead_connections->len; i++)
- remove_connection (plugin, dead_connections->pdata[i]);
- g_ptr_array_free (dead_connections, TRUE);
+ c_list_for_each_entry (rd, &rd_lst_head, rd_lst) {
+
+ sd = _storage_data_new (&new_storages, rd->filename);
+
+ sd->unmanaged_spec = g_strdup (rd->unmanaged_spec);
+ sd->unrecognized_spec = g_strdup (rd->unrecognized_spec);
+ sd->connection = g_object_ref (rd->connection);
+
+ if (!rd->connection)
+ continue;
+
+ sd2 = _storages_by_uuid (&priv->storages, rd->uuid);
+ if (sd2)
+ sd->storage = g_object_ref (sd2->storage);
+ else {
+ sd->storage = nm_settings_storage_new (NM_SETTINGS_PLUGIN (plugin),
+ rd->uuid,
+ rd->filename);
+ }
+
+ if (!g_hash_table_replace (new_storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd->storage), sd))
+ nm_assert_not_reached ();
}
+
+ if (_LOGT_ENABLED ()) {
+ c_list_for_each_entry (sd, &new_storages.sd_lst_head, sd_lst) {
+ StorageData *sd_f;
+ StorageData *sd_u;
+ gboolean connection_changed;
+
+ sd_f = _storages_by_filename (&priv->storages, sd->filename);
+
+ if (!sd->connection) {
+
+ if (!sd_f) {
+ _LOGT ("load[%s]: %s spec \"%s\" added",
+ sd->filename,
+ sd->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd->unmanaged_spec ?: sd->unrecognized_spec);
+ continue;
+ }
+
+ if ( nm_streq0 (sd->unmanaged_spec, sd_f->unmanaged_spec)
+ && nm_streq0 (sd->unrecognized_spec, sd_f->unrecognized_spec)) {
+ _LOGT ("load[%s]: %s spec \"%s\" unchanged",
+ sd->filename,
+ sd->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd->unmanaged_spec ?: sd->unrecognized_spec);
+ continue;
+ }
+
+ if (!sd_f->connection) {
+ _LOGT ("load[%s]: %s spec \"%s\" changed (was %s spec \"%s\" before)",
+ sd->filename,
+ sd->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd->unmanaged_spec ?: sd->unrecognized_spec,
+ sd_f->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd_f->unmanaged_spec ?: sd_f->unrecognized_spec);
+ continue;
+ }
+
+ sd2 = _storages_by_uuid (&new_storages, nm_settings_storage_get_uuid (sd_f->storage));
+
+ if (!sd2) {
+ _LOGT ("load[%s]: %s spec \"%s\" changed (was %s (%s) before)",
+ sd->filename,
+ sd->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd->unmanaged_spec ?: sd->unrecognized_spec,
+ nm_settings_storage_get_uuid (sd_f->storage),
+ nm_connection_get_id (sd_f->connection));
+ continue;
+ }
+
+ _LOGT ("load[%s]: %s spec \"%s\" changed (was %s (%s) before which got renamed to \"%s\")",
+ sd->filename,
+ sd->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd->unmanaged_spec ?: sd->unrecognized_spec,
+ nm_settings_storage_get_uuid (sd_f->storage),
+ nm_connection_get_id (sd_f->connection),
+ sd2->filename);
+ continue;
+
+ }
+
+ sd_u = _storages_by_uuid (&priv->storages, nm_settings_storage_get_uuid (sd->storage));
+
+ if (!sd_u) {
+ if (!sd_f) {
+ _LOGT ("load[%s]: add %s (%s)",
+ sd->filename,
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection));
+ } else {
+ nm_assert (!sd_f->connection);
+ _LOGT ("load[%s]: add %s (%s) (was %s spec \"%s\")",
+ sd->filename,
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection),
+ sd_f->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd_f->unmanaged_spec ?: sd_f->unrecognized_spec);
+ }
+ continue;
+ }
+
+ connection_changed = !_connection_equals (sd->connection, sd_u->connection);
+
+ if (sd_u == sd_f) {
+ if (!connection_changed) {
+ _LOGT ("load[%s]: unchanged %s (%s)",
+ sd->filename,
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection));
+ continue;
+ }
+ _LOGT ("load[%s]: update %s (%s) (modified)",
+ sd->filename,
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection));
+ continue;
+ }
+
+ if (!sd_f) {
+ _LOGT ("load[%s]: update %s (%s) (%sfile renamed from \"%s\")",
+ sd->filename,
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection),
+ connection_changed ? "modified, " : "",
+ sd_u->filename);
+ continue;
+ }
+
+ if (!sd_f->connection) {
+ _LOGT ("load[%s]: update %s (%s) (%sfile renamed from \"%s\" replacing %s spec \"%s\")",
+ sd->filename,
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection),
+ connection_changed ? "modified, " : "",
+ sd_u->filename,
+ sd_f->unmanaged_spec ? "unmanaged" : "unrecognized",
+ sd_f->unmanaged_spec ?: sd_f->unrecognized_spec);
+ continue;
+ }
+
+ rd = g_hash_table_lookup (rd_idx_by_uuid, nm_settings_storage_get_uuid (sd_f->storage));
+
+ if (!rd) {
+ _LOGT ("load[%s]: update %s (%s) (%sfile renamed from \"%s\" replacing %s (%s))",
+ sd->filename,
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection),
+ connection_changed ? "modified, " : "",
+ sd_u->filename,
+ nm_settings_storage_get_uuid (sd_f->storage),
+ nm_connection_get_id (sd_f->connection));
+ continue;
+ }
+
+ _LOGT ("load[%s]: update %s (%s) (%sfile renamed from \"%s\" and renaming previous %s (%s) to \"%s\")",
+ sd->filename,
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection),
+ connection_changed ? "modified, " : "",
+ sd_u->filename,
+ nm_settings_storage_get_uuid (sd_f->storage),
+ nm_connection_get_id (sd_f->connection),
+ rd->filename);
+ }
+ }
+
+
+ c_list_for_each_entry (sd, &new_storages.sd_lst_head, sd_lst) {
+ if (sd->connection) {
+ _nm_settings_storage_set_filename (sd->storage, sd->filename);
+ g_ptr_array_add (storages_changed, g_object_ref (sd->storage));
+ }
+ }
+
+
+ nm_clear_pointer (&rd_idx_by_uuid, g_hash_table_destroy);
+ nm_clear_pointer (&rd_idx_by_filename, g_hash_table_destroy);
+
+ while ((rd = c_list_first_entry (&rd_lst_head, ReadData, rd_lst)))
+ _read_data_destroy (rd);
+
+
+ nm_clear_pointer (&priv->storages.uuid_idx, g_hash_table_destroy);
+ nm_clear_pointer (&priv->storages.filename_idx, g_hash_table_destroy);
+
+ nm_assert (c_list_is_empty (&priv->storages.sd_lst_head));
+
+ priv->storages.uuid_idx = new_storages.uuid_idx;
+ priv->storages.filename_idx= new_storages.filename_idx;
+ c_list_swap (&priv->storages.sd_lst_head, &new_storages.sd_lst_head);
+
+
+ if (storages_changed->len > 0) {
+ gs_unref_hashtable GHashTable *h_unique = NULL;
+ guint i;
+
+ h_unique = g_hash_table_new (nm_direct_hash, NULL);
+
+ for (i = 0; i < storages_changed->len; i++) {
+ NMSettingsStorage *storage = storages_changed->pdata[i];
+
+ if (!g_hash_table_add (h_unique, storage))
+ continue;
+
+ sd = _storages_by_uuid (&priv->storages, nm_settings_storage_get_uuid (storage));
+ callback (NM_SETTINGS_PLUGIN (self),
+ storage,
+ sd && sd->connection ? sd->connection : NULL,
+ user_data);
+ }
+ }
+
+
+ _unhandled_specs_reset (self);
+
+ nm_assert_self (self);
}
-static GSList *
-get_connections (NMSettingsPlugin *config)
+typedef struct {
+ GHashTable *filename_idx;
+ const char *allowed_filename;
+} AllowFilenameData;
+
+#define ALLOW_FILENAME_DATA(_filename_idx, _allowed_filename) \
+ (&((AllowFilenameData) { \
+ .filename_idx = (_filename_idx), \
+ .allowed_filename = (_allowed_filename), \
+ }))
+
+static gboolean
+_allow_filename_cb (const char *filename,
+ gpointer user_data)
{
- SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config);
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin);
- GSList *list = NULL;
- GHashTableIter iter;
- NMIfcfgConnection *connection;
+ const AllowFilenameData *allow_filename_data = user_data;
+
+ if (!g_hash_table_contains (allow_filename_data->filename_idx, &filename))
+ return TRUE;
+ if ( allow_filename_data->allowed_filename
+ && nm_streq (allow_filename_data->allowed_filename, filename))
+ return TRUE;
+ return FALSE;
+}
- if (!priv->initialized) {
- read_connections (plugin);
- priv->initialized = TRUE;
+static gboolean
+add_connection (NMSettingsPlugin *plugin,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
+{
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ gs_unref_object NMConnection *reread = NULL;
+ gs_free char *full_filename = NULL;
+ GError *local = NULL;
+ const char *uuid;
+ StorageData *sd_f;
+ gboolean reread_same;
+
+ nm_assert_self (self);
+ nm_assert (NM_IS_CONNECTION (connection));
+ nm_assert (out_storage && !*out_storage);
+ nm_assert (out_connection && !*out_connection);
+
+ uuid = nm_connection_get_uuid (connection);
+
+ if (_storages_by_uuid (&priv->storages, uuid)) {
+ _LOGT ("add: %s, \"%s\": failed to add connection that already exists",
+ uuid,
+ nm_connection_get_id (connection));
+ g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "connection with UUID %s already exists",
+ uuid);
+ return FALSE;
}
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
- if ( !nm_ifcfg_connection_get_unmanaged_spec (connection)
- && !nm_ifcfg_connection_get_unrecognized_spec (connection))
- list = g_slist_prepend (list, connection);
+ if (!nms_ifcfg_rh_writer_write_connection (connection,
+ IFCFG_DIR,
+ NULL,
+ _allow_filename_cb,
+ ALLOW_FILENAME_DATA (priv->storages.filename_idx, NULL),
+ &full_filename,
+ &reread,
+ &reread_same,
+ &local)) {
+ _LOGT ("add: %s (%s): failed to add connection: %s",
+ nm_connection_get_uuid (connection),
+ nm_connection_get_id (connection),
+ local->message);
+ g_propagate_error (error, local);
+ return FALSE;
}
- return list;
+ if ( !reread
+ || reread_same)
+ nm_g_object_ref_set (&reread, connection);
+
+ uuid = nm_connection_get_uuid (reread);
+
+ nm_assert (full_filename && full_filename[0] == '/');
+
+ _LOGT ("add[%s]: profile %s (%s) written%s",
+ full_filename,
+ uuid,
+ nm_connection_get_id (reread),
+ _connection_equals (connection, reread) ? "" : " (profile mutated)");
+
+ sd_f = _storage_data_new (&priv->storages, full_filename);
+
+ sd_f->connection = g_steal_pointer (&reread);
+ sd_f->storage = nm_settings_storage_new (plugin, uuid, full_filename);
+
+ nmtst_connection_assert_unchanging (sd_f->connection);
+
+ if (!g_hash_table_replace (priv->storages.uuid_idx, (char *) nm_settings_storage_get_uuid (sd_f->storage), sd_f))
+ nm_assert_not_reached ();
+
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+
+ nm_assert_self (self);
+
+ return TRUE;
}
static gboolean
-load_connection (NMSettingsPlugin *config,
- const char *filename)
+update_connection (NMSettingsPlugin *plugin,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
{
- SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config);
- NMIfcfgConnection *connection;
- char *ifcfg_path;
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ StorageData *sd_f;
+ const char *filename;
+ const char *uuid;
+ GError *local = NULL;
+ gs_unref_object NMConnection *reread = NULL;
+ gboolean connection_changed;
+ gboolean reread_same;
- if (!nm_utils_file_is_in_path (filename, IFCFG_DIR))
- return FALSE;
+ nm_assert_self (self);
+ nm_assert (NM_IS_CONNECTION (connection));
+ nm_assert (nm_connection_verify (connection, NULL));
+ nm_assert (!error || !*error);
+
+ uuid = nm_settings_storage_get_uuid (storage);
+
+ nm_assert (nm_streq0 (uuid, nm_connection_get_uuid (connection)));
+
+ filename = nm_settings_storage_get_filename (storage);
+
+ nm_assert (filename);
- /* get the real ifcfg-path. This allows us to properly
- * handle load command using a route-* file etc. */
- ifcfg_path = utils_detect_ifcfg_path (filename, FALSE);
- if (!ifcfg_path)
+ sd_f = _storages_by_filename (&priv->storages, filename);
+
+ nm_assert (sd_f);
+ nm_assert (sd_f == _storages_by_uuid (&priv->storages, uuid));
+
+ if (!nms_ifcfg_rh_writer_write_connection (connection,
+ IFCFG_DIR,
+ sd_f->filename,
+ _allow_filename_cb,
+ ALLOW_FILENAME_DATA (priv->storages.filename_idx, sd_f->filename),
+ NULL,
+ &reread,
+ &reread_same,
+ &local)) {
+ _LOGT ("add: %s (%s): failed to update connection: %s",
+ nm_connection_get_uuid (connection),
+ nm_connection_get_id (connection),
+ local->message);
+ g_propagate_error (error, local);
return FALSE;
+ }
+
+ if ( !reread
+ || reread_same)
+ nm_g_object_ref_set (&reread, connection);
+
+ connection_changed = !_connection_equals (reread, sd_f->connection);
+
+ _LOGT ("update[%s]: %s (%s) %s%s",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (connection),
+ connection_changed ? "updated" : "unchanged",
+ _connection_equals (reread, connection) ? "" : " (profile mutated)");
+
+ if (connection_changed)
+ nm_g_object_ref_set (&sd_f->connection, reread);
+
+ *out_storage = g_object_ref (sd_f->storage);
+ *out_connection = g_object_ref (sd_f->connection);
+
+ nm_assert_self (self);
+
+ return TRUE;
+}
+
+static gboolean
+delete_connection (NMSettingsPlugin *plugin,
+ NMSettingsStorage *storage,
+ gboolean remove_from_disk,
+ GError **error)
+{
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ StorageData *sd_f;
+ const char *operation_message;
+ const char *filename;
+ const char *uuid;
+
+ nm_assert_self (self);
+ nm_assert (!error || !*error);
+
+ filename = nm_settings_storage_get_filename (storage);
+ uuid = nm_settings_storage_get_uuid (storage);
+
+ nm_assert (filename);
+
+ sd_f = _storages_by_filename (&priv->storages, filename);
+
+ g_return_val_if_fail (sd_f, TRUE);
- connection = find_by_path (plugin, ifcfg_path);
- update_connection (plugin, NULL, ifcfg_path, connection, TRUE, NULL, NULL);
- if (!connection)
- connection = find_by_path (plugin, ifcfg_path);
+ if (!remove_from_disk)
+ operation_message = "only dropped from memory";
+ else {
+ gs_free char *keyfile = utils_get_keys_path (filename);
+ gs_free char *routefile = utils_get_route_path (filename);
+ gs_free char *route6file = utils_get_route6_path (filename);
+ const char *const files[] = { filename, keyfile, routefile, route6file };
+ gboolean any_deleted = FALSE;
+ gboolean any_failure = FALSE;
+ int i;
- g_free (ifcfg_path);
- return (connection != NULL);
+ for (i = 0; i < G_N_ELEMENTS (files); i++) {
+ int errsv;
+
+ if (unlink (files[i]) == 0) {
+ any_deleted = TRUE;
+ continue;
+ }
+ errsv = errno;
+ if (errsv == ENOENT)
+ continue;
+
+ _LOGW ("delete[%s]: failure to delete file \"%s\": %s",
+ filename,
+ files[i],
+ nm_strerror_native (errsv));
+ any_failure = TRUE;
+ }
+ if (any_failure)
+ operation_message = "failed to delete files from disk";
+ else if (any_deleted)
+ operation_message = "deleted from disk";
+ else
+ operation_message = "does not exist on disk";
+ }
+
+ _LOGT ("delete[%s]: %s (%s) %s",
+ sd_f->filename,
+ uuid,
+ nm_connection_get_id (sd_f->connection),
+ operation_message);
+
+ if (!g_hash_table_remove (priv->storages.uuid_idx, uuid))
+ nm_assert_not_reached ();
+
+ if (!g_hash_table_remove (priv->storages.filename_idx, sd_f))
+ nm_assert_not_reached ();
+
+ nm_assert_self (self);
+
+ return TRUE;
}
+/*****************************************************************************/
+
static void
-reload_connections (NMSettingsPlugin *config)
+_unhandled_specs_reset (NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ gs_unref_hashtable GHashTable *unmanaged_specs = NULL;
+ gs_unref_hashtable GHashTable *unrecognized_specs = NULL;
+ StorageData *sd;
+
+ unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+ unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+
+ c_list_for_each_entry (sd, &priv->storages.sd_lst_head, sd_lst) {
+ if (sd->unmanaged_spec)
+ g_hash_table_add (unmanaged_specs, g_strdup (sd->unmanaged_spec));
+ if (sd->unrecognized_spec)
+ g_hash_table_add (unrecognized_specs, g_strdup (sd->unrecognized_spec));
+ }
+
+ if (!nm_utils_hashtable_same_keys (unmanaged_specs, priv->unmanaged_specs)) {
+ g_hash_table_unref (priv->unmanaged_specs);
+ priv->unmanaged_specs = g_steal_pointer (&unmanaged_specs);
+ }
+ if (!nm_utils_hashtable_same_keys (unrecognized_specs, priv->unrecognized_specs)) {
+ g_hash_table_unref (priv->unrecognized_specs);
+ priv->unrecognized_specs = g_steal_pointer (&unrecognized_specs);
+ }
- read_connections (plugin);
+ if (!unmanaged_specs)
+ _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
+ if (!unrecognized_specs)
+ _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
}
-static GSList *
-get_unhandled_specs (NMSettingsPlugin *config,
- const char *property)
-{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) config);
- GSList *list = NULL, *list_iter;
- GHashTableIter iter;
- gpointer connection;
- char *spec;
- gboolean found;
-
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, &connection)) {
- g_object_get (connection, property, &spec, NULL);
- if (spec) {
- /* Ignore duplicates */
- for (list_iter = list, found = FALSE; list_iter; list_iter = g_slist_next (list_iter)) {
- if (g_str_equal (list_iter->data, spec)) {
- found = TRUE;
- break;
- }
- }
- if (found)
- g_free (spec);
- else
- list = g_slist_prepend (list, spec);
+static void
+_unhandled_specs_merge_read_data (NMSIfcfgRHPlugin *self,
+ CList *rd_lst_head)
+{
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ gboolean unmanaged_changed = FALSE;
+ gboolean unrecognized_changed = FALSE;
+ ReadData *rd;
+
+ c_list_for_each_entry (rd, rd_lst_head, rd_lst) {
+ if ( rd->unmanaged_spec
+ && !g_hash_table_contains (priv->unmanaged_specs, rd->unmanaged_spec)) {
+ unmanaged_changed = TRUE;
+ g_hash_table_add (priv->unmanaged_specs, g_strdup (rd->unmanaged_spec));
+ }
+ if ( rd->unrecognized_spec
+ && !g_hash_table_contains (priv->unrecognized_specs, rd->unrecognized_spec)) {
+ unrecognized_changed = TRUE;
+ g_hash_table_add (priv->unrecognized_specs, g_strdup (rd->unrecognized_spec));
}
}
- return list;
+
+ if (unmanaged_changed)
+ _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
+ if (unrecognized_changed)
+ _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
}
static GSList *
-get_unmanaged_specs (NMSettingsPlugin *config)
+_unhandled_specs_from_hashtable (GHashTable *hash)
{
- return get_unhandled_specs (config, NM_IFCFG_CONNECTION_UNMANAGED_SPEC);
+ gs_free const char **keys = NULL;
+ GSList *list = NULL;
+ guint i, l;
+
+ keys = nm_utils_strdict_get_keys (hash, TRUE, &l);
+ for (i = l; i > 0; ) {
+ i--;
+ list = g_slist_prepend (list, g_strdup (keys[i]));
+ }
+ return list;
}
static GSList *
-get_unrecognized_specs (NMSettingsPlugin *config)
+get_unmanaged_specs (NMSettingsPlugin *plugin)
{
- return get_unhandled_specs (config, NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC);
+ return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unmanaged_specs);
}
-static NMSettingsConnection *
-add_connection (NMSettingsPlugin *config,
- NMConnection *connection,
- gboolean save_to_disk,
- GError **error)
+static GSList *
+get_unrecognized_specs (NMSettingsPlugin *plugin)
{
- SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (config);
- gs_free char *path = NULL;
- gs_unref_object NMConnection *reread = NULL;
-
- if (save_to_disk) {
- if (!nms_ifcfg_rh_writer_write_connection (connection, IFCFG_DIR, NULL, NULL, NULL, &path, &reread, NULL, error))
- return NULL;
- } else {
- if (!nms_ifcfg_rh_writer_can_write_connection (connection, error))
- return NULL;
- }
- return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error));
+ return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unrecognized_specs);
}
+/*****************************************************************************/
+
static void
-impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
+impl_ifcfgrh_get_ifcfg_details (NMSIfcfgRHPlugin *self,
GDBusMethodInvocation *context,
const char *in_ifcfg)
{
- NMIfcfgConnection *connection;
- NMSettingConnection *s_con;
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+ gs_free char *ifcfg_path = NULL;
+ StorageData *sd_f;
const char *uuid;
const char *path;
- gs_free char *ifcfg_path = NULL;
- if (!g_path_is_absolute (in_ifcfg)) {
+ if (in_ifcfg[0] != '/') {
g_dbus_method_invocation_return_error (context,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -631,10 +1373,9 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
return;
}
- connection = find_by_path (plugin, ifcfg_path);
- if ( !connection
- || nm_ifcfg_connection_get_unmanaged_spec (connection)
- || nm_ifcfg_connection_get_unrecognized_spec (connection)) {
+ sd_f = _storages_by_filename (&priv->storages, ifcfg_path);
+ if ( !sd_f
+ || !sd_f->connection) {
g_dbus_method_invocation_return_error (context,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -642,25 +1383,16 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
return;
}
- s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection)));
- if (!s_con) {
- g_dbus_method_invocation_return_error (context,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_FAILED,
- "unable to retrieve the connection setting");
- return;
- }
+ uuid = nm_settings_storage_get_uuid (sd_f->storage);
- uuid = nm_setting_connection_get_uuid (s_con);
- if (!uuid) {
- g_dbus_method_invocation_return_error (context,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_FAILED,
- "unable to get the UUID");
- return;
- }
+ /* It is ugly that the ifcfg-rh plugin needs to call back into NMSettings this
+ * way.
+ * There are alternatives (like invoking a signal), but they are all significant
+ * extra code (and performance overhead). So the quick and dirty solution here
+ * is likely to be simpler than getting this right (also from point of readability!).
+ */
+ path = nm_settings_get_dbus_path_for_uuid (nm_settings_get (), uuid);
- path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection));
if (!path) {
g_dbus_method_invocation_return_error (context,
NM_SETTINGS_ERROR,
@@ -676,9 +1408,9 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
/*****************************************************************************/
static void
-_dbus_clear (SettingsPluginIfcfg *self)
+_dbus_clear (NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
guint id;
nm_clear_g_signal_handler (priv->dbus.connection, &priv->dbus.signal_id);
@@ -700,7 +1432,7 @@ _dbus_connection_closed (GDBusConnection *connection,
gpointer user_data)
{
_LOGW ("dbus: %s bus closed", IFCFGRH1_BUS_NAME);
- _dbus_clear (SETTINGS_PLUGIN_IFCFG (user_data));
+ _dbus_clear (NMS_IFCFG_RH_PLUGIN (user_data));
/* Retry or recover? */
}
@@ -715,21 +1447,23 @@ _method_call (GDBusConnection *connection,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
- SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data);
- const char *ifcfg;
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (user_data);
- if ( !nm_streq (interface_name, IFCFGRH1_IFACE1_NAME)
- || !nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) {
- g_dbus_method_invocation_return_error (invocation,
- G_DBUS_ERROR,
- G_DBUS_ERROR_UNKNOWN_METHOD,
- "Unknown method %s",
- method_name);
- return;
+ if (nm_streq (interface_name, IFCFGRH1_IFACE1_NAME)) {
+ if (nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) {
+ const char *ifcfg;
+
+ g_variant_get (parameters, "(&s)", &ifcfg);
+ impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg);
+ return;
+ }
}
- g_variant_get (parameters, "(&s)", &ifcfg);
- impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_UNKNOWN_METHOD,
+ "Unknown method %s",
+ method_name);
}
static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO (
@@ -754,8 +1488,8 @@ _dbus_request_name_done (GObject *source_object,
gpointer user_data)
{
GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
- SettingsPluginIfcfg *self;
- SettingsPluginIfcfgPrivate *priv;
+ NMSIfcfgRHPlugin *self;
+ NMSIfcfgRHPluginPrivate *priv;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *ret = NULL;
guint32 result;
@@ -764,8 +1498,8 @@ _dbus_request_name_done (GObject *source_object,
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
- self = SETTINGS_PLUGIN_IFCFG (user_data);
- priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ self = NMS_IFCFG_RH_PLUGIN (user_data);
+ priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
g_clear_object (&priv->dbus.cancellable);
@@ -812,8 +1546,8 @@ _dbus_create_done (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
- SettingsPluginIfcfg *self;
- SettingsPluginIfcfgPrivate *priv;
+ NMSIfcfgRHPlugin *self;
+ NMSIfcfgRHPluginPrivate *priv;
gs_free_error GError *error = NULL;
GDBusConnection *connection;
@@ -821,8 +1555,8 @@ _dbus_create_done (GObject *source_object,
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
- self = SETTINGS_PLUGIN_IFCFG (user_data);
- priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ self = NMS_IFCFG_RH_PLUGIN (user_data);
+ priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
g_clear_object (&priv->dbus.cancellable);
@@ -856,9 +1590,9 @@ _dbus_create_done (GObject *source_object,
}
static void
-_dbus_setup (SettingsPluginIfcfg *self)
+_dbus_setup (NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
gs_free char *address = NULL;
gs_free_error GError *error = NULL;
@@ -886,9 +1620,9 @@ config_changed_cb (NMConfig *config,
NMConfigData *config_data,
NMConfigChangeFlags changes,
NMConfigData *old_data,
- SettingsPluginIfcfg *self)
+ NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfgPrivate *priv;
+ NMSIfcfgRHPluginPrivate *priv;
/* If the dbus connection for some reason is borked the D-Bus service
* won't be offered.
@@ -900,7 +1634,7 @@ config_changed_cb (NMConfig *config,
| NM_CONFIG_CHANGE_CAUSE_SIGUSR1))
return;
- priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
if ( !priv->dbus.connection
&& !priv->dbus.cancellable)
_dbus_setup (self);
@@ -909,23 +1643,26 @@ config_changed_cb (NMConfig *config,
/*****************************************************************************/
static void
-settings_plugin_ifcfg_init (SettingsPluginIfcfg *plugin)
+nms_ifcfg_rh_plugin_init (NMSIfcfgRHPlugin *self)
{
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) plugin);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
+
+ priv->config = g_object_ref (nm_config_get ());
- priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+ priv->unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+ priv->unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+
+ _storages_init (&priv->storages);
}
static void
constructed (GObject *object)
{
- SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (object);
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
- G_OBJECT_CLASS (settings_plugin_ifcfg_parent_class)->constructed (object);
+ G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->constructed (object);
- priv->config = nm_config_get ();
- g_object_add_weak_pointer ((GObject *) priv->config, (gpointer *) &priv->config);
g_signal_connect (priv->config,
NM_CONFIG_SIGNAL_CONFIG_CHANGED,
G_CALLBACK (config_changed_cb),
@@ -937,40 +1674,44 @@ constructed (GObject *object)
static void
dispose (GObject *object)
{
- SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (object);
- SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object);
+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
- if (priv->config) {
- g_object_remove_weak_pointer ((GObject *) priv->config, (gpointer *) &priv->config);
+ if (priv->config)
g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
- priv->config = NULL;
- }
+ /* FIXME(shutdown) we need a stop method so that we can unregistering the D-Bus service
+ * when NMSettings is shutting down, and not when the instance gets destroyed. */
_dbus_clear (self);
- if (priv->connections) {
- g_hash_table_destroy (priv->connections);
- priv->connections = NULL;
- }
+ nm_clear_pointer (&priv->storages.uuid_idx, g_hash_table_destroy);
+ nm_clear_pointer (&priv->storages.filename_idx, g_hash_table_destroy);
+
+ g_clear_object (&priv->config);
+
+ G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->dispose (object);
- G_OBJECT_CLASS (settings_plugin_ifcfg_parent_class)->dispose (object);
+ nm_clear_pointer (&priv->unmanaged_specs, g_hash_table_destroy);
+ nm_clear_pointer (&priv->unrecognized_specs, g_hash_table_destroy);
}
static void
-settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass)
+nms_ifcfg_rh_plugin_class_init (NMSIfcfgRHPluginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass);
object_class->constructed = constructed;
- object_class->dispose = dispose;
+ object_class->dispose = dispose;
- plugin_class->get_connections = get_connections;
- plugin_class->add_connection = add_connection;
- plugin_class->load_connection = load_connection;
- plugin_class->reload_connections = reload_connections;
- plugin_class->get_unmanaged_specs = get_unmanaged_specs;
+ plugin_class->plugin_name = "ifcfg-rh";
+ plugin_class->get_unmanaged_specs = get_unmanaged_specs;
plugin_class->get_unrecognized_specs = get_unrecognized_specs;
+ plugin_class->load_connection = load_connection;
+ plugin_class->reload_connections = reload_connections;
+ plugin_class->add_connection = add_connection;
+ plugin_class->update_connection = update_connection;
+ plugin_class->delete_connection = delete_connection;
}
/*****************************************************************************/
@@ -978,5 +1719,5 @@ settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass)
G_MODULE_EXPORT NMSettingsPlugin *
nm_settings_plugin_factory (void)
{
- return g_object_new (SETTINGS_TYPE_PLUGIN_IFCFG, NULL);
+ return g_object_new (NMS_TYPE_IFCFG_RH_PLUGIN, NULL);
}
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
index 88c3dc7939..1db36083ff 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h
@@ -20,19 +20,19 @@
* Copyright (C) 2007 - 2008 Red Hat, Inc.
*/
-#ifndef _PLUGIN_H_
-#define _PLUGIN_H_
+#ifndef __NMS_IFCFG_RH_PLUGIN_H__
+#define __NMS_IFCFG_RH_PLUGIN_H__
-#define SETTINGS_TYPE_PLUGIN_IFCFG (settings_plugin_ifcfg_get_type ())
-#define SETTINGS_PLUGIN_IFCFG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfg))
-#define SETTINGS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfgClass))
-#define SETTINGS_IS_PLUGIN_IFCFG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFCFG))
-#define SETTINGS_IS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFCFG))
-#define SETTINGS_PLUGIN_IFCFG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfgClass))
+#define NMS_TYPE_IFCFG_RH_PLUGIN (nms_ifcfg_rh_plugin_get_type ())
+#define NMS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPlugin))
+#define NMS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass))
+#define NMS_IS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFCFG_RH_PLUGIN))
+#define NMS_IS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFCFG_RH_PLUGIN))
+#define NMS_IFCFG_RH_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass))
-typedef struct _SettingsPluginIfcfg SettingsPluginIfcfg;
-typedef struct _SettingsPluginIfcfgClass SettingsPluginIfcfgClass;
+typedef struct _NMSIfcfgRHPlugin NMSIfcfgRHPlugin;
+typedef struct _NMSIfcfgRHPluginClass NMSIfcfgRHPluginClass;
-GType settings_plugin_ifcfg_get_type (void);
+GType nms_ifcfg_rh_plugin_get_type (void);
-#endif /* _PLUGIN_H_ */
+#endif /* __NMS_IFCFG_RH_PLUGIN_H__ */
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index 99e48d6b2a..177c60ff4a 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -6028,20 +6028,3 @@ nmtst_connection_from_file (const char *filename,
error,
NULL);
}
-
-guint
-devtimeout_from_file (const char *filename)
-{
- shvarFile *ifcfg;
- guint devtimeout;
-
- g_return_val_if_fail (filename != NULL, 0);
-
- ifcfg = svOpenFile (filename, NULL);
- if (!ifcfg)
- return 0;
-
- devtimeout = svGetValueInt64 (ifcfg, "DEVTIMEOUT", 10, 0, G_MAXUINT, 0);
- svCloseFile (ifcfg);
- return devtimeout;
-}
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h
index be402565a8..8008e052bc 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h
@@ -17,8 +17,8 @@
* Copyright (C) 2008 Red Hat, Inc.
*/
-#ifndef __READER_H__
-#define __READER_H__
+#ifndef __NMS_IFCFG_RH_READER_H__
+#define __NMS_IFCFG_RH_READER_H__
#include "nm-connection.h"
@@ -27,12 +27,10 @@ NMConnection *connection_from_file (const char *filename,
GError **error,
gboolean *out_ignore_error);
-guint devtimeout_from_file (const char *filename);
-
NMConnection *nmtst_connection_from_file (const char *filename,
const char *network_file,
const char *test_type,
char **out_unhandled,
GError **error);
-#endif /* __READER_H__ */
+#endif /* __NMS_IFCFG_RH_READER_H__ */
diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build
index 42edd4388a..365ae1a9c5 100644
--- a/src/settings/plugins/ifupdown/meson.build
+++ b/src/settings/plugins/ifupdown/meson.build
@@ -15,7 +15,6 @@ libnms_ifupdown_core = static_library(
)
sources = files(
- 'nms-ifupdown-connection.c',
'nms-ifupdown-plugin.c',
)
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c b/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
deleted file mode 100644
index 13187c4454..0000000000
--- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* NetworkManager system settings service (ifupdown)
- *
- * Alexander Sack <asac@ubuntu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2007,2008 Canonical Ltd.
- */
-
-#include "nm-default.h"
-
-#include "nms-ifupdown-connection.h"
-
-#include <glib/gstdio.h>
-
-#include "nm-dbus-interface.h"
-#include "nm-utils.h"
-#include "nm-setting-wireless-security.h"
-#include "settings/nm-settings-connection.h"
-#include "settings/nm-settings-plugin.h"
-
-#include "nms-ifupdown-parser.h"
-
-/*****************************************************************************/
-
-struct _NMIfupdownConnection {
- NMSettingsConnection parent;
-};
-
-struct _NMIfupdownConnectionClass {
- NMSettingsConnectionClass parent;
-};
-
-G_DEFINE_TYPE (NMIfupdownConnection, nm_ifupdown_connection, NM_TYPE_SETTINGS_CONNECTION)
-
-/*****************************************************************************/
-
-#define _NMLOG_PREFIX_NAME "ifupdown"
-#define _NMLOG_DOMAIN LOGD_SETTINGS
-#define _NMLOG(level, ...) \
- nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
- "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
- _NMLOG_PREFIX_NAME": " \
- _NM_UTILS_MACRO_REST (__VA_ARGS__))
-
-/*****************************************************************************/
-
-static void
-nm_ifupdown_connection_init (NMIfupdownConnection *connection)
-{
-}
-
-NMIfupdownConnection *
-nm_ifupdown_connection_new (if_block *block)
-{
- NMIfupdownConnection *connection;
- GError *error = NULL;
-
- g_return_val_if_fail (block != NULL, NULL);
-
- connection = g_object_new (NM_TYPE_IFUPDOWN_CONNECTION, NULL);
-
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- if (!ifupdown_update_connection_from_if_block (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection)),
- block,
- &error)) {
- _LOGW ("invalid connection read from /etc/network/interfaces: %s",
- error->message);
- g_object_unref (connection);
- return NULL;
- }
-
- return connection;
-}
-
-static void
-nm_ifupdown_connection_class_init (NMIfupdownConnectionClass *ifupdown_connection_class)
-{
-}
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.h b/src/settings/plugins/ifupdown/nms-ifupdown-connection.h
deleted file mode 100644
index eee3ae0ff1..0000000000
--- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* NetworkManager system settings service (ifupdown)
- *
- * Alexander Sack <asac@ubuntu.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2008 Canonical Ltd.
- */
-
-#ifndef __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__
-#define __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__
-
-#include "settings/nm-settings-connection.h"
-
-#include "nms-ifupdown-interface-parser.h"
-
-#define NM_TYPE_IFUPDOWN_CONNECTION (nm_ifupdown_connection_get_type ())
-#define NM_IFUPDOWN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnection))
-#define NM_IFUPDOWN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionClass))
-#define NM_IS_IFUPDOWN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFUPDOWN_CONNECTION))
-#define NM_IS_IFUPDOWN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFUPDOWN_CONNECTION))
-#define NM_IFUPDOWN_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionClass))
-
-typedef struct _NMIfupdownConnection NMIfupdownConnection;
-typedef struct _NMIfupdownConnectionClass NMIfupdownConnectionClass;
-
-GType nm_ifupdown_connection_get_type (void);
-
-NMIfupdownConnection *nm_ifupdown_connection_new (if_block *block);
-
-#endif /* __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__ */
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
index f83c5ea0ef..7e2cb5b92f 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
@@ -651,22 +651,20 @@ update_ip6_setting_from_if_block (NMConnection *connection,
return TRUE;
}
-gboolean
-ifupdown_update_connection_from_if_block (NMConnection *connection,
- if_block *block,
- GError **error)
+NMConnection *
+ifupdown_new_connection_from_if_block (if_block *block,
+ GError **error)
{
+ gs_unref_object NMConnection *connection = NULL;
const char *type;
gs_free char *idstr = NULL;
gs_free char *uuid = NULL;
NMSettingConnection *s_con;
- gboolean success = FALSE;
- s_con = nm_connection_get_setting_connection (connection);
- if (!s_con) {
- s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
- nm_connection_add_setting (connection, NM_SETTING (s_con));
- }
+ connection = nm_simple_connection_new ();
+
+ s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
type = _ifupdownplugin_guess_connection_type (block);
idstr = g_strconcat ("Ifupdown (", block->name, ")", NULL);
@@ -681,7 +679,7 @@ ifupdown_update_connection_from_if_block (NMConnection *connection,
NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
NULL);
- _LOGI ("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s",
+ _LOGD ("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s",
block->name, type, idstr, nm_setting_connection_get_uuid (s_con));
if (nm_streq (type, NM_SETTING_WIRED_SETTING_NAME))
@@ -691,13 +689,16 @@ ifupdown_update_connection_from_if_block (NMConnection *connection,
update_wireless_security_setting_from_if_block (connection, block);
}
- if (ifparser_haskey (block, "inet6"))
- success = update_ip6_setting_from_if_block (connection, block, error);
- else
- success = update_ip4_setting_from_if_block (connection, block, error);
+ if (ifparser_haskey (block, "inet6")) {
+ if (!update_ip6_setting_from_if_block (connection, block, error))
+ return FALSE;
+ } else {
+ if (!update_ip4_setting_from_if_block (connection, block, error))
+ return FALSE;
+ }
- if (success == TRUE)
- success = nm_connection_verify (connection, error);
+ if (!nm_connection_normalize (connection, NULL, NULL, error))
+ return NULL;
- return success;
+ return g_steal_pointer (&connection);
}
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
index c17e92392f..2e5b5e51a7 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
@@ -19,16 +19,13 @@
* (C) Copyright 2008 Canonical Ltd.
*/
-#ifndef __PARSER_H__
-#define __PARSER_H__
+#ifndef __NMS_IFUPDOWN_PARSER_H__
+#define __NMS_IFUPDOWN_PARSER_H__
#include "nm-connection.h"
-
#include "nms-ifupdown-interface-parser.h"
-gboolean
-ifupdown_update_connection_from_if_block (NMConnection *connection,
- if_block *block,
- GError **error);
+NMConnection *ifupdown_new_connection_from_if_block (if_block *block,
+ GError **error);
-#endif /* __PARSER_H__ */
+#endif /* __NMS_IFUPDOWN_PARSER_H__ */
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index 23f4015609..f8941021e5 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -24,23 +24,12 @@
#include "nms-ifupdown-plugin.h"
-#include <arpa/inet.h>
-#include <gmodule.h>
-
-#include "nm-setting-connection.h"
-#include "nm-dbus-interface.h"
-#include "settings/nm-settings-plugin.h"
-#include "nm-setting-ip4-config.h"
-#include "nm-setting-wireless.h"
-#include "nm-setting-wired.h"
-#include "nm-setting-ppp.h"
-#include "nm-utils.h"
#include "nm-core-internal.h"
-#include "NetworkManagerUtils.h"
#include "nm-config.h"
+#include "settings/nm-settings-plugin.h"
+#include "settings/nm-settings-storage.h"
#include "nms-ifupdown-interface-parser.h"
-#include "nms-ifupdown-connection.h"
#include "nms-ifupdown-parser.h"
#define ENI_INTERFACES_FILE "/etc/network/interfaces"
@@ -50,28 +39,33 @@
/*****************************************************************************/
typedef struct {
+ NMConnection *connection;
+ NMSettingsStorage *storage;
+} StorageData;
+
+typedef struct {
/* Stores an entry for blocks/interfaces read from /e/n/i and (if exists)
- * the NMIfupdownConnection associated with the block.
+ * the StorageData associated with the block.
*/
GHashTable *eni_ifaces;
bool ifupdown_managed:1;
bool initialized:1;
-} SettingsPluginIfupdownPrivate;
+} NMSIfupdownPluginPrivate;
-struct _SettingsPluginIfupdown {
+struct _NMSIfupdownPlugin {
NMSettingsPlugin parent;
- SettingsPluginIfupdownPrivate _priv;
+ NMSIfupdownPluginPrivate _priv;
};
-struct _SettingsPluginIfupdownClass {
+struct _NMSIfupdownPluginClass {
NMSettingsPluginClass parent;
};
-G_DEFINE_TYPE (SettingsPluginIfupdown, settings_plugin_ifupdown, NM_TYPE_SETTINGS_PLUGIN)
+G_DEFINE_TYPE (NMSIfupdownPlugin, nms_ifupdown_plugin, NM_TYPE_SETTINGS_PLUGIN)
-#define SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfupdown, SETTINGS_IS_PLUGIN_IFUPDOWN)
+#define NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfupdownPlugin, NMS_IS_IFUPDOWN_PLUGIN)
/*****************************************************************************/
@@ -85,50 +79,60 @@ G_DEFINE_TYPE (SettingsPluginIfupdown, settings_plugin_ifupdown, NM_TYPE_SETTING
/*****************************************************************************/
-static void initialize (SettingsPluginIfupdown *self);
+static void initialize (NMSIfupdownPlugin *self);
/*****************************************************************************/
-/* Returns the plugins currently known list of connections. The returned
- * list is freed by the system settings service.
- */
-static GSList*
-get_connections (NMSettingsPlugin *plugin)
+static void
+_storage_data_destroy (StorageData *sd)
{
- SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin);
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
- GSList *list = NULL;
+ g_object_unref (sd->connection);
+ nm_g_object_unref (sd->storage);
+ g_slice_free (StorageData, sd);
+}
+
+/*****************************************************************************/
+
+static void
+reload_connections (NMSettingsPlugin *plugin,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data)
+{
+ NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self);
GHashTableIter iter;
- void *value;
+ StorageData *sd;
if (G_UNLIKELY (!priv->initialized))
initialize (self);
if (!priv->ifupdown_managed) {
- _LOGD ("get_connections: not connections due to managed=false");
- return NULL;
+ _LOGD ("load: no connections due to managed=false");
+ return;
}
g_hash_table_iter_init (&iter, priv->eni_ifaces);
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- if (value)
- list = g_slist_prepend (list, value);
- }
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &sd)) {
+ if (!sd)
+ continue;
- _LOGD ("get_connections: %u connections", g_slist_length (list));
- return list;
+ _LOGD ("load: %s (%s)",
+ nm_settings_storage_get_uuid (sd->storage),
+ nm_connection_get_id (sd->connection));
+ callback (plugin,
+ sd->storage,
+ sd->connection,
+ user_data);
+ }
}
-/*
- * Return a list of device specifications which NetworkManager should not
- * manage. Returned list will be freed by the system settings service, and
- * each element must be allocated using g_malloc() or its variants.
- */
+/*****************************************************************************/
+
static GSList*
get_unmanaged_specs (NMSettingsPlugin *plugin)
{
- SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin);
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
+ NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self);
GSList *specs = NULL;
GHashTableIter iter;
const char *iface;
@@ -151,15 +155,15 @@ get_unmanaged_specs (NMSettingsPlugin *plugin)
/*****************************************************************************/
static void
-initialize (SettingsPluginIfupdown *self)
+initialize (NMSIfupdownPlugin *self)
{
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self);
gs_unref_hashtable GHashTable *auto_ifaces = NULL;
nm_auto_ifparser if_parser *parser = NULL;
if_block *block;
- GHashTableIter con_iter;
+ GHashTableIter h_iter;
const char *block_name;
- NMIfupdownConnection *conn;
+ StorageData *sd;
nm_assert (!priv->initialized);
priv->initialized = TRUE;
@@ -176,6 +180,12 @@ initialize (SettingsPluginIfupdown *self)
}
if (nm_streq (block->type, "iface")) {
+ gs_free_error GError *local = NULL;
+ gs_unref_object NMConnection *connection = NULL;
+ gs_unref_object NMSettingsStorage *storage = NULL;
+ const char *uuid = NULL;
+ StorageData *sd_repl;
+
/* Bridge configuration */
if (g_str_has_prefix (block->name, "br")) {
/* Try to find bridge ports */
@@ -208,13 +218,13 @@ initialize (SettingsPluginIfupdown *self)
if (nm_streq (token, "none"))
continue;
if (state == 0) {
- conn = g_hash_table_lookup (priv->eni_ifaces, block->name);
- if (!conn) {
+ sd = g_hash_table_lookup (priv->eni_ifaces, block->name);
+ if (!sd) {
_LOGD ("parse: adding bridge port \"%s\"", token);
g_hash_table_insert (priv->eni_ifaces, g_strdup (token), NULL);
} else {
_LOGD ("parse: adding bridge port \"%s\" (have connection %s)", token,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
+ nm_settings_storage_get_uuid (sd->storage));
}
}
}
@@ -226,54 +236,71 @@ initialize (SettingsPluginIfupdown *self)
if (nm_streq (block->name, "lo"))
continue;
- /* Remove any connection for this block that was previously found */
- conn = g_hash_table_lookup (priv->eni_ifaces, block->name);
- if (conn) {
+ sd_repl = g_hash_table_lookup (priv->eni_ifaces, block->name);
+ if (sd_repl) {
+ storage = g_steal_pointer (&sd_repl->storage);
_LOGD ("parse: replace connection \"%s\" (%s)",
block->name,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
- nm_settings_connection_delete (NM_SETTINGS_CONNECTION (conn), NULL);
+ nm_settings_storage_get_uuid (sd_repl->storage));
g_hash_table_remove (priv->eni_ifaces, block->name);
}
- /* add the new connection */
- conn = nm_ifupdown_connection_new (block);
- if (conn) {
- _LOGD ("parse: adding connection \"%s\" (%s)", block->name,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
- } else
- _LOGD ("parse: adding place holder for connection \"%s\"", block->name);
- g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), conn);
+ connection = ifupdown_new_connection_from_if_block (block, &local);
+
+ if (!connection) {
+ _LOGD ("parse: adding place holder for \"%s\"%s%s%s",
+ block->name,
+ NM_PRINT_FMT_QUOTED (local, " (", local->message, ")", ""));
+ sd = NULL;
+ } else {
+ uuid = nm_connection_get_uuid (connection);
+ sd = g_slice_new (StorageData);
+ *sd = (StorageData) {
+ .connection = g_steal_pointer (&connection),
+ .storage = g_steal_pointer (&storage)
+ ?: nm_settings_storage_new (NM_SETTINGS_PLUGIN (self), uuid, NULL),
+ };
+ _LOGD ("parse: adding connection \"%s\" (%s)", block->name, uuid);
+ }
+
+ g_hash_table_replace (priv->eni_ifaces, g_strdup (block->name), sd);
continue;
}
if (nm_streq (block->type, "mapping")) {
- conn = g_hash_table_lookup (priv->eni_ifaces, block->name);
- if (!conn) {
+ sd = g_hash_table_lookup (priv->eni_ifaces, block->name);
+ if (!sd) {
_LOGD ("parse: adding mapping \"%s\"", block->name);
g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), NULL);
} else {
_LOGD ("parse: adding mapping \"%s\" (have connection %s)", block->name,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
+ nm_settings_storage_get_uuid (sd->storage));
}
continue;
}
}
/* Make 'auto' interfaces autoconnect=TRUE */
- g_hash_table_iter_init (&con_iter, priv->eni_ifaces);
- while (g_hash_table_iter_next (&con_iter, (gpointer) &block_name, (gpointer) &conn)) {
- NMSettingConnection *setting;
-
- if ( !conn
- || !auto_ifaces
- || !g_hash_table_contains (auto_ifaces, block_name))
- continue;
+ if (auto_ifaces) {
+ g_hash_table_iter_init (&h_iter, priv->eni_ifaces);
+ while (g_hash_table_iter_next (&h_iter, (gpointer *) &block_name, (gpointer *) &sd)) {
+ if ( !sd
+ || !g_hash_table_contains (auto_ifaces, block_name))
+ continue;
+ g_object_set (nm_connection_get_setting_connection (sd->connection),
+ NM_SETTING_CONNECTION_AUTOCONNECT,
+ TRUE,
+ NULL);
+ }
+ }
- /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
- setting = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (conn)));
- g_object_set (setting, NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, NULL);
+#if NM_MORE_ASSERTS
+ g_hash_table_iter_init (&h_iter, priv->eni_ifaces);
+ while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &sd)) {
+ if (sd)
+ nmtst_connection_assert_unchanging (sd->connection);
}
+#endif
/* Check the config file to find out whether to manage interfaces */
priv->ifupdown_managed = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
@@ -281,51 +308,39 @@ initialize (SettingsPluginIfupdown *self)
NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED,
!IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT);
_LOGI ("management mode: %s", priv->ifupdown_managed ? "managed" : "unmanaged");
-
- /* Now if we're running in managed mode, let NM know there are new connections */
- if (priv->ifupdown_managed) {
- GHashTableIter iter;
-
- g_hash_table_iter_init (&iter, priv->eni_ifaces);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &conn)) {
- if (conn) {
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (conn));
- }
- }
- }
}
/*****************************************************************************/
static void
-settings_plugin_ifupdown_init (SettingsPluginIfupdown *self)
+nms_ifupdown_plugin_init (NMSIfupdownPlugin *self)
{
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self);
- priv->eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+ priv->eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) _storage_data_destroy);
}
static void
dispose (GObject *object)
{
- SettingsPluginIfupdown *plugin = SETTINGS_PLUGIN_IFUPDOWN (object);
- SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (plugin);
+ NMSIfupdownPlugin *plugin = NMS_IFUPDOWN_PLUGIN (object);
+ NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (plugin);
g_clear_pointer (&priv->eni_ifaces, g_hash_table_destroy);
- G_OBJECT_CLASS (settings_plugin_ifupdown_parent_class)->dispose (object);
+ G_OBJECT_CLASS (nms_ifupdown_plugin_parent_class)->dispose (object);
}
static void
-settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass)
+nms_ifupdown_plugin_class_init (NMSIfupdownPluginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass);
object_class->dispose = dispose;
- plugin_class->get_connections = get_connections;
+ plugin_class->plugin_name = "ifupdown";
+ plugin_class->reload_connections = reload_connections;
plugin_class->get_unmanaged_specs = get_unmanaged_specs;
}
@@ -334,5 +349,5 @@ settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass)
G_MODULE_EXPORT NMSettingsPlugin *
nm_settings_plugin_factory (void)
{
- return g_object_new (SETTINGS_TYPE_PLUGIN_IFUPDOWN, NULL);
+ return g_object_new (NMS_TYPE_IFUPDOWN_PLUGIN, NULL);
}
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h
index 6e0b74ee6b..10ea2be44e 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h
@@ -19,21 +19,21 @@
* (C) Copyright 2008 Canonical Ltd.
*/
-#ifndef _PLUGIN_H_
-#define _PLUGIN_H_
+#ifndef __NMS_IFUPDOWN_PLUGIN_H__
+#define __NMS_IFUPDOWN_PLUGIN_H__
#define PLUGIN_NAME "ifupdown"
-#define SETTINGS_TYPE_PLUGIN_IFUPDOWN (settings_plugin_ifupdown_get_type ())
-#define SETTINGS_PLUGIN_IFUPDOWN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdown))
-#define SETTINGS_PLUGIN_IFUPDOWN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdownClass))
-#define SETTINGS_IS_PLUGIN_IFUPDOWN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN))
-#define SETTINGS_IS_PLUGIN_IFUPDOWN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFUPDOWN))
-#define SETTINGS_PLUGIN_IFUPDOWN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdownClass))
+#define NMS_TYPE_IFUPDOWN_PLUGIN (nms_ifupdown_plugin_get_type ())
+#define NMS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPlugin))
+#define NMS_IFUPDOWN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass))
+#define NMS_IS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFUPDOWN_PLUGIN))
+#define NMS_IS_IFUPDOWN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFUPDOWN_PLUGIN))
+#define NMS_IFUPDOWN_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass))
-typedef struct _SettingsPluginIfupdown SettingsPluginIfupdown;
-typedef struct _SettingsPluginIfupdownClass SettingsPluginIfupdownClass;
+typedef struct _NMSIfupdownPlugin NMSIfupdownPlugin;
+typedef struct _NMSIfupdownPluginClass NMSIfupdownPluginClass;
-GType settings_plugin_ifupdown_get_type (void);
+GType nms_ifupdown_plugin_get_type (void);
-#endif /* _PLUGIN_H_ */
+#endif /* __NMS_IFUPDOWN_PLUGIN_H__ */
diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
index 1191047dc2..bad0a6b766 100644
--- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -30,6 +30,29 @@
/*****************************************************************************/
+#define _connection_from_if_block(block) \
+ ({ \
+ NMConnection *_con; \
+ if_block *_block = (block); \
+ GError *_local = NULL; \
+ \
+ g_assert (_block); \
+ _con = ifupdown_new_connection_from_if_block (_block, &_local); \
+ nmtst_assert_success (NM_IS_CONNECTION (_con), _local); \
+ nmtst_assert_connection_verifies_without_normalization (_con); \
+ _con; \
+ })
+
+#define _connection_first_from_parser(parser) \
+ ({ \
+ if_parser *_parser = (parser); \
+ \
+ g_assert (_parser); \
+ _connection_from_if_block (ifparser_getfirst (_parser)); \
+ })
+
+/*****************************************************************************/
+
typedef struct {
char *key;
char *data;
@@ -452,26 +475,14 @@ test16_missing_newline (void)
static void
test17_read_static_ipv4 (void)
{
- NMConnection *connection;
+ gs_unref_object NMConnection *connection = NULL;
NMSettingConnection *s_con;
NMSettingIPConfig *s_ip4;
NMSettingWired *s_wired;
- GError *error = NULL;
- gboolean success;
NMIPAddress *ip4_addr;
- if_block *block = NULL;
nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test17-wired-static-verify-ip4");
- block = ifparser_getfirst (parser);
- connection = nm_simple_connection_new();
- g_assert (connection);
-
- ifupdown_update_connection_from_if_block (connection, block, &error);
- g_assert_no_error (error);
-
- success = nm_connection_verify (connection, &error);
- g_assert_no_error (error);
- g_assert (success);
+ connection = _connection_first_from_parser (parser);
/* ===== CONNECTION SETTING ===== */
s_con = nm_connection_get_setting_connection (connection);
@@ -500,32 +511,19 @@ test17_read_static_ipv4 (void)
g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip4), ==, 2);
g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 0), ==, "example.com");
g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 1), ==, "foo.example.com");
-
- g_object_unref (connection);
}
static void
test18_read_static_ipv6 (void)
{
- NMConnection *connection;
+ gs_unref_object NMConnection *connection = NULL;
NMSettingConnection *s_con;
NMSettingIPConfig *s_ip6;
NMSettingWired *s_wired;
- GError *error = NULL;
- gboolean success;
NMIPAddress *ip6_addr;
- if_block *block = NULL;
nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test18-wired-static-verify-ip6");
- block = ifparser_getfirst (parser);
- connection = nm_simple_connection_new();
- g_assert (connection);
- ifupdown_update_connection_from_if_block (connection, block, &error);
- g_assert_no_error (error);
-
- success = nm_connection_verify (connection, &error);
- g_assert_no_error (error);
- g_assert (success);
+ connection = _connection_first_from_parser (parser);
/* ===== CONNECTION SETTING ===== */
s_con = nm_connection_get_setting_connection (connection);
@@ -554,30 +552,17 @@ test18_read_static_ipv6 (void)
g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip6), ==, 2);
g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 0), ==, "example.com");
g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 1), ==, "foo.example.com");
-
- g_object_unref (connection);
}
static void
test19_read_static_ipv4_plen (void)
{
- NMConnection *connection;
+ gs_unref_object NMConnection *connection = NULL;
NMSettingIPConfig *s_ip4;
- GError *error = NULL;
NMIPAddress *ip4_addr;
- if_block *block = NULL;
- gboolean success;
nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test19-wired-static-verify-ip4-plen");
- block = ifparser_getfirst (parser);
- connection = nm_simple_connection_new();
- g_assert (connection);
- ifupdown_update_connection_from_if_block (connection, block, &error);
- g_assert_no_error (error);
-
- success = nm_connection_verify (connection, &error);
- g_assert_no_error (error);
- g_assert (success);
+ connection = _connection_first_from_parser (parser);
/* ===== IPv4 SETTING ===== */
s_ip4 = nm_connection_get_setting_ip4_config (connection);
@@ -588,8 +573,6 @@ test19_read_static_ipv4_plen (void)
g_assert (ip4_addr != NULL);
g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "10.0.0.3");
g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 8);
-
- g_object_unref (connection);
}
static void
@@ -670,4 +653,3 @@ main (int argc, char **argv)
return g_test_run ();
}
-
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c
deleted file mode 100644
index faf39abbfd..0000000000
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 - 2012 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nms-keyfile-connection.h"
-
-#include <glib/gstdio.h>
-
-#include "nm-dbus-interface.h"
-#include "nm-setting-connection.h"
-#include "nm-utils.h"
-
-#include "settings/nm-settings-plugin.h"
-
-#include "nms-keyfile-reader.h"
-#include "nms-keyfile-writer.h"
-#include "nms-keyfile-utils.h"
-
-/*****************************************************************************/
-
-struct _NMSKeyfileConnection {
- NMSettingsConnection parent;
-};
-
-struct _NMSKeyfileConnectionClass {
- NMSettingsConnectionClass parent;
-};
-
-G_DEFINE_TYPE (NMSKeyfileConnection, nms_keyfile_connection, NM_TYPE_SETTINGS_CONNECTION)
-
-/*****************************************************************************/
-
-static gboolean
-commit_changes (NMSettingsConnection *connection,
- NMConnection *new_connection,
- NMSettingsConnectionCommitReason commit_reason,
- NMConnection **out_reread_connection,
- char **out_logmsg_change,
- GError **error)
-{
- gs_free char *path = NULL;
- gs_unref_object NMConnection *reread = NULL;
- gboolean reread_same = FALSE;
-
- nm_assert (out_reread_connection && !*out_reread_connection);
- nm_assert (!out_logmsg_change || !*out_logmsg_change);
-
- if (!nms_keyfile_writer_connection (new_connection,
- TRUE,
- nm_settings_connection_get_filename (connection),
- NM_FLAGS_ALL (commit_reason, NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION
- | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED),
- NULL,
- NULL,
- &path,
- &reread,
- &reread_same,
- error))
- return FALSE;
-
- if (!nm_streq0 (path, nm_settings_connection_get_filename (connection))) {
- gs_free char *old_path = g_strdup (nm_settings_connection_get_filename (connection));
-
- nm_settings_connection_set_filename (connection, path);
- if (old_path) {
- NM_SET_OUT (out_logmsg_change,
- g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and rename from \"%s\"",
- NMS_KEYFILE_CONNECTION_LOG_ARG (connection),
- old_path));
- } else {
- NM_SET_OUT (out_logmsg_change,
- g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and persist connection",
- NMS_KEYFILE_CONNECTION_LOG_ARG (connection)));
- }
- } else {
- NM_SET_OUT (out_logmsg_change,
- g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT,
- NMS_KEYFILE_CONNECTION_LOG_ARG (connection)));
- }
-
- if (reread && !reread_same)
- *out_reread_connection = g_steal_pointer (&reread);
-
- return TRUE;
-}
-
-static gboolean
-delete (NMSettingsConnection *connection,
- GError **error)
-{
- const char *path;
-
- path = nm_settings_connection_get_filename (connection);
- if (path)
- g_unlink (path);
- return TRUE;
-}
-
-/*****************************************************************************/
-
-static void
-nms_keyfile_connection_init (NMSKeyfileConnection *connection)
-{
-}
-
-NMSKeyfileConnection *
-nms_keyfile_connection_new (NMConnection *source,
- const char *full_path,
- const char *profile_dir,
- GError **error)
-{
- GObject *object;
- NMConnection *tmp;
- const char *uuid;
- gboolean update_unsaved = TRUE;
-
- nm_assert (source || full_path);
- nm_assert (!full_path || full_path[0] == '/');
- nm_assert (!profile_dir || profile_dir[0] == '/');
-
- /* If we're given a connection already, prefer that instead of re-reading */
- if (source)
- tmp = g_object_ref (source);
- else {
- tmp = nms_keyfile_reader_from_file (full_path, profile_dir, error);
- if (!tmp)
- return NULL;
-
- uuid = nm_connection_get_uuid (tmp);
- if (!uuid) {
- g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
- "Connection in file %s had no UUID", full_path);
- g_object_unref (tmp);
- return NULL;
- }
-
- /* If we just read the connection from disk, it's clearly not Unsaved */
- update_unsaved = FALSE;
- }
-
- object = g_object_new (NMS_TYPE_KEYFILE_CONNECTION,
- NM_SETTINGS_CONNECTION_FILENAME, full_path,
- NULL);
-
- /* Update our settings with what was read from the file */
- if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
- tmp,
- update_unsaved
- ? NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED
- : NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- NULL,
- error)) {
- g_object_unref (object);
- object = NULL;
- }
-
- g_object_unref (tmp);
- return (NMSKeyfileConnection *) object;
-}
-
-static void
-nms_keyfile_connection_class_init (NMSKeyfileConnectionClass *keyfile_connection_class)
-{
- NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (keyfile_connection_class);
-
- settings_class->commit_changes = commit_changes;
- settings_class->delete = delete;
-}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.h b/src/settings/plugins/keyfile/nms-keyfile-connection.h
deleted file mode 100644
index 2e7a59d910..0000000000
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* NetworkManager system settings service - keyfile plugin
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 - 2012 Red Hat, Inc.
- */
-
-#ifndef __NMS_KEYFILE_CONNECTION_H__
-#define __NMS_KEYFILE_CONNECTION_H__
-
-#include "settings/nm-settings-connection.h"
-
-#define NMS_TYPE_KEYFILE_CONNECTION (nms_keyfile_connection_get_type ())
-#define NMS_KEYFILE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnection))
-#define NMS_KEYFILE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnectionClass))
-#define NMS_IS_KEYFILE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_KEYFILE_CONNECTION))
-#define NMS_IS_KEYFILE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_KEYFILE_CONNECTION))
-#define NMS_KEYFILE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnectionClass))
-
-typedef struct _NMSKeyfileConnection NMSKeyfileConnection;
-typedef struct _NMSKeyfileConnectionClass NMSKeyfileConnectionClass;
-
-GType nms_keyfile_connection_get_type (void);
-
-NMSKeyfileConnection *nms_keyfile_connection_new (NMConnection *source,
- const char *full_path,
- const char *profile_dir,
- GError **error);
-
-#endif /* __NMS_KEYFILE_CONNECTION_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index 46432fd257..3b9435440b 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -15,7 +15,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 - 2013 Red Hat, Inc.
+ * Copyright (C) 2008 - 2018 Red Hat, Inc.
*/
#include "nm-default.h"
@@ -27,6 +27,10 @@
#include <sys/types.h>
#include <glib/gstdio.h>
+#include "nm-std-aux/c-list-util.h"
+#include "nm-glib-aux/nm-c-list.h"
+#include "nm-glib-aux/nm-io-utils.h"
+
#include "nm-connection.h"
#include "nm-setting.h"
#include "nm-setting-connection.h"
@@ -35,20 +39,76 @@
#include "nm-core-internal.h"
#include "nm-keyfile-internal.h"
+#include "systemd/nm-sd-utils-shared.h"
+
#include "settings/nm-settings-plugin.h"
+#include "settings/nm-settings-storage.h"
-#include "nms-keyfile-connection.h"
+#include "nms-keyfile-storage.h"
#include "nms-keyfile-writer.h"
+#include "nms-keyfile-reader.h"
#include "nms-keyfile-utils.h"
/*****************************************************************************/
typedef struct {
- GHashTable *connections; /* uuid::connection */
+ CList crld_lst;
+
+ char *full_filename;
+ const char *filename;
+
+ /* the profile loaded from the file. Note that this profile is only relevant
+ * during _do_reload_all(). The winning profile at the end of reload will
+ * be referenced as connection_exported, the connection field here will be
+ * cleared. */
+ NMConnection *connection;
+
+ /* the following fields are only required during _do_reload_all() for comparing
+ * which profile is the most relevant one (in case multple files provide a profile
+ * with the same UUID). */
+ struct timespec stat_mtime;
+ dev_t stat_dev;
+ ino_t stat_ino;
+ NMSKeyfileStorageType storage_type:3;
+ guint storage_priority:13;
+ NMTernary is_nm_generated_opt:3;
+ NMTernary is_volatile_opt:3;
+} ConnReloadData;
+
+typedef struct _NMSKeyfileConnReloadHead {
+ CList crld_lst_head;
+
+ char *loaded_path_etc;
+ char *loaded_path_run;
+} ConnReloadHead;
- gboolean initialized;
+typedef struct {
NMConfig *config;
+
+ /* there can/could be multiple read-only directories. For example, one
+ * could set dirname_libs to
+ * - /usr/lib/NetworkManager/profiles/
+ * - /etc/NetworkManager/system-connections
+ * and leave dirname_etc unset. In this case, there would be multiple
+ * read-only directories.
+ *
+ * Directories that come later have higher priority and shadow profiles
+ * from earlier directories.
+ *
+ * Currently, this is only an array with zero or one elements. It could be
+ * easily extended to support multiple read-only directories.
+ */
+ char *dirname_libs[2];
+ char *dirname_etc;
+ char *dirname_run;
+
+ struct {
+ CList lst_head;
+ GHashTable *idx;
+ GHashTable *filename_idx;
+ } storages;
+
} NMSKeyfilePluginPrivate;
struct _NMSKeyfilePlugin {
@@ -62,7 +122,7 @@ struct _NMSKeyfilePluginClass {
G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN)
-#define NMS_KEYFILE_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSKeyfilePlugin, NMS_IS_KEYFILE_PLUGIN)
+#define NMS_KEYFILE_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSKeyfilePlugin, NMS_IS_KEYFILE_PLUGIN, NMSettingsPlugin)
/*****************************************************************************/
@@ -76,430 +136,1241 @@ G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN)
/*****************************************************************************/
-static void
-connection_removed_cb (NMSettingsConnection *sett_conn, NMSKeyfilePlugin *self)
+static void _storage_reload_data_head_clear (NMSKeyfileStorage *storage);
+
+/*****************************************************************************/
+
+static gboolean
+_ignore_filename (NMSKeyfileStorageType storage_type,
+ const char *filename)
{
- g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections,
- nm_settings_connection_get_uuid (sett_conn));
+ /* for backward-compatibility, we don't require an extension for
+ * files under "/etc/...". */
+ return nm_keyfile_utils_ignore_filename (filename,
+ (storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC));
}
-/* Monitoring */
+static const char *
+_get_plugin_dir (NMSKeyfilePluginPrivate *priv)
+{
+ /* the plugin dir is only needed to generate connection.uuid value via
+ * nm_keyfile_read_ensure_uuid(). This is either the configured /etc
+ * directory, of the compile-time default (in case the /etc directory
+ * is disabled). */
+ return priv->dirname_etc ?: NM_KEYFILE_PATH_NAME_ETC_DEFAULT;
+}
-static void
-remove_connection (NMSKeyfilePlugin *self, NMSKeyfileConnection *connection)
+static gboolean
+_path_detect_storage_type (const char *full_filename,
+ const char *const*dirname_libs,
+ const char *dirname_etc,
+ const char *dirname_run,
+ NMSKeyfileStorageType *out_storage_type,
+ const char **out_dirname,
+ const char **out_filename,
+ GError **error)
{
- gboolean removed;
+ NMSKeyfileStorageType storage_type;
+ const char *filename = NULL;
+ const char *dirname = NULL;
+ guint i;
- g_return_if_fail (connection != NULL);
+ if (full_filename[0] != '/') {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN,
+ "filename is not an absolute path");
+ return FALSE;
+ }
- _LOGI ("removed " NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection));
+ if ( dirname_run
+ && (filename = nm_utils_file_is_in_path (full_filename, dirname_run))) {
+ storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN;
+ dirname = dirname_run;
+ } else if ( dirname_etc
+ && (filename = nm_utils_file_is_in_path (full_filename, dirname_etc))) {
+ storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC;
+ dirname = dirname_etc;
+ } else {
+ for (i = 0; dirname_libs && dirname_libs[i]; i++) {
+ if ((filename = nm_utils_file_is_in_path (full_filename, dirname_libs[i]))) {
+ storage_type = NMS_KEYFILE_STORAGE_TYPE_LIB;
+ dirname = dirname_libs[i];
+ break;
+ }
+ }
+ if (!dirname) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN,
+ "filename is not inside a keyfile directory");
+ return FALSE;
+ }
+ }
- /* Removing from the hash table should drop the last reference */
- g_object_ref (connection);
- g_signal_handlers_disconnect_by_func (connection, connection_removed_cb, self);
- removed = g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections,
- nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection)));
- nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
- g_object_unref (connection);
+ if (_ignore_filename (storage_type, filename)) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN,
+ "filename is not a valid keyfile");
+ return FALSE;
+ }
- g_return_if_fail (removed);
+ NM_SET_OUT (out_storage_type, storage_type);
+ NM_SET_OUT (out_dirname, dirname);
+ NM_SET_OUT (out_filename, filename);
+ return TRUE;
}
-static NMSKeyfileConnection *
-find_by_path (NMSKeyfilePlugin *self, const char *path)
+/*****************************************************************************/
+
+static NMConnection *
+_read_from_file (const char *full_filename,
+ const char *plugin_dir,
+ struct stat *out_stat,
+ NMTernary *out_is_nm_generated,
+ NMTernary *out_is_volatile,
+ GError **error)
{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
- GHashTableIter iter;
- NMSettingsConnection *candidate = NULL;
+ NMConnection *connection;
+
+ nm_assert (full_filename && full_filename[0] == '/');
+
+ connection = nms_keyfile_reader_from_file (full_filename, plugin_dir, out_stat, out_is_nm_generated, out_is_volatile, error);
+
+ nm_assert (!connection || (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS));
+ nm_assert (!connection || nm_utils_is_uuid (nm_connection_get_uuid (connection)));
+
+ return connection;
+}
+
+/*****************************************************************************/
- g_return_val_if_fail (path != NULL, NULL);
+static void
+_conn_reload_data_destroy (ConnReloadData *storage_data)
+{
+ c_list_unlink_stale (&storage_data->crld_lst);
+ nm_g_object_unref (storage_data->connection);
+ g_free (storage_data->full_filename);
+ g_slice_free (ConnReloadData, storage_data);
+}
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
- if (g_strcmp0 (path, nm_settings_connection_get_filename (candidate)) == 0)
- return NMS_KEYFILE_CONNECTION (candidate);
+static ConnReloadData *
+_conn_reload_data_new (guint storage_priority,
+ NMSKeyfileStorageType storage_type,
+ char *full_filename_take,
+ NMConnection *connection_take,
+ NMTernary is_nm_generated_opt,
+ NMTernary is_volatile_opt,
+ const struct stat *st)
+{
+ ConnReloadData *storage_data;
+
+ nm_assert (NM_IN_SET (is_nm_generated_opt, NM_TERNARY_DEFAULT, FALSE, TRUE));
+ nm_assert (NM_IN_SET (is_nm_generated_opt, NM_TERNARY_DEFAULT, FALSE, TRUE));
+
+ storage_data = g_slice_new (ConnReloadData);
+ *storage_data = (ConnReloadData) {
+ .storage_type = storage_type,
+ .storage_priority = storage_priority,
+ .full_filename = full_filename_take,
+ .filename = strrchr (full_filename_take, '/') + 1,
+ .connection = connection_take,
+ .is_nm_generated_opt = is_nm_generated_opt,
+ .is_volatile_opt = is_volatile_opt,
+ };
+ if (st) {
+ storage_data->stat_mtime = st->st_mtim;
+ storage_data->stat_dev = st->st_dev;
+ storage_data->stat_ino = st->st_ino;
}
- return NULL;
-}
-
-/* update_connection:
- * @self: the plugin instance
- * @source: if %NULL, this re-reads the connection from @full_path
- * and updates it. When passing @source, this adds a connection from
- * memory.
- * @full_path: the filename of the keyfile to be loaded
- * @connection: an existing connection that might be updated.
- * If given, @connection must be an existing connection that is currently
- * owned by the plugin.
- * @protect_existing_connection: if %TRUE, and !@connection, we don't allow updating
- * an existing connection with the same UUID.
- * If %TRUE and @connection, allow updating only if the reload would modify
- * @connection (without changing its UUID) or if we would create a new connection.
- * In other words, if this parameter is %TRUE, we only allow creating a
- * new connection (with an unseen UUID) or updating the passed in @connection
- * (whereas the UUID cannot change).
- * Note, that this allows for @connection to be replaced by a new connection.
- * @protected_connections: (allow-none): if given, we only update an
- * existing connection if it is not contained in this hash.
- * @error: error in case of failure
- *
- * Loads a connection from file @full_path. This can both be used to
- * load a connection initially or to update an existing connection.
- *
- * If you pass in an existing connection and the reloaded file happens
- * to have a different UUID, the connection is deleted.
- * Beware, that means that after the function, you have a dangling pointer
- * if the returned connection is different from @connection.
- *
- * Returns: the updated connection.
- * */
-static NMSKeyfileConnection *
-update_connection (NMSKeyfilePlugin *self,
- NMConnection *source,
- const char *full_path,
- NMSKeyfileConnection *connection,
- gboolean protect_existing_connection,
- GHashTable *protected_connections,
- GError **error)
+
+ nm_assert (storage_data->storage_type == storage_type);
+ nm_assert (storage_data->storage_priority == storage_priority);
+ nm_assert (storage_data->full_filename);
+ nm_assert (storage_data->full_filename[0] == '/');
+ nm_assert (storage_data->filename);
+ nm_assert (storage_data->filename[0]);
+ nm_assert (!strchr (storage_data->filename, '/'));
+
+ return storage_data;
+}
+
+static int
+_conn_reload_data_cmp_by_priority (const CList *lst_a,
+ const CList *lst_b,
+ const void *user_data)
{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
- NMSKeyfileConnection *connection_new;
- NMSKeyfileConnection *connection_by_uuid;
- GError *local = NULL;
- const char *uuid;
+ const ConnReloadData *a = c_list_entry (lst_a, ConnReloadData, crld_lst);
+ const ConnReloadData *b = c_list_entry (lst_b, ConnReloadData, crld_lst);
+
+ /* we sort more important entries first. */
+
+ /* sorting by storage-priority implies sorting by storage-type too.
+ * That is, because for different storage-types, we assign different storage-priorities
+ * and their sort order corresponds (with inverted order). Assert for that. */
+ nm_assert ( a->storage_type == b->storage_type
+ || ( (a->storage_priority != b->storage_priority)
+ && (a->storage_type < b->storage_type) == (a->storage_priority > b->storage_priority)));
+
+ /* sort by storage-priority, smaller is more important. */
+ NM_CMP_FIELD_UNSAFE (a, b, storage_priority);
- g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL);
- g_return_val_if_fail (full_path || source, NULL);
+ /* newer files are more important. */
+ NM_CMP_FIELD (b, a, stat_mtime.tv_sec);
+ NM_CMP_FIELD (b, a, stat_mtime.tv_nsec);
- if (full_path)
- _LOGD ("loading from file \"%s\"...", full_path);
+ NM_CMP_FIELD_STR (a, b, filename);
- if ( !nm_utils_file_is_in_path (full_path, nms_keyfile_utils_get_path ())
- && !nm_utils_file_is_in_path (full_path, NM_KEYFILE_PATH_NAME_RUN)) {
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "File not in recognized system-connections directory");
+ nm_assert_not_reached ();
+ return 0;
+}
+
+/* stat(@loaded_path) and if the path is the same as any of the ones from
+ * @crld_lst_head, move the found entry to the front and return TRUE.
+ * Otherwise, do nothing and return FALSE. */
+static gboolean
+_conn_reload_data_prioritize_loaded (CList *crld_lst_head,
+ const char *loaded_path)
+{
+ ConnReloadData *storage_data;
+ struct stat st_loaded;
+
+ nm_assert (loaded_path);
+ nm_assert (!nm_streq (loaded_path, NM_KEYFILE_PATH_NMLOADED_NULL));
+
+ if (loaded_path[0] != '/')
return FALSE;
- }
- connection_new = nms_keyfile_connection_new (source, full_path, nms_keyfile_utils_get_path (), &local);
- if (!connection_new) {
- /* Error; remove the connection */
- if (source)
- _LOGW ("error creating connection %s: %s", nm_connection_get_uuid (source), local->message);
- else
- _LOGW ("error loading connection from file %s: %s", full_path, local->message);
- if ( connection
- && !protect_existing_connection
- && (!protected_connections || !g_hash_table_contains (protected_connections, connection)))
- remove_connection (self, connection);
- g_propagate_error (error, local);
- return NULL;
+ /* we compare the file based on the inode, not based on the path.
+ * stat() the file. */
+ if (stat (loaded_path, &st_loaded) != 0)
+ return FALSE;
+
+ c_list_for_each_entry (storage_data, crld_lst_head, crld_lst) {
+ if ( storage_data->stat_dev == st_loaded.st_dev
+ && storage_data->stat_ino == st_loaded.st_ino) {
+ nm_c_list_move_front (crld_lst_head, &storage_data->crld_lst);
+ return TRUE;
+ }
}
- uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new));
- connection_by_uuid = g_hash_table_lookup (priv->connections, uuid);
+ return FALSE;
+}
- if ( connection
- && connection != connection_by_uuid) {
+/*****************************************************************************/
- if ( (protect_existing_connection && connection_by_uuid != NULL)
- || (protected_connections && g_hash_table_contains (protected_connections, connection))) {
- NMSKeyfileConnection *conflicting = (protect_existing_connection && connection_by_uuid != NULL) ? connection_by_uuid : connection;
+static void
+_nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */,
+ gpointer storage /* NMSKeyfileStorage */,
+ gboolean tracked)
+{
+#if NM_MORE_ASSERTS
+ nm_assert (!plugin || NMS_IS_KEYFILE_PLUGIN (plugin));
+ nm_assert (NMS_IS_KEYFILE_STORAGE (storage));
+ nm_assert (!plugin || plugin == nm_settings_storage_get_plugin (storage));
+ nm_assert (({
+ const char *f = nms_keyfile_storage_get_filename (storage);
+ !f || f[0] == '/';
+ }));
+ nm_assert (nm_utils_is_uuid (nms_keyfile_storage_get_uuid (storage)));
+
+ nm_assert ( !tracked
+ || !plugin
+ || c_list_contains (&NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.lst_head,
+ &NMS_KEYFILE_STORAGE (storage)->storage_lst));
+
+ nm_assert ( !tracked
+ || !plugin
+ || storage == g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx,
+ nms_keyfile_storage_get_uuid (storage)));
+#endif
+}
- if (source)
- _LOGW ("cannot update protected "NMS_KEYFILE_CONNECTION_LOG_FMT" connection due to conflicting UUID %s", NMS_KEYFILE_CONNECTION_LOG_ARG (conflicting), uuid);
- else
- _LOGW ("cannot load %s due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, full_path, NMS_KEYFILE_CONNECTION_LOG_ARG (conflicting));
- g_object_unref (connection_new);
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Cannot update protected connection due to conflicting UUID");
- return NULL;
- }
+void
+_nms_keyfile_storage_clear (NMSKeyfileStorage *storage)
+{
+ _nm_assert_storage (NULL, storage, FALSE);
- /* The new connection has a different UUID then the original one.
- * Remove @connection. */
- remove_connection (self, connection);
- }
+ c_list_unlink (&storage->storage_lst);
- if ( connection_by_uuid
- && ( (!connection && protect_existing_connection)
- || (protected_connections && g_hash_table_contains (protected_connections, connection_by_uuid)))) {
- if (source)
- _LOGW ("cannot update connection due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_by_uuid));
- else
- _LOGW ("cannot load %s due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, full_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_by_uuid));
- g_object_unref (connection_new);
- g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Skip updating protected connection during reload");
- return NULL;
- }
+ _storage_reload_data_head_clear (storage);
- if (connection_by_uuid) {
- const char *old_path;
-
- old_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid));
-
- if (nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)),
- nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
- NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
- NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
- /* Nothing to do... except updating the path. */
- if (old_path && g_strcmp0 (old_path, full_path) != 0)
- _LOGI ("rename \"%s\" to "NMS_KEYFILE_CONNECTION_LOG_FMT" without other changes", old_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- } else {
- /* An existing connection changed. */
- if (source)
- _LOGI ("update "NMS_KEYFILE_CONNECTION_LOG_FMT" from %s", NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new), NMS_KEYFILE_CONNECTION_LOG_PATH (old_path));
- else if (!g_strcmp0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new))))
- _LOGI ("update "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- else if (old_path)
- _LOGI ("rename \"%s\" to "NMS_KEYFILE_CONNECTION_LOG_FMT, old_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- else
- _LOGI ("update and persist "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
-
- if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid),
- nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
- NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
- NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
- "keyfile-update",
- &local)) {
- /* Shouldn't ever get here as 'connection_new' was verified by the reader already
- * and the UUID did not change. */
- g_assert_not_reached ();
- }
- g_assert_no_error (local);
- }
- nm_settings_connection_set_filename (NM_SETTINGS_CONNECTION (connection_by_uuid), full_path);
- g_object_unref (connection_new);
- return connection_by_uuid;
- } else {
- if (source)
- _LOGI ("add connection "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- else
- _LOGI ("new connection "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
- g_hash_table_insert (priv->connections, g_strdup (uuid), connection_new);
-
- g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED,
- G_CALLBACK (connection_removed_cb),
- self);
-
- if (!source) {
- /* Only raise the signal if we were called without source, i.e. if we read the connection from file.
- * Otherwise, we were called by add_connection() which does not expect the signal. */
- _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
- NM_SETTINGS_CONNECTION (connection_new));
- }
+ g_clear_object (&storage->connection_exported);
+}
+
+static void
+_storage_destroy (NMSKeyfileStorage *storage)
+{
+ _nm_assert_storage (NULL, storage, TRUE);
- return connection_new;
+ _nms_keyfile_storage_clear (storage);
+ g_object_unref (storage);
+}
+
+static ConnReloadHead *
+_storage_reload_data_head_ensure (NMSKeyfileStorage *storage)
+{
+ ConnReloadHead *hd;
+
+ hd = storage->_reload_data_head;
+ if (!hd) {
+ hd = g_slice_new (ConnReloadHead);
+ *hd = (ConnReloadHead) {
+ .crld_lst_head = C_LIST_INIT (hd->crld_lst_head),
+ };
+ storage->_reload_data_head = hd;
}
+
+ return hd;
}
static void
-config_changed_cb (NMConfig *config,
- NMConfigData *config_data,
- NMConfigChangeFlags changes,
- NMConfigData *old_data,
- NMSKeyfilePlugin *self)
+_storage_reload_data_head_clear (NMSKeyfileStorage *storage)
{
- gs_free char *old_value = NULL;
- gs_free char *new_value = NULL;
+ ConnReloadHead *hd;
+ ConnReloadData *storage_data;
- old_value = nm_config_data_get_value (old_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
- new_value = nm_config_data_get_value (config_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
+ hd = g_steal_pointer (&storage->_reload_data_head);
+ if (!hd)
+ return;
- if (!nm_streq0 (old_value, new_value))
- _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
+ while ((storage_data = c_list_first_entry (&hd->crld_lst_head, ConnReloadData, crld_lst)))
+ _conn_reload_data_destroy (storage_data);
+
+ g_free (hd->loaded_path_run);
+ g_free (hd->loaded_path_etc);
+ g_slice_free (ConnReloadHead, hd);
}
-static GHashTable *
-_paths_from_connections (GHashTable *connections)
+static gboolean
+_storage_has_equal_connection (NMSKeyfileStorage *storage,
+ NMConnection *connection)
{
- GHashTableIter iter;
- NMSKeyfileConnection *connection;
- GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal);
+ nm_assert (NM_IS_CONNECTION (connection));
- g_hash_table_iter_init (&iter, connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
- const char *path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection));
+ return storage->connection_exported
+ && nm_connection_compare (connection,
+ storage->connection_exported,
+ NM_SETTING_COMPARE_FLAG_EXACT);
+}
- if (path)
- g_hash_table_add (paths, (void *) path);
+static void
+_storage_set_type (NMSKeyfileStorage *storage,
+ NMSKeyfileStorageType storage_type,
+ NMTernary is_nm_generated_opt,
+ NMTernary is_volatile_opt)
+{
+ storage->storage_type_exported = storage_type;
+ if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) {
+ storage->is_nm_generated = (is_nm_generated_opt == NM_TERNARY_TRUE);
+ storage->is_volatile = (is_volatile_opt == NM_TERNARY_TRUE);
+ } else {
+ storage->is_nm_generated = FALSE;
+ storage->is_volatile = FALSE;
}
- return paths;
}
-static int
-_sort_paths (const char **f1, const char **f2, GHashTable *paths)
+/*****************************************************************************/
+
+typedef struct {
+ GHashTable *filename_idx;
+ const char *allowed_filename;
+} AllowFilenameData;
+
+#define ALLOW_FILENAME_DATA(_filename_idx, _allowed_filename) \
+ (&((AllowFilenameData) { \
+ .filename_idx = (_filename_idx), \
+ .allowed_filename = (_allowed_filename), \
+ }))
+
+static gboolean
+_allow_filename_cb (const char *filename,
+ gpointer user_data)
{
- struct stat st;
- gboolean c1, c2;
- gint64 m1, m2;
+ const AllowFilenameData *allow_filename_data = user_data;
+
+ if (!g_hash_table_contains (allow_filename_data->filename_idx, filename))
+ return TRUE;
+ if ( allow_filename_data->allowed_filename
+ && nm_streq (allow_filename_data->allowed_filename, filename))
+ return TRUE;
+ return FALSE;
+}
+
+static NMSKeyfileStorage *
+_storages_get (NMSKeyfilePlugin *self,
+ const char *uuid,
+ gboolean create)
+{
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
+ NMSKeyfileStorage *storage;
+
+ nm_assert (uuid && nm_utils_is_uuid (uuid));
+
+ storage = g_hash_table_lookup (priv->storages.idx, uuid);
+ if ( !storage
+ && create) {
+ storage = nms_keyfile_storage_new (self, uuid);
+ c_list_link_tail (&priv->storages.lst_head, &storage->storage_lst);
+ if (!g_hash_table_insert (priv->storages.idx, (char *) nms_keyfile_storage_get_uuid (storage), storage))
+ nm_assert_not_reached ();
+ }
+
+ return storage;
+}
+
+static void
+_storages_set_full_filename (NMSKeyfilePluginPrivate *priv,
+ NMSKeyfileStorage *storage,
+ char *full_filename_take)
+{
+ const char *filename;
+
+ _nm_assert_storage (NULL, storage, TRUE);
+ nm_assert (full_filename_take && full_filename_take[0] == '/');
+
+ filename = nms_keyfile_storage_get_filename (storage);
+ if (filename) {
+ if (!g_hash_table_remove (priv->storages.filename_idx, filename))
+ nm_assert_not_reached ();
+ }
+
+ _nm_settings_storage_set_filename_take (NM_SETTINGS_STORAGE (storage), full_filename_take);
+
+ if (!g_hash_table_insert (priv->storages.filename_idx,
+ (char *) nms_keyfile_storage_get_filename (storage),
+ storage))
+ nm_assert_not_reached ();
+}
+
+static void
+_storages_remove (NMSKeyfilePluginPrivate *priv,
+ NMSKeyfileStorage *storage,
+ gboolean destroy /* or else steal */)
+{
+ const char *filename;
+
+ nm_assert (priv);
+ nm_assert (NMS_IS_KEYFILE_STORAGE (storage));
+ nm_assert (c_list_contains (&priv->storages.lst_head, &storage->storage_lst));
+ nm_assert (g_hash_table_lookup (priv->storages.idx, nms_keyfile_storage_get_uuid (storage)) == storage);
- c1 = !!g_hash_table_contains (paths, *f1);
- c2 = !!g_hash_table_contains (paths, *f2);
- if (c1 != c2)
- return c1 ? -1 : 1;
+ c_list_unlink (&storage->storage_lst);
- m1 = stat (*f1, &st) == 0 ? (gint64) st.st_mtime : G_MININT64;
- m2 = stat (*f2, &st) == 0 ? (gint64) st.st_mtime : G_MININT64;
- if (m1 != m2)
- return m1 > m2 ? -1 : 1;
+ filename = nms_keyfile_storage_get_filename (storage);
+ if (filename) {
+ if (!g_hash_table_remove (priv->storages.filename_idx, filename))
+ nm_assert_not_reached ();
+
+ /* we don't clear the filename of the storage, although we drop it.
+ *
+ * Probably nobody cares about the earlier filename at this point, but
+ * incase someone would (e.g. for logging that the storage was removed),
+ * it makes sense to keep what was previously. */
+ }
- return strcmp (*f1, *f2);
+ if (destroy) {
+ if (!g_hash_table_remove (priv->storages.idx, nms_keyfile_storage_get_uuid (storage)))
+ nm_assert_not_reached ();
+ } else {
+ if (!g_hash_table_steal (priv->storages.idx, nms_keyfile_storage_get_uuid (storage)))
+ nm_assert_not_reached ();
+ }
}
+/*****************************************************************************/
+
static void
-_read_dir (GPtrArray *filenames,
- const char *path,
- gboolean require_extension)
+_load_dir (NMSKeyfilePlugin *self,
+ guint storage_priority,
+ NMSKeyfileStorageType storage_type,
+ const char *dirname,
+ const char *plugin_dir)
{
+ const char *filename;
GDir *dir;
- const char *item;
- GError *error = NULL;
+ gs_unref_hashtable GHashTable *dupl_filenames = NULL;
- dir = g_dir_open (path, 0, &error);
- if (!dir) {
- _LOGD ("cannot read directory '%s': %s", path, error->message);
- g_clear_error (&error);
+ if (!dirname)
return;
- }
- while ((item = g_dir_read_name (dir))) {
- if (nm_keyfile_utils_ignore_filename (item, require_extension))
+ dir = g_dir_open (dirname, 0, NULL);
+ if (!dir)
+ return;
+
+ dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+
+ while ((filename = g_dir_read_name (dir))) {
+ gs_unref_object NMConnection *connection = NULL;
+ gs_free_error GError *error = NULL;
+ NMSKeyfileStorage *storage;
+ ConnReloadData *storage_data;
+ gs_free char *full_filename = NULL;
+ struct stat st;
+ ConnReloadHead *hd;
+ NMTernary is_nm_generated_opt;
+ NMTernary is_volatile_opt;
+
+ if (!g_hash_table_add (dupl_filenames, g_strdup (filename))) {
+ /* ensure no duplicates. */
+ continue;
+ }
+
+ if (_ignore_filename (storage_type, filename)) {
+ gs_free char *loaded_uuid = NULL;
+ gs_free char *loaded_path = NULL;
+
+ if (!nms_keyfile_loaded_uuid_read (dirname,
+ filename,
+ NULL,
+ &loaded_uuid,
+ &loaded_path)) {
+ _LOGT ("load: \"%s/%s\": skip file due to invalid filename", dirname, filename);
+ continue;
+ }
+ if (!NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN,
+ NMS_KEYFILE_STORAGE_TYPE_ETC)) {
+ _LOGT ("load: \"%s/%s\": skip loaded file from read-only directory", dirname, filename);
+ continue;
+ }
+ storage = _storages_get (self, loaded_uuid, TRUE);
+ hd = _storage_reload_data_head_ensure (storage);
+ if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) {
+ nm_assert (!hd->loaded_path_run);
+ hd->loaded_path_run = g_steal_pointer (&loaded_path);
+ } else {
+ nm_assert (!hd->loaded_path_etc);
+ hd->loaded_path_etc = g_steal_pointer (&loaded_path);
+ }
+ continue;
+ }
+
+ full_filename = g_build_filename (dirname, filename, NULL);
+
+ connection = _read_from_file (full_filename, plugin_dir, &st, &is_nm_generated_opt, &is_volatile_opt, &error);
+ if (!connection) {
+ _LOGW ("load: \"%s\": failed to load connection: %s", full_filename, error->message);
continue;
- g_ptr_array_add (filenames, g_build_filename (path, item, NULL));
+ }
+
+ storage = _storages_get (self, nm_connection_get_uuid (connection), TRUE);
+ hd = _storage_reload_data_head_ensure (storage);
+ storage_data = _conn_reload_data_new (storage_priority,
+ storage_type,
+ g_steal_pointer (&full_filename),
+ g_steal_pointer (&connection),
+ is_nm_generated_opt,
+ is_volatile_opt,
+ &st);
+ c_list_link_tail (&hd->crld_lst_head, &storage_data->crld_lst);
}
+
g_dir_close (dir);
}
+/*****************************************************************************/
static void
-read_connections (NMSettingsPlugin *config)
+reload_connections (NMSettingsPlugin *plugin,
+ NMSettingsPluginConnectionReloadCallback callback,
+ gpointer user_data)
{
- NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config);
+ NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin);
NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
- GHashTable *alive_connections;
- GHashTableIter iter;
- NMSKeyfileConnection *connection;
- GPtrArray *dead_connections = NULL;
+ CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted);
+ const char *plugin_dir = _get_plugin_dir (priv);
+ gs_unref_ptrarray GPtrArray *storages_modified = NULL;
+ NMSKeyfileStorage *storage_safe;
+ NMSKeyfileStorage *storage;
guint i;
- GPtrArray *filenames;
- GHashTable *paths;
- filenames = g_ptr_array_new_with_free_func (g_free);
+#if NM_MORE_ASSERTS
+ c_list_for_each_entry (storage, &priv->storages.lst_head, storage_lst) {
+ nm_assert (!storage->_reload_data_head);
+ nm_assert (NM_IS_CONNECTION (storage->connection_exported));
+ }
+#endif
+
+ storages_modified = g_ptr_array_new_with_free_func (g_object_unref);
+
+ _load_dir (self, 1, NMS_KEYFILE_STORAGE_TYPE_RUN, priv->dirname_run, plugin_dir);
+ _load_dir (self, 2, NMS_KEYFILE_STORAGE_TYPE_ETC, priv->dirname_etc, plugin_dir);
+ for (i = 0; priv->dirname_libs[i]; i++)
+ _load_dir (self, 3 + i, NMS_KEYFILE_STORAGE_TYPE_LIB, priv->dirname_libs[i], plugin_dir);
+
+ /* process the loaded information. */
+ c_list_for_each_entry_safe (storage, storage_safe, &priv->storages.lst_head, storage_lst) {
+ ConnReloadHead *hd;
+ ConnReloadData *rd, *rd_best;
+ gboolean connection_modified;
+ gboolean connection_renamed;
+ gboolean loaded_path_masked = FALSE;
+ const char *loaded_dirname = NULL;
+ gs_free char *loaded_path = NULL;
+
+ hd = storage->_reload_data_head;
+
+ nm_assert (!hd || ( !c_list_is_empty (&hd->crld_lst_head)
+ || hd->loaded_path_run
+ || hd->loaded_path_etc));
+ nm_assert (hd || nms_keyfile_storage_get_filename (storage));
+
+ /* find and steal the loaded-path (if any) */
+ if (hd) {
+ if (hd->loaded_path_run) {
+ if (hd->loaded_path_etc) {
+ gs_free char *f1 = NULL;
+ gs_free char *f2 = NULL;
+
+ _LOGT ("load: \"%s\": shadowed by \"%s\"",
+ (f1 = nms_keyfile_loaded_uuid_filename (priv->dirname_etc, nms_keyfile_storage_get_uuid (storage), FALSE)),
+ (f2 = nms_keyfile_loaded_uuid_filename (priv->dirname_run, nms_keyfile_storage_get_uuid (storage), FALSE)));
+ nm_clear_g_free (&hd->loaded_path_etc);
+ }
+ loaded_dirname = priv->dirname_run;
+ loaded_path = g_steal_pointer (&hd->loaded_path_run);
+ } else if (hd->loaded_path_etc) {
+ loaded_dirname = priv->dirname_etc;
+ loaded_path = g_steal_pointer (&hd->loaded_path_etc);
+ }
+ }
+ nm_assert ((!loaded_path) == (!loaded_dirname));
+
+ /* sort storage datas by priority. */
+ if (hd)
+ c_list_sort (&hd->crld_lst_head, _conn_reload_data_cmp_by_priority, NULL);
+
+ if (loaded_path) {
+ if (nm_streq (loaded_path, NM_KEYFILE_PATH_NMLOADED_NULL)) {
+ loaded_path_masked = TRUE;
+ nm_clear_g_free (&loaded_path);
+ } else if (!_conn_reload_data_prioritize_loaded (&hd->crld_lst_head, loaded_path)) {
+ gs_free char *f1 = NULL;
+
+ _LOGT ("load: \"%s\": ignore invalid target \"%s\"",
+ (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)),
+ loaded_path);
+ nm_clear_g_free (&loaded_path);
+ }
+ }
- _read_dir (filenames, NM_KEYFILE_PATH_NAME_RUN, TRUE);
- _read_dir (filenames, nms_keyfile_utils_get_path (), FALSE);
+ rd_best = hd
+ ? c_list_first_entry (&hd->crld_lst_head, ConnReloadData, crld_lst)
+ : NULL;
+ if ( !rd_best
+ || loaded_path_masked) {
+
+ /* after reload, no file references this profile (or the files are masked from loading
+ * via a symlink to /dev/null). */
+
+ if (_LOGT_ENABLED ()) {
+ gs_free char *f1 = NULL;
+
+ if (!hd) {
+ _LOGT ("load: \"%s\": file no longer exists for profile with UUID \"%s\" (remove profile)",
+ nms_keyfile_storage_get_filename (storage),
+ nms_keyfile_storage_get_uuid (storage));
+ } else if (rd_best) {
+ c_list_for_each_entry (rd, &hd->crld_lst_head, crld_lst) {
+ _LOGT ("load: \"%s\": profile %s masked by \"%s\" file symlinking \"%s\"%s",
+ rd->full_filename,
+ nms_keyfile_storage_get_uuid (storage),
+ f1 ?: (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)),
+ NM_KEYFILE_PATH_NMLOADED_NULL,
+ storage->connection_exported ? " (remove profile)" : "");
+ }
+ } else if (loaded_path_masked) {
+ _LOGT ("load: \"%s\": symlinks \"%s\" to hide UUID \"%s\"%s",
+ (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)),
+ NM_KEYFILE_PATH_NMLOADED_NULL,
+ nms_keyfile_storage_get_uuid (storage),
+ storage->connection_exported ? " (remove profile)" : "");
+ } else {
+ _LOGT ("load: \"%s\": symlinks \"%s\" but there are no profiles with UUID \"%s\"%s",
+ (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)),
+ loaded_path,
+ nms_keyfile_storage_get_uuid (storage),
+ storage->connection_exported ? " (remove profile)" : "");
+ }
+ }
- alive_connections = g_hash_table_new (nm_direct_hash, NULL);
+ if (!storage->connection_exported)
+ _storages_remove (priv, storage, TRUE);
+ else {
+ _storages_remove (priv, storage, FALSE);
+ c_list_link_tail (&lst_conn_info_deleted, &storage->storage_lst);
+ }
+ continue;
+ }
- /* While reloading, we don't replace connections that we already loaded while
- * iterating over the files.
- *
- * To have sensible, reproducible behavior, sort the paths by last modification
- * time preferring older files.
- */
- paths = _paths_from_connections (priv->connections);
- g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths);
- g_hash_table_destroy (paths);
-
- for (i = 0; i < filenames->len; i++) {
- connection = update_connection (self, NULL, filenames->pdata[i], NULL, FALSE, alive_connections, NULL);
- if (connection)
- g_hash_table_add (alive_connections, connection);
- }
- g_ptr_array_free (filenames, TRUE);
-
- g_hash_table_iter_init (&iter, priv->connections);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
- if ( !g_hash_table_contains (alive_connections, connection)
- && nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection))) {
- if (!dead_connections)
- dead_connections = g_ptr_array_new ();
- g_ptr_array_add (dead_connections, connection);
+ c_list_for_each_entry (rd, &hd->crld_lst_head, crld_lst) {
+ if (rd_best != rd) {
+ _LOGT ("load: \"%s\": profile %s shadowed by \"%s\" file",
+ rd->full_filename,
+ nms_keyfile_storage_get_uuid (storage),
+ rd_best->full_filename);
+ }
+ }
+
+ connection_modified = !_storage_has_equal_connection (storage, rd_best->connection);
+ connection_renamed = !nms_keyfile_storage_get_filename (storage)
+ || !nm_streq (nms_keyfile_storage_get_filename (storage), rd_best->full_filename);
+
+ {
+ gs_free char *f1 = NULL;
+
+ _LOGT ("load: \"%s\": profile %s (%s) loaded (%s)"
+ "%s%s%s"
+ "%s%s%s",
+ rd_best->full_filename,
+ nms_keyfile_storage_get_uuid (storage),
+ nm_connection_get_id (rd_best->connection),
+ connection_modified ? (nms_keyfile_storage_get_filename (storage) ? "updated" : "added" ) : "unchanged",
+ NM_PRINT_FMT_QUOTED (loaded_path,
+ " (hinted by \"",
+ (f1 = nms_keyfile_loaded_uuid_filename (loaded_dirname, nms_keyfile_storage_get_uuid (storage), FALSE)),
+ "\")",
+ ""),
+ NM_PRINT_FMT_QUOTED (connection_renamed && nms_keyfile_storage_get_filename (storage),
+ " (renamed from \"",
+ nms_keyfile_storage_get_filename (storage),
+ "\")",
+ ""));
}
+
+ _storage_set_type (storage, rd_best->storage_type, rd_best->is_nm_generated_opt, rd_best->is_volatile_opt);
+
+ if (connection_modified)
+ nm_g_object_ref_set (&storage->connection_exported, rd_best->connection);
+
+ _storages_set_full_filename (priv,
+ storage,
+ g_steal_pointer (&rd_best->full_filename));
+
+ /* we don't need the reload data anymore. Drop it. */
+ _storage_reload_data_head_clear (storage);
+
+ g_ptr_array_add (storages_modified, g_object_ref (storage));
}
- g_hash_table_destroy (alive_connections);
- if (dead_connections) {
- for (i = 0; i < dead_connections->len; i++)
- remove_connection (self, dead_connections->pdata[i]);
- g_ptr_array_free (dead_connections, TRUE);
+ /* raise events. */
+
+ c_list_for_each_entry_safe (storage, storage_safe, &lst_conn_info_deleted, storage_lst) {
+ callback (NM_SETTINGS_PLUGIN (self),
+ NM_SETTINGS_STORAGE (storage),
+ NULL,
+ user_data);
+ _storage_destroy (storage);
}
-}
-/*****************************************************************************/
+ for (i = 0; i < storages_modified->len; i++) {
+ storage = storages_modified->pdata[i];
-static GSList *
-get_connections (NMSettingsPlugin *config)
-{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config);
+ if (storage != _storages_get (self, nms_keyfile_storage_get_uuid (storage), FALSE)) {
+ /* hm? The profile was deleted in the meantime? That is only possible
+ * if the signal handler called again into the plugin. In any case, the event
+ * was already emitted. Skip. */
+ continue;
+ }
- if (!priv->initialized) {
- read_connections (config);
- priv->initialized = TRUE;
+ nm_assert (NM_IS_CONNECTION (storage->connection_exported));
+
+ callback (NM_SETTINGS_PLUGIN (self),
+ NM_SETTINGS_STORAGE (storage),
+ storage->connection_exported,
+ user_data);
}
- return _nm_utils_hash_values_to_slist (priv->connections);
}
static gboolean
-load_connection (NMSettingsPlugin *config,
- const char *filename)
-{
- NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN ((NMSKeyfilePlugin *) config);
- NMSKeyfileConnection *connection;
- gboolean require_extension;
-
- if (nm_utils_file_is_in_path (filename, nms_keyfile_utils_get_path ()))
- require_extension = FALSE;
- else if (nm_utils_file_is_in_path (filename, NM_KEYFILE_PATH_NAME_RUN))
- require_extension = TRUE;
- else
+load_connection (NMSettingsPlugin *plugin,
+ const char *full_filename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ NMSettingsStorage **out_storage_replaced,
+ GError **error)
+{
+ NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin);
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
+ NMSKeyfileStorageType storage_type;
+ const char *f_filename;
+ const char *f_dirname;
+ const char *uuid;
+ NMSKeyfileStorage *storage_by_filename;
+ NMSKeyfileStorage *storage = NULL;
+ gs_unref_object NMConnection *connection_new = NULL;
+ gboolean connection_modified;
+ gboolean connection_renamed;
+ gs_free_error GError *local = NULL;
+ gboolean loaded_uuid_success;
+ gs_free char *loaded_uuid_filename = NULL;
+ NMTernary is_nm_generated_opt;
+ NMTernary is_volatile_opt;
+
+ nm_assert (out_storage && !*out_storage);
+ nm_assert (out_connection && !*out_connection);
+ nm_assert (out_storage_replaced && !*out_storage_replaced);
+
+ if (!_path_detect_storage_type (full_filename,
+ (const char *const*) priv->dirname_libs,
+ priv->dirname_etc,
+ priv->dirname_run,
+ &storage_type,
+ &f_dirname,
+ &f_filename,
+ error))
return FALSE;
- if (nm_keyfile_utils_ignore_filename (filename, require_extension))
+ storage_by_filename = g_hash_table_lookup (priv->storages.filename_idx, full_filename);
+
+ connection_new = _read_from_file (full_filename, _get_plugin_dir (priv), NULL, &is_nm_generated_opt, &is_volatile_opt, &local);
+
+ if (!connection_new) {
+ struct stat st;
+
+ if ( storage_by_filename
+ && stat (full_filename, &st) != 0
+ && errno == ENOENT) {
+
+ /* If the file no longer exists, we accept that as command to remove the connection.
+ *
+ * That goes in line with reloading ifcfg-file files that now have NM_CONTROLLED=no.
+ * That will unload the ifcfg file. Likewise, removing the keyfile and loading
+ * the removed path will drop the connection as well.
+ *
+ * In other cases, a loading error will not unload an existing connection. Imagine
+ * you edit the file and have a typo. Then load will simply fail and not removing
+ * the profile. */
+
+ *out_storage_replaced = g_object_ref (NM_SETTINGS_STORAGE (storage_by_filename));
+
+ _LOGT ("load: \"%s\": unload previously loaded connection %s (file no longer exists)",
+ full_filename,
+ nms_keyfile_storage_get_uuid (storage_by_filename));
+
+ _storages_remove (priv, storage_by_filename, TRUE);
+
+ /* Despite we did not *load* anything, we successfully *unloaded*. Return success. */
+ return TRUE;
+ }
+
+ _LOGT ("load: \"%s\": failed to load connection: %s", full_filename, local->message);
+ g_propagate_error (error, g_steal_pointer (&local));
return FALSE;
+ }
- connection = update_connection (self, NULL, filename, find_by_path (self, filename), TRUE, NULL, NULL);
+ uuid = nm_connection_get_uuid (connection_new);
+
+ if ( storage_by_filename
+ && nm_streq (uuid, nms_keyfile_storage_get_uuid (storage_by_filename))) {
+ nm_assert (storage_by_filename == _storages_get (self, uuid, FALSE));
+ storage = g_steal_pointer (&storage_by_filename);
+ } else
+ storage = _storages_get (self, uuid, TRUE);
+
+ connection_modified = !_storage_has_equal_connection (storage, connection_new);
+ connection_renamed = !nms_keyfile_storage_get_filename (storage)
+ || !nm_streq (nms_keyfile_storage_get_filename (storage), full_filename);
+
+ /* FIXME(settings-rework): we now always mark the loaded profile via a symlink
+ * and even for deleted profiles, we remember that they got deleted via a symlink
+ * to /dev/null.
+ * The problem is that these symlinks don't get garbage collected. It'complicated
+ * to understand when a symlink is really no longer needed and when no other profile
+ * references it.
+ *
+ * I think the only solution here is to periodically load all files (temporarily,
+ * without actually using them) to find the existing files and UUIDs.
+ * And then prune the symlinks for UUIDs that are no longer in use.
+ *
+ * This periodic cleanup should also be used to prune /var/lib/NetworkManager/timestamps
+ * and /var/lib/NetworkManager/seen-bssids. */
+ loaded_uuid_success = nms_keyfile_loaded_uuid_write (priv->dirname_run,
+ uuid,
+ full_filename,
+ TRUE,
+ &loaded_uuid_filename);
+
+ _LOGT ("load: \"%s/%s\": profile %s (%s) loaded (%s)"
+ "%s%s%s"
+ "%s%s%s"
+ " (%s%s%s)",
+ f_dirname,
+ f_filename,
+ nms_keyfile_storage_get_uuid (storage),
+ nm_connection_get_id (connection_new),
+ connection_modified ? (storage->connection_exported ? "updated" : "added" ) : "unchanged",
+ NM_PRINT_FMT_QUOTED (connection_renamed && nms_keyfile_storage_get_filename (storage),
+ " (renamed from \"",
+ nms_keyfile_storage_get_filename (storage),
+ "\")",
+ ""),
+ NM_PRINT_FMT_QUOTED (storage_by_filename,
+ " (replaces ",
+ nms_keyfile_storage_get_uuid (storage_by_filename),
+ ")",
+ ""),
+ NM_PRINT_FMT_QUOTED (loaded_uuid_success,
+ "marked as preferred by \"",
+ loaded_uuid_filename,
+ "\"",
+ "failed to write loaded file"));
+
+ _storage_set_type (storage, storage_type, is_nm_generated_opt, is_volatile_opt);
+
+ if (connection_renamed) {
+ _storages_set_full_filename (priv,
+ storage,
+ g_build_filename (f_dirname, f_filename, NULL));
+ }
- return (connection != NULL);
+ if (connection_modified)
+ nm_g_object_ref_set (&storage->connection_exported, connection_new);
+
+ *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage));
+ *out_connection = g_object_ref (storage->connection_exported);
+ *out_storage_replaced = nm_g_object_ref (NM_SETTINGS_STORAGE (storage_by_filename));
+
+ return TRUE;
}
-static void
-reload_connections (NMSettingsPlugin *config)
+gboolean
+nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
+ NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean in_memory,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
{
- read_connections (config);
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
+ gs_unref_object NMConnection *reread = NULL;
+ gs_free char *loaded_uuid_filename = NULL;
+ gs_free char *full_filename = NULL;
+ NMSKeyfileStorageType storage_type;
+ gboolean loaded_uuid_success;
+ NMSKeyfileStorage *storage;
+ GError *local = NULL;
+ const char *uuid;
+
+ nm_assert (NM_IS_CONNECTION (connection));
+ nm_assert (out_storage && !*out_storage);
+ nm_assert (out_connection && !*out_connection);
+
+ uuid = nm_connection_get_uuid (connection);
+
+ if (_storages_get (self, uuid, FALSE)) {
+ _LOGT ("add: %s, \"%s\": failed to add connection that already exists",
+ uuid,
+ nm_connection_get_id (connection));
+ g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "connection with UUID %s already exists",
+ uuid);
+ return FALSE;
+ }
+
+ storage_type = !in_memory && priv->dirname_etc
+ ? NMS_KEYFILE_STORAGE_TYPE_ETC
+ : NMS_KEYFILE_STORAGE_TYPE_RUN;
+
+ if (!nms_keyfile_writer_connection (connection,
+ is_nm_generated,
+ is_volatile,
+ storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC
+ ? priv->dirname_etc
+ : priv->dirname_run,
+ _get_plugin_dir (priv),
+ NULL,
+ FALSE,
+ FALSE,
+ _allow_filename_cb,
+ ALLOW_FILENAME_DATA (priv->storages.filename_idx, NULL),
+ &full_filename,
+ &reread,
+ NULL,
+ &local)) {
+ _LOGT ("add: %s, \"%s\": failed to add connection: %s",
+ nm_connection_get_uuid (connection),
+ nm_connection_get_id (connection),
+ local->message);
+ g_propagate_error (error, local);
+ return FALSE;
+ }
+
+ nm_assert (_nm_connection_verify (reread, NULL) == NM_SETTING_VERIFY_SUCCESS);
+ nm_assert (nm_streq0 (nm_connection_get_uuid (connection), nm_connection_get_uuid (reread)));
+
+ uuid = nm_connection_get_uuid (reread);
+
+ nm_assert (full_filename && full_filename[0] == '/');
+ nm_assert (!_storages_get (self, uuid, FALSE));
+ nm_assert (!g_hash_table_contains (priv->storages.filename_idx, full_filename));
+
+ loaded_uuid_success = nms_keyfile_loaded_uuid_write (priv->dirname_run,
+ uuid,
+ full_filename,
+ TRUE,
+ &loaded_uuid_filename);
+
+ _LOGT ("add: \"%s\": profile %s (%s) written"
+ " (%s%s%s)",
+ full_filename,
+ uuid,
+ nm_connection_get_id (connection),
+ NM_PRINT_FMT_QUOTED (loaded_uuid_success,
+ "indicated by \"",
+ loaded_uuid_filename,
+ "\"",
+ "failed to write loaded file"));
+
+ storage = _storages_get (self, uuid, TRUE);
+
+ nm_assert (!storage->connection_exported);
+
+ _storage_set_type (storage, storage_type, is_nm_generated, is_volatile);
+
+ storage->connection_exported = g_object_ref (reread);
+
+ _storages_set_full_filename (priv,
+ storage,
+ g_steal_pointer (&full_filename));
+
+ *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage));
+ *out_connection = g_steal_pointer (&reread);
+
+ return TRUE;
}
-static NMSettingsConnection *
-add_connection (NMSettingsPlugin *config,
+static gboolean
+add_connection (NMSettingsPlugin *plugin,
NMConnection *connection,
- gboolean save_to_disk,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
GError **error)
{
- NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config);
- gs_free char *path = NULL;
+ return nms_keyfile_plugin_add_connection (NMS_KEYFILE_PLUGIN (plugin),
+ connection,
+ FALSE,
+ FALSE,
+ FALSE,
+ out_storage,
+ out_connection,
+ error);
+}
+
+gboolean
+nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
+ NMSettingsStorage *storage_x,
+ NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean force_rename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
+{
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
+ NMSKeyfileStorage *storage = NMS_KEYFILE_STORAGE (storage_x);
+ gs_unref_object NMConnection *connection_clone = NULL;
gs_unref_object NMConnection *reread = NULL;
+ const char *previous_filename = NULL;
+ const char *uuid;
+ gs_free char *full_filename = NULL;
+ gboolean loaded_uuid_success;
+ gs_free char *loaded_uuid_filename = NULL;
+ NMSKeyfileStorageType storage_type;
+ gboolean connection_modified;
+ gboolean connection_renamed;
+ GError *local = NULL;
+
+ _nm_assert_storage (self, storage, TRUE);
+ nm_assert (NM_IS_CONNECTION (connection));
+ nm_assert (nm_connection_verify (connection, NULL));
+ nm_assert (!error || !*error);
+ nm_assert (nm_streq (nms_keyfile_storage_get_uuid (storage), nm_connection_get_uuid (connection)));
+ nm_assert ( storage->storage_type_exported == NMS_KEYFILE_STORAGE_TYPE_RUN
+ || ( !is_nm_generated
+ && !is_volatile));
+
+ if (!priv->dirname_etc)
+ storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN;
+ else {
+ storage_type = storage->storage_type_exported;
+ if (storage_type == NMS_KEYFILE_STORAGE_TYPE_LIB)
+ storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC;
+ }
+
+ previous_filename = nms_keyfile_storage_get_filename (storage);
+ uuid = nms_keyfile_storage_get_uuid (storage);
if (!nms_keyfile_writer_connection (connection,
- save_to_disk,
- NULL,
- FALSE,
- NULL,
- NULL,
- &path,
+ is_nm_generated,
+ is_volatile,
+ storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC
+ ? priv->dirname_etc
+ : priv->dirname_run,
+ _get_plugin_dir (priv),
+ previous_filename,
+ (storage_type != storage->storage_type_exported),
+ force_rename,
+ _allow_filename_cb,
+ ALLOW_FILENAME_DATA (priv->storages.filename_idx, previous_filename),
+ &full_filename,
&reread,
NULL,
- error))
- return NULL;
+ &local)) {
+ _LOGW ("commit: failure to write %s (%s) to disk: %s",
+ uuid,
+ nm_connection_get_id (connection_clone),
+ local->message);
+ g_propagate_error (error, local);
+ return FALSE;
+ }
+
+ loaded_uuid_success = nms_keyfile_loaded_uuid_write (priv->dirname_run,
+ uuid,
+ full_filename,
+ TRUE,
+ &loaded_uuid_filename);
+
+ connection_modified = !_storage_has_equal_connection (storage, connection);
+ connection_renamed = !nm_streq (previous_filename, full_filename);
+
+ _LOGT ("commit: \"%s\": profile %s (%s) written%s"
+ "%s%s%s"
+ " (%s%s%s)",
+ full_filename,
+ uuid,
+ nm_connection_get_id (connection),
+ connection_modified ? " (modified)" : "",
+ NM_PRINT_FMT_QUOTED (connection_renamed,
+ " (renamed from \"",
+ previous_filename,
+ "\")",
+ ""),
+ NM_PRINT_FMT_QUOTED (loaded_uuid_success,
+ "indicated by \"",
+ loaded_uuid_filename,
+ "\"",
+ "failed to write loaded file"));
+
+ storage->storage_type_exported = storage_type;
+
+ if (connection_renamed) {
+ _storages_set_full_filename (priv,
+ storage,
+ g_steal_pointer (&full_filename));
+ }
+
+ if (connection_modified)
+ nm_g_object_ref_set (&storage->connection_exported, connection);
+
+ *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage));
+ *out_connection = g_object_ref (storage->connection_exported);
+ return TRUE;
+}
+
+static gboolean
+update_connection (NMSettingsPlugin *plugin,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error)
+{
+ return nms_keyfile_plugin_update_connection (NMS_KEYFILE_PLUGIN (plugin),
+ storage,
+ connection,
+ FALSE,
+ FALSE,
+ FALSE,
+ out_storage,
+ out_connection,
+ error);
+}
+
+static gboolean
+delete_connection (NMSettingsPlugin *plugin,
+ NMSettingsStorage *storage_x,
+ gboolean remove_from_disk,
+ GError **error)
+{
+ NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin);
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
+ gs_unref_object NMSKeyfileStorage *storage = g_object_ref (NMS_KEYFILE_STORAGE (storage_x));
+ gboolean loaded_uuid_success;
+ gs_free char *loaded_uuid_filename = NULL;
+ const char *remove_from_disk_errmsg = NULL;
+ const char *operation_message;
+ const char *previous_filename;
+ const char *uuid;
+
+ _nm_assert_storage (self, storage, TRUE);
+ nm_assert (!error || !*error);
+
+ previous_filename = nms_keyfile_storage_get_filename (storage);
+ uuid = nms_keyfile_storage_get_uuid (storage);
+
+ if (!remove_from_disk)
+ operation_message = "only dropped from memory";
+ else if (!NM_IN_SET (storage->storage_type_exported, NMS_KEYFILE_STORAGE_TYPE_ETC,
+ NMS_KEYFILE_STORAGE_TYPE_RUN))
+ operation_message = "dropped readonly file from memory";
+ else if (unlink (previous_filename) != 0) {
+ int errsv;
+
+ errsv = errno;
+ if (errsv != ENOENT) {
+ remove_from_disk_errmsg = nm_strerror_native (errsv);
+ operation_message = "failed to delete from disk";
+ } else
+ operation_message = "does not exist on disk";
+ } else
+ operation_message = "deleted from disk";
+
+ loaded_uuid_success = nms_keyfile_loaded_uuid_write (priv->dirname_run,
+ uuid,
+ NM_KEYFILE_PATH_NMLOADED_NULL,
+ FALSE,
+ &loaded_uuid_filename);
+
+ _LOGT ("delete: %s: profile %s %s"
+ "%s%s%s"
+ " (%s%s%s)",
+ previous_filename,
+ uuid,
+ operation_message,
+ NM_PRINT_FMT_QUOTED (remove_from_disk_errmsg, " (", remove_from_disk_errmsg, ")", ""),
+ NM_PRINT_FMT_QUOTED (loaded_uuid_success,
+ "indicated by \"",
+ loaded_uuid_filename,
+ "\"",
+ "failed to write loaded file"));
+
+ _storages_remove (priv, storage, TRUE);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+config_changed_cb (NMConfig *config,
+ NMConfigData *config_data,
+ NMConfigChangeFlags changes,
+ NMConfigData *old_data,
+ NMSKeyfilePlugin *self)
+{
+ gs_free char *old_value = NULL;
+ gs_free char *new_value = NULL;
- return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error));
+ old_value = nm_config_data_get_value (old_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
+ new_value = nm_config_data_get_value (config_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
+
+ if (!nm_streq0 (old_value, new_value))
+ _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
}
static GSList *
get_unmanaged_specs (NMSettingsPlugin *config)
{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config);
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (config);
gs_free char *value = NULL;
value = nm_config_data_get_value (nm_config_get_data (priv->config),
@@ -517,7 +1388,46 @@ nms_keyfile_plugin_init (NMSKeyfilePlugin *plugin)
NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin);
priv->config = g_object_ref (nm_config_get ());
- priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+
+ c_list_init (&priv->storages.lst_head);
+ priv->storages.filename_idx = g_hash_table_new (nm_str_hash, g_str_equal);
+ priv->storages.idx = g_hash_table_new_full (nm_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify) _storage_destroy);
+
+ /* dirname_libs are a set of read-only directories with lower priority than /etc or /run.
+ * There is nothing complicated about having multiple of such directories, so dirname_libs
+ * is a list (which currently only has at most one directory). */
+ priv->dirname_libs[0] = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_LIB), FALSE);
+ priv->dirname_libs[1] = NULL;
+ priv->dirname_run = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_RUN), FALSE);
+ priv->dirname_etc = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
+ NM_CONFIG_KEYFILE_GROUP_KEYFILE,
+ NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH,
+ NM_CONFIG_GET_VALUE_STRIP);
+ if (priv->dirname_etc && priv->dirname_etc[0] == '\0') {
+ /* special case: configure an empty keyfile path so that NM has no writable keyfile
+ * directory. In this case, NM will only honor dirname_libs and dirname_run, meaning
+ * it cannot persist profile to non-volatile memory. */
+ nm_clear_g_free (&priv->dirname_etc);
+ } else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') {
+ /* either invalid path or unspecified. Use the default. */
+ g_free (priv->dirname_etc);
+ priv->dirname_etc = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_ETC_DEFAULT), FALSE);
+ } else
+ nm_sd_utils_path_simplify (priv->dirname_etc, FALSE);
+
+ /* no duplicates */
+ if (NM_IN_STRSET (priv->dirname_libs[0], priv->dirname_etc,
+ priv->dirname_run))
+ nm_clear_g_free (&priv->dirname_libs[0]);
+ if (NM_IN_STRSET (priv->dirname_etc, priv->dirname_run))
+ nm_clear_g_free (&priv->dirname_etc);
+
+ nm_assert (!priv->dirname_libs[0] || priv->dirname_libs[0][0] == '/');
+ nm_assert (!priv->dirname_etc || priv->dirname_etc[0] == '/');
+ nm_assert ( priv->dirname_run && priv->dirname_run[0] == '/');
}
static void
@@ -555,17 +1465,20 @@ nms_keyfile_plugin_new (void)
static void
dispose (GObject *object)
{
- NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) object);
-
- if (priv->connections) {
- g_hash_table_destroy (priv->connections);
- priv->connections = NULL;
- }
+ NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (object);
+ NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self);
- if (priv->config) {
+ if (priv->config)
g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, object);
- g_clear_object (&priv->config);
- }
+
+ nm_clear_pointer (&priv->storages.filename_idx, g_hash_table_destroy);
+ nm_clear_pointer (&priv->storages.idx, g_hash_table_destroy);
+
+ nm_clear_g_free (&priv->dirname_libs[0]);
+ nm_clear_g_free (&priv->dirname_etc);
+ nm_clear_g_free (&priv->dirname_run);
+
+ g_clear_object (&priv->config);
G_OBJECT_CLASS (nms_keyfile_plugin_parent_class)->dispose (object);
}
@@ -579,9 +1492,11 @@ nms_keyfile_plugin_class_init (NMSKeyfilePluginClass *klass)
object_class->constructed = constructed;
object_class->dispose = dispose;
- plugin_class->get_connections = get_connections;
- plugin_class->load_connection = load_connection;
+ plugin_class->plugin_name = "keyfile";
+ plugin_class->get_unmanaged_specs = get_unmanaged_specs;
plugin_class->reload_connections = reload_connections;
+ plugin_class->load_connection = load_connection;
plugin_class->add_connection = add_connection;
- plugin_class->get_unmanaged_specs = get_unmanaged_specs;
+ plugin_class->update_connection = update_connection;
+ plugin_class->delete_connection = delete_connection;
}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.h b/src/settings/plugins/keyfile/nms-keyfile-plugin.h
index 95403c779b..7d5b762285 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.h
@@ -21,6 +21,9 @@
#ifndef __NMS_KEYFILE_PLUGIN_H__
#define __NMS_KEYFILE_PLUGIN_H__
+#include "settings/nm-settings-plugin.h"
+#include "settings/nm-settings-storage.h"
+
#define NMS_TYPE_KEYFILE_PLUGIN (nms_keyfile_plugin_get_type ())
#define NMS_KEYFILE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePlugin))
#define NMS_KEYFILE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePluginClass))
@@ -35,4 +38,23 @@ GType nms_keyfile_plugin_get_type (void);
NMSKeyfilePlugin *nms_keyfile_plugin_new (void);
+gboolean nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
+ NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean in_memory,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
+gboolean nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ gboolean force_rename,
+ NMSettingsStorage **out_storage,
+ NMConnection **out_connection,
+ GError **error);
+
#endif /* __NMS_KEYFILE_PLUGIN_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index 76b4828b42..a6562a04dc 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -161,6 +161,9 @@ nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
NMConnection *
nms_keyfile_reader_from_file (const char *full_filename,
const char *profile_dir,
+ struct stat *out_stat,
+ NMTernary *out_is_nm_generated,
+ NMTernary *out_is_volatile,
GError **error)
{
gs_unref_keyfile GKeyFile *key_file = NULL;
@@ -170,9 +173,12 @@ nms_keyfile_reader_from_file (const char *full_filename,
nm_assert (full_filename && full_filename[0] == '/');
nm_assert (!profile_dir || profile_dir[0] == '/');
+ NM_SET_OUT (out_is_nm_generated, NM_TERNARY_DEFAULT);
+ NM_SET_OUT (out_is_volatile, NM_TERNARY_DEFAULT);
+
if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_KEYFILE,
full_filename,
- NULL,
+ out_stat,
error))
return NULL;
@@ -194,6 +200,16 @@ nms_keyfile_reader_from_file (const char *full_filename,
connection = NULL;
}
+ NM_SET_OUT (out_is_nm_generated, nm_key_file_get_boolean (key_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_NM_GENERATED,
+ NM_TERNARY_DEFAULT));
+
+ NM_SET_OUT (out_is_volatile, nm_key_file_get_boolean (key_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_VOLATILE,
+ NM_TERNARY_DEFAULT));
+
return connection;
}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.h b/src/settings/plugins/keyfile/nms-keyfile-reader.h
index 430096ebb7..b17b6dd77b 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h
@@ -30,8 +30,13 @@ NMConnection *nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
gboolean verbose,
GError **error);
+struct stat;
+
NMConnection *nms_keyfile_reader_from_file (const char *full_filename,
const char *profile_dir,
+ struct stat *out_stat,
+ NMTernary *out_is_nm_generated,
+ NMTernary *out_is_volatile,
GError **error);
#endif /* __NMS_KEYFILE_READER_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.c b/src/settings/plugins/keyfile/nms-keyfile-storage.c
new file mode 100644
index 0000000000..b3786c1286
--- /dev/null
+++ b/src/settings/plugins/keyfile/nms-keyfile-storage.c
@@ -0,0 +1,101 @@
+/* NetworkManager system settings service - keyfile plugin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nms-keyfile-storage.h"
+
+#include "nm-utils.h"
+#include "nms-keyfile-plugin.h"
+
+/*****************************************************************************/
+
+struct _NMSKeyfileStorageClass {
+ NMSettingsStorageClass parent;
+};
+
+G_DEFINE_TYPE (NMSKeyfileStorage, nms_keyfile_storage, NM_TYPE_SETTINGS_STORAGE)
+
+/*****************************************************************************/
+
+static void
+nms_keyfile_storage_init (NMSKeyfileStorage *self)
+{
+ c_list_init (&self->storage_lst);
+}
+
+NMSKeyfileStorage *
+nms_keyfile_storage_new (NMSKeyfilePlugin *plugin,
+ const char *uuid)
+{
+ nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin));
+ nm_assert (nm_utils_is_uuid (uuid));
+
+ return g_object_new (NMS_TYPE_KEYFILE_STORAGE,
+ NM_SETTINGS_STORAGE_PLUGIN, plugin,
+ NM_SETTINGS_STORAGE_UUID, uuid,
+ NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMSKeyfileStorage *self = NMS_KEYFILE_STORAGE (object);
+
+ _nms_keyfile_storage_clear (self);
+
+ G_OBJECT_CLASS (nms_keyfile_storage_parent_class)->dispose (object);
+}
+
+static void
+nms_keyfile_storage_class_init (NMSKeyfileStorageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dispose;
+}
+
+/*****************************************************************************/
+
+#include "settings/nm-settings-connection.h"
+
+void
+nm_settings_storage_load_sett_flags (NMSettingsStorage *self,
+ NMSettingsConnectionIntFlags *sett_flags,
+ NMSettingsConnectionIntFlags *sett_mask)
+{
+ NMSKeyfileStorage *s;
+
+ *sett_flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
+ *sett_mask = NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
+
+ if (!NMS_IS_KEYFILE_STORAGE (self))
+ return;
+
+ s = NMS_KEYFILE_STORAGE (self);
+ if (s->storage_type_exported != NMS_KEYFILE_STORAGE_TYPE_RUN)
+ return;
+
+ if (s->is_nm_generated)
+ *sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED;
+
+ if (s->is_volatile)
+ *sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
+}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.h b/src/settings/plugins/keyfile/nms-keyfile-storage.h
new file mode 100644
index 0000000000..0f0e7fdc70
--- /dev/null
+++ b/src/settings/plugins/keyfile/nms-keyfile-storage.h
@@ -0,0 +1,118 @@
+/* NetworkManager system settings service - keyfile plugin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ */
+
+#ifndef __NMS_KEYFILE_STORAGE_H__
+#define __NMS_KEYFILE_STORAGE_H__
+
+#include "c-list/src/c-list.h"
+#include "settings/nm-settings-storage.h"
+#include "nms-keyfile-utils.h"
+
+/*****************************************************************************/
+
+#define NMS_TYPE_KEYFILE_STORAGE (nms_keyfile_storage_get_type ())
+#define NMS_KEYFILE_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorage))
+#define NMS_KEYFILE_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass))
+#define NMS_IS_KEYFILE_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_KEYFILE_STORAGE))
+#define NMS_IS_KEYFILE_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_KEYFILE_STORAGE))
+#define NMS_KEYFILE_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass))
+
+typedef struct {
+ NMSettingsStorage parent;
+
+ CList storage_lst;
+
+ /* Keep a reference of the exported connection. We only need this during
+ * reload and load, to check whether the content on disk changed. */
+ NMConnection *connection_exported;
+
+ /* Only relevant during reload-all. It contains a list of all the files
+ * that are associated with this UUID. In general, any number of
+ * files can have connection profiles for a particular UUID. During
+ * _do_reload_all(), we need to load all of them and find the best one.
+ *
+ * This is to simplify reload, because we can figure out whether a UUID
+ * was added, unchanged, or removed by reusing the list of tracked storages.
+ *
+ * After reload-all, this list is cleared again. */
+ struct _NMSKeyfileConnReloadHead *_reload_data_head;
+
+ NMSKeyfileStorageType storage_type_exported;
+
+ bool is_nm_generated:1;
+ bool is_volatile:1;
+} NMSKeyfileStorage;
+
+typedef struct _NMSKeyfileStorageClass NMSKeyfileStorageClass;
+
+GType nms_keyfile_storage_get_type (void);
+
+struct _NMSKeyfilePlugin;
+
+NMSKeyfileStorage *nms_keyfile_storage_new (struct _NMSKeyfilePlugin *plugin,
+ const char *uuid);
+
+void _nms_keyfile_storage_clear (NMSKeyfileStorage *storage);
+
+/*****************************************************************************/
+
+static inline const char *
+nms_keyfile_storage_get_uuid (NMSKeyfileStorage *self)
+{
+ nm_assert (NMS_IS_KEYFILE_STORAGE (self));
+
+ return nm_settings_storage_get_uuid (NM_SETTINGS_STORAGE (self));
+}
+
+static inline const char *
+nms_keyfile_storage_get_filename (NMSKeyfileStorage *self)
+{
+ nm_assert (NMS_IS_KEYFILE_STORAGE (self));
+
+ return nm_settings_storage_get_filename (NM_SETTINGS_STORAGE (self));
+}
+
+/*****************************************************************************/
+
+static inline gboolean
+nm_settings_storage_is_keyfile (NMSettingsStorage *self,
+ gboolean *out_is_in_memory)
+{
+ if (NMS_IS_KEYFILE_STORAGE (self)) {
+ NM_SET_OUT (out_is_in_memory, (((NMSKeyfileStorage *) self)->storage_type_exported == NMS_KEYFILE_STORAGE_TYPE_RUN));
+ return TRUE;
+ }
+ NM_SET_OUT (out_is_in_memory, FALSE);
+ return FALSE;
+}
+
+static inline gboolean
+nm_settings_storage_is_in_memory (NMSettingsStorage *self)
+{
+ return NMS_IS_KEYFILE_STORAGE (self)
+ && (((NMSKeyfileStorage *) self)->storage_type_exported == NMS_KEYFILE_STORAGE_TYPE_RUN);
+}
+
+enum _NMSettingsConnectionIntFlags;
+
+void nm_settings_storage_load_sett_flags (NMSettingsStorage *self,
+ enum _NMSettingsConnectionIntFlags *sett_flags,
+ enum _NMSettingsConnectionIntFlags *sett_mask);
+
+#endif /* __NMS_KEYFILE_STORAGE_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c
index f2f9f37b7c..af0dcc56d1 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c
@@ -14,7 +14,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * (C) Copyright 2010 Red Hat, Inc.
+ * (C) Copyright 2010 - 2018 Red Hat, Inc.
*/
#include "nm-default.h"
@@ -41,7 +41,8 @@ nms_keyfile_loaded_uuid_filename (const char *dirname,
char filename[250];
nm_assert (dirname && dirname[0] == '/');
- nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/'));
+ nm_assert ( nm_utils_is_uuid (uuid)
+ && !strchr (uuid, '/'));
if (g_snprintf (filename,
sizeof (filename),
@@ -167,7 +168,8 @@ nms_keyfile_loaded_uuid_write (const char *dirname,
gs_free char *full_filename = NULL;
nm_assert (dirname && dirname[0] == '/');
- nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/'));
+ nm_assert ( nm_utils_is_uuid (uuid)
+ && !strchr (uuid, '/'));
nm_assert (!loaded_path || loaded_path[0] == '/');
full_filename_tmp = nms_keyfile_loaded_uuid_filename (dirname, uuid, TRUE);
@@ -294,22 +296,3 @@ nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype,
NM_SET_OUT (out_st, st);
return TRUE;
}
-
-/*****************************************************************************/
-
-const char *
-nms_keyfile_utils_get_path (void)
-{
- static char *path = NULL;
-
- if (G_UNLIKELY (!path)) {
- path = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
- NM_CONFIG_KEYFILE_GROUP_KEYFILE,
- NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH,
- NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
- if (!path)
- path = g_strdup (""NM_KEYFILE_PATH_NAME_ETC_DEFAULT"");
- }
- return path;
-}
-
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h
index 30b033adba..59b86d3142 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h
@@ -14,7 +14,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * (C) Copyright 2010-2016 Red Hat, Inc.
+ * (C) Copyright 2010 - 2018 Red Hat, Inc.
*/
#ifndef __NMS_KEYFILE_UTILS_H__
@@ -22,18 +22,18 @@
#include "NetworkManagerUtils.h"
-#define NMS_KEYFILE_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory")
-#define NMS_KEYFILE_CONNECTION_LOG_FMT "%s (%s,\"%s\")"
-#define NMS_KEYFILE_CONNECTION_LOG_ARG(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con))
-#define NMS_KEYFILE_CONNECTION_LOG_FMTD "%s (%s,\"%s\",%p)"
-#define NMS_KEYFILE_CONNECTION_LOG_ARGD(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)), (con)
-
typedef enum {
NMS_KEYFILE_FILETYPE_KEYFILE,
NMS_KEYFILE_FILETYPE_NMLOADED,
} NMSKeyfileFiletype;
-const char *nms_keyfile_utils_get_path (void);
+typedef enum {
+ /* the order here matters. Higher numbers are more important. E.g. /etc shadows
+ * connections from /usr/lib. */
+ NMS_KEYFILE_STORAGE_TYPE_LIB, /* read-only, e.g. /usr/lib */
+ NMS_KEYFILE_STORAGE_TYPE_ETC, /* read-write, persistent, e.g. /etc */
+ NMS_KEYFILE_STORAGE_TYPE_RUN, /* read-write, runtime only, e.g. /var/run */
+} NMSKeyfileStorageType;
/*****************************************************************************/
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index a0c3c17b98..54e62b7373 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -168,6 +168,8 @@ _handler_write (NMConnection *connection,
static gboolean
_internal_write_connection (NMConnection *connection,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
const char *keyfile_dir,
const char *profile_dir,
gboolean with_extension,
@@ -212,6 +214,21 @@ _internal_write_connection (NMConnection *connection,
kf_file = nm_keyfile_write (connection, _handler_write, &info, error);
if (!kf_file)
return FALSE;
+
+ if (is_nm_generated) {
+ g_key_file_set_boolean (kf_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_NM_GENERATED,
+ TRUE);
+ }
+
+ if (is_volatile) {
+ g_key_file_set_boolean (kf_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_VOLATILE,
+ TRUE);
+ }
+
kf_content_buf = g_key_file_to_data (kf_file, &kf_content_len, error);
if (!kf_content_buf)
return FALSE;
@@ -337,8 +354,12 @@ _internal_write_connection (NMConnection *connection,
gboolean
nms_keyfile_writer_connection (NMConnection *connection,
- gboolean save_to_disk,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ const char *keyfile_dir,
+ const char *profile_dir,
const char *existing_path,
+ gboolean existing_path_read_only,
gboolean force_rename,
NMSKeyfileWriterAllowFilenameCb allow_filename_cb,
gpointer allow_filename_user_data,
@@ -347,21 +368,16 @@ nms_keyfile_writer_connection (NMConnection *connection,
gboolean *out_reread_same,
GError **error)
{
- const char *keyfile_dir;
-
- if (save_to_disk)
- keyfile_dir = nms_keyfile_utils_get_path ();
- else
- keyfile_dir = NM_KEYFILE_PATH_NAME_RUN;
-
return _internal_write_connection (connection,
+ is_nm_generated,
+ is_volatile,
keyfile_dir,
- nms_keyfile_utils_get_path (),
+ profile_dir,
TRUE,
0,
0,
existing_path,
- FALSE,
+ existing_path_read_only,
force_rename,
allow_filename_cb,
allow_filename_user_data,
@@ -382,6 +398,8 @@ nms_keyfile_writer_test_connection (NMConnection *connection,
GError **error)
{
return _internal_write_connection (connection,
+ FALSE,
+ FALSE,
keyfile_dir,
keyfile_dir,
FALSE,
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h
index db41b81c50..4fb9a20638 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h
@@ -27,8 +27,12 @@ typedef gboolean (*NMSKeyfileWriterAllowFilenameCb) (const char *check_filename,
gpointer allow_filename_user_data);
gboolean nms_keyfile_writer_connection (NMConnection *connection,
- gboolean save_to_disk,
+ gboolean is_nm_generated,
+ gboolean is_volatile,
+ const char *keyfile_dir,
+ const char *profile_dir,
const char *existing_path,
+ gboolean existing_path_read_only,
gboolean force_rename,
NMSKeyfileWriterAllowFilenameCb allow_filename_cb,
gpointer allow_filename_user_data,
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
index 232c690431..9b035c2034 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
@@ -72,6 +72,9 @@ check_ip_route (NMSettingIPConfig *config, int idx, const char *destination, int
\
_connection = nms_keyfile_reader_from_file (full_filename, \
NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
(nmtst_get_rand_uint32 () % 2) ? &_error : NULL); \
nmtst_assert_success (_connection, _error); \
nmtst_assert_connection_verifies_without_normalization (_connection); \