diff options
Diffstat (limited to 'src/settings')
83 files changed, 8751 insertions, 6777 deletions
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 814dee85..d361c06a 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -311,7 +310,6 @@ validate_identifier (const char *identifier, GError **error) static void agent_register_permissions_done (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data) { @@ -319,47 +317,36 @@ agent_register_permissions_done (NMAuthChain *chain, NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); NMSecretAgent *agent; const char *sender; - GError *local = NULL; NMAuthCallResult result; CList *iter; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); priv->chains = g_slist_remove (priv->chains, chain); - if (error) { - local = g_error_new (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED, - "Failed to request agent permissions: %s", - error->message); - g_dbus_method_invocation_take_error (context, local); - } else { - agent = nm_auth_chain_steal_data (chain, "agent"); - g_assert (agent); + agent = nm_auth_chain_steal_data (chain, "agent"); + nm_assert (agent); - result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED); - if (result == NM_AUTH_CALL_RESULT_YES) - nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, TRUE); + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED); + if (result == NM_AUTH_CALL_RESULT_YES) + nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, TRUE); - result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN); - if (result == NM_AUTH_CALL_RESULT_YES) - nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, TRUE); + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN); + if (result == NM_AUTH_CALL_RESULT_YES) + nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, TRUE); - priv->agent_version_id += 1; - sender = nm_secret_agent_get_dbus_owner (agent); - g_hash_table_insert (priv->agents, g_strdup (sender), agent); - _LOGI (agent, "agent registered"); - g_dbus_method_invocation_return_value (context, NULL); + priv->agent_version_id += 1; + sender = nm_secret_agent_get_dbus_owner (agent); + g_hash_table_insert (priv->agents, g_strdup (sender), agent); + _LOGI (agent, "agent registered"); + g_dbus_method_invocation_return_value (context, NULL); - /* Signal an agent was registered */ - g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent); + /* Signal an agent was registered */ + g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent); - /* Add this agent to any in-progress secrets requests */ - c_list_for_each (iter, &priv->requests) - request_add_agent (c_list_entry (iter, Request, lst_request), agent); - } - - nm_auth_chain_destroy (chain); + /* Add this agent to any in-progress secrets requests */ + c_list_for_each (iter, &priv->requests) + request_add_agent (c_list_entry (iter, Request, lst_request), agent); } static NMSecretAgent * @@ -537,8 +524,7 @@ request_free (Request *req) case REQUEST_TYPE_CON_DEL: g_object_unref (req->con.connection); g_free (req->con.path); - if (req->con.chain) - nm_auth_chain_destroy (req->con.chain); + nm_clear_pointer (&req->con.chain, nm_auth_chain_destroy); if (req->request_type == REQUEST_TYPE_CON_GET) { g_free (req->con.get.setting_name); g_strfreev (req->con.get.hints); @@ -815,11 +801,8 @@ request_remove_agent (Request *req, NMSecretAgent *agent) case REQUEST_TYPE_CON_GET: case REQUEST_TYPE_CON_SAVE: case REQUEST_TYPE_CON_DEL: - if (req->con.chain) { - /* This cancels the pending authorization requests. */ - nm_auth_chain_destroy (req->con.chain); - req->con.chain = NULL; - } + /* This cancels the pending authorization requests. */ + nm_clear_pointer (&req->con.chain, nm_auth_chain_destroy); break; default: g_assert_not_reached (); @@ -1017,7 +1000,6 @@ _con_get_request_start_proceed (Request *req, gboolean include_system_secrets) static void _con_get_request_start_validated (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data) { @@ -1031,30 +1013,20 @@ _con_get_request_start_validated (NMAuthChain *chain, req->con.chain = NULL; - if (error) { - _LOGD (req->current, "agent "LOG_REQ_FMT" MODIFY check error: %s", - LOG_REQ_ARG (req), - error->message); - /* Try the next agent */ - request_next_agent (req); - } else { - /* If the agent obtained the 'modify' permission, we send all system secrets - * to it. If it didn't, we still ask it for secrets, but we don't send - * any system secrets. - */ - perm = nm_auth_chain_get_data (chain, "perm"); - g_assert (perm); - if (nm_auth_chain_get_result (chain, perm) == NM_AUTH_CALL_RESULT_YES) - req->con.current_has_modify = TRUE; - - _LOGD (req->current, "agent "LOG_REQ_FMT" MODIFY check result %s", - LOG_REQ_ARG (req), - req->con.current_has_modify ? "YES" : "NO"); + /* If the agent obtained the 'modify' permission, we send all system secrets + * to it. If it didn't, we still ask it for secrets, but we don't send + * any system secrets. + */ + perm = nm_auth_chain_get_data (chain, "perm"); + g_assert (perm); + if (nm_auth_chain_get_result (chain, perm) == NM_AUTH_CALL_RESULT_YES) + req->con.current_has_modify = TRUE; - _con_get_request_start_proceed (req, req->con.current_has_modify); - } + _LOGD (req->current, "agent "LOG_REQ_FMT" MODIFY check result %s", + LOG_REQ_ARG (req), + req->con.current_has_modify ? "YES" : "NO"); - nm_auth_chain_destroy (chain); + _con_get_request_start_proceed (req, req->con.current_has_modify); } static void @@ -1100,7 +1072,7 @@ _con_get_request_start (Request *req) perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM; nm_auth_chain_set_data (req->con.chain, "perm", (gpointer) perm, NULL); - nm_auth_chain_add_call (req->con.chain, perm, TRUE); + nm_auth_chain_add_call_unsafe (req->con.chain, perm, TRUE); } else { _LOGD (NULL, "("LOG_REQ_FMT") requesting user-owned secrets from agent %s", LOG_REQ_ARG (req), agent_dbus_owner); @@ -1478,7 +1450,6 @@ nm_agent_manager_all_agents_have_capability (NMAgentManager *manager, static void agent_permissions_changed_done (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data) { @@ -1492,21 +1463,15 @@ agent_permissions_changed_done (NMAuthChain *chain, agent = nm_auth_chain_get_data (chain, "agent"); g_assert (agent); - if (error) - _LOGD (agent, "failed to request updated agent permissions"); - else { - _LOGD (agent, "updated agent permissions"); - - if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED) == NM_AUTH_CALL_RESULT_YES) - share_protected = TRUE; - if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN) == NM_AUTH_CALL_RESULT_YES) - share_open = TRUE; - } + _LOGD (agent, "updated agent permissions"); + + if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED) == NM_AUTH_CALL_RESULT_YES) + share_protected = TRUE; + if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN) == NM_AUTH_CALL_RESULT_YES) + share_open = TRUE; nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, share_protected); nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, share_open); - - nm_auth_chain_destroy (chain); } static void diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h index 8c27f26b..949ab6bc 100644 --- a/src/settings/nm-agent-manager.h +++ b/src/settings/nm-agent-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c index 836ab21d..2aa1476d 100644 --- a/src/settings/nm-secret-agent.c +++ b/src/settings/nm-secret-agent.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -25,6 +24,7 @@ #include <sys/types.h> #include <pwd.h> +#include "nm-glib-aux/nm-dbus-aux.h" #include "nm-dbus-interface.h" #include "nm-dbus-manager.h" #include "nm-core-internal.h" @@ -54,7 +54,10 @@ typedef struct { NMDBusManager *bus_mgr; GDBusConnection *connection; CList requests; - gulong on_disconnected_id; + union { + gulong obj_signal; + guint dbus_signal; + } on_disconnected_id; bool connection_is_private:1; } NMSecretAgentPrivate; @@ -614,15 +617,12 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self, static void _on_disconnected_cleanup (NMSecretAgentPrivate *priv) { - if (priv->on_disconnected_id) { - if (priv->connection_is_private) { - g_signal_handler_disconnect (priv->bus_mgr, - priv->on_disconnected_id); - } else { - g_dbus_connection_signal_unsubscribe (priv->connection, - priv->on_disconnected_id); - } - priv->on_disconnected_id = 0; + if (priv->connection_is_private) { + nm_clear_g_signal_handler (priv->bus_mgr, + &priv->on_disconnected_id.obj_signal); + } else { + nm_clear_g_dbus_connection_signal (priv->connection, + &priv->on_disconnected_id.dbus_signal); } g_clear_object (&priv->connection); @@ -744,21 +744,16 @@ nm_secret_agent_new (GDBusMethodInvocation *context, /* we cannot subscribe to notify::g-name-owner because that doesn't work * for unique names and it doesn't work for private connections. */ if (priv->connection_is_private) { - priv->on_disconnected_id = g_signal_connect (priv->bus_mgr, - NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, - G_CALLBACK (_on_disconnected_private_connection), - self); + priv->on_disconnected_id.obj_signal = g_signal_connect (priv->bus_mgr, + NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, + G_CALLBACK (_on_disconnected_private_connection), + self); } else { - priv->on_disconnected_id = g_dbus_connection_signal_subscribe (priv->connection, - "org.freedesktop.DBus", /* name */ - "org.freedesktop.DBus", /* interface */ - "NameOwnerChanged", /* signal name */ - "/org/freedesktop/DBus", /* path */ - priv->dbus_owner, /* arg0 */ - G_DBUS_SIGNAL_FLAGS_NONE, - _on_disconnected_name_owner_changed, - self, - NULL); + priv->on_disconnected_id.dbus_signal = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->connection, + priv->dbus_owner, + _on_disconnected_name_owner_changed, + self, + NULL); } return self; diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h index 64b103aa..209a1009 100644 --- a/src/settings/nm-secret-agent.h +++ b/src/settings/nm-secret-agent.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 3fdaa598..25a27e68 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -25,6 +24,7 @@ #include "c-list/src/c-list.h" +#include "nm-glib-aux/nm-keyfile-aux.h" #include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-config.h" #include "nm-config-data.h" @@ -37,14 +37,18 @@ #include "NetworkManagerUtils.h" #include "nm-core-internal.h" #include "nm-audit-manager.h" - -#define SETTINGS_TIMESTAMPS_FILE NMSTATEDIR "/timestamps" -#define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids" +#include "nm-settings.h" +#include "settings/plugins/keyfile/nms-keyfile-storage.h" #define AUTOCONNECT_RETRIES_UNSET -2 #define AUTOCONNECT_RETRIES_FOREVER -1 #define AUTOCONNECT_RESET_RETRIES_TIMER 300 +#define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES ((NMSettingsUpdate2Flags) ( NM_SETTINGS_UPDATE2_FLAG_TO_DISK \ + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY \ + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED \ + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) + /*****************************************************************************/ NMConnection ** @@ -70,13 +74,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 @@ -86,18 +88,12 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct _NMSettingsConnectionPrivate { - NMAgentManager *agent_mgr; - NMSessionMonitor *session_monitor; - gulong session_changed_id; - - NMSettingsConnectionIntFlags flags:5; - - bool removed:1; - bool ready:1; + NMSettings *settings; - bool timestamp_set:1; + NMKeyFileDB *kf_db_timestamps; + NMKeyFileDB *kf_db_seen_bssids; - NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4; + NMAgentManager *agent_mgr; /* List of pending authentication requests */ CList auth_lst_head; @@ -106,12 +102,18 @@ 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 * connection in-memory at all. */ - NMConnection *system_secrets; + GVariant *system_secrets; /* Caches secrets from agents during the activation process; if new system * secrets are returned from an agent, they get written out to disk, @@ -119,9 +121,7 @@ typedef struct _NMSettingsConnectionPrivate { * secrets, and would wipe out any agent-owned or not-saved secrets the * agent also returned. */ - NMConnection *agent_secrets; - - char *filename; + GVariant *agent_secrets; GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */ @@ -130,10 +130,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) @@ -152,7 +163,7 @@ G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT const char *__uuid = (self) ? nm_settings_connection_get_uuid (self) : NULL; \ \ if (self) { \ - g_snprintf (__prefix, sizeof (__prefix), "%s[%p%s%s]", _NMLOG_PREFIX_NAME, self, __uuid ? "," : "", __uuid ?: ""); \ + g_snprintf (__prefix, sizeof (__prefix), "%s["NM_HASH_OBFUSCATE_PTR_FMT"%s%s]", _NMLOG_PREFIX_NAME, NM_HASH_OBFUSCATE_PTR (self), __uuid ? "," : "", __uuid ?: ""); \ __p_prefix = __prefix; \ } \ _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, __uuid, \ @@ -167,6 +178,92 @@ 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); + } +} + +/*****************************************************************************/ + +gboolean +nm_settings_connection_still_valid (NMSettingsConnection *self) +{ + gboolean valid; + + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); + + valid = !c_list_is_empty (&self->_connections_lst); + + nm_assert (valid == nm_settings_has_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings, self)); + + return valid; +} + +/*****************************************************************************/ + +static GHashTable * +_seen_bssids_hash_new (void) +{ + return g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); +} + /*****************************************************************************/ NMConnection * @@ -177,6 +274,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 @@ -206,34 +347,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; @@ -243,20 +373,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; } /*****************************************************************************/ @@ -302,7 +425,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; } } @@ -312,421 +436,196 @@ nm_settings_connection_check_permission (NMSettingsConnection *self, /*****************************************************************************/ -static gboolean -secrets_filter_cb (NMSetting *setting, - const char *secret, - NMSettingSecretFlags flags, - gpointer user_data) +static void +update_system_secrets_cache (NMSettingsConnection *self, NMConnection *new) { - NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data); - - /* Returns TRUE to remove the secret */ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + gs_unref_object NMConnection *connection_cloned = NULL; + gs_unref_variant GVariant *old_secrets = NULL; - /* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */ - if ( (flags == NM_SETTING_SECRET_FLAG_NONE) - && (filter_flags == NM_SETTING_SECRET_FLAG_NONE)) - return FALSE; + old_secrets = g_steal_pointer (&priv->system_secrets); - /* Otherwise if the secret has at least one of the desired flags keep it */ - return (flags & filter_flags) ? FALSE : TRUE; -} + if (!new) + goto out; -static void -update_system_secrets_cache (NMSettingsConnection *self) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection, + * in particular if there are no secrets to begin with. */ - if (priv->system_secrets) - g_object_unref (priv->system_secrets); - priv->system_secrets = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); + connection_cloned = nm_simple_connection_new_clone (new); /* Clear out non-system-owned and not-saved secrets */ - nm_connection_clear_secrets_with_flags (priv->system_secrets, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_NONE)); + _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 update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMSettingSecretFlags filter_flags = NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_AGENT_OWNED; + gs_unref_object NMConnection *connection_cloned = NULL; + gs_unref_variant GVariant *old_secrets = NULL; - if (priv->agent_secrets) - g_object_unref (priv->agent_secrets); - priv->agent_secrets = nm_simple_connection_new_clone ( new - ?: nm_settings_connection_get_connection (self)); + old_secrets = g_steal_pointer (&priv->agent_secrets); - /* Clear out non-system-owned secrets */ - nm_connection_clear_secrets_with_flags (priv->agent_secrets, - secrets_filter_cb, - GUINT_TO_POINTER (filter_flags)); -} + if (!new) + goto out; -static void -secrets_cleared_cb (NMConnection *connection, NMSettingsConnection *self) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection, + * in particular if there are no secrets to begin with. */ - /* Clear agent secrets when connection's secrets are cleared since agent - * secrets are transient. - */ - if (priv->agent_secrets) - g_object_unref (priv->agent_secrets); - priv->agent_secrets = NULL; -} + connection_cloned = nm_simple_connection_new_clone (new); -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; + /* Clear out non-system-owned secrets */ + _nm_connection_clear_secrets_by_secret_flags (connection_cloned, + NM_SETTING_SECRET_FLAG_NOT_SAVED + | NM_SETTING_SECRET_FLAG_AGENT_OWNED); - 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; - } + priv->agent_secrets = nm_g_variant_ref_sink (nm_connection_to_dbus (connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS)); - nm_settings_connection_set_flags_full (self, ALL, flags); +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"); + } } -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); -} + if (!nm_settings_connection_still_valid (self)) + return; -static gboolean -_delete (NMSettingsConnection *self, GError **error) -{ - NMSettingsConnectionClass *klass; - GError *local = NULL; - const char *filename; + /* FIXME: add API to NMConnection so that we can clone a profile without secrets. */ - nm_assert (NM_IS_SETTINGS_CONNECTION (self)); + connection_cloned = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - 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; + nm_connection_clear_secrets (connection_cloned); + + 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_object NMConnection *simple = 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) { - simple = nm_simple_connection_new_clone (new_connection); - nm_connection_clear_secrets_with_flags (simple, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - new_agent_secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - g_clear_object (&simple); - } - - /* 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. */ - simple = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - nm_connection_clear_secrets_with_flags (simple, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - con_agent_secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - - 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); - - /* 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) { - GVariant *dict; + if (g_variant_n_children (secrets) == 0) + return TRUE; - dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - if (dict) { - (void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, dict, NULL); - g_variant_unref (dict); - } + 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; } - if (con_agent_secrets) - (void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, con_agent_secrets, NULL); + secrets = secrets_setting; } - /* Apply agent-owned secrets from the new connection so that - * they can be sent to agents */ - if (new_agent_secrets) { - (void) 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; } -static void -remove_entry_from_db (NMSettingsConnection *self, const char* db_name) -{ - GKeyFile *key_file; - const char *db_file; - - if (strcmp (db_name, "timestamps") == 0) - db_file = SETTINGS_TIMESTAMPS_FILE; - else if (strcmp (db_name, "seen-bssids") == 0) - db_file = SETTINGS_SEEN_BSSIDS_FILE; - else - return; - - key_file = g_key_file_new (); - if (g_key_file_load_from_file (key_file, db_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) { - const char *connection_uuid; - char *data; - gsize len; - GError *error = NULL; - - connection_uuid = nm_settings_connection_get_uuid (self); - - g_key_file_remove_key (key_file, db_name, connection_uuid, NULL); - data = g_key_file_to_data (key_file, &len, &error); - if (data) { - g_file_set_contents (db_file, data, len, &error); - g_free (data); - } - if (error) { - _LOGW ("error writing %s file '%s': %s", db_name, db_file, error->message); - g_error_free (error); - } - } - g_key_file_free (key_file); -} - 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; - 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); - - /* Remove timestamp from timestamps database file */ - remove_entry_from_db (self, "timestamps"); + 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); +} - /* Remove connection from seen-bssids database file */ - remove_entry_from_db (self, "seen-bssids"); +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, + allow_add_to_no_auto_default); } /*****************************************************************************/ @@ -788,13 +687,6 @@ _get_secrets_info_free (NMSettingsConnectionCallId *call_id) g_slice_free (NMSettingsConnectionCallId, call_id); } -static gboolean -supports_secrets (NMSettingsConnection *self, const char *setting_name) -{ - /* All secrets supported */ - return TRUE; -} - typedef struct { NMSettingSecretFlags required; NMSettingSecretFlags forbidden; @@ -918,25 +810,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; } - 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; } @@ -957,9 +861,11 @@ get_secrets_done_cb (NMAgentManager *manager, NMSettingsConnectionPrivate *priv; NMConnection *applied_connection; gs_free_error GError *local = NULL; - GVariant *dict = 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; @@ -1020,62 +926,69 @@ get_secrets_done_cb (NMAgentManager *manager, setting_name, call_id); - if (priv->system_secrets) - dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); + 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 (!dict || nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, dict, &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); - 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, @@ -1091,18 +1004,17 @@ get_secrets_done_cb (NMAgentManager *manager, nm_connection_clear_secrets (applied_connection); - if (!dict || nm_connection_update_secrets (applied_connection, setting_name, dict, NULL)) { - gs_unref_variant GVariant *filtered_secrets = NULL; + if ( !system_secrets + || nm_connection_update_secrets (applied_connection, setting_name, system_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); } } _get_secrets_info_callback (call_id, agent_username, setting_name, local); g_clear_error (&local); - if (dict) - g_variant_unref (dict); out: _get_secrets_info_free (call_id); @@ -1163,7 +1075,6 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, gpointer callback_data) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - GVariant *existing_secrets = NULL; NMAgentManagerCallId call_id_a; gs_free char *joined_hints = NULL; NMSettingsConnectionCallId *call_id; @@ -1200,14 +1111,6 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, goto schedule_dummy; } - /* Use priv->system_secrets to work around the fact that nm_connection_clear_secrets() - * will clear secrets on this object's settings. - */ - if (priv->system_secrets) - existing_secrets = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - if (existing_secrets) - g_variant_ref_sink (existing_secrets); - /* we remember the current version-id of the secret-agents. The version-id is strictly increasing, * as new agents register the number. We know hence, that this request was made against a certain * set of secret-agents. @@ -1215,19 +1118,20 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, * Then we know that the this request probably did not yet include the latest secret-agent. */ priv->last_secret_agent_version_id = nm_agent_manager_get_agent_version_id (priv->agent_mgr); + /* Use priv->system_secrets to work around the fact that nm_connection_clear_secrets() + * will clear secrets on this object's settings. + */ call_id_a = nm_agent_manager_get_secrets (priv->agent_mgr, nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), nm_settings_connection_get_connection (self), subject, - existing_secrets, + priv->system_secrets, setting_name, flags, hints, get_secrets_done_cb, call_id); - g_assert (call_id_a); - if (existing_secrets) - g_variant_unref (existing_secrets); + nm_assert (call_id_a); _LOGD ("(%s:%p) secrets requested flags 0x%X hints '%s'", setting_name, @@ -1420,37 +1324,6 @@ auth_start (NMSettingsConnection *self, /**** DBus method handlers ************************************/ -static gboolean -check_writable (NMConnection *self, GError **error) -{ - NMSettingConnection *s_con; - - g_return_val_if_fail (NM_IS_CONNECTION (self), FALSE); - - s_con = nm_connection_get_setting_connection (self); - if (!s_con) { - g_set_error_literal (error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Connection did not have required 'connection' setting"); - return FALSE; - } - - /* If the connection is read-only, that has to be changed at the source of - * the problem (ex a system settings plugin that can't write connections out) - * instead of over D-Bus. - */ - if (nm_setting_connection_get_read_only (s_con)) { - g_set_error_literal (error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_READ_ONLY_CONNECTION, - "Connection is read-only"); - return FALSE; - } - - return TRUE; -} - static void get_settings_auth_cb (NMSettingsConnection *self, GDBusMethodInvocation *context, @@ -1458,46 +1331,41 @@ get_settings_auth_cb (NMSettingsConnection *self, GError *error, gpointer data) { - if (error) + gs_free const char **seen_bssids = NULL; + NMConnectionSerializationOptions options = { + }; + GVariant *settings; + + if (error) { g_dbus_method_invocation_return_gerror (context, error); - else { - gs_unref_object NMConnection *dupl_con = NULL; - GVariant *settings; - NMSettingConnection *s_con; - NMSettingWireless *s_wifi; - guint64 timestamp = 0; - gs_free char **bssids = NULL; - - dupl_con = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - - /* Timestamp is not updated in connection's 'timestamp' property, - * because it would force updating the connection and in turn - * writing to /etc periodically, which we want to avoid. Rather real - * timestamps are kept track of in a private variable. So, substitute - * timestamp property with the real one here before returning the settings. - */ - nm_settings_connection_get_timestamp (self, ×tamp); - if (timestamp) { - s_con = nm_connection_get_setting_connection (dupl_con); - g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL); - } - /* Seen BSSIDs are not updated in 802-11-wireless 'seen-bssids' property - * from the same reason as timestamp. Thus we put it here to GetSettings() - * return settings too. - */ - bssids = nm_settings_connection_get_seen_bssids (self); - s_wifi = nm_connection_get_setting_wireless (dupl_con); - if (bssids && bssids[0] && s_wifi) - g_object_set (s_wifi, NM_SETTING_WIRELESS_SEEN_BSSIDS, bssids, NULL); - - /* Secrets should *never* be returned by the GetSettings method, they - * get returned by the GetSecrets method which can be better - * protected against leakage of secrets to unprivileged callers. - */ - settings = nm_connection_to_dbus (dupl_con, NM_CONNECTION_SERIALIZE_NO_SECRETS); - g_dbus_method_invocation_return_value (context, - g_variant_new ("(@a{sa{sv}})", settings)); + return; } + + /* Timestamp is not updated in connection's 'timestamp' property, + * because it would force updating the connection and in turn + * writing to /etc periodically, which we want to avoid. Rather real + * timestamps are kept track of in a private variable. So, substitute + * timestamp property with the real one here before returning the settings. + */ + options.timestamp.has = TRUE; + nm_settings_connection_get_timestamp (self, &options.timestamp.val); + + /* Seen BSSIDs are not updated in 802-11-wireless 'seen-bssids' property + * from the same reason as timestamp. Thus we put it here to GetSettings() + * return settings too. + */ + seen_bssids = nm_settings_connection_get_seen_bssids (self); + options.seen_bssids = seen_bssids; + + /* Secrets should *never* be returned by the GetSettings method, they + * get returned by the GetSecrets method which can be better + * protected against leakage of secrets to unprivileged callers. + */ + settings = nm_connection_to_dbus_full (nm_settings_connection_get_connection (self), + NM_CONNECTION_SERIALIZE_NO_SECRETS, + &options); + g_dbus_method_invocation_return_value (context, + g_variant_new ("(@a{sa{sv}})", settings)); } static void @@ -1533,28 +1401,6 @@ typedef struct { } UpdateInfo; static void -cached_secrets_to_connection (NMSettingsConnection *self, NMConnection *connection) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - GVariant *secrets_dict; - - if (priv->agent_secrets) { - secrets_dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - if (secrets_dict) { - (void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL); - g_variant_unref (secrets_dict); - } - } - if (priv->system_secrets) { - secrets_dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - if (secrets_dict) { - (void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL); - g_variant_unref (secrets_dict); - } - } -} - -static void update_complete (NMSettingsConnection *self, UpdateInfo *info, GError *error) @@ -1566,7 +1412,7 @@ update_complete (NMSettingsConnection *self, g_variant_builder_init (&result, G_VARIANT_TYPE ("a{sv}")); g_dbus_method_invocation_return_value (info->context, - g_variant_new ("(@a{sv})", g_variant_builder_end (&result))); + g_variant_new ("(a{sv})", &result)); } else g_dbus_method_invocation_return_value (info->context, NULL); @@ -1587,24 +1433,28 @@ update_auth_cb (NMSettingsConnection *self, GError *error, gpointer data) { + 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); return; } + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + if (info->new_settings) { if (!_nm_connection_aggregate (info->new_settings, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)) { /* If the new connection has no secrets, we do not want to remove all * secrets, rather we keep all the existing ones. Do that by merging * them in to the new connection. */ - cached_secrets_to_connection (self, info->new_settings); + if (priv->agent_secrets) + nm_connection_update_secrets (info->new_settings, NULL, priv->agent_secrets, NULL); + if (priv->system_secrets) + nm_connection_update_secrets (info->new_settings, NULL, priv->system_secrets, NULL); } else { /* Cache the new secrets from the agent, as stuff like inotify-triggered * changes to connection's backing config files will blow them away if @@ -1628,45 +1478,38 @@ 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; + nm_assert ( !NM_FLAGS_ANY (info->flags, _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES) + || nm_utils_is_power_of_two (info->flags & _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES)); 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_TO_DISK; + else if (NM_FLAGS_ANY (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_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, - TRUE); - } - 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_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY) + ? NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE + : NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL) + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS + | ( NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT) + ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT + : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE), + "update-from-dbus", &local); if (!local) { @@ -1677,15 +1520,17 @@ update_auth_cb (NMSettingsConnection *self, * Only send secrets to agents of the same UID that called update too. */ for_agent = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - nm_connection_clear_secrets_with_flags (for_agent, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + _nm_connection_clear_secrets_by_secret_flags (for_agent, + NM_SETTING_SECRET_FLAG_AGENT_OWNED); nm_agent_manager_save_secrets (info->agent_mgr, nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), for_agent, info->subject); } + /* Reset auto retries back to default since connection was updated */ + nm_settings_connection_autoconnect_retries_reset (self); + update_complete (self, info, local); } @@ -1727,13 +1572,6 @@ settings_connection_update (NMSettingsConnection *self, UpdateInfo *info; const char *permission; - /* If the connection is read-only, that has to be changed at the source of - * the problem (ex a system settings plugin that can't write connections out) - * instead of over D-Bus. - */ - if (!check_writable (nm_settings_connection_get_connection (self), &error)) - goto error; - /* Check if the settings are valid first */ if (new_settings) { if (!g_variant_is_of_type (new_settings, NM_VARIANT_TYPE_CONNECTION)) { @@ -1858,16 +1696,13 @@ impl_settings_connection_update2 (NMDBusObject *obj, GVariantIter iter; const char *args_name; NMSettingsUpdate2Flags flags; - const NMSettingsUpdate2Flags ALL_PERSIST_MODES = NM_SETTINGS_UPDATE2_FLAG_TO_DISK - | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY - | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED - | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY; g_variant_get (parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args); - if (NM_FLAGS_ANY (flags_u, ~((guint32) (ALL_PERSIST_MODES | - NM_SETTINGS_UPDATE2_FLAG_VOLATILE | - NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)))) { + if (NM_FLAGS_ANY (flags_u, ~((guint32) ( _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES + | NM_SETTINGS_UPDATE2_FLAG_VOLATILE + | NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT + | NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY)))) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, "Unknown flags"); @@ -1877,11 +1712,12 @@ impl_settings_connection_update2 (NMDBusObject *obj, flags = (NMSettingsUpdate2Flags) flags_u; - if ( ( NM_FLAGS_ANY (flags, ALL_PERSIST_MODES) - && !nm_utils_is_power_of_two (flags & ALL_PERSIST_MODES)) + if ( ( NM_FLAGS_ANY (flags, _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES) + && !nm_utils_is_power_of_two (flags & _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES)) || ( NM_FLAGS_HAS (flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE) - && !NM_FLAGS_ANY (flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED | - NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))) { + && !NM_FLAGS_ANY (flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, "Conflicting flags"); @@ -1889,13 +1725,7 @@ impl_settings_connection_update2 (NMDBusObject *obj, return; } - if (!g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_INVALID_ARGUMENTS, - "args is of invalid type"); - g_dbus_method_invocation_take_error (invocation, error); - return; - } + nm_assert (g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))); g_variant_iter_init (&iter, args); while (g_variant_iter_next (&iter, "{&sv}", &args_name, NULL)) { @@ -1921,7 +1751,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); @@ -1932,15 +1761,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 * @@ -1972,8 +1797,7 @@ impl_settings_connection_delete (NMDBusObject *obj, gs_unref_object NMAuthSubject *subject = NULL; GError *error = NULL; - if (!check_writable (nm_settings_connection_get_connection (self), &error)) - goto err; + nm_assert (nm_settings_connection_still_valid (self)); subject = _new_auth_subject (invocation, &error); if (!subject) @@ -2088,25 +1912,13 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self, return; } - /* Clear secrets in connection and caches */ - nm_connection_clear_secrets (nm_settings_connection_get_connection (self)); - if (priv->system_secrets) - nm_connection_clear_secrets (priv->system_secrets); - if (priv->agent_secrets) - nm_connection_clear_secrets (priv->agent_secrets); + 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); @@ -2147,40 +1959,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); } /*****************************************************************************/ @@ -2202,14 +2002,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) @@ -2218,7 +2010,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); @@ -2338,11 +2131,13 @@ gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self, guint64 *out_timestamp) { + NMSettingsConnectionPrivate *priv; + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); - if (out_timestamp) - *out_timestamp = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp; - return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp_set; + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + NM_SET_OUT (out_timestamp, priv->timestamp); + return priv->timestamp_set; } /** @@ -2350,98 +2145,113 @@ nm_settings_connection_get_timestamp (NMSettingsConnection *self, * @self: the #NMSettingsConnection * @timestamp: timestamp to set into the connection and to store into * the timestamps database - * @flush_to_disk: if %TRUE, commit timestamp update to persistent storage * * Updates the connection and timestamps database with the provided timestamp. **/ void nm_settings_connection_update_timestamp (NMSettingsConnection *self, - guint64 timestamp, - gboolean flush_to_disk) + guint64 timestamp) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); const char *connection_uuid; - GKeyFile *timestamps_file; - char *data, *tmp; - gsize len; - GError *error = NULL; + char sbuf[60]; g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); - /* Update timestamp in private storage */ priv->timestamp = timestamp; priv->timestamp_set = TRUE; - if (flush_to_disk == FALSE) - return; - if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) + if (!priv->kf_db_timestamps) return; - /* Save timestamp to timestamps database file */ - timestamps_file = g_key_file_new (); - if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) { - if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) - _LOGW ("error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message); - g_clear_error (&error); - } - connection_uuid = nm_settings_connection_get_uuid (self); - tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); - g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp); - g_free (tmp); - - data = g_key_file_to_data (timestamps_file, &len, &error); - if (data) { - g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error); - g_free (data); - } - if (error) { - _LOGW ("error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message); - g_error_free (error); + if (connection_uuid) { + nm_key_file_db_set_value (priv->kf_db_timestamps, + connection_uuid, + nm_sprintf_buf (sbuf, "%" G_GUINT64_FORMAT, timestamp)); } - g_key_file_free (timestamps_file); } -/** - * 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_read_and_fill_timestamp (NMSettingsConnection *self) +_nm_settings_connection_register_kf_dbs (NMSettingsConnection *self, + NMKeyFileDB *kf_db_timestamps, + NMKeyFileDB *kf_db_seen_bssids) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - gs_unref_keyfile GKeyFile *timestamps_file = NULL; - gs_free_error GError *error = NULL; - gs_free char *tmp_str = NULL; + NMSettingsConnectionPrivate *priv; const char *connection_uuid; - gint64 timestamp; g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); + g_return_if_fail (kf_db_timestamps); + g_return_if_fail (kf_db_seen_bssids); - timestamps_file = g_key_file_new (); - if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) { - _LOGD ("failed to read connection timestamp: %s", error->message); - return; - } + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); connection_uuid = nm_settings_connection_get_uuid (self); - tmp_str = g_key_file_get_value (timestamps_file, "timestamps", connection_uuid, &error); - if (!tmp_str) { - _LOGD ("failed to read connection timestamp: %s", error->message); - return; - } - timestamp = _nm_utils_ascii_str_to_int64 (tmp_str, 10, 0, G_MAXINT64, -1); - if (timestamp < 0) { - _LOGD ("failed to read connection timestamp: %s", "invalid number"); - return; + if (priv->kf_db_timestamps != kf_db_timestamps) { + gs_free char *tmp_str = NULL; + guint64 timestamp; + + nm_key_file_db_unref (priv->kf_db_timestamps); + priv->kf_db_timestamps = nm_key_file_db_ref (kf_db_timestamps); + + tmp_str = nm_key_file_db_get_value (priv->kf_db_timestamps, connection_uuid); + + timestamp = _nm_utils_ascii_str_to_uint64 (tmp_str, 10, 0, G_MAXUINT64, G_MAXUINT64); + if (timestamp != G_MAXUINT64) { + priv->timestamp = timestamp; + priv->timestamp_set = TRUE; + _LOGT ("read timestamp %"G_GUINT64_FORMAT" from keyfile database \"%s\"", + timestamp, nm_key_file_db_get_filename (priv->kf_db_timestamps)); + } else + _LOGT ("no timestamp from keyfile database \"%s\"", + nm_key_file_db_get_filename (priv->kf_db_timestamps)); } - priv->timestamp = timestamp; - priv->timestamp_set = TRUE; + if (priv->kf_db_seen_bssids != kf_db_seen_bssids) { + gs_strfreev char **tmp_strv = NULL; + gsize i, len; + + nm_key_file_db_unref (priv->kf_db_seen_bssids); + priv->kf_db_seen_bssids = nm_key_file_db_ref (kf_db_seen_bssids); + + tmp_strv = nm_key_file_db_get_string_list (priv->kf_db_seen_bssids, connection_uuid, &len); + + nm_clear_pointer (&priv->seen_bssids, g_hash_table_unref); + + if (len > 0) { + _LOGT ("read %zu seen-bssids from keyfile database \"%s\"", + len, + nm_key_file_db_get_filename (priv->kf_db_seen_bssids)); + priv->seen_bssids = _seen_bssids_hash_new (); + for (i = len; i > 0; ) + g_hash_table_add (priv->seen_bssids, g_steal_pointer (&tmp_strv[--i])); + nm_clear_g_free (&tmp_strv); + } else { + NMSettingWireless *s_wifi; + + _LOGT ("no seen-bssids from keyfile database \"%s\"", + nm_key_file_db_get_filename (priv->kf_db_seen_bssids)); + + /* If this connection didn't have an entry in the seen-bssids database, + * maybe this is the first time we've read it in, so populate the + * seen-bssids list from the deprecated seen-bssids property of the + * wifi setting. + */ + s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (self)); + if (s_wifi) { + len = nm_setting_wireless_get_num_seen_bssids (s_wifi); + if (len > 0) { + priv->seen_bssids = _seen_bssids_hash_new (); + for (i = 0; i < len; i++) { + const char *bssid = nm_setting_wireless_get_seen_bssid (s_wifi, i); + + g_hash_table_add (priv->seen_bssids, g_strdup (bssid)); + } + } + } + } + } } /** @@ -2453,25 +2263,14 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self) * Returns: (transfer container) list of seen BSSIDs (in the standard hex-digits-and-colons notation). * The caller is responsible for freeing the list, but not the content. **/ -char ** +const char ** nm_settings_connection_get_seen_bssids (NMSettingsConnection *self) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - GHashTableIter iter; - char **bssids, *bssid; - int i; - g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL); - bssids = g_new (char *, g_hash_table_size (priv->seen_bssids) + 1); - - i = 0; - g_hash_table_iter_init (&iter, priv->seen_bssids); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid)) - bssids[i++] = bssid; - bssids[i] = NULL; - - return bssids; + return nm_utils_strdict_get_keys (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, + TRUE, + NULL); } /** @@ -2485,10 +2284,15 @@ gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *self, const char *bssid) { + NMSettingsConnectionPrivate *priv; + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); - g_return_val_if_fail (bssid != NULL, FALSE); + g_return_val_if_fail (bssid, FALSE); + + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, bssid); + return priv->seen_bssids + && g_hash_table_contains (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, bssid); } /** @@ -2504,108 +2308,29 @@ nm_settings_connection_add_seen_bssid (NMSettingsConnection *self, const char *seen_bssid) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + gs_free const char **strv = NULL; const char *connection_uuid; - GKeyFile *seen_bssids_file; - char *data, *bssid_str; - const char **list; - gsize len; - GError *error = NULL; - GHashTableIter iter; - guint n; g_return_if_fail (seen_bssid != NULL); - if (g_hash_table_lookup (priv->seen_bssids, seen_bssid)) - return; /* Already in the list */ - - /* Add the new BSSID; let the hash take ownership of the allocated BSSID string */ - bssid_str = g_strdup (seen_bssid); - g_hash_table_insert (priv->seen_bssids, bssid_str, bssid_str); - - /* Build up a list of all the BSSIDs in string form */ - n = 0; - list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *)); - g_hash_table_iter_init (&iter, priv->seen_bssids); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str)) - list[n++] = bssid_str; - - /* Save BSSID to seen-bssids file */ - seen_bssids_file = g_key_file_new (); - g_key_file_set_list_separator (seen_bssids_file, ','); - if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) { - if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - _LOGW ("error parsing seen-bssids file '%s': %s", - SETTINGS_SEEN_BSSIDS_FILE, error->message); - } - g_clear_error (&error); - } + if (!priv->seen_bssids) + priv->seen_bssids = _seen_bssids_hash_new (); - connection_uuid = nm_settings_connection_get_uuid (self); - g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n); - g_free (list); + g_hash_table_add (priv->seen_bssids, g_strdup (seen_bssid)); - data = g_key_file_to_data (seen_bssids_file, &len, &error); - if (data) { - g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error); - g_free (data); - } - g_key_file_free (seen_bssids_file); + if (!priv->kf_db_seen_bssids) + return; - if (error) { - _LOGW ("error saving seen-bssids to file '%s': %s", - SETTINGS_SEEN_BSSIDS_FILE, error->message); - g_error_free (error); - } -} + connection_uuid = nm_settings_connection_get_uuid (self); + if (!connection_uuid) + return; -/** - * nm_settings_connection_read_and_fill_seen_bssids: - * @self: the #NMSettingsConnection - * - * Retrieves seen BSSIDs of the connection from database file and stores then into the - * connection private data. - **/ -void -nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - const char *connection_uuid; - GKeyFile *seen_bssids_file; - char **tmp_strv = NULL; - gsize i, len = 0; - NMSettingWireless *s_wifi; - - /* Get seen BSSIDs from database file */ - seen_bssids_file = g_key_file_new (); - g_key_file_set_list_separator (seen_bssids_file, ','); - if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) { - connection_uuid = nm_settings_connection_get_uuid (self); - tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL); - } - g_key_file_free (seen_bssids_file); - - /* Update connection's seen-bssids */ - if (tmp_strv) { - g_hash_table_remove_all (priv->seen_bssids); - for (i = 0; i < len; i++) - g_hash_table_insert (priv->seen_bssids, tmp_strv[i], tmp_strv[i]); - g_free (tmp_strv); - } else { - /* If this connection didn't have an entry in the seen-bssids database, - * maybe this is the first time we've read it in, so populate the - * seen-bssids list from the deprecated seen-bssids property of the - * wifi setting. - */ - s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (self)); - if (s_wifi) { - len = nm_setting_wireless_get_num_seen_bssids (s_wifi); - for (i = 0; i < len; i++) { - char *bssid_dup = g_strdup (nm_setting_wireless_get_seen_bssid (s_wifi, i)); + strv = nm_utils_strdict_get_keys (priv->seen_bssids, TRUE, NULL); - g_hash_table_insert (priv->seen_bssids, bssid_dup, bssid_dup); - } - } - } + nm_key_file_db_set_string_list (priv->kf_db_seen_bssids, + connection_uuid, + strv ?: NM_PTRARRAY_EMPTY (const char *), + -1); } /*****************************************************************************/ @@ -2767,46 +2492,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 @@ -2820,9 +2505,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 * @@ -2834,7 +2519,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 * @@ -2845,6 +2541,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) { @@ -2855,35 +2588,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->seen_bssids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + 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 @@ -2895,6 +2612,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)); @@ -2904,80 +2623,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); - } - - g_clear_object (&priv->system_secrets); - g_clear_object (&priv->agent_secrets); + 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); + g_clear_object (&priv->storage); - 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; - } -} - -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", ); @@ -3087,12 +2755,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; - - klass->supports_secrets = supports_secrets; obj_properties[PROP_UNSAVED] = g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "", @@ -3100,12 +2764,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, @@ -3115,29 +2773,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 e796b716..61d5c246 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -22,11 +21,116 @@ #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_ADD_REASON_NONE = 0, + + NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT = (1u << 0), + +} NMSettingsConnectionAddReason; + +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. + * + * See also %NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY flag, to prevent partial reapply + * during Update2(). */ + 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), + + NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT = (1u << 8), + +} 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 currently in-memory, remove + * it from /run. Depending on the shadowed-storage, the pre-existing + * file is reused when moving the storage. + * + * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_TO_DISK. */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK, + + /* Update in-memory (i.e. persist to /run). If the profile is currently on disk, + * then a reference to the profile is remembered as "shadowed-storage". + * Later, when storing again to persistant storage, the shawowed-storage is + * updated. When deleting the profile, the shadowed-storage is also deleted + * from disk. + * + * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY. */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + + /* Update in-memory (i.e. persist to /run). This is almost like + * %NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, except the in-memory profile + * remembers not to own the shadowed-storage ("shadowed-owned"). + * The diffrence is that when deleting the in-memory profile, the original + * profile is not deleted but instead the nmmeta tombstone remembers the + * shadowed-storage and re-used it when re-adding the profile. + * + * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED. */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED, + + /* Update in-memory (i.e. persist to /run). If the profile is currently on disk, + * delete it from disk. + * + * If the profile is in-memory and has a shadowed-storage, the original profile + * will be deleted from disk. + * + * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY. */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + + /* This only updates the connection in-memory. Note that "in-memory" above + * means to write to keyfile in /run. This mode really means to not notify the + * settings plugin about the change. This should be only used for updating + * secrets. + */ + 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)) @@ -34,7 +138,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" @@ -45,9 +148,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 @@ -62,38 +162,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, @@ -107,7 +206,6 @@ typedef enum { | NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS), } NMSettingsAutoconnectBlockedReason; -struct _NMSettingsConnectionCallId; typedef struct _NMSettingsConnectionCallId NMSettingsConnectionCallId; typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass; @@ -116,30 +214,29 @@ 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); - gboolean (*commit_changes) (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionCommitReason commit_reason, - NMConnection **out_reread_connection, - char **out_logmsg_change, - GError **error); +NMSettingsConnection *nm_settings_connection_new (void); - gboolean (*delete) (NMSettingsConnection *self, - GError **error); +NMConnection *nm_settings_connection_get_connection (NMSettingsConnection *self); - gboolean (*supports_secrets) (NMSettingsConnection *self, - const char *setting_name); -}; +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); + +gboolean nm_settings_connection_still_valid (NMSettingsConnection *self); + +const char *nm_settings_connection_get_filename (NMSettingsConnection *self); guint64 nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *self); @@ -147,34 +244,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, @@ -200,36 +281,62 @@ 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); int nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); +struct _NMKeyFileDB; + +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); void nm_settings_connection_update_timestamp (NMSettingsConnection *self, - guint64 timestamp, - gboolean flush_to_disk); - -void nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self); + guint64 timestamp); -char **nm_settings_connection_get_seen_bssids (NMSettingsConnection *self); +const char **nm_settings_connection_get_seen_bssids (NMSettingsConnection *self); gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *self, const char *bssid); @@ -237,8 +344,6 @@ gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *self, void nm_settings_connection_add_seen_bssid (NMSettingsConnection *self, const char *seen_bssid); -void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self); - int nm_settings_connection_autoconnect_retries_get (NMSettingsConnection *self); void nm_settings_connection_autoconnect_retries_set (NMSettingsConnection *self, int retries); @@ -262,14 +367,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); @@ -279,4 +376,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 e7275631..09010931 100644 --- a/src/settings/nm-settings-plugin.c +++ b/src/settings/nm-settings-plugin.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -23,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,114 +42,228 @@ G_DEFINE_TYPE (NMSettingsPlugin, nm_settings_plugin, G_TYPE_OBJECT) /*****************************************************************************/ -void -nm_settings_plugin_initialize (NMSettingsPlugin *self) +int +nm_settings_plugin_cmp_by_priority (const NMSettingsPlugin *a, + const NMSettingsPlugin *b, + const GSList *plugin_list) { - g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self)); + nm_assert (NM_IS_SETTINGS_PLUGIN (a)); + nm_assert (NM_IS_SETTINGS_PLUGIN (b)); - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->initialize) - NM_SETTINGS_PLUGIN_GET_CLASS (self)->initialize (self); + if (a != b) { + int idx_a = g_slist_index ((GSList *) plugin_list, a); + int idx_b = g_slist_index ((GSList *) plugin_list, b); + + /* the plugins must be found in the list. */ + nm_assert (idx_a >= 0); + nm_assert (idx_b >= 0); + + /* 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); + } + + return 0; } +/*****************************************************************************/ + 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; - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection) - return NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection (self, filename); - return FALSE; + g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL); + + 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, + NMSettingsPluginConnectionLoadCallback 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) +NMSettingsPluginConnectionLoadEntry * +nm_settings_plugin_create_connection_load_entries (const char *const*filenames, + gsize *out_len) { - g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL); + NMSettingsPluginConnectionLoadEntry *entries; + gsize len; + gsize i; + + len = NM_PTRARRAY_LEN (filenames); + if (len == 0) { + *out_len = 0; + return NULL; + } + + entries = g_new (NMSettingsPluginConnectionLoadEntry, len); + for (i = 0; i < len; i++) { + entries[i] = (NMSettingsPluginConnectionLoadEntry) { + .filename = filenames[i], + .error = NULL, + .handled = FALSE, + }; + } - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs) - return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs (self); - return NULL; + *out_len = len; + return entries; } -GSList * -nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self) +void +nm_settings_plugin_load_connections (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL); + NMSettingsPluginClass *klass; - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs) - return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs (self); - return NULL; + g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self)); + + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + if (klass->load_connections) + klass->load_connections (self, entries, n_entries, callback, user_data); } -/** - * 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 * +void +nm_settings_plugin_load_connections_done (NMSettingsPlugin *self) +{ + NMSettingsPluginClass *klass; + + g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self)); + + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + if (klass->load_connections_done) + klass->load_connections_done (self); +} + +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, + 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); - g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn); + 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; + } + + return klass->delete_connection (self, + storage, + error); } +/*****************************************************************************/ + void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self) { @@ -177,15 +292,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 fdd48f2b..4df3472e 100644 --- a/src/settings/nm-settings-plugin.h +++ b/src/settings/nm-settings-plugin.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -24,6 +23,21 @@ #include "nm-connection.h" +#include "nm-settings-storage.h" + +typedef struct _NMSettingsPlugin NMSettingsPlugin; + +typedef void (*NMSettingsPluginConnectionLoadCallback) (NMSettingsPlugin *self, + NMSettingsStorage *storage, + NMConnection *connection, + gpointer user_data); + +typedef struct { + const char *filename; + GError *error; + bool handled:1; +} NMSettingsPluginConnectionLoadEntry; + #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)) @@ -33,35 +47,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; - /* Called when the plugin is loaded to initialize it */ - void (*initialize) (NMSettingsPlugin *plugin); - - /* 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 @@ -71,7 +64,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 @@ -83,51 +76,137 @@ 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 set of filenames. */ - NMSettingsConnection * (*add_connection) (NMSettingsPlugin *plugin, - NMConnection *connection, - gboolean save_to_disk, - GError **error); + void (*load_connections) (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); + + /* Requests that the plugin reload all connection files from disk, + * and emit signals reflecting new, changed, and removed connections. + */ + void (*reload_connections) (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); + + void (*load_connections_done) (NMSettingsPlugin *self); + + 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, + GError **error); + + const char *plugin_name; + } NMSettingsPluginClass; +/*****************************************************************************/ + GType nm_settings_plugin_get_type (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_unmanaged_specs (NMSettingsPlugin *self); +GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self); + +void nm_settings_plugin_reload_connections (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); + +NMSettingsPluginConnectionLoadEntry *nm_settings_plugin_create_connection_load_entries (const char *const*filenames, + gsize *out_len); + +void nm_settings_plugin_load_connections (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); + +void nm_settings_plugin_load_connections_done (NMSettingsPlugin *self); + +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, + GError **error); + +/*****************************************************************************/ + typedef NMSettingsPlugin *(*NMSettingsPluginFactoryFunc) (void); -/* Plugin's factory function that returns a #NMSettingsPlugin */ NMSettingsPlugin *nm_settings_plugin_factory (void); -void nm_settings_plugin_initialize (NMSettingsPlugin *config); - -GSList *nm_settings_plugin_get_connections (NMSettingsPlugin *plugin); +/***************************************************************************** + * Internal API + *****************************************************************************/ -gboolean nm_settings_plugin_load_connection (NMSettingsPlugin *plugin, - const char *filename); -void nm_settings_plugin_reload_connections (NMSettingsPlugin *plugin); +void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self); -GSList *nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *plugin); -GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *plugin); +void _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *self); -NMSettingsConnection *nm_settings_plugin_add_connection (NMSettingsPlugin *plugin, - NMConnection *connection, - gboolean save_to_disk, - GError **error); +/*****************************************************************************/ -/* internal API */ +int nm_settings_plugin_cmp_by_priority (const NMSettingsPlugin *a, + const NMSettingsPlugin *b, + const GSList *plugin_list); -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 00000000..935b5b48 --- /dev/null +++ b/src/settings/nm-settings-storage.c @@ -0,0 +1,178 @@ +/* + * 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; + + /* 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_keyfile_run (a), + nm_settings_storage_is_keyfile_run (b)); + + NM_CMP_RETURN (nm_settings_plugin_cmp_by_priority (nm_settings_storage_get_plugin (a), + nm_settings_storage_get_plugin (b), + plugin_list)); + + klass = NM_SETTINGS_STORAGE_GET_CLASS (a); + if (klass != NM_SETTINGS_STORAGE_GET_CLASS (b)) { + /* one plugin must return storages of the same type. Otherwise, it's + * unclear how cmp_fcn() should compare them. */ + nm_assert_not_reached (); + return 0; + } + + if (klass->cmp_fcn) + NM_CMP_RETURN (klass->cmp_fcn (a, b)); + + return 0; +} + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE ( + PROP_PLUGIN, + PROP_UUID, + PROP_FILENAME, +); + +G_DEFINE_TYPE (NMSettingsStorage, nm_settings_storage, G_TYPE_OBJECT) + +/*****************************************************************************/ + +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 (!self->_uuid || 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) +{ + c_list_init (&self->_storage_lst); + c_list_init (&self->_storage_by_uuid_lst); +} + +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); + + c_list_unlink_stale (&self->_storage_lst); + c_list_unlink_stale (&self->_storage_by_uuid_lst); + + 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->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_WRITABLE | + 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 00000000..c43145b5 --- /dev/null +++ b/src/settings/nm-settings-storage.h @@ -0,0 +1,122 @@ +/* + * 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__ + +/*****************************************************************************/ + +#include "c-list/src/c-list.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; + CList _storage_lst; + CList _storage_by_uuid_lst; +} NMSettingsStorage; + +typedef struct { + GObjectClass parent; + + int (*cmp_fcn) (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 (const NMSettingsStorage *self) +{ + GType nm_settings_plugin_get_type (void); + + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); + + nm_assert (G_TYPE_CHECK_INSTANCE_TYPE (self->_plugin, nm_settings_plugin_get_type ())); + return self->_plugin; +} + +static inline const char * +nm_settings_storage_get_uuid (const 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_uuid_opt (const NMSettingsStorage *self) +{ + gboolean nm_utils_is_uuid (const char *str); + + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); + + nm_assert (!self->_uuid || nm_utils_is_uuid (self->_uuid)); + return self->_uuid; +} + +static inline const char * +nm_settings_storage_get_filename (const NMSettingsStorage *self) +{ + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); + + return self->_filename; +} + +/*****************************************************************************/ + +#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-utils.c b/src/settings/nm-settings-utils.c new file mode 100644 index 00000000..0d636537 --- /dev/null +++ b/src/settings/nm-settings-utils.c @@ -0,0 +1,175 @@ +/* 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) 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-settings-utils.h" + +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include "nm-settings-plugin.h" + +/*****************************************************************************/ + +const struct timespec * +nm_sett_util_stat_mtime (const char *filename, + gboolean do_lstat, + struct timespec *out_val) +{ + struct stat st; + struct timeval now_tv; + + if (filename) { + if (do_lstat) { + if (lstat (filename, &st) == 0) { + *out_val = st.st_mtim; + return out_val; + } + } else { + if (stat (filename, &st) == 0) { + *out_val = st.st_mtim; + return out_val; + } + } + } + + if (gettimeofday (&now_tv, NULL) == 0) { + *out_val = (struct timespec) { + .tv_sec = now_tv.tv_sec, + .tv_nsec = now_tv.tv_usec * 1000u, + }; + return out_val; + } + + *out_val = (struct timespec) { }; + return out_val; +} + +/*****************************************************************************/ + +gboolean +nm_sett_util_allow_filename_cb (const char *filename, + gpointer user_data) +{ + const NMSettUtilAllowFilenameData *allow_filename_data = user_data; + + if ( allow_filename_data->allowed_filename + && nm_streq (allow_filename_data->allowed_filename, filename)) + return TRUE; + + return !g_hash_table_contains (allow_filename_data->idx_by_filename, filename); +} + +/*****************************************************************************/ + +void +nm_sett_util_storage_by_uuid_head_destroy (NMSettUtilStorageByUuidHead *sbuh) +{ + CList *iter; + + while ((iter = c_list_first (&sbuh->_storage_by_uuid_lst_head))) + c_list_unlink (iter); + g_free (sbuh); +} + +/*****************************************************************************/ + +void +nm_sett_util_storages_clear (NMSettUtilStorages *storages) +{ + nm_clear_pointer (&storages->idx_by_uuid, g_hash_table_destroy); + nm_clear_pointer (&storages->idx_by_filename, g_hash_table_destroy); + nm_assert (c_list_is_empty (&storages->_storage_lst_head)); +} + +void +nm_sett_util_storages_add_take (NMSettUtilStorages *storages, + gpointer storage_take_p /* NMSettingsStorage *, take reference */) +{ + NMSettingsStorage *storage_take = storage_take_p; + NMSettUtilStorageByUuidHead *sbuh; + const char *uuid; + + nm_assert (storage_take); + nm_assert (c_list_is_empty (&storage_take->_storage_lst)); + nm_assert (c_list_is_empty (&storage_take->_storage_by_uuid_lst)); + nm_assert (nm_settings_storage_get_filename (storage_take)); + + if (!g_hash_table_replace (storages->idx_by_filename, + (char *) nm_settings_storage_get_filename (storage_take), + storage_take /* takes ownership of reference. */)) + nm_assert_not_reached (); + + uuid = nm_settings_storage_get_uuid_opt (storage_take); + + if (uuid) { + sbuh = nm_sett_util_storages_lookup_by_uuid (storages, uuid); + if (!sbuh) { + gsize l = strlen (uuid) + 1; + + sbuh = g_malloc (sizeof (NMSettUtilStorageByUuidHead) + l); + sbuh->uuid = sbuh->uuid_data; + c_list_init (&sbuh->_storage_by_uuid_lst_head); + memcpy (sbuh->uuid_data, uuid, l); + g_hash_table_add (storages->idx_by_uuid, sbuh); + } + c_list_link_tail (&sbuh->_storage_by_uuid_lst_head, &storage_take->_storage_by_uuid_lst); + } + + c_list_link_tail (&storages->_storage_lst_head, &storage_take->_storage_lst); +} + +gpointer /* NMSettingsStorage * */ +nm_sett_util_storages_steal (NMSettUtilStorages *storages, + gpointer storage_p /* NMSettingsStorage **/) +{ + NMSettingsStorage *storage = storage_p; + NMSettUtilStorageByUuidHead *sbuh; + const char *uuid; + + nm_assert (storage); + nm_assert (nm_sett_util_storages_lookup_by_filename (storages, nm_settings_storage_get_filename (storage)) == storage); + nm_assert (c_list_contains (&storages->_storage_lst_head, &storage->_storage_lst)); + + uuid = nm_settings_storage_get_uuid_opt (storage); + + if (!uuid) { + nm_assert (c_list_is_empty (&storage->_storage_by_uuid_lst)); + } else { + nm_assert (!c_list_is_empty (&storage->_storage_by_uuid_lst)); + + sbuh = nm_sett_util_storages_lookup_by_uuid (storages, uuid); + + nm_assert (sbuh); + nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &storage->_storage_by_uuid_lst)); + c_list_unlink (&storage->_storage_by_uuid_lst); + + if (c_list_is_empty (&sbuh->_storage_by_uuid_lst_head)) + g_hash_table_remove (storages->idx_by_uuid, sbuh); + } + + c_list_unlink (&storage->_storage_lst); + + g_hash_table_steal (storages->idx_by_filename, nm_settings_storage_get_filename (storage)); + + return storage; +} diff --git a/src/settings/nm-settings-utils.h b/src/settings/nm-settings-utils.h new file mode 100644 index 00000000..a2a22dc4 --- /dev/null +++ b/src/settings/nm-settings-utils.h @@ -0,0 +1,110 @@ +/* + * 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 2019 Red Hat, Inc. + */ + +#ifndef __NM_SETTINGS_UTILS_H__ +#define __NM_SETTINGS_UTILS_H__ + +#include "nm-settings-storage.h" + +/*****************************************************************************/ + +struct timespec; + +const struct timespec *nm_sett_util_stat_mtime (const char *filename, + gboolean do_lstat, + struct timespec *out_val); + +/*****************************************************************************/ + +typedef struct { + const char *uuid; + + CList _storage_by_uuid_lst_head; + + char uuid_data[]; +} NMSettUtilStorageByUuidHead; + +typedef struct { + CList _storage_lst_head; + GHashTable *idx_by_filename; + GHashTable *idx_by_uuid; +} NMSettUtilStorages; + +void nm_sett_util_storage_by_uuid_head_destroy (NMSettUtilStorageByUuidHead *sbuh); + +#define NM_SETT_UTIL_STORAGES_INIT(storages, storage_destroy_fcn) \ + { \ + ._storage_lst_head = C_LIST_INIT (((storages)._storage_lst_head)), \ + .idx_by_filename = g_hash_table_new_full (nm_str_hash, \ + g_str_equal, \ + NULL, \ + (GDestroyNotify) storage_destroy_fcn), \ + .idx_by_uuid = g_hash_table_new_full (nm_pstr_hash, \ + nm_pstr_equal, \ + NULL, \ + (GDestroyNotify) nm_sett_util_storage_by_uuid_head_destroy), \ + } + +void nm_sett_util_storages_clear (NMSettUtilStorages *storages); + +#define nm_auto_clear_sett_util_storages nm_auto(nm_sett_util_storages_clear) + +void nm_sett_util_storages_add_take (NMSettUtilStorages *storages, + gpointer storage_take_p); + +gpointer nm_sett_util_storages_steal (NMSettUtilStorages *storages, + gpointer storage_p); + +/*****************************************************************************/ + +static inline gpointer /* NMSettingsStorage * */ +nm_sett_util_storages_lookup_by_filename (NMSettUtilStorages *storages, + const char *filename) +{ + nm_assert (filename); + + return g_hash_table_lookup (storages->idx_by_filename, filename); +} + +static inline NMSettUtilStorageByUuidHead * +nm_sett_util_storages_lookup_by_uuid (NMSettUtilStorages *storages, + const char *uuid) +{ + nm_assert (uuid); + + return g_hash_table_lookup (storages->idx_by_uuid, &uuid); +} + +/*****************************************************************************/ + +typedef struct { + GHashTable *idx_by_filename; + const char *allowed_filename; +} NMSettUtilAllowFilenameData; + +#define NM_SETT_UTIL_ALLOW_FILENAME_DATA(_storages, _allowed_filename) \ + (&((NMSettUtilAllowFilenameData) { \ + .idx_by_filename = (_storages)->idx_by_filename, \ + .allowed_filename = (_allowed_filename), \ + })) + +gboolean nm_sett_util_allow_filename_cb (const char *filename, + gpointer user_data); + +#endif /* __NM_SETTINGS_UTILS_H__ */ diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 8e18a33e..430d2776 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Søren Sandmann <sandmann@daimi.au.dk> @@ -37,6 +36,8 @@ #endif #include "nm-libnm-core-intern/nm-common-macros.h" +#include "nm-glib-aux/nm-keyfile-aux.h" +#include "nm-keyfile-internal.h" #include "nm-dbus-interface.h" #include "nm-connection.h" #include "nm-setting-8021x.h" @@ -60,6 +61,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 +72,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,16 +82,255 @@ /*****************************************************************************/ -#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); + sd->prioritize = FALSE; + 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; -EXPORT(nm_settings_connection_get_type) -EXPORT(nm_settings_connection_update) + 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 +} + +static gboolean +_storage_data_is_alive (StorageData *sd) +{ + /* If the storage tracks a connection, it is considered alive. + * + * Meta-data storages are special: they never track a connection. + * We need to check them specially to know when to drop them. */ + return sd->connection + || nm_settings_storage_is_meta_data_alive (sd->storage); +} /*****************************************************************************/ -static NM_CACHED_QUARK_FCN ("plugin-module-path", plugin_module_path_quark) -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; +} + +/** + * _sett_conn_entry_storage_find_conflicting_storage: + * @sett_conn_entry: the list of settings-storages for the given UUID. + * @target_plugin: the settings plugin to check + * @storage_check_including: (allow-none): optionally compare against this storage. + * @plugins: the list of plugins sorted in descending priority. This determines + * the priority and whether a storage conflicts. + * + * If we were to add the a storage to @target_plugin, then this function checks + * whether there are already other storages that would hide the storage after we + * add it. Those conflicting/hiding storages are a problem, because they have higher + * priority, so we cannot add the storage. + * + * @storage_check_including is optional, and if given then it checks whether updating + * the profile in this storage would result in confict. This is the check before + * update-connection. If this parameter is omitted, then it's about what happens + * when adding a new profile (add-connection). + * + * Returns: the conflicting storage or %NULL if there is none. + */ +static NMSettingsStorage * +_sett_conn_entry_storage_find_conflicting_storage (SettConnEntry *sett_conn_entry, + NMSettingsPlugin *target_plugin, + NMSettingsStorage *storage_check_including, + const GSList *plugins) +{ + StorageData *sd; + + if (!sett_conn_entry) + return NULL; + + if ( storage_check_including + && nm_settings_storage_is_keyfile_run (storage_check_including)) { + /* the storage we check against is in-memory. It always has highest + * priority, so there can be no other conflicting storages. */ + return NULL; + } + + /* Finds the first (highest priority) storage that has a connection. + * Note that due to tombstones (that have a high priority), the connection + * may not actually be exposed. This is to find hidden/shadowed storages + * that provide a connection. */ + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + nm_assert (NM_IS_SETTINGS_STORAGE (sd->storage)); + + if (!sd->connection) { + /* We only consider storages with connection. In particular, + * tombstones are not relevant, because we can delete them to + * resolve the conflict. */ + continue; + } + + if (sd->storage == storage_check_including) { + /* ok, the storage is the one we are about to check. All other + * storages are lower priority, so there is no storage that hides + * our storage_check_including. */ + return NULL; + } + + if (nm_settings_plugin_cmp_by_priority (nm_settings_storage_get_plugin (sd->storage), + target_plugin, + plugins) <= 0) { + /* the plugin of the existing storage is less important than @target_plugin. + * We have no conflicting/hiding storage. */ + return NULL; + } + + /* Found. If we would add the profile to @target_plugin, then it would be hidden + * by existing_storage. */ + return sd->storage; + } + + return NULL; +} + +static NMSettingsStorage * +_sett_conn_entry_find_shadowed_storage (SettConnEntry *sett_conn_entry, + const char *shadowed_storage_filename, + NMSettingsStorage *blacklisted_storage) +{ + StorageData *sd; + + if (!shadowed_storage_filename) + return NULL; + + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + + nm_assert (NM_IS_SETTINGS_STORAGE (sd->storage)); + + if (!sd->connection) + continue; + + if (blacklisted_storage == sd->storage) + continue; + + if (!nm_streq0 (nm_settings_storage_get_filename_for_shadowed_storage (sd->storage), shadowed_storage_filename)) + continue; + + return sd->storage; + } + + return NULL; +} /*****************************************************************************/ @@ -115,25 +357,45 @@ typedef struct { NMConfig *config; - GSList *auths; + NMPlatform *platform; + + NMHostnameManager *hostname_manager; + + NMSessionMonitor *session_monitor; + + CList auth_lst_head; + + NMSKeyfilePlugin *keyfile_plugin; GSList *plugins; + 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; + GSList *unmanaged_specs; GSList *unrecognized_specs; - NMHostnameManager *hostname_manager; - + 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; @@ -152,6 +414,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__) @@ -161,367 +426,286 @@ 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 unmanaged_specs_changed (NMSettingsPlugin *config, gpointer user_data); -static void unrecognized_specs_changed (NMSettingsPlugin *config, gpointer user_data); - -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); - NMSettingsConnection *sett_conn; - - if (priv->startup_complete) - 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; - } - } - - g_clear_object (&priv->startup_complete_blocked_by); - - /* 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); - - priv->startup_complete = TRUE; - _notify (self, PROP_STARTUP_COMPLETE); + g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn); } static void -connection_ready_changed (NMSettingsConnection *conn, - GParamSpec *pspec, - gpointer user_data) +_emit_connection_updated (NMSettings *self, + NMSettingsConnection *sett_conn, + NMSettingsConnectionUpdateReason update_reason) { - NMSettings *self = NM_SETTINGS (user_data); - - if (nm_settings_connection_get_ready (conn)) - check_startup_complete (self); + _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 -plugin_connection_added (NMSettingsPlugin *config, - NMSettingsConnection *connection, - NMSettings *self) +_emit_connection_removed (NMSettings *self, + NMSettingsConnection *sett_conn) { - claim_connection (self, connection); + g_signal_emit (self, signals[CONNECTION_REMOVED], 0, sett_conn); } static void -load_connections (NMSettings *self) +_emit_connection_flags_changed (NMSettings *self, + NMSettingsConnection *sett_conn) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GSList *iter; + g_signal_emit (self, signals[CONNECTION_FLAGS_CHANGED], 0, sett_conn); +} - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); - GSList *plugin_connections; - GSList *elt; +/*****************************************************************************/ - plugin_connections = nm_settings_plugin_get_connections (plugin); +typedef struct { + NMSettingsConnection *sett_conn; + gint64 start_at; + gint64 timeout; +} StartupCompleteData; - // FIXME: ensure connections from plugins loaded with a lower priority - // get rejected when they conflict with connections from a higher - // priority plugin. +static void +_startup_complete_data_destroy (StartupCompleteData *scd) +{ + g_object_unref (scd->sett_conn); + g_slice_free (StartupCompleteData, scd); +} - for (elt = plugin_connections; elt; elt = g_slist_next (elt)) - claim_connection (self, elt->data); +static gboolean +_startup_complete_check_is_ready (NMPlatform *platform, + NMSettingsConnection *sett_conn) +{ + const NMPlatformLink *plink; + const char *ifname; - g_slist_free (plugin_connections); + /* FIXME: instead of just looking for the interface name, it would be better + * to wait for a device that is compatible with the profile. */ - 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); - } + ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (sett_conn)); - priv->connections_loaded = TRUE; - _notify (self, PROP_CONNECTIONS); + if (!ifname) + return TRUE; - unmanaged_specs_changed (NULL, self); - unrecognized_specs_changed (NULL, self); + plink = nm_platform_link_get_by_ifname (platform, ifname); + return plink && plink->initialized; } -static void -impl_settings_list_connections (NMDBusObject *obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended *method_info, - GDBusConnection *dbus_connection, - const char *sender, - GDBusMethodInvocation *invocation, - GVariant *parameters) +static gboolean +_startup_complete_timeout_cb (gpointer user_data) { - NMSettings *self = NM_SETTINGS (obj); + NMSettings *self = user_data; NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - gs_free const char **strv = NULL; - strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head, - priv->connections_len, - G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst), - TRUE); - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(^ao)", strv)); -} - -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); - - c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) { - if (nm_streq (uuid, nm_settings_connection_get_uuid (candidate))) - return candidate; - } - - return NULL; + priv->startup_complete_timeout_id = 0; + _startup_complete_check (self, 0); + return G_SOURCE_REMOVE; } static void -impl_settings_get_connection_by_uuid (NMDBusObject *obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended *method_info, - GDBusConnection *dbus_connection, - const char *sender, - GDBusMethodInvocation *invocation, - GVariant *parameters) +_startup_complete_platform_change_cb (NMPlatform *platform, + int obj_type_i, + int ifindex, + const NMPlatformLink *link, + int change_type_i, + NMSettings *self) { - NMSettings *self = NM_SETTINGS (obj); - NMSettingsConnection *sett_conn; - gs_unref_object NMAuthSubject *subject = NULL; - GError *error = NULL; - const char *uuid; + const NMPlatformSignalChangeType change_type = change_type_i; + NMSettingsPrivate *priv; + const char *ifname; - g_variant_get (parameters, "(&s)", &uuid); + if (change_type == NM_PLATFORM_SIGNAL_REMOVED) + return; - sett_conn = nm_settings_get_connection_by_uuid (self, uuid); - if (!sett_conn) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_INVALID_CONNECTION, - "No connection with the UUID was found."); - goto error; - } + if (!link->initialized) + return; - subject = nm_auth_subject_new_unix_process_from_context (invocation); - if (!subject) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - "Unable to determine UID of request."); - goto error; - } + priv = NM_SETTINGS_GET_PRIVATE (self); - if (!nm_auth_is_subject_in_acl_set_error (nm_settings_connection_get_connection (sett_conn), - subject, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - &error)) - goto error; + ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by)); + if ( ifname + && !nm_streq (ifname, link->name)) + return; - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(o)", - nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)))); - return; + nm_assert (priv->startup_complete_timeout_id > 0); -error: - g_dbus_method_invocation_take_error (invocation, error); + nm_clear_g_source (&priv->startup_complete_timeout_id); + priv->startup_complete_timeout_id = g_idle_add (_startup_complete_timeout_cb, self); } static void -_clear_connections_cached_list (NMSettingsPrivate *priv) +_startup_complete_check (NMSettings *self, + gint64 now_us) { - if (!priv->connections_cached_list) + 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; + } - nm_assert (priv->connections_len == NM_PTRARRAY_LEN (priv->connections_cached_list)); + if (!priv->startup_complete_idx) + goto ready; -#if NM_MORE_ASSERTS - /* set the pointer to a bogus value. This makes it more apparent - * if somebody has a reference to the cached list and still uses - * it. That is a bug, this code just tries to make it blow up - * more eagerly. */ - memset (priv->connections_cached_list, - 0xdeaddead, - sizeof (NMSettingsConnection *) * (priv->connections_len + 1)); -#endif + if (!now_us) + now_us = nm_utils_get_monotonic_timestamp_us (); - nm_clear_g_free (&priv->connections_cached_list); -} + next_expiry = 0; -/** - * nm_settings_get_connections: - * @self: the #NMSettings - * @out_len: (out) (allow-none): returns the number of returned - * connections. - * - * Returns: (transfer none): a list of NMSettingsConnections. The list is - * unsorted and NULL terminated. The result is never %NULL, in case of no - * connections, it returns an empty list. - * The returned list is cached internally, only valid until the next - * NMSettings operation. - */ -NMSettingsConnection *const* -nm_settings_get_connections (NMSettings *self, guint *out_len) -{ - NMSettingsPrivate *priv; - NMSettingsConnection **v; - NMSettingsConnection *con; - guint i; + g_hash_table_iter_init (&iter, priv->startup_complete_idx); + while (g_hash_table_iter_next (&iter, (gpointer *) &scd, NULL)) { + gint64 expiry; - g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + if (scd->start_at == 0) { + /* once ready, the decision is remembered and there is nothing + * left to check. */ + continue; + } - priv = NM_SETTINGS_GET_PRIVATE (self); + expiry = scd->start_at + scd->timeout; + if (expiry <= now_us) { + scd->start_at = 0; + continue; + } - nm_assert (priv->connections_len == c_list_length (&priv->connections_lst_head)); + if (_startup_complete_check_is_ready (priv->platform, scd->sett_conn)) { + scd->start_at = 0; + continue; + } - if (G_UNLIKELY (!priv->connections_cached_list)) { - v = g_new (NMSettingsConnection *, priv->connections_len + 1); + next_expiry = expiry; + next_sett_conn = scd->sett_conn; + /* we found one timeout for which to wait. that's good enough. */ + break; + } - i = 0; - c_list_for_each_entry (con, &priv->connections_lst_head, _connections_lst) { - nm_assert (i < priv->connections_len); - v[i++] = con; + 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); } - nm_assert (i == priv->connections_len); - v[i] = NULL; - - priv->connections_cached_list = v; + 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; } - NM_SET_OUT (out_len, priv->connections_len); - return priv->connections_cached_list; + nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy); + nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id); + +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); } -/** - * nm_settings_get_connections_clone: - * @self: the #NMSetting - * @out_len: (allow-none): optional output argument - * @func: caller-supplied function for filtering connections - * @func_data: caller-supplied data passed to @func - * @sort_compare_func: (allow-none): optional function pointer for - * sorting the returned list. - * @sort_data: user data for @sort_compare_func. - * - * Returns: (transfer container) (element-type NMSettingsConnection): - * an NULL terminated array of #NMSettingsConnection objects that were - * filtered by @func (or all connections if no filter was specified). - * The order is arbitrary. - * Caller is responsible for freeing the returned array with free(), - * the contained values do not need to be unrefed. - */ -NMSettingsConnection ** -nm_settings_get_connections_clone (NMSettings *self, - guint *out_len, - NMSettingsConnectionFilterFunc func, - gpointer func_data, - GCompareDataFunc sort_compare_func, - gpointer sort_data) +static void +_startup_complete_notify_connection (NMSettings *self, + NMSettingsConnection *sett_conn, + gboolean forget) { - NMSettingsConnection *const*list_cached; - NMSettingsConnection **list; - guint len, i, j; - - g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); - - list_cached = nm_settings_get_connections (self, &len); - -#if NM_MORE_ASSERTS - nm_assert (list_cached); - for (i = 0; i < len; i++) - nm_assert (NM_IS_SETTINGS_CONNECTION (list_cached[i])); - nm_assert (!list_cached[i]); -#endif - - list = g_new (NMSettingsConnection *, ((gsize) len + 1)); - if (func) { - for (i = 0, j = 0; i < len; i++) { - if (func (self, list_cached[i], func_data)) - list[j++] = list_cached[i]; + 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; } - list[j] = NULL; - len = j; - } else - memcpy (list, list_cached, sizeof (list[0]) * ((gsize) len + 1)); - - if ( len > 1 - && sort_compare_func) { - g_qsort_with_data (list, len, sizeof (NMSettingsConnection *), - sort_compare_func, sort_data); } - NM_SET_OUT (out_len, len); - return list; -} - -NMSettingsConnection * -nm_settings_get_connection_by_path (NMSettings *self, const char *path) -{ - NMSettingsPrivate *priv; - NMSettingsConnection *connection; - - g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); - g_return_val_if_fail (path, NULL); - - priv = NM_SETTINGS_GET_PRIVATE (self); - connection = nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)), - path); - if ( !connection - || !NM_IS_SETTINGS_CONNECTION (connection)) - return NULL; + 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; + } + } - nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst)); - return connection; + _startup_complete_check (self, now_us); } -gboolean -nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection) +const char * +nm_settings_get_startup_complete_blocked_reason (NMSettings *self) { - gboolean has; - - g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); - g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE); - - has = !c_list_is_empty (&connection->_connections_lst); - - nm_assert (has == nm_c_list_contains_entry (&NM_SETTINGS_GET_PRIVATE (self)->connections_lst_head, - connection, - _connections_lst)); - nm_assert (({ - NMSettingsConnection *candidate = NULL; - const char *path; - - path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); - if (path) - candidate = nm_settings_get_connection_by_path (self, path); - - (has == (connection == candidate)); - })); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + const char *uuid = NULL; - return has; + 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"; } +/*****************************************************************************/ + const GSList * nm_settings_get_unmanaged_specs (NMSettings *self) { @@ -531,57 +715,57 @@ nm_settings_get_unmanaged_specs (NMSettings *self) } static gboolean -find_spec (GSList *spec_list, const char *spec) -{ - GSList *iter; - - for (iter = spec_list; iter; iter = g_slist_next (iter)) { - if (!strcmp ((const char *) iter->data, spec)) - return TRUE; - } - return FALSE; -} - -static void update_specs (NMSettings *self, GSList **specs_ptr, GSList * (*get_specs_func) (NMSettingsPlugin *)) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GSList *new = NULL; GSList *iter; - g_slist_free_full (*specs_ptr, g_free); - *specs_ptr = NULL; - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - GSList *specs, *specs_iter; + GSList *specs; - specs = get_specs_func (NM_SETTINGS_PLUGIN (iter->data)); - for (specs_iter = specs; specs_iter; specs_iter = specs_iter->next) { - if (!find_spec (*specs_ptr, (const char *) specs_iter->data)) { - *specs_ptr = g_slist_prepend (*specs_ptr, specs_iter->data); - } else - g_free (specs_iter->data); + specs = get_specs_func (iter->data); + while (specs) { + GSList *s = specs; + + specs = g_slist_remove_link (specs, s); + if (nm_utils_g_slist_find_str (new, s->data)) { + g_free (s->data); + g_slist_free_1 (s); + continue; + } + s->next = new; + new = s; } + } - g_slist_free (specs); + if (nm_utils_g_slist_strlist_cmp (new, *specs_ptr) == 0) { + g_slist_free_full (new, g_free); + return FALSE; } + + g_slist_free_full (*specs_ptr, g_free); + *specs_ptr = new; + return TRUE; + } 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); - update_specs (self, &priv->unmanaged_specs, - nm_settings_plugin_get_unmanaged_specs); - _notify (self, PROP_UNMANAGED_SPECS); + if (update_specs (self, &priv->unmanaged_specs, + nm_settings_plugin_get_unmanaged_specs)) + _notify (self, PROP_UNMANAGED_SPECS); } 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); @@ -590,521 +774,1605 @@ unrecognized_specs_changed (NMSettingsPlugin *config, nm_settings_plugin_get_unrecognized_specs); } +/*****************************************************************************/ + static void -add_plugin (NMSettings *self, NMSettingsPlugin *plugin, const char *path) +connection_flags_changed (NMSettingsConnection *sett_conn, + gpointer user_data) { - NMSettingsPrivate *priv; + _emit_connection_flags_changed (NM_SETTINGS (user_data), sett_conn); +} - nm_assert (NM_IS_SETTINGS (self)); - nm_assert (NM_IS_SETTINGS_PLUGIN (plugin)); +/*****************************************************************************/ - priv = NM_SETTINGS_GET_PRIVATE (self); +static SettConnEntry * +_sett_conn_entries_get (NMSettings *self, + const char *uuid) +{ + nm_assert (uuid); + return g_hash_table_lookup (NM_SETTINGS_GET_PRIVATE (self)->sce_idx, &uuid); +} - nm_assert (!g_slist_find (priv->plugins, plugin)); +static SettConnEntry * +_sett_conn_entries_create_and_add (NMSettings *self, + const char *uuid) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + SettConnEntry *sett_conn_entry; - priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin)); + sett_conn_entry = _sett_conn_entry_new (uuid); - nm_settings_plugin_initialize (plugin); + 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); - _LOGI ("Loaded settings plugin: %s (%s%s%s)", - G_OBJECT_TYPE_NAME (plugin), - NM_PRINT_FMT_QUOTED (path, "\"", path, "\"", "internal")); + return sett_conn_entry; } -static gboolean -add_plugin_load_file (NMSettings *self, const char *pname, GError **error) +static void +_sett_conn_entries_remove_and_destroy (NMSettings *self, + SettConnEntry *sett_conn_entry) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - gs_free char *full_name = NULL; - gs_free char *path = NULL; - gs_unref_object NMSettingsPlugin *plugin = NULL; - GModule *module; - NMSettingsPluginFactoryFunc factory_func; - GSList *iter; - struct stat st; - int errsv; - - full_name = g_strdup_printf ("nm-settings-plugin-%s", pname); - path = g_module_build_path (NMPLUGINDIR, full_name); - for (iter = priv->plugins; iter; iter = iter->next) { - if (nm_streq0 (path, - g_object_get_qdata (iter->data, - plugin_module_path_quark ()))) - return TRUE; - } - - if (stat (path, &st) != 0) { - errsv = errno; - _LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, nm_strerror_native (errsv)); - return TRUE; - } - if (!S_ISREG (st.st_mode)) { - _LOGW ("could not load plugin '%s' from file '%s': not a file", pname, path); - return TRUE; - } - if (st.st_uid != 0) { - _LOGW ("could not load plugin '%s' from file '%s': file must be owned by root", pname, path); - return TRUE; - } - if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { - _LOGW ("could not load plugin '%s' from file '%s': invalid file permissions", pname, path); - return TRUE; - } + 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); +} - module = g_module_open (path, G_MODULE_BIND_LOCAL); - if (!module) { - _LOGW ("could not load plugin '%s' from file '%s': %s", - pname, path, g_module_error ()); - return TRUE; - } +/*****************************************************************************/ - /* errors after this point are fatal, because we loaded the shared library already. */ +static int +_sett_conn_entry_sds_update_cmp_ascending (const StorageData *sd_a, + const StorageData *sd_b, + const GSList *plugins) +{ + const NMSettingsMetaData *meta_data_a; + const NMSettingsMetaData *meta_data_b; + bool is_keyfile_run_a; + bool is_keyfile_run_b; + + /* Sort storages by priority. More important storages are sorted + * higher (ascending sort). For example, if "sd_a" is more important than + * "sd_b" (sd_a>sd_b), a positive integer is returned. */ + + meta_data_a = nm_settings_storage_is_meta_data (sd_a->storage); + meta_data_b = nm_settings_storage_is_meta_data (sd_b->storage); + + /* runtime storages (both connections and meta-data) are always more + * important. */ + is_keyfile_run_a = nm_settings_storage_is_keyfile_run (sd_a->storage); + is_keyfile_run_b = nm_settings_storage_is_keyfile_run (sd_b->storage); + if (is_keyfile_run_a != is_keyfile_run_b) { + + if ( !meta_data_a + && !meta_data_b) { + /* Ok, both are non-meta-data providing actual profiles. But one is in /run and one is in + * another storage. In this case we first honor whether one of the storages is explicitly + * prioritized. The prioritize flag is an in-memory hack to overwrite relative priorities + * contrary to what exists on-disk. + * + * This is done because when we use explicit D-Bus API (like update-connection) + * to update a profile, then we really want to prioritize the candidate + * despite having multiple other profiles. + * + * The example is if you have the same UUID twice in /run (one of them shadowed). + * If you move it to disk, then one of the profiles gets deleted and re-created + * on disk, but that on-disk profile must win against the remainging profile in + * /run. At least until the next reload/restart. */ + NM_CMP_FIELD_UNSAFE (sd_a, sd_b, prioritize); + } - if (!g_module_symbol (module, "nm_settings_plugin_factory", (gpointer) (&factory_func))) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not find plugin '%s' factory function.", - pname); - g_module_close (module); - return FALSE; + /* in-memory has higher priority. That is regardless of whether any of + * them is meta-data/tombstone or a profile. + * + * That works, because if any of them are tombstones/metadata, then we are in full + * control. There can by only one meta-data file, which is fully owned (and accordingly + * created/deleted) by NetworkManager. + * + * The only case where this might not be right is if we have profiles + * in /run that are shadowed. When we move such a profile to disk, then + * a conflict might arise. That is handled by "prioritize" above! */ + NM_CMP_DIRECT (is_keyfile_run_a, is_keyfile_run_b); } - /* after accessing the plugin we cannot unload it anymore, because the glib - * types cannot be properly unregistered. */ - g_module_make_resident (module); + /* After we determined that both profiles are either in /run or not, + * tombstones are always more important than non-tombstones. */ + NM_CMP_DIRECT (meta_data_a && meta_data_a->is_tombstone, + meta_data_b && meta_data_b->is_tombstone); - plugin = (*factory_func) (); - if (!NM_IS_SETTINGS_PLUGIN (plugin)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "plugin '%s' returned invalid settings plugin", - pname); - return FALSE; - } + /* Again, prioritized entries are sorted first (higher priority). */ + NM_CMP_FIELD_UNSAFE (sd_a, sd_b, prioritize); - add_plugin (self, NM_SETTINGS_PLUGIN (plugin), path); - g_object_set_qdata_full (G_OBJECT (plugin), - plugin_module_path_quark (), - g_steal_pointer (&path), - g_free); - return TRUE; + /* finally, compare the storages. This basically honors the timestamp + * of the profile and the relative order of the source plugin (via the + * @plugins list). */ + return nm_settings_storage_cmp (sd_a->storage, sd_b->storage, plugins); } -static void -add_plugin_keyfile (NMSettings *self) +static int +_sett_conn_entry_sds_update_cmp (const CList *ls_a, + const CList *ls_b, + gconstpointer user_data) { - gs_unref_object NMSKeyfilePlugin *keyfile_plugin = NULL; - - keyfile_plugin = nms_keyfile_plugin_new (); - add_plugin (self, NM_SETTINGS_PLUGIN (keyfile_plugin), NULL); + /* we sort highest priority storages first (descending). Hence, the order is swapped. */ + return _sett_conn_entry_sds_update_cmp_ascending (c_list_entry (ls_b, StorageData, sd_lst), + c_list_entry (ls_a, StorageData, sd_lst), + user_data); } -static gboolean -load_plugins (NMSettings *self, const char **plugins, GError **error) +static void +_sett_conn_entry_sds_update (NMSettings *self, + SettConnEntry *sett_conn_entry) { - const char **iter; - gboolean keyfile_added = FALSE; - gboolean success = TRUE; - gboolean add_ibft = FALSE; - gboolean has_no_ibft; - gssize idx_no_ibft, idx_ibft; - - idx_ibft = nm_utils_strv_find_first ((char **) plugins, -1, "ibft"); - idx_no_ibft = nm_utils_strv_find_first ((char **) plugins, -1, "no-ibft"); - has_no_ibft = idx_no_ibft >= 0 && idx_no_ibft > idx_ibft; -#if WITH_SETTINGS_PLUGIN_IBFT - add_ibft = idx_no_ibft < 0 && idx_ibft < 0; -#endif + 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). + */ - for (iter = plugins; iter && *iter; iter++) { - const char *pname = *iter; + /* first go through all storages that we track and check whether they + * got an update...*/ - if (!*pname || strchr (pname, '/')) { - _LOGW ("ignore invalid plugin \"%s\"", pname); - continue; + reprioritize = FALSE; + c_list_for_each_entry (sd, &sett_conn_entry->dirty_sd_lst_head, sd_lst) { + if (sd->prioritize) { + reprioritize = TRUE; + break; } + } - if (NM_IN_STRSET (pname, "ifcfg-suse", "ifnet")) { - _LOGW ("skipping deprecated plugin %s", pname); - continue; - } + nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head); - if (nm_streq (pname, "no-ibft")) - continue; - if (has_no_ibft && nm_streq (pname, "ibft")) - continue; + c_list_for_each_entry_safe (sd, sd_safe, &sett_conn_entry->sd_lst_head, sd_lst) { - /* keyfile plugin is built-in now */ - if (nm_streq (pname, "keyfile")) { - if (!keyfile_added) { - add_plugin_keyfile (self); - keyfile_added = TRUE; - } + 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 (except maybe reprioritize). */ + if (reprioritize) + sd->prioritize = FALSE; continue; } - if (nm_utils_strv_find_first ((char **) plugins, - iter - plugins, - pname) >= 0) { - /* the plugin is already mentioned in the list previously. - * Don't load a duplicate. */ - continue; - } + nm_g_object_ref_set (&sd->connection, sd_dirty->connection); + sd->prioritize = sd_dirty->prioritize; - success = add_plugin_load_file (self, pname, error); - if (!success) - break; + _storage_data_destroy (sd_dirty); + } - if (add_ibft && nm_streq (pname, "ifcfg-rh")) { - /* The plugin ibft is not explicitly mentioned but we just enabled "ifcfg-rh". - * Enable "ibft" by default after "ifcfg-rh". */ - pname = "ibft"; - add_ibft = FALSE; + nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head); - success = add_plugin_load_file (self, "ibft", error); - if (!success) - break; - } + /* 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); + + nm_assert_storage_data_lst (&sett_conn_entry->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); } - /* If keyfile plugin was not among configured plugins, add it as the last one */ - if (!keyfile_added && success) - add_plugin_keyfile (self); + nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head); + nm_assert (c_list_is_empty (&sett_conn_entry->dirty_sd_lst_head)); - return success; + /* 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); + } + + if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT)) { + nm_settings_connection_autoconnect_blocked_reason_set (sett_conn, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, + TRUE); + } + + 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_keyfile_run (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); -#define NM_DBUS_SERVICE_OPENCONNECT "org.freedesktop.NetworkManager.openconnect" -#define NM_OPENCONNECT_KEY_GATEWAY "gateway" -#define NM_OPENCONNECT_KEY_COOKIE "cookie" -#define NM_OPENCONNECT_KEY_GWCERT "gwcert" -#define NM_OPENCONNECT_KEY_XMLCONFIG "xmlconfig" -#define NM_OPENCONNECT_KEY_LASTHOST "lasthost" -#define NM_OPENCONNECT_KEY_AUTOCONNECT "autoconnect" -#define NM_OPENCONNECT_KEY_CERTSIGS "certsigs" + if ( !priv->started + || priv->startup_complete_idx) + _startup_complete_notify_connection (self, sett_conn, TRUE); +} static void -openconnect_migrate_hack (NMConnection *connection) +_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) { - NMSettingVpn *s_vpn; - NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NOT_SAVED; + StorageData *sd_best; - /* Huge hack. There were some openconnect changes that needed to happen - * pretty late, too late to get into distros. Migration has already - * happened for many people, and their secret flags are wrong. But we - * don't want to requrie re-migration, so we have to fix it up here. Ugh. - */ + c_list_unlink (&sett_conn_entry->sce_dirty_lst); + + _sett_conn_entry_sds_update (self, sett_conn_entry); + + 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; + } - s_vpn = nm_connection_get_setting_vpn (connection); - if (s_vpn == NULL) + 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; + } - if (g_strcmp0 (nm_setting_vpn_get_service_type (s_vpn), NM_DBUS_SERVICE_OPENCONNECT) == 0) { - /* These are different for every login session, and should not be stored */ - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GATEWAY, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_COOKIE, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GWCERT, flags, NULL); + if (override_sett_flags) { + NMSettingsConnectionIntFlags s_f, s_m; - /* These are purely internal data for the auth-dialog, and should be stored */ - flags = 0; - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_XMLCONFIG, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_LASTHOST, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_AUTOCONNECT, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_CERTSIGS, flags, NULL); + 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); } + + 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 -claim_connection (NMSettings *self, NMSettingsConnection *sett_conn) +_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; - GError *error = NULL; - const char *path; - NMSettingsConnection *existing; - - 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))); + 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); + } +} - priv = NM_SETTINGS_GET_PRIVATE (self); +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; - /* prevent duplicates */ - if (!c_list_is_empty (&sett_conn->_connections_lst)) { - nm_assert (c_list_contains (&priv->connections_lst_head, &sett_conn->_connections_lst)); - return; + nm_assert_valid_settings_storage (NULL, storage); + + uuid = nm_settings_storage_get_uuid (storage); + + 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; + const NMSettingsMetaData *meta_data; + const char *shadowed_storage; + gboolean shadowed_owned; + + filename = nm_settings_storage_get_filename (storage); + if (connection) { + shadowed_storage = nm_settings_storage_get_shadowed_storage (storage, &shadowed_owned); + _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event with connection \"%s\"%s%s%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, "\")", ""), + NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", "")); + } else if ((meta_data = nm_settings_storage_is_meta_data (storage))) { + nm_assert (meta_data->is_tombstone); + shadowed_storage = nm_settings_storage_get_shadowed_storage (storage, &shadowed_owned); + _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for %shiding profile%s%s%s%s%s%s", + sett_conn_entry->uuid, + NM_SETTINGS_STORAGE_PRINT_ARG (storage), + nm_settings_storage_is_meta_data_alive (storage) ? "" : "dropping ", + NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""), + NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", "")); + } 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, "\")", "")); + } } - /* 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; + /* 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); } - 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; + if (prioritize) { + StorageData *sd2; + + /* 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; } - /* Read timestamp from look-aside file and put it into the connection's data */ - nm_settings_connection_read_and_fill_timestamp (sett_conn); + nm_c_list_move_tail (&priv->sce_dirty_lst_head, &sett_conn_entry->sce_dirty_lst); - /* Read seen-bssids from look-aside file and put it into the connection's data */ - nm_settings_connection_read_and_fill_seen_bssids (sett_conn); + return sett_conn_entry; +} - /* Ensure its initial visibility is up-to-date */ - nm_settings_connection_recheck_visibility (sett_conn); +/*****************************************************************************/ - /* Evil openconnect migration hack */ - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - openconnect_migrate_hack (nm_settings_connection_get_connection (sett_conn)); +static void +_plugin_connections_reload_cb (NMSettingsPlugin *plugin, + NMSettingsStorage *storage, + NMConnection *connection, + gpointer user_data) +{ + _connection_changed_track (user_data, storage, connection, FALSE); +} - /* 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); +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); } - _clear_connections_cached_list (priv); + _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); - 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))); + for (iter = priv->plugins; iter; iter = iter->next) + nm_settings_plugin_load_connections_done (iter->data); +} - g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn); - _notify (self, PROP_CONNECTIONS); +/*****************************************************************************/ + +static gboolean +_add_connection_to_first_plugin (NMSettings *self, + SettConnEntry *sett_conn_entry, + NMConnection *new_connection, + gboolean in_memory, + NMSettingsConnectionIntFlags sett_flags, + const char *shadowed_storage, + gboolean shadowed_owned, + 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 (!in_memory) { + NMSettingsStorage *conflicting_storage; + + conflicting_storage = _sett_conn_entry_storage_find_conflicting_storage (sett_conn_entry, plugin, NULL, priv->plugins); + if (conflicting_storage) { + /* we have a connection provided by a plugin with higher priority than the one + * we would want to add the connection. We cannot do that, because doing so + * would result in adding a connection that gets hidden by the existing profile. + * Also, since we test the plugins in order of priority, all following plugins + * are unsuitable. + * + * Multiple connection plugins are so cumbersome, especially if they are unable + * to add the connection. I suggest to disable all plugins except keyfile. */ + _LOGT ("add-connection: failed to add %s/'%s': there is an existing storage "NM_SETTINGS_STORAGE_PRINT_FMT" with higher priority", + nm_connection_get_uuid (new_connection), + nm_connection_get_id (new_connection), + NM_SETTINGS_STORAGE_PRINT_ARG (conflicting_storage)); + nm_assert (first_error); + break; + } + } + + if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) { + success = nms_keyfile_plugin_add_connection (priv->keyfile_plugin, + new_connection, + in_memory, + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED), + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE), + shadowed_storage, + shadowed_owned, + &storage, + &connection_to_add, + &add_error); + } else { + if (in_memory) + continue; + nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)); + nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_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_settings_connection_added (sett_conn); + nm_assert (first_error); + g_propagate_error (error, first_error); + return FALSE; } static gboolean -secrets_filter_cb (NMSetting *setting, - const char *secret, - NMSettingSecretFlags flags, - gpointer user_data) +_update_connection_to_plugin (NMSettings *self, + NMSettingsStorage *storage, + NMConnection *connection, + NMSettingsConnectionIntFlags sett_flags, + gboolean force_rename, + const char *shadowed_storage, + gboolean shadowed_owned, + NMSettingsStorage **out_new_storage, + NMConnection **out_new_connection, + GError **error) { - NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + NMSettingsPlugin *plugin; + gboolean success; + + plugin = nm_settings_storage_get_plugin (storage); + + if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) { + success = nms_keyfile_plugin_update_connection (priv->keyfile_plugin, + 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), + shadowed_storage, + shadowed_owned, + force_rename, + out_new_storage, + out_new_connection, + error); + } else { + nm_assert (!shadowed_storage); + nm_assert (!shadowed_owned); + success = nm_settings_plugin_update_connection (plugin, + storage, + connection, + out_new_storage, + out_new_connection, + error); + } - /* Returns TRUE to remove the secret */ + return success; +} - /* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */ - if ( (flags == NM_SETTING_SECRET_FLAG_NONE) - && (filter_flags == NM_SETTING_SECRET_FLAG_NONE)) - return FALSE; +static void +_set_nmmeta_tombstone (NMSettings *self, + const char *uuid, + gboolean tombstone_on_disk, + gboolean tombstone_in_memory, + const char *shadowed_storage) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gs_unref_object NMSettingsStorage *tombstone_1_storage = NULL; + gs_unref_object NMSettingsStorage *tombstone_2_storage = NULL; + + if (tombstone_on_disk) { + if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin, + FALSE, + uuid, + FALSE, + TRUE, + NULL, + &tombstone_1_storage, + NULL)) + tombstone_in_memory = TRUE; + if (tombstone_1_storage) + _connection_changed_track (self, tombstone_1_storage, NULL, FALSE); + } - /* Otherwise if the secret has at least one of the desired flags keep it */ - return (flags & filter_flags) ? FALSE : TRUE; + if (tombstone_in_memory) { + if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin, + FALSE, + uuid, + TRUE, + TRUE, + shadowed_storage, + &tombstone_2_storage, + NULL)) { + nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin, + TRUE, + uuid, + TRUE, + TRUE, + shadowed_storage, + &tombstone_2_storage, + NULL); + } + _connection_changed_track (self, tombstone_2_storage, NULL, 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. + * @add_reason: the add-reason flags. + * @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, + NMSettingsConnectionAddReason add_reason, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnection **out_sett_conn, GError **error) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GSList *iter; - NMSettingsConnection *added = NULL; - NMSettingsConnection *candidate = NULL; + NMSettingsPrivate *priv; + gs_unref_object NMConnection *connection_cloned_1 = NULL; + gs_unref_object NMConnection *new_connection = NULL; + gs_unref_object NMSettingsStorage *new_storage = NULL; + gs_unref_object NMSettingsStorage *shadowed_storage = NULL; + NMSettingsStorage *update_storage = NULL; + gs_free_error GError *local = NULL; + SettConnEntry *sett_conn_entry; const char *uuid; + StorageData *sd; + gboolean new_in_memory; + gboolean success; + const char *shadowed_storage_filename = NULL; + + priv = NM_SETTINGS_GET_PRIVATE (self); + + nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY)); + + new_in_memory = (persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK); + + nm_assert (!NM_FLAGS_ANY (sett_flags, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK)); + + if (NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE + | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) { + nm_assert (new_in_memory); + new_in_memory = TRUE; + } + + nm_assert (!NM_FLAGS_ANY (add_reason, ~NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT)); + + 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; + sett_conn_entry = _sett_conn_entries_get (self, uuid); + if (_sett_conn_entry_get_conn (sett_conn_entry)) { + 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 (sett_conn_entry) { + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + if (!nm_settings_storage_is_meta_data (sd->storage)) + continue; + shadowed_storage = nm_g_object_ref (_sett_conn_entry_find_shadowed_storage (sett_conn_entry, + nm_settings_storage_get_shadowed_storage (sd->storage, NULL), + NULL)); + if (shadowed_storage) { + /* We have a nmmeta tombstone that indicates that a storage is shadowed. + * + * This happens when deleting a in-memory profile that was decoupled from + * the persitant storage with NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED. + * We need to take over this storage again... */ + break; + } } } - /* 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_object NMConnection *simple = 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. */ - simple = nm_simple_connection_new_clone (connection); - nm_connection_clear_secrets_with_flags (simple, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - - 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 ( shadowed_storage + && !new_in_memory) { + NMSettingsStorage *conflicting_storage; + + conflicting_storage = _sett_conn_entry_storage_find_conflicting_storage (sett_conn_entry, + nm_settings_storage_get_plugin (shadowed_storage), + shadowed_storage, + priv->plugins); + if (conflicting_storage) { + /* We cannot add the profile as @shadowed_storage, because there is another, existing storage + * that would hide it. Just add it as new storage. In general, this leads to duplication of profiles, + * but the circumstances where this happens are very exotic (you need at least one additional settings + * plugin, then going through the paths of making shadowed_storage in-memory-detached and delete it, + * and finally adding the conflicting storage outside of NM and restart/reload). */ + _LOGT ("ignore shadowed storage "NM_SETTINGS_STORAGE_PRINT_FMT" due to conflicting storage "NM_SETTINGS_STORAGE_PRINT_FMT, + NM_SETTINGS_STORAGE_PRINT_ARG (shadowed_storage), + NM_SETTINGS_STORAGE_PRINT_ARG (conflicting_storage)); + } else + update_storage = shadowed_storage; + } + + shadowed_storage_filename = ( shadowed_storage + && !update_storage) + ? nm_settings_storage_get_filename_for_shadowed_storage (shadowed_storage) + : NULL; + +again_add_connection: + + if (!update_storage) { + success = _add_connection_to_first_plugin (self, + sett_conn_entry, + connection, + new_in_memory, + sett_flags, + shadowed_storage_filename, + FALSE, + &new_storage, + &new_connection, + &local); + } else { + success = _update_connection_to_plugin (self, + update_storage, + connection, + sett_flags, + FALSE, + shadowed_storage_filename, + FALSE, + &new_storage, + &new_connection, + &local); + if (!success) { + if (!NMS_IS_KEYFILE_STORAGE (update_storage)) { + /* hm, the intended storage is not keyfile (it's ifcfg-rh). This settings + * plugin may not support the new connection. So step back and retry adding + * the profile anew. */ + _LOGT ("failure to add profile as existing storage \"%s\": %s", + nm_settings_storage_get_filename (update_storage), + local->message); + update_storage = NULL; + g_clear_object (&shadowed_storage); + shadowed_storage_filename = NULL; + g_clear_error (&local); + goto again_add_connection; } - claim_connection (self, added); - return added; } - _LOGD ("Failed to add %s/'%s': %s", - nm_connection_get_uuid (connection), - nm_connection_get_id (connection), - add_error->message); - g_clear_error (&add_error); } - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "No plugin supported adding this connection"); - return NULL; + if (!success) { + if (!update_storage) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "failure adding connection: %s", + local->message); + } else { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "failure writing connection to existing storage \"%s\": %s", + nm_settings_storage_get_filename (update_storage), + local->message); + } + return FALSE; + } + + sett_conn_entry = _connection_changed_track (self, new_storage, new_connection, TRUE); + + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + const NMSettingsMetaData *meta_data; + gs_unref_object NMSettingsStorage *new_tombstone_storage = NULL; + gboolean in_memory; + gboolean simulate; + + meta_data = nm_settings_storage_is_meta_data_alive (sd->storage); + if ( !meta_data + || !meta_data->is_tombstone) + continue; + + if (nm_settings_storage_is_keyfile_run (sd->storage)) + in_memory = TRUE; + else { + if (nm_settings_storage_is_keyfile_run (new_storage)) { + /* Don't remove the file from /etc if we just wrote an in-memory connection */ + continue; + } + in_memory = FALSE; + } + + simulate = FALSE; +again_delete_tombstone: + if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin, + simulate, + uuid, + in_memory, + FALSE, + NULL, + &new_tombstone_storage, + NULL)) { + /* Ups, something went wrong. We really need to get rid of the tombstone. At least + * forget about it in-memory. Upong next restart/reload, this might be reverted + * however :( .*/ + if (!simulate) { + simulate = TRUE; + goto again_delete_tombstone; + } + } + if (new_tombstone_storage) + _connection_changed_track (self, new_tombstone_storage, NULL, FALSE); + } + + _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_FLAGS_HAS (add_reason, NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT) + ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT + : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE)); + + 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) +{ + 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_unref_object NMSettingsStorage *cur_storage = NULL; + gs_unref_object NMSettingsStorage *new_storage = NULL; + NMSettingsStorage *drop_storage = NULL; + SettConnEntry *sett_conn_entry; + gboolean cur_in_memory; + gboolean new_in_memory; + const char *uuid; + gboolean tombstone_in_memory = FALSE; + gboolean tombstone_on_disk = FALSE; + + 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_TO_DISK, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY)); + + 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)); + + sett_conn_entry = _sett_conn_entries_get (self, uuid); + + nm_assert (_sett_conn_entry_get_conn (sett_conn_entry) == sett_conn); + + if (connection) { + gs_free_error GError *local = NULL; + + 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_keyfile_run (cur_storage); + + if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP) { + persist_mode = cur_in_memory + ? NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY + : NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + } + + 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_NO_PERSIST)) { + /* making a default-wired-connection a regular connection implies persisting + * it to disk (unless specified differently). + * + * Actually, this line is probably unreached, because we should not use + * NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST to toggle the nm-generated + * flag. */ + nm_assert_not_reached (); + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + } + } + } + + if ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST + && NM_FLAGS_ANY (sett_mask, 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_mask, + 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 persist 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; + } + + if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK) + new_in_memory = FALSE; + else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + 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 gets 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); + } + + if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST) { + new_storage = g_object_ref (cur_storage); + new_connection_real = connection; + _LOGT ("update[%s]: %s: update profile \"%s\" (not persisted)", + nm_settings_storage_get_uuid (cur_storage), + log_context_name, + nm_connection_get_id (connection)); + } else { + NMSettingsStorage *shadowed_storage; + const char *cur_shadowed_storage_filename; + const char *new_shadowed_storage_filename = NULL; + gboolean cur_shadowed_owned; + gboolean new_shadowed_owned = FALSE; + NMSettingsStorage *update_storage = NULL; + gs_free_error GError *local = NULL; + gboolean success; + + cur_shadowed_storage_filename = nm_settings_storage_get_shadowed_storage (cur_storage, &cur_shadowed_owned); + + shadowed_storage = _sett_conn_entry_find_shadowed_storage (sett_conn_entry, cur_shadowed_storage_filename, cur_storage); + if (!shadowed_storage) { + cur_shadowed_storage_filename = NULL; + cur_shadowed_owned = FALSE; + } + + if ( new_in_memory + && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) { + if (cur_in_memory) { + drop_storage = shadowed_storage; + update_storage = cur_storage; + } else + drop_storage = cur_storage; + } else if ( !new_in_memory + && cur_in_memory + && shadowed_storage) { + drop_storage = cur_storage; + update_storage = shadowed_storage; + } else if (new_in_memory != cur_in_memory) { + if (!new_in_memory) + drop_storage = cur_storage; + else if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) + drop_storage = cur_storage; + else { + nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED)); + } + } else if (nm_settings_storage_is_keyfile_lib (cur_storage)) { + /* the profile is a keyfile in /usr/lib. It cannot be overwritten, we must migrate it + * from /usr/lib to /etc. */ + } else + update_storage = cur_storage; + + if (new_in_memory) { + if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) { + /* pass */ + } else if (!cur_in_memory) { + new_shadowed_storage_filename = nm_settings_storage_get_filename_for_shadowed_storage (cur_storage); + if ( new_shadowed_storage_filename + && persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED) + new_shadowed_owned = TRUE; + } else { + new_shadowed_storage_filename = cur_shadowed_storage_filename; + if ( new_shadowed_storage_filename + && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY) + new_shadowed_owned = TRUE; + } + } + + if (!update_storage) { + success = _add_connection_to_first_plugin (self, + sett_conn_entry, + connection, + new_in_memory, + sett_flags, + new_shadowed_storage_filename, + new_shadowed_owned, + &new_storage, + &new_connection, + &local); + } else { + success = _update_connection_to_plugin (self, + update_storage, + connection, + sett_flags, + update_reason, + new_shadowed_storage_filename, + new_shadowed_owned, + &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 " : "", + update_storage ? "update" : "write", + nm_connection_get_id (connection), + local->message); + if (!ignore_failure) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "failed to %s connection: %s", + update_storage ? "update" : "write", + local->message); + return FALSE; + } + + new_storage = g_object_ref (cur_storage); + new_connection_real = connection; + } else { + gs_unref_variant GVariant *agent_owned_secrets = NULL; + + _LOGT ("update[%s]: %s: %s profile \"%s\"", + nm_settings_storage_get_uuid (cur_storage), + log_context_name, + update_storage ? "update" : "write", + nm_connection_get_id (connection)); + + 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))); + + + 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); + if (!new_connection_real) { + nm_assert_not_reached (); + new_connection_real = new_connection; + } + } + } + + nm_assert (NM_IS_SETTINGS_STORAGE (new_storage)); + nm_assert (NM_IS_CONNECTION (new_connection_real)); + + _connection_changed_track (self, new_storage, new_connection_real, TRUE); + + if ( drop_storage + && drop_storage != new_storage) { + gs_free_error GError *local = NULL; + + if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (drop_storage), + drop_storage, + &local)) { + const char *filename; + + filename = nm_settings_storage_get_filename (drop_storage); + _LOGT ("update[%s]: failed to delete moved storage "NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s: %s", + nm_settings_storage_get_uuid (drop_storage), + NM_SETTINGS_STORAGE_PRINT_ARG (drop_storage), + NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""), + local->message); + /* there is no aborting back form this. We must get rid of the connection and + * cannot do better than log a message. Proceed, but remember to write tombstones. */ + if (nm_settings_storage_is_keyfile_run (cur_storage)) + tombstone_in_memory = TRUE; + else + tombstone_on_disk = TRUE; + } else + _connection_changed_track (self, drop_storage, NULL, FALSE); + } + + _set_nmmeta_tombstone (self, + uuid, + tombstone_on_disk, + tombstone_in_memory, + NULL); + + _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 allow_add_to_no_auto_default) +{ + NMSettingsStorage *cur_storage; + NMSettingsStorage *shadowed_storage; + NMSettingsStorage *shadowed_storage_unowned = NULL; + NMSettingsStorage *drop_storages[2] = { }; + gs_free_error GError *local = NULL; + SettConnEntry *sett_conn_entry; + const char *cur_shadowed_storage_filename; + const char *new_shadowed_storage_filename = NULL; + gboolean cur_shadowed_owned; + const char *uuid; + gboolean tombstone_in_memory = FALSE; + gboolean tombstone_on_disk = FALSE; + int i; + + g_return_if_fail (NM_IS_SETTINGS (self)); + g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn)); + g_return_if_fail (nm_settings_has_connection (self, 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); + g_return_if_fail (sett_conn_entry->storage == cur_storage); + + if (NMS_IS_KEYFILE_STORAGE (cur_storage)) { + NMSKeyfileStorage *s = NMS_KEYFILE_STORAGE (cur_storage); + + if (NM_IN_SET (s->storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC)) + drop_storages[0] = cur_storage; + else + tombstone_on_disk = TRUE; + } else + drop_storages[0] = cur_storage; + + cur_shadowed_storage_filename = nm_settings_storage_get_shadowed_storage (cur_storage, &cur_shadowed_owned); + + shadowed_storage = _sett_conn_entry_find_shadowed_storage (sett_conn_entry, cur_shadowed_storage_filename, cur_storage); + if (shadowed_storage) { + if (!cur_shadowed_owned) + shadowed_storage_unowned = g_steal_pointer (&shadowed_storage); + } + drop_storages[1] = shadowed_storage; + + for (i = 0; i < (int) G_N_ELEMENTS (drop_storages); i++) { + NMSettingsStorage *storage; + StorageData *sd; + + storage = drop_storages[i]; + if (!storage) + continue; + + if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (storage), + storage, + &local)) { + _LOGT ("delete-connection: failed to delete storage "NM_SETTINGS_STORAGE_PRINT_FMT": %s", + NM_SETTINGS_STORAGE_PRINT_ARG (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 log a message. Proceed, but remember to write tombstones. */ + if (nm_settings_storage_is_keyfile_run (cur_storage)) + tombstone_in_memory = TRUE; + else + tombstone_on_disk = TRUE; + sett_conn_entry = _sett_conn_entries_get (self, uuid); + } else + sett_conn_entry = _connection_changed_track (self, storage, NULL, FALSE); + + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + if (NM_IN_SET (sd->storage, drop_storages[0], + drop_storages[1])) + continue; + if (!_storage_data_is_alive (sd)) + continue; + if (nm_settings_storage_is_meta_data (sd->storage)) + continue; + + if (sd->storage == shadowed_storage_unowned) { + /* this only happens if we leak a profile on disk after NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED. + * We need to write a tombstone and remember the shadowed-storage. */ + tombstone_in_memory = TRUE; + new_shadowed_storage_filename = nm_settings_storage_get_filename (shadowed_storage_unowned); + continue; + } + + /* we have still conflicting storages. We need to hide them with tombstones. */ + if (nm_settings_storage_is_keyfile_run (sd->storage)) { + tombstone_in_memory = TRUE; + continue; + } + tombstone_on_disk = TRUE; + } + } + + _set_nmmeta_tombstone (self, + uuid, + tombstone_on_disk, + tombstone_in_memory, + new_shadowed_storage_filename); + + _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, @@ -1118,9 +2386,8 @@ send_agent_owned_secrets (NMSettings *self, * Only send secrets to agents of the same UID that called update too. */ for_agent = nm_simple_connection_new_clone (nm_settings_connection_get_connection (sett_conn)); - nm_connection_clear_secrets_with_flags (for_agent, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + _nm_connection_clear_secrets_by_secret_flags (for_agent, + NM_SETTING_SECRET_FLAG_AGENT_OWNED); nm_agent_manager_save_secrets (priv->agent_mgr, nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)), for_agent, @@ -1129,52 +2396,50 @@ send_agent_owned_secrets (NMSettings *self, static void pk_add_cb (NMAuthChain *chain, - GError *chain_error, GDBusMethodInvocation *context, gpointer user_data) { NMSettings *self = NM_SETTINGS (user_data); - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMAuthCallResult result; - GError *error = NULL; + gs_free_error GError *error = NULL; NMConnection *connection = NULL; gs_unref_object NMSettingsConnection *added = NULL; NMSettingsAddCallback callback; gpointer callback_data; NMAuthSubject *subject; const char *perm; - gboolean save_to_disk; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); - priv->auths = g_slist_remove (priv->auths, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); perm = nm_auth_chain_get_data (chain, "perm"); - g_assert (perm); + nm_assert (perm); + result = nm_auth_chain_get_result (chain, perm); - if (chain_error) { - error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "Error checking authorization: %s", - chain_error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, "Insufficient privileges."); } 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, "add-reason")), + 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). * But we need to keep it alive for a bit longer, precisely to check wehther * it's still known to the setting manager. */ - g_object_ref (added); + nm_g_object_ref (added); } callback = nm_auth_chain_get_data (chain, "callback"); @@ -1184,49 +2449,17 @@ pk_add_cb (NMAuthChain *chain, callback (self, added, error, context, subject, callback_data); /* Send agent-owned secrets to the agents */ - if (!error && added && nm_settings_has_connection (self, added)) + if ( added + && nm_settings_has_connection (self, added)) send_agent_owned_secrets (self, added, subject); - - g_clear_error (&error); - nm_auth_chain_destroy (chain); -} - -/* FIXME: remove if/when kernel supports adhoc wpa */ -static gboolean -is_adhoc_wpa (NMConnection *connection) -{ - NMSettingWireless *s_wifi; - NMSettingWirelessSecurity *s_wsec; - const char *mode, *key_mgmt; - - /* The kernel doesn't support Ad-Hoc WPA connections well at this time, - * and turns them into open networks. It's been this way since at least - * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. - */ - - s_wifi = nm_connection_get_setting_wireless (connection); - if (!s_wifi) - return FALSE; - - mode = nm_setting_wireless_get_mode (s_wifi); - if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) != 0) - return FALSE; - - s_wsec = nm_connection_get_setting_wireless_security (connection); - if (!s_wsec) - return FALSE; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - if (g_strcmp0 (key_mgmt, "wpa-none") != 0) - return FALSE; - - return TRUE; } void nm_settings_add_connection_dbus (NMSettings *self, NMConnection *connection, - gboolean save_to_disk, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionAddReason add_reason, + NMSettingsConnectionIntFlags sett_flags, NMAuthSubject *subject, GDBusMethodInvocation *context, NMSettingsAddCallback callback, @@ -1242,8 +2475,10 @@ 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)) { + if (_nm_connection_verify (connection, &tmp_error) != NM_SETTING_VERIFY_SUCCESS) { error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "The connection was invalid: %s", @@ -1252,11 +2487,11 @@ nm_settings_add_connection_dbus (NMSettings *self, goto done; } - /* The kernel doesn't support Ad-Hoc WPA connections well at this time, + /* FIXME: The kernel doesn't support Ad-Hoc WPA connections well at this time, * and turns them into open networks. It's been this way since at least * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. */ - if (is_adhoc_wpa (connection)) { + if (nm_utils_connection_is_adhoc_wpa (connection)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "WPA Ad-Hoc disabled due to kernel bugs"); @@ -1275,7 +2510,7 @@ nm_settings_add_connection_dbus (NMSettings *self, * request affects more than just the caller, require 'modify.system'. */ s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); + nm_assert (s_con); if (nm_setting_connection_get_num_permissions (s_con) == 1) perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN; else @@ -1290,14 +2525,17 @@ nm_settings_add_connection_dbus (NMSettings *self, goto done; } - priv->auths = g_slist_append (priv->auths, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); + nm_auth_chain_set_data (chain, "perm", (gpointer) perm, NULL); nm_auth_chain_set_data (chain, "connection", g_object_ref (connection), g_object_unref); 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_add_call (chain, perm, TRUE); + nm_auth_chain_set_data (chain, "persist-mode", GUINT_TO_POINTER (persist_mode), NULL); + nm_auth_chain_set_data (chain, "add-reason", GUINT_TO_POINTER (add_reason), 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; done: @@ -1314,27 +2552,42 @@ settings_add_connection_add_cb (NMSettings *self, NMAuthSubject *subject, gpointer user_data) { + gboolean is_add_connection_2 = GPOINTER_TO_INT (user_data); + if (error) { g_dbus_method_invocation_return_gerror (context, error); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, NULL, FALSE, NULL, subject, error->message); + return; + } + + if (is_add_connection_2) { + GVariantBuilder builder; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_dbus_method_invocation_return_value (context, + g_variant_new ("(oa{sv})", + nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)), + &builder)); } else { g_dbus_method_invocation_return_value (context, g_variant_new ("(o)", nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)))); - nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE, NULL, - subject, NULL); } + nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE, NULL, + subject, NULL); } static void settings_add_connection_helper (NMSettings *self, GDBusMethodInvocation *context, + gboolean is_add_connection_2, GVariant *settings, - gboolean save_to_disk) + NMSettingsAddConnection2Flags flags) { gs_unref_object NMConnection *connection = NULL; GError *error = NULL; gs_unref_object NMAuthSubject *subject = NULL; + NMSettingsConnectionPersistMode persist_mode; connection = _nm_simple_connection_new_from_dbus (settings, NM_SETTING_PARSE_FLAGS_STRICT @@ -1356,13 +2609,24 @@ settings_add_connection_helper (NMSettings *self, return; } + if (NM_FLAGS_HAS (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK)) + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + else { + nm_assert (NM_FLAGS_HAS (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY)); + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; + } + nm_settings_add_connection_dbus (self, connection, - save_to_disk, + persist_mode, + NM_FLAGS_HAS (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT) + ? NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT + : NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, subject, context, settings_add_connection_add_cb, - NULL); + GINT_TO_POINTER (!!is_add_connection_2)); } static void @@ -1378,7 +2642,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, FALSE, settings, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK); } static void @@ -1394,14 +2658,79 @@ 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, FALSE, settings, NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY); +} + +static void +impl_settings_add_connection2 (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettings *self = NM_SETTINGS (obj); + gs_unref_variant GVariant *settings = NULL; + gs_unref_variant GVariant *args = NULL; + NMSettingsAddConnection2Flags flags; + const char *args_name; + GVariantIter iter; + guint32 flags_u; + + g_variant_get (parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args); + + if (NM_FLAGS_ANY (flags_u, ~((guint32) ( NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY + | NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)))) { + g_dbus_method_invocation_take_error (invocation, + g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, + "Unknown flags")); + return; + } + + flags = flags_u; + + if (!NM_FLAGS_ANY (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY)) { + g_dbus_method_invocation_take_error (invocation, + g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, + "Requires either to-disk (0x1) or in-memory (0x2) flags")); + return; + } + + if (NM_FLAGS_ALL (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY)) { + g_dbus_method_invocation_take_error (invocation, + g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, + "Cannot set to-disk (0x1) and in-memory (0x2) flags together")); + return; + } + + nm_assert (g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))); + + g_variant_iter_init (&iter, args); + while (g_variant_iter_next (&iter, "{&sv}", &args_name, NULL)) { + g_dbus_method_invocation_take_error (invocation, + g_error_new (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, + "Unsupported argument '%s'", args_name)); + return; + } + + settings_add_connection_helper (self, invocation, TRUE, settings, flags); } +/*****************************************************************************/ + 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) @@ -1409,9 +2738,8 @@ impl_settings_load_connections (NMDBusObject *obj, NMSettings *self = NM_SETTINGS (obj); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); gs_unref_ptrarray GPtrArray *failures = NULL; - GSList *iter; - guint i; gs_free const char **filenames = NULL; + gs_free char *op_result_str = NULL; g_variant_get (parameters, "(^a&s)", &filenames); @@ -1426,31 +2754,68 @@ impl_settings_load_connections (NMDBusObject *obj, NM_SETTINGS_ERROR_PERMISSION_DENIED)) return; - if (filenames) { - for (i = 0; filenames[i]; i++) { - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); + if ( filenames + && filenames[0]) { + NMSettingsPluginConnectionLoadEntry *entries; + gsize n_entries; + gsize i; + GSList *iter; - if (nm_settings_plugin_load_connection (plugin, filenames[i])) - break; - } + entries = nm_settings_plugin_create_connection_load_entries (filenames, &n_entries); - if (!iter) { - if (!g_path_is_absolute (filenames[i])) - _LOGW ("connection filename '%s' is not an absolute path", filenames[i]); - if (!failures) - failures = g_ptr_array_new (); - g_ptr_array_add (failures, (char *) filenames[i]); - } + for (iter = priv->plugins; iter; iter = iter->next) { + NMSettingsPlugin *plugin = iter->data; + + nm_settings_plugin_load_connections (plugin, + entries, + n_entries, + _plugin_connections_reload_cb, + self); + } + + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *entry = &entries[i]; + + if (!entry->handled) + _LOGW ("load: no settings plugin could load \"%s\"", entry->filename); + else if (entry->error) { + _LOGW ("load: failure to load \"%s\": %s", entry->filename, entry->error->message); + g_clear_error (&entry->error); + } else + continue; + + if (!failures) + failures = g_ptr_array_new (); + g_ptr_array_add (failures, (char *) entry->filename); } + + nm_clear_g_free (&entries); + + _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); + + for (iter = priv->plugins; iter; iter = iter->next) + nm_settings_plugin_load_connections_done (iter->data); } if (failures) g_ptr_array_add (failures, NULL); + nm_audit_log_connection_op (NM_AUDIT_OP_CONNS_LOAD, + NULL, + !failures, + (op_result_str = g_strjoinv (",", (char **) filenames)), + invocation, + NULL); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b^as)", - (gboolean) (!!failures), + (gboolean) (!failures), failures ? (const char **) failures->pdata : NM_PTRARRAY_EMPTY (const char *))); @@ -1466,8 +2831,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 @@ -1480,11 +2843,9 @@ 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); + _plugin_connections_reload (self); - nm_settings_plugin_reload_connections (plugin); - } + nm_audit_log_connection_op (NM_AUDIT_OP_CONNS_RELOAD, NULL, TRUE, NULL, invocation, NULL); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE)); } @@ -1492,8 +2853,427 @@ impl_settings_reload_connections (NMDBusObject *obj, /*****************************************************************************/ static void +_clear_connections_cached_list (NMSettingsPrivate *priv) +{ + if (!priv->connections_cached_list) + return; + + nm_assert (priv->connections_len == NM_PTRARRAY_LEN (priv->connections_cached_list)); + +#if NM_MORE_ASSERTS + /* set the pointer to a bogus value. This makes it more apparent + * if somebody has a reference to the cached list and still uses + * it. That is a bug, this code just tries to make it blow up + * more eagerly. */ + memset (priv->connections_cached_list, + 0xdeaddead, + sizeof (NMSettingsConnection *) * (priv->connections_len + 1)); +#endif + + nm_clear_g_free (&priv->connections_cached_list); +} + +static void +impl_settings_list_connections (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettings *self = NM_SETTINGS (obj); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gs_free const char **strv = NULL; + + strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head, + priv->connections_len, + G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst), + TRUE); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(^ao)", strv)); +} + +NMSettingsConnection * +nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid) +{ + g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + g_return_val_if_fail (uuid != NULL, NULL); + + return _sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid)); +} + +const char * +nm_settings_get_dbus_path_for_uuid (NMSettings *self, + const char *uuid) +{ + NMSettingsConnection *sett_conn; + + 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 +impl_settings_get_connection_by_uuid (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettings *self = NM_SETTINGS (obj); + NMSettingsConnection *sett_conn; + gs_unref_object NMAuthSubject *subject = NULL; + GError *error = NULL; + const char *uuid; + + g_variant_get (parameters, "(&s)", &uuid); + + sett_conn = nm_settings_get_connection_by_uuid (self, uuid); + if (!sett_conn) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No connection with the UUID was found."); + goto error; + } + + subject = nm_auth_subject_new_unix_process_from_context (invocation); + if (!subject) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to determine UID of request."); + goto error; + } + + if (!nm_auth_is_subject_in_acl_set_error (nm_settings_connection_get_connection (sett_conn), + subject, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + &error)) + goto error; + + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)))); + return; + +error: + g_dbus_method_invocation_take_error (invocation, error); +} + +/** + * nm_settings_get_connections: + * @self: the #NMSettings + * @out_len: (out) (allow-none): returns the number of returned + * connections. + * + * Returns: (transfer none): a list of NMSettingsConnections. The list is + * unsorted and NULL terminated. The result is never %NULL, in case of no + * connections, it returns an empty list. + * The returned list is cached internally, only valid until the next + * NMSettings operation. + */ +NMSettingsConnection *const* +nm_settings_get_connections (NMSettings *self, guint *out_len) +{ + NMSettingsPrivate *priv; + NMSettingsConnection **v; + NMSettingsConnection *con; + guint i; + + g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + + priv = NM_SETTINGS_GET_PRIVATE (self); + + nm_assert (priv->connections_len == c_list_length (&priv->connections_lst_head)); + + if (G_UNLIKELY (!priv->connections_cached_list)) { + v = g_new (NMSettingsConnection *, priv->connections_len + 1); + + i = 0; + c_list_for_each_entry (con, &priv->connections_lst_head, _connections_lst) { + nm_assert (i < priv->connections_len); + v[i++] = con; + } + nm_assert (i == priv->connections_len); + v[i] = NULL; + + priv->connections_cached_list = v; + } + + NM_SET_OUT (out_len, priv->connections_len); + return priv->connections_cached_list; +} + +/** + * nm_settings_get_connections_clone: + * @self: the #NMSetting + * @out_len: (allow-none): optional output argument + * @func: caller-supplied function for filtering connections + * @func_data: caller-supplied data passed to @func + * @sort_compare_func: (allow-none): optional function pointer for + * sorting the returned list. + * @sort_data: user data for @sort_compare_func. + * + * Returns: (transfer container) (element-type NMSettingsConnection): + * an NULL terminated array of #NMSettingsConnection objects that were + * filtered by @func (or all connections if no filter was specified). + * The order is arbitrary. + * Caller is responsible for freeing the returned array with free(), + * the contained values do not need to be unrefed. + */ +NMSettingsConnection ** +nm_settings_get_connections_clone (NMSettings *self, + guint *out_len, + NMSettingsConnectionFilterFunc func, + gpointer func_data, + GCompareDataFunc sort_compare_func, + gpointer sort_data) +{ + NMSettingsConnection *const*list_cached; + NMSettingsConnection **list; + guint len, i, j; + + g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + + list_cached = nm_settings_get_connections (self, &len); + +#if NM_MORE_ASSERTS + nm_assert (list_cached); + for (i = 0; i < len; i++) + nm_assert (NM_IS_SETTINGS_CONNECTION (list_cached[i])); + nm_assert (!list_cached[i]); +#endif + + list = g_new (NMSettingsConnection *, ((gsize) len + 1)); + if (func) { + for (i = 0, j = 0; i < len; i++) { + if (func (self, list_cached[i], func_data)) + list[j++] = list_cached[i]; + } + list[j] = NULL; + len = j; + } else + memcpy (list, list_cached, sizeof (list[0]) * ((gsize) len + 1)); + + if ( len > 1 + && sort_compare_func) { + g_qsort_with_data (list, len, sizeof (NMSettingsConnection *), + sort_compare_func, sort_data); + } + NM_SET_OUT (out_len, len); + return list; +} + +NMSettingsConnection * +nm_settings_get_connection_by_path (NMSettings *self, const char *path) +{ + NMSettingsPrivate *priv; + NMSettingsConnection *connection; + + g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + g_return_val_if_fail (path, NULL); + + priv = NM_SETTINGS_GET_PRIVATE (self); + + connection = nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)), + path); + if ( !connection + || !NM_IS_SETTINGS_CONNECTION (connection)) + return NULL; + + nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst)); + return connection; +} + +gboolean +nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection) +{ + gboolean has; + + g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE); + + has = !c_list_is_empty (&connection->_connections_lst); + + nm_assert (has == nm_c_list_contains_entry (&NM_SETTINGS_GET_PRIVATE (self)->connections_lst_head, + connection, + _connections_lst)); + nm_assert (({ + NMSettingsConnection *candidate = NULL; + const char *path; + + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); + if (path) + candidate = nm_settings_get_connection_by_path (self, path); + + (has == (connection == candidate)); + })); + + return has; +} + +/*****************************************************************************/ + +static void +add_plugin (NMSettings *self, + NMSettingsPlugin *plugin, + const char *pname, + const char *path) +{ + NMSettingsPrivate *priv; + + 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)); + + priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin)); + + nm_shutdown_wait_obj_register_full (G_OBJECT (plugin), + g_strdup_printf ("%s-settings-plugin", pname), + TRUE); + + _LOGI ("Loaded settings plugin: %s (%s%s%s)", + pname, + NM_PRINT_FMT_QUOTED (path, "\"", path, "\"", "internal")); +} + +static gboolean +add_plugin_load_file (NMSettings *self, const char *pname, GError **error) +{ + gs_free char *full_name = NULL; + gs_free char *path = NULL; + gs_unref_object NMSettingsPlugin *plugin = NULL; + GModule *module; + NMSettingsPluginFactoryFunc factory_func; + struct stat st; + int errsv; + + full_name = g_strdup_printf ("nm-settings-plugin-%s", pname); + path = g_module_build_path (NMPLUGINDIR, full_name); + + if (stat (path, &st) != 0) { + errsv = errno; + _LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, nm_strerror_native (errsv)); + return TRUE; + } + if (!S_ISREG (st.st_mode)) { + _LOGW ("could not load plugin '%s' from file '%s': not a file", pname, path); + return TRUE; + } + if (st.st_uid != 0) { + _LOGW ("could not load plugin '%s' from file '%s': file must be owned by root", pname, path); + return TRUE; + } + if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { + _LOGW ("could not load plugin '%s' from file '%s': invalid file permissions", pname, path); + return TRUE; + } + + module = g_module_open (path, G_MODULE_BIND_LOCAL); + if (!module) { + _LOGW ("could not load plugin '%s' from file '%s': %s", + pname, path, g_module_error ()); + return TRUE; + } + + /* errors after this point are fatal, because we loaded the shared library already. */ + + if (!g_module_symbol (module, "nm_settings_plugin_factory", (gpointer) (&factory_func))) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Could not find plugin '%s' factory function.", + pname); + g_module_close (module); + return FALSE; + } + + /* after accessing the plugin we cannot unload it anymore, because the glib + * types cannot be properly unregistered. */ + g_module_make_resident (module); + + plugin = (*factory_func) (); + if (!NM_IS_SETTINGS_PLUGIN (plugin)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "plugin '%s' returned invalid settings plugin", + pname); + return FALSE; + } + + add_plugin (self, NM_SETTINGS_PLUGIN (plugin), pname, path); + return TRUE; +} + +static void +add_plugin_keyfile (NMSettings *self) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + + if (priv->keyfile_plugin) + return; + priv->keyfile_plugin = nms_keyfile_plugin_new (); + add_plugin (self, NM_SETTINGS_PLUGIN (priv->keyfile_plugin), "keyfile", NULL); +} + +static gboolean +load_plugins (NMSettings *self, const char *const*plugins, GError **error) +{ + const char *const*iter; + gboolean success = TRUE; + + for (iter = plugins; iter && *iter; iter++) { + const char *pname = *iter; + + if (!*pname || strchr (pname, '/')) { + _LOGW ("ignore invalid plugin \"%s\"", pname); + continue; + } + + if (NM_IN_STRSET (pname, "ifcfg-suse", "ifnet", "ibft", "no-ibft")) { + _LOGW ("skipping deprecated plugin %s", pname); + continue; + } + + /* keyfile plugin is built-in now */ + if (nm_streq (pname, "keyfile")) { + add_plugin_keyfile (self); + continue; + } + + if (nm_utils_strv_find_first ((char **) plugins, + iter - plugins, + pname) >= 0) { + /* the plugin is already mentioned in the list previously. + * Don't load a duplicate. */ + continue; + } + + success = add_plugin_load_file (self, pname, error); + if (!success) + break; + } + + /* If keyfile plugin was not among configured plugins, add it as the last one */ + if (success) + add_plugin_keyfile (self); + + return success; +} + +/*****************************************************************************/ + +static void pk_hostname_cb (NMAuthChain *chain, - GError *chain_error, GDBusMethodInvocation *context, gpointer user_data) { @@ -1503,19 +3283,14 @@ pk_hostname_cb (NMAuthChain *chain, GError *error = NULL; const char *hostname; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); - priv->auths = g_slist_remove (priv->auths, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME); /* If our NMSettingsConnection is already gone, do nothing */ - if (chain_error) { - error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "Error checking authorization: %s", - chain_error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, "Insufficient privileges."); @@ -1533,8 +3308,6 @@ pk_hostname_cb (NMAuthChain *chain, g_dbus_method_invocation_take_error (context, error); else g_dbus_method_invocation_return_value (context, NULL); - - nm_auth_chain_destroy (chain); } static void @@ -1571,18 +3344,27 @@ impl_settings_save_hostname (NMDBusObject *obj, return; } - priv->auths = g_slist_append (priv->auths, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE); nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free); } /*****************************************************************************/ +static void +_hostname_changed_cb (NMHostnameManager *hostname_manager, + GParamSpec *pspec, + gpointer user_data) +{ + _notify (user_data, PROP_HOSTNAME); +} + +/*****************************************************************************/ + static gboolean have_connection_for_device (NMSettings *self, NMDevice *device) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - NMSettingConnection *s_con; NMSettingWired *s_wired; const char *setting_hwaddr; const char *perm_hw_addr; @@ -1595,32 +3377,36 @@ have_connection_for_device (NMSettings *self, NMDevice *device) /* Find a wired connection locked to the given MAC address, if any */ c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst) { NMConnection *connection = nm_settings_connection_get_connection (sett_conn); - const char *ctype, *iface; + NMSettingConnection *s_con = nm_connection_get_setting_connection (connection); + const char *ctype; + const char *iface; + + ctype = nm_setting_connection_get_connection_type (s_con); + if (!NM_IN_STRSET (ctype, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_PPPOE_SETTING_NAME)) + continue; if (!nm_device_check_connection_compatible (device, connection, NULL)) continue; - s_con = nm_connection_get_setting_connection (connection); + if (nm_settings_connection_default_wired_get_device (sett_conn)) + continue; - iface = nm_setting_connection_get_interface_name (s_con); - if (iface && strcmp (iface, nm_device_get_iface (device)) != 0) + if (NM_FLAGS_ANY (nm_settings_connection_get_flags (sett_conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) continue; - ctype = nm_setting_connection_get_connection_type (s_con); - if ( strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME) - && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) + iface = nm_setting_connection_get_interface_name (s_con); + if (!nm_streq0 (iface, nm_device_get_iface (device))) continue; s_wired = nm_connection_get_setting_wired (connection); - if ( !s_wired && nm_streq (ctype, NM_SETTING_PPPOE_SETTING_NAME)) { /* No wired setting; therefore the PPPoE connection applies to any device */ return TRUE; } - nm_assert (s_wired); - setting_hwaddr = nm_setting_wired_get_mac_address (s_wired); if (setting_hwaddr) { /* A connection mac-locked to this device */ @@ -1641,38 +3427,26 @@ 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); + _LOGT ("auto-default: forget association between %s (%s) and device %s (%s)", + nm_settings_connection_get_uuid (sett_conn), + nm_settings_connection_get_id (sett_conn), + nm_device_get_iface (device), + add_to_no_auto_default ? "persisted" : "temporary"); + + 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); @@ -1681,7 +3455,8 @@ default_wired_clear_tag (NMSettings *self, static void device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; + NMSettingsPrivate *priv; NMSettingsConnection *added; GError *error = NULL; @@ -1692,22 +3467,33 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self) G_CALLBACK (device_realized), self); + priv = NM_SETTINGS_GET_PRIVATE (self); + /* If the device isn't managed or it already has a default wired connection, * ignore it. */ if ( !nm_device_get_managed (device, FALSE) || g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()) - || have_connection_for_device (self, device)) + || have_connection_for_device (self, device) + || nm_config_get_no_auto_default_for_device (priv->config, device)) return; connection = nm_device_new_default_connection (device); if (!connection) return; - /* Add the connection */ - added = nm_settings_add_connection (self, connection, FALSE, &error); - g_object_unref (connection); - + _LOGT ("auto-default: creating in-memory connection %s (%s) for device %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + nm_device_get_iface (device)); + + nm_settings_add_connection (self, + connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + 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", @@ -1718,11 +3504,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), @@ -1735,6 +3519,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); @@ -1758,33 +3544,166 @@ 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); } } /*****************************************************************************/ -const char * -nm_settings_get_startup_complete_blocked_reason (NMSettings *self) +static void +session_monitor_changed_cb (NMSessionMonitor *session_monitor, + 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); - return uuid ?: "unknown"; + 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; + } + } } /*****************************************************************************/ +G_GNUC_PRINTF (4, 5) static void -_hostname_changed_cb (NMHostnameManager *hostname_manager, - GParamSpec *pspec, +_kf_db_log_fcn (NMKeyFileDB *kf_db, + int syslog_level, + gpointer user_data, + const char *fmt, + ...) +{ + NMSettings *self = user_data; + NMLogLevel level = nm_log_level_from_syslog (syslog_level); + + if (_NMLOG_ENABLED (level)) { + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gs_free char *msg = NULL; + va_list ap; + const char *prefix; + + va_start (ap, fmt); + msg = g_strdup_vprintf (fmt, ap); + va_end (ap); + + if (priv->kf_db_timestamps == kf_db) + prefix = "timestamps"; + else if (priv->kf_db_seen_bssids == kf_db) + prefix = "seen-bssids"; + else { + nm_assert_not_reached (); + prefix = "???"; + } + + _NMLOG (level, "[%s-keyfile]: %s", prefix, msg); + } +} + +static gboolean +_kf_db_got_dirty_flush (NMSettings *self, + gboolean is_timestamps) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + const char *prefix; + NMKeyFileDB *kf_db; + + if (is_timestamps) { + prefix = "timestamps"; + kf_db = priv->kf_db_timestamps; + priv->kf_db_flush_idle_id_timestamps = 0; + } else { + prefix = "seen-bssids"; + kf_db = priv->kf_db_seen_bssids; + priv->kf_db_flush_idle_id_seen_bssids = 0; + } + + if (nm_key_file_db_is_dirty (kf_db)) + nm_key_file_db_to_file (kf_db, FALSE); + else { + _LOGT ("[%s-keyfile]: skip saving changes to \"%s\"", + prefix, + nm_key_file_db_get_filename (kf_db)); + } + + return G_SOURCE_REMOVE; +} + +static gboolean +_kf_db_got_dirty_flush_timestamps_cb (gpointer user_data) +{ + return _kf_db_got_dirty_flush (user_data, + TRUE); +} + +static gboolean +_kf_db_got_dirty_flush_seen_bssids_cb (gpointer user_data) +{ + return _kf_db_got_dirty_flush (user_data, + FALSE); +} + +static void +_kf_db_got_dirty_fcn (NMKeyFileDB *kf_db, gpointer user_data) { - _notify (user_data, PROP_HOSTNAME); + NMSettings *self = user_data; + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GSourceFunc idle_func; + guint *p_id; + const char *prefix; + + if (priv->kf_db_timestamps == kf_db) { + prefix = "timestamps"; + p_id = &priv->kf_db_flush_idle_id_timestamps; + idle_func = _kf_db_got_dirty_flush_timestamps_cb; + } else if (priv->kf_db_seen_bssids == kf_db) { + prefix = "seen-bssids"; + p_id = &priv->kf_db_flush_idle_id_seen_bssids; + idle_func = _kf_db_got_dirty_flush_seen_bssids_cb; + } else { + nm_assert_not_reached (); + return; + } + + if (*p_id != 0) + return; + _LOGT ("[%s-keyfile]: schedule flushing changes to disk", prefix); + *p_id = g_idle_add_full (G_PRIORITY_LOW, idle_func, self, NULL); +} + +void +nm_settings_kf_db_write (NMSettings *self) +{ + NMSettingsPrivate *priv; + + g_return_if_fail (NM_IS_SETTINGS (self)); + + priv = NM_SETTINGS_GET_PRIVATE (self); + if (priv->kf_db_timestamps) + nm_key_file_db_to_file (priv->kf_db_timestamps, TRUE); + if (priv->kf_db_seen_bssids) + nm_key_file_db_to_file (priv->kf_db_seen_bssids, TRUE); } /*****************************************************************************/ @@ -1794,19 +3713,47 @@ 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, + _kf_db_got_dirty_fcn, + self); + priv->kf_db_seen_bssids = nm_key_file_db_new (NMSTATEDIR "/seen-bssids", + "seen-bssids", + _kf_db_log_fcn, + _kf_db_got_dirty_fcn, + self); + nm_key_file_db_start (priv->kf_db_timestamps); + nm_key_file_db_start (priv->kf_db_seen_bssids); + /* Load the plugins; fail if a plugin is not found. */ plugins = nm_config_data_get_plugins (nm_config_get_data_orig (priv->config), TRUE); - if (!load_plugins (self, (const char **) plugins, error)) + if (!load_plugins (self, (const char *const*) plugins, error)) return FALSE; - load_connections (self); - check_startup_complete (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); + } + + _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), @@ -1814,6 +3761,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; } @@ -1825,39 +3780,29 @@ get_property (GObject *object, guint prop_id, { NMSettings *self = NM_SETTINGS (object); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - const GSList *specs, *iter; - guint i; - char **strvs; const char **strv; switch (prop_id) { case PROP_UNMANAGED_SPECS: - specs = nm_settings_get_unmanaged_specs (self); - strvs = g_new (char *, g_slist_length ((GSList *) specs) + 1); - i = 0; - for (iter = specs; iter; iter = iter->next) - strvs[i++] = g_strdup (iter->data); - strvs[i] = NULL; - g_value_take_boxed (value, strvs); + g_value_take_boxed (value, + _nm_utils_slist_to_strv (nm_settings_get_unmanaged_specs (self), + TRUE)); break; case PROP_HOSTNAME: g_value_set_string (value, - priv->hostname_manager - ? nm_hostname_manager_get_hostname (priv->hostname_manager) - : NULL); + priv->hostname_manager + ? nm_hostname_manager_get_hostname (priv->hostname_manager) + : NULL); break; case PROP_CAN_MODIFY: 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)); @@ -1875,10 +3820,24 @@ nm_settings_init (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (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 * @@ -1892,11 +3851,18 @@ dispose (GObject *object) { NMSettings *self = NM_SETTINGS (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); - g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_destroy); - priv->auths = NULL; + while ((iter = c_list_first (&priv->auth_lst_head))) + nm_auth_chain_destroy (nm_auth_chain_parent_lst_entry (iter)); if (priv->hostname_manager) { g_signal_handlers_disconnect_by_func (priv->hostname_manager, @@ -1905,6 +3871,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 +3892,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); @@ -1929,11 +3907,22 @@ finalize (GObject *object) g_signal_handlers_disconnect_by_data (plugin, self); } + g_clear_object (&priv->keyfile_plugin); + 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); + nm_key_file_db_to_file (priv->kf_db_seen_bssids, FALSE); + nm_key_file_db_destroy (priv->kf_db_timestamps); + 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 ( @@ -2001,6 +3990,21 @@ static const NMDBusInterfaceInfoExtended interface_info_settings = { ), NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "AddConnection2", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("settings", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"), + NM_DEFINE_GDBUS_ARG_INFO ("args", "a{sv}"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("path", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("result", "a{sv}"), + ), + ), + .handle = impl_settings_add_connection2, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( "LoadConnections", .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("filenames", "as"), @@ -2104,7 +4108,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 eb74c09c..d2bf72d6 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Søren Sandmann <sandmann@daimi.au.dk> @@ -28,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)) @@ -68,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, @@ -79,7 +81,9 @@ typedef void (*NMSettingsAddCallback) (NMSettings *settings, void nm_settings_add_connection_dbus (NMSettings *self, NMConnection *connection, - gboolean save_to_disk, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionAddReason add_reason, + NMSettingsConnectionIntFlags sett_flags, NMAuthSubject *subject, GDBusMethodInvocation *context, NMSettingsAddCallback callback, @@ -94,16 +98,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, + NMSettingsConnectionAddReason add_reason, + 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 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); @@ -114,4 +139,6 @@ void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean qu const char *nm_settings_get_startup_complete_blocked_reason (NMSettings *self); +void nm_settings_kf_db_write (NMSettings *settings); + #endif /* __NM_SETTINGS_H__ */ diff --git a/src/settings/plugins/ibft/meson.build b/src/settings/plugins/ibft/meson.build deleted file mode 100644 index c33b24cc..00000000 --- a/src/settings/plugins/ibft/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -name = 'nm-settings-plugin-ibft' - -libnms_ibft_core = static_library( - 'nms-ibft-core', - 'nms-ibft-reader.c', - dependencies: nm_dep, -) - -sources = files( - 'nms-ibft-connection.c', - 'nms-ibft-plugin.c', -) - -libnm_settings_plugin_ibft = shared_module( - name, - sources: sources, - dependencies: nm_dep, - link_with: libnms_ibft_core, - link_args: ldflags_linker_script_settings, - link_depends: linker_script_settings, - install: true, - install_dir: nm_plugindir, -) - -core_plugins += libnm_settings_plugin_ibft - -# FIXME: check_so_symbols replacement -''' -run_target( - 'check-local-symbols-settings-ibft', - command: [check_so_symbols, libnm_settings_plugin_ibft.full_path()], - depends: libnm_settings_plugin_ibft, -) - -check-local-symbols-settings-ibft: src/settings/plugins/ibft/libnm-settings-plugin-ibft.la - $(call check_so_symbols,$(builddir)/src/settings/plugins/ibft/.libs/libnm-settings-plugin-ibft.so) -''' - -if enable_tests - subdir('tests') -endif diff --git a/src/settings/plugins/ibft/nms-ibft-connection.c b/src/settings/plugins/ibft/nms-ibft-connection.c deleted file mode 100644 index a36d8a31..00000000 --- a/src/settings/plugins/ibft/nms-ibft-connection.c +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ibft-connection.h" - -#include <net/ethernet.h> -#include <netinet/ether.h> -#include <glib/gstdio.h> - -#include "nms-ibft-reader.h" - -/*****************************************************************************/ - -struct _NMSIbftConnection { - NMSettingsConnection parent; -}; - -struct _NMSIbftConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMSIbftConnection, nms_ibft_connection, NM_TYPE_SETTINGS_CONNECTION) - -/*****************************************************************************/ - -static void -nms_ibft_connection_init (NMSIbftConnection *connection) -{ -} - -NMSIbftConnection * -nms_ibft_connection_new (const GPtrArray *block, GError **error) -{ - NMConnection *source; - GObject *object; - - source = nms_ibft_reader_get_connection_from_block (block, error); - if (!source) - return NULL; - - object = g_object_new (NMS_TYPE_IBFT_CONNECTION, NULL); - /* Update settings with what was read from iscsiadm */ - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object), - source, - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - NULL, - error)) - g_clear_object (&object); - - return (NMSIbftConnection *) object; -} - -static void -nms_ibft_connection_class_init (NMSIbftConnectionClass *ibft_connection_class) -{ -} - diff --git a/src/settings/plugins/ibft/nms-ibft-connection.h b/src/settings/plugins/ibft/nms-ibft-connection.h deleted file mode 100644 index de050662..00000000 --- a/src/settings/plugins/ibft/nms-ibft-connection.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 2014 Red Hat, Inc. - */ - -#ifndef __NMS_IBFT_CONNECTION_H__ -#define __NMS_IBFT_CONNECTION_H__ - -#include "settings/nm-settings-connection.h" - -#define NMS_TYPE_IBFT_CONNECTION (nms_ibft_connection_get_type ()) -#define NMS_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IBFT_CONNECTION, NMSIbftConnection)) -#define NMS_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IBFT_CONNECTION, NMSIbftConnectionClass)) -#define NMS_IS_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IBFT_CONNECTION)) -#define NMS_IS_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IBFT_CONNECTION)) -#define NMS_IBFT_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IBFT_CONNECTION, NMSIbftConnectionClass)) - -typedef struct _NMSIbftConnection NMSIbftConnection; -typedef struct _NMSIbftConnectionClass NMSIbftConnectionClass; - -GType nms_ibft_connection_get_type (void); - -NMSIbftConnection *nms_ibft_connection_new (const GPtrArray *block, - GError **error); - -#endif /* __NMS_IBFT_CONNECTION_H__ */ diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c deleted file mode 100644 index 47051995..00000000 --- a/src/settings/plugins/ibft/nms-ibft-plugin.c +++ /dev/null @@ -1,155 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ibft-plugin.h" - -#include <unistd.h> -#include <gmodule.h> - -#include "nm-setting-connection.h" -#include "settings/nm-settings-plugin.h" -#include "NetworkManagerUtils.h" - -#include "nms-ibft-reader.h" -#include "nms-ibft-connection.h" - -/*****************************************************************************/ - -typedef struct { - GHashTable *connections; /* uuid::connection */ - gboolean initialized; -} NMSIbftPluginPrivate; - -struct _NMSIbftPlugin { - NMSettingsPlugin parent; - NMSIbftPluginPrivate _priv; -}; - -struct _NMSIbftPluginClass { - NMSettingsPluginClass parent; -}; - -G_DEFINE_TYPE (NMSIbftPlugin, nms_ibft_plugin, NM_TYPE_SETTINGS_PLUGIN); - -#define NMS_IBFT_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIbftPlugin, NMS_IS_IBFT_PLUGIN) - -/*****************************************************************************/ - -static NMSIbftPlugin *nms_ibft_plugin_get (void); - -NM_DEFINE_SINGLETON_GETTER (NMSIbftPlugin, nms_ibft_plugin_get, NMS_TYPE_IBFT_PLUGIN); - -/*****************************************************************************/ - -static void -read_connections (NMSIbftPlugin *self) -{ - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - nm_auto_free_ibft_blocks GSList *blocks = NULL; - GSList *iter; - gs_free_error GError *error = NULL; - NMSIbftConnection *connection; - - if (!nms_ibft_reader_load_blocks ("/sbin/iscsiadm", &blocks, &error)) { - nm_log_dbg (LOGD_SETTINGS, "ibft: failed to read iscsiadm records: %s", error->message); - return; - } - - for (iter = blocks; iter; iter = iter->next) { - connection = nms_ibft_connection_new (iter->data, &error); - if (!connection) { - nm_log_warn (LOGD_SETTINGS, "ibft: failed to read iscsiadm record: %s", error->message); - g_clear_error (&error); - continue; - } - - nm_log_info (LOGD_SETTINGS, "ibft: read connection '%s'", - nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (connection))); - g_hash_table_insert (priv->connections, - g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))), - connection); - } -} - -static GSList * -get_connections (NMSettingsPlugin *config) -{ - NMSIbftPlugin *self = NMS_IBFT_PLUGIN (config); - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - GSList *list = NULL; - GHashTableIter iter; - NMSIbftConnection *connection; - - if (!priv->initialized) { - read_connections (self); - priv->initialized = TRUE; - } - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) - list = g_slist_prepend (list, connection); - - return list; -} - -/*****************************************************************************/ - -static void -nms_ibft_plugin_init (NMSIbftPlugin *self) -{ - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); -} - -static void -dispose (GObject *object) -{ - NMSIbftPlugin *self = NMS_IBFT_PLUGIN (object); - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - - if (priv->connections) { - g_hash_table_destroy (priv->connections); - priv->connections = NULL; - } - - G_OBJECT_CLASS (nms_ibft_plugin_parent_class)->dispose (object); -} - -static void -nms_ibft_plugin_class_init (NMSIbftPluginClass *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; -} - -/*****************************************************************************/ - -G_MODULE_EXPORT NMSettingsPlugin * -nm_settings_plugin_factory (void) -{ - return NM_SETTINGS_PLUGIN (g_object_ref (nms_ibft_plugin_get ())); -} diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.h b/src/settings/plugins/ibft/nms-ibft-plugin.h deleted file mode 100644 index 33a7620b..00000000 --- a/src/settings/plugins/ibft/nms-ibft-plugin.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 2014 Red Hat, Inc. - */ - -#ifndef __NMS_IBFT_PLUGIN_H__ -#define __NMS_IBFT_PLUGIN_H__ - -#define NMS_TYPE_IBFT_PLUGIN (nms_ibft_plugin_get_type ()) -#define NMS_IBFT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IBFT_PLUGIN, NMSIbftPlugin)) -#define NMS_IBFT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IBFT_PLUGIN, NMSIbftPluginClass)) -#define NMS_IS_IBFT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IBFT_PLUGIN)) -#define NMS_IS_IBFT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IBFT_PLUGIN)) -#define NMS_IBFT_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IBFT_PLUGIN, NMSIbftPluginClass)) - -typedef struct _NMSIbftPlugin NMSIbftPlugin; -typedef struct _NMSIbftPluginClass NMSIbftPluginClass; - -GType nms_ibft_plugin_get_type (void); - -#endif /* __NMS_IBFT_PLUGIN_H__ */ diff --git a/src/settings/plugins/ibft/nms-ibft-reader.c b/src/settings/plugins/ibft/nms-ibft-reader.c deleted file mode 100644 index fde30383..00000000 --- a/src/settings/plugins/ibft/nms-ibft-reader.c +++ /dev/null @@ -1,535 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ibft-reader.h" - -#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#include <sys/wait.h> -#include <sys/inotify.h> -#include <sys/ioctl.h> -#include <unistd.h> - -#include "nm-core-internal.h" -#include "platform/nm-platform.h" -#include "NetworkManagerUtils.h" - -/*****************************************************************************/ - -#define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, " " msg) - -/* Removes trailing whitespace and whitespace before and immediately after the '=' */ -static char * -remove_most_whitespace (const char *src) -{ - char *s_new, *s2; - const char *svalue; - - src = nm_str_skip_leading_spaces (src); - - svalue = strchr (src, '='); - if (!svalue || svalue == src) - return NULL; - - s_new = g_new (char, strlen (src) + 1); - - memcpy (s_new, src, svalue - src); - s_new[svalue - src] = '\0'; - g_strchomp (s_new); - - svalue++; - while (*svalue && g_ascii_isspace (*svalue)) - svalue++; - - s2 = strchr (s_new, '\0'); - s2[0] = '='; - strcpy (++s2, svalue); - g_strchomp (s2); - - return s_new; -} - -#define TAG_BEGIN "# BEGIN RECORD" -#define TAG_END "# END RECORD" - -/** - * nms_ibft_reader_load_blocks: - * @iscsiadm_path: path to iscsiadm program - * @out_blocks: on return if successful, a #GSList of #GPtrArray, or %NULL on - * failure - * @error: location for an error on failure - * - * Parses iscsiadm output and returns a #GSList of #GPtrArray in the @out_blocks - * argument on success, otherwise @out_blocks is set to %NULL. Each #GPtrArray - * in @out_blocks contains the lines from an iscsiadm interface block. - * - * Returns: %TRUE on success, %FALSE on errors - */ -gboolean -nms_ibft_reader_load_blocks (const char *iscsiadm_path, - GSList **out_blocks, - GError **error) -{ - const char *argv[4] = { iscsiadm_path, "-m", "fw", NULL }; - const char *envp[1] = { NULL }; - nm_auto_free_ibft_blocks GSList *blocks = NULL; - gs_free char *out = NULL; - gs_free char *err = NULL; - gs_free const char **lines = NULL; - GPtrArray *block_lines = NULL; - gsize i; - int status = 0; - - g_return_val_if_fail (iscsiadm_path != NULL, FALSE); - g_return_val_if_fail (out_blocks != NULL && *out_blocks == NULL, FALSE); - - if (!g_spawn_sync ("/", (char **) argv, (char **) envp, 0, - NULL, NULL, &out, &err, &status, error)) - return FALSE; - - if (!WIFEXITED (status)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "iBFT: %s exited abnormally.", iscsiadm_path); - return FALSE; - } - - if (WEXITSTATUS (status) != 0) { - if (err) { - char *nl; - - /* the error message contains newlines. concatenate the lines with whitespace */ - for (nl = err; *nl; nl++) { - if (*nl == '\n') - *nl = ' '; - } - } - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "iBFT: %s exited with error %d. Message: '%s'", - iscsiadm_path, WEXITSTATUS (status), err ?: "(none)"); - return FALSE; - } - - nm_log_dbg (LOGD_SETTINGS, "iBFT records:\n%s", out); - - lines = nm_utils_strsplit_set (out, "\n\r"); - for (i = 0; lines && lines[i]; i++) { - const char *ss = lines[i]; - - if (!g_ascii_strncasecmp (ss, TAG_BEGIN, NM_STRLEN (TAG_BEGIN))) { - if (block_lines) { - PARSE_WARNING ("malformed iscsiadm record: missing END RECORD."); - nm_clear_pointer (&block_lines, g_ptr_array_unref); - } - /* Start new record */ - block_lines = g_ptr_array_new_full (15, g_free); - } else if (!g_ascii_strncasecmp (ss, TAG_END, NM_STRLEN (TAG_END))) { - if (block_lines) { - if (block_lines->len) - blocks = g_slist_prepend (blocks, g_steal_pointer (&block_lines)); - else - g_ptr_array_unref (g_steal_pointer (&block_lines)); - } - } else if (block_lines) { - char *s = remove_most_whitespace (ss); - - if (!s) { - PARSE_WARNING ("malformed iscsiadm record: no = in '%s'.", ss); - nm_clear_pointer (&block_lines, g_ptr_array_unref); - } else - g_ptr_array_add (block_lines, s); - } - } - - if (block_lines) { - PARSE_WARNING ("malformed iscsiadm record: missing # END RECORD."); - nm_clear_pointer (&block_lines, g_ptr_array_unref); - } - - *out_blocks = g_steal_pointer (&blocks); - return TRUE; -} - -#define ISCSI_HWADDR_TAG "iface.hwaddress" -#define ISCSI_BOOTPROTO_TAG "iface.bootproto" -#define ISCSI_IPADDR_TAG "iface.ipaddress" -#define ISCSI_SUBNET_TAG "iface.subnet_mask" -#define ISCSI_GATEWAY_TAG "iface.gateway" -#define ISCSI_DNS1_TAG "iface.primary_dns" -#define ISCSI_DNS2_TAG "iface.secondary_dns" -#define ISCSI_VLAN_ID_TAG "iface.vlan_id" -#define ISCSI_IFACE_TAG "iface.net_ifacename" - -static const char * -match_iscsiadm_tag (const char *line, const char *tag) -{ - gsize taglen = strlen (tag); - - if (g_ascii_strncasecmp (line, tag, taglen) != 0) - return NULL; - if (line[taglen] != '=') - return NULL; - return line + taglen + 1; -} - -/** - * nms_ibft_reader_parse_block: - * @block: an array of iscsiadm interface block lines - * @error: return location for errors - * @...: pairs of key (const char *) : location (const char **) indicating the - * key to look for and the location to store the retrieved value in - * - * Parses an iscsiadm interface block into variables requested by the caller. - * Callers should verify the returned data is complete and valid. Returned - * strings are owned by @block and should not be used after @block is freed. - * - * Returns: %TRUE if at least , %FALSE on failure - */ -gboolean -nms_ibft_reader_parse_block (const GPtrArray *block, GError **error, ...) -{ - gboolean success = FALSE; - const char **out_value, *p; - va_list ap; - const char *key; - guint i; - - g_return_val_if_fail (block != NULL, FALSE); - g_return_val_if_fail (block->len > 0, FALSE); - - /* Find requested keys and populate return values */ - va_start (ap, error); - while ((key = va_arg (ap, const char *))) { - out_value = va_arg (ap, const char **); - *out_value = NULL; - for (i = 0; i < block->len; i++) { - p = match_iscsiadm_tag (g_ptr_array_index (block, i), key); - if (p) { - *out_value = p; - success = TRUE; - break; - } - } - } - va_end (ap); - - if (!success) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: failed to match at least one iscsiadm block field"); - } - return success; -} - -static gboolean -ip4_setting_add_from_block (const GPtrArray *block, - NMConnection *connection, - GError **error) -{ - NMSettingIPConfig *s_ip4 = NULL; - NMIPAddress *addr; - const char *s_method = NULL; - const char *s_ipaddr = NULL; - const char *s_gateway = NULL; - const char *s_dns1 = NULL; - const char *s_dns2 = NULL; - const char *s_netmask = NULL; - guint32 netmask = 0; - guint32 prefix; - - g_assert (block); - - if (!nms_ibft_reader_parse_block (block, error, - ISCSI_BOOTPROTO_TAG, &s_method, - ISCSI_IPADDR_TAG, &s_ipaddr, - ISCSI_SUBNET_TAG, &s_netmask, - ISCSI_GATEWAY_TAG, &s_gateway, - ISCSI_DNS1_TAG, &s_dns1, - ISCSI_DNS2_TAG, &s_dns2, - NULL)) - goto error; - - if (!s_method) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_BOOTPROTO_TAG); - goto error; - } - - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - - if (!g_ascii_strcasecmp (s_method, "dhcp")) { - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - goto success; - } else if (g_ascii_strcasecmp (s_method, "static") != 0) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: unknown " ISCSI_BOOTPROTO_TAG " '%s'.", - s_method); - goto error; - } - - /* Static configuration stuff */ - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL); - - /* IP address */ - if (!s_ipaddr || !nm_utils_ipaddr_valid (AF_INET, s_ipaddr)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid IP address '%s'.", - s_ipaddr); - goto error; - } - - /* Subnet/prefix */ - if (!s_netmask || inet_pton (AF_INET, s_netmask, &netmask) != 1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid subnet mask '%s'.", - s_netmask); - goto error; - } - prefix = nm_utils_ip4_netmask_to_prefix (netmask); - - if (s_gateway && !nm_utils_ipaddr_valid (AF_INET, s_gateway)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid IP gateway '%s'.", - s_gateway); - goto error; - } - - if (s_dns1 && !nm_utils_ipaddr_valid (AF_INET, s_dns1)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid DNS1 address '%s'.", - s_dns1); - goto error; - } - - if (s_dns2 && !nm_utils_ipaddr_valid (AF_INET, s_dns2)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid DNS2 address '%s'.", - s_dns2); - goto error; - } - - addr = nm_ip_address_new (AF_INET, s_ipaddr, prefix, error); - if (!addr) { - g_prefix_error (error, "iBFT: malformed iscsiadm record: "); - goto error; - } - - nm_setting_ip_config_add_address (s_ip4, addr); - nm_ip_address_unref (addr); - - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, s_gateway, NULL); - - if (s_dns1) - nm_setting_ip_config_add_dns (s_ip4, s_dns1); - if (s_dns2) - nm_setting_ip_config_add_dns (s_ip4, s_dns2); - -success: - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - return TRUE; - -error: - g_clear_object (&s_ip4); - return FALSE; -} - -static gboolean -connection_setting_add (const GPtrArray *block, - NMConnection *connection, - const char *type, - const char *prefix, - const char *iface, - GError **error) -{ - NMSetting *s_con; - char *id, *uuid; - const char *s_hwaddr = NULL, *s_ip4addr = NULL, *s_vlanid; - - if (!nms_ibft_reader_parse_block (block, error, - ISCSI_VLAN_ID_TAG, &s_vlanid, - ISCSI_HWADDR_TAG, &s_hwaddr, - ISCSI_IPADDR_TAG, &s_ip4addr, - NULL)) - return FALSE; - if (!s_hwaddr) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG); - return FALSE; - } - - id = g_strdup_printf ("iBFT%s%s %s", - prefix ? " " : "", - prefix ?: "", - iface); - - uuid = _nm_utils_uuid_generate_from_strings ("ibft", - s_hwaddr, - s_vlanid ? "V" : "v", - s_vlanid ?: "", - s_ip4addr ? "A" : "DHCP", - s_ip4addr ?: "", - NULL); - - s_con = nm_setting_connection_new (); - g_object_set (s_con, - NM_SETTING_CONNECTION_TYPE, type, - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_ID, id, - NM_SETTING_CONNECTION_READ_ONLY, TRUE, - NULL); - - g_free (uuid); - g_free (id); - - nm_connection_add_setting (connection, NM_SETTING (s_con)); - return TRUE; -} - -static gboolean -is_ibft_vlan_device (const GPtrArray *block) -{ - char *s_vlan_id = NULL; - - if (nms_ibft_reader_parse_block (block, NULL, ISCSI_VLAN_ID_TAG, &s_vlan_id, NULL)) { - g_assert (s_vlan_id); - - /* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it - * means "no VLAN". - */ - if (_nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1) - return TRUE; - } - return FALSE; -} - -static gboolean -vlan_setting_add_from_block (const GPtrArray *block, - NMConnection *connection, - GError **error) -{ - NMSetting *s_vlan = NULL; - const char *vlan_id_str = NULL; - gint64 vlan_id = -1; - gboolean success; - - g_assert (block); - g_assert (connection); - - /* This won't fail since this function shouldn't be called unless the - * iBFT VLAN ID exists and is > 0. - */ - success = nms_ibft_reader_parse_block (block, NULL, ISCSI_VLAN_ID_TAG, &vlan_id_str, NULL); - g_assert (success); - g_assert (vlan_id_str); - - /* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it means "no VLAN" */ - vlan_id = _nm_utils_ascii_str_to_int64 (vlan_id_str, 10, 1, 4095, -1); - if (vlan_id == -1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid VLAN_ID '%s'", vlan_id_str); - return FALSE; - } - - s_vlan = nm_setting_vlan_new (); - g_object_set (s_vlan, NM_SETTING_VLAN_ID, (guint32) vlan_id, NULL); - nm_connection_add_setting (connection, NM_SETTING (s_vlan)); - - return TRUE; -} - -static gboolean -wired_setting_add_from_block (const GPtrArray *block, - NMConnection *connection, - GError **error) -{ - NMSetting *s_wired = NULL; - const char *hwaddr = NULL; - - g_assert (block); - g_assert (connection); - - if (!nms_ibft_reader_parse_block (block, NULL, ISCSI_HWADDR_TAG, &hwaddr, NULL)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG); - return FALSE; - } - - if (!nm_utils_hwaddr_valid (hwaddr, ETH_ALEN)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid " ISCSI_HWADDR_TAG " '%s'.", - hwaddr); - return FALSE; - } - - s_wired = nm_setting_wired_new (); - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, hwaddr, NULL); - - nm_connection_add_setting (connection, s_wired); - return TRUE; -} - -NMConnection * -nms_ibft_reader_get_connection_from_block (const GPtrArray *block, GError **error) -{ - NMConnection *connection = NULL; - gboolean is_vlan = FALSE; - const char *iface = NULL; - - g_assert (block); - - if (!nms_ibft_reader_parse_block (block, error, ISCSI_IFACE_TAG, &iface, NULL)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_IFACE_TAG); - return NULL; - } - - connection = nm_simple_connection_new (); - - is_vlan = is_ibft_vlan_device (block); - if (is_vlan && !vlan_setting_add_from_block (block, connection, error)) - goto error; - - /* Always have a wired setting; for VLAN it defines the parent */ - if (!wired_setting_add_from_block (block, connection, error)) - goto error; - - if (!ip4_setting_add_from_block (block, connection, error)) - goto error; - - if (!connection_setting_add (block, - connection, - is_vlan ? NM_SETTING_VLAN_SETTING_NAME : NM_SETTING_WIRED_SETTING_NAME, - is_vlan ? "VLAN" : NULL, - iface, - error)) - goto error; - - if (!nm_connection_normalize (connection, NULL, NULL, error)) - goto error; - - return connection; - -error: - g_object_unref (connection); - return NULL; -} - diff --git a/src/settings/plugins/ibft/nms-ibft-reader.h b/src/settings/plugins/ibft/nms-ibft-reader.h deleted file mode 100644 index baa81e99..00000000 --- a/src/settings/plugins/ibft/nms-ibft-reader.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 2014 Red Hat, Inc. - */ - -#ifndef __NMS_IBFT_READER_H__ -#define __NMS_IBFT_READER_H__ - -#include "nm-connection.h" - -static inline void -_nm_auto_free_ibft_blocks (GSList **p_blocks) -{ - if (*p_blocks) - g_slist_free_full (*p_blocks, (GDestroyNotify) g_ptr_array_unref); -} -#define nm_auto_free_ibft_blocks nm_auto (_nm_auto_free_ibft_blocks) - -gboolean nms_ibft_reader_load_blocks (const char *iscsiadm_path, - GSList **out_blocks, - GError **error); - -NMConnection *nms_ibft_reader_get_connection_from_block (const GPtrArray *block, GError **error); - -gboolean nms_ibft_reader_parse_block (const GPtrArray *block, GError **error, ...) G_GNUC_NULL_TERMINATED; - -#endif /* __NMS_IBFT_READER_H__ */ diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 deleted file mode 100755 index 54f02da6..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10000.500.250.1 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 deleted file mode 100755 index ebd7a9ca..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = blah.foo.bar.baz -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry deleted file mode 100755 index 4e326048..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway deleted file mode 100755 index 5390a6c3..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = bb.cc.dd.ee -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr deleted file mode 100755 index b41cd1f1..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = aa.bb.cc.dd -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record deleted file mode 100755 index 22b34e6f..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = DHCP -iface.gateway = 10.16.52.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp b/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp deleted file mode 100755 index 556b0586..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = DHCP -iface.gateway = 10.16.52.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f1 -iface.bootproto = DHCP -iface.gateway = 10.16.52.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth1 -node.name = iqn.1.2008-11.com.blahblah:iscsi1 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-static b/src/settings/plugins/ibft/tests/iscsiadm-test-static deleted file mode 100755 index 51711480..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-static +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f1 -iface.bootproto = DHCP -iface.gateway = 10.16.52.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth1 -node.name = iqn.1.2008-11.com.blahblah:iscsi1 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan b/src/settings/plugins/ibft/tests/iscsiadm-test-vlan deleted file mode 100755 index 59b80bd0..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD 6.2.0.873-21 -iface.initiatorname = iqn.2010-04.org.ipxe:d05faa97-c4be-44f6-a723-efde9aa399a0 -iface.transport_name = tcp -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.6.200 -iface.subnet_mask = 255.255.255.0 -iface.vlan_id = 123 -iface.net_ifacename = eth0 -node.name = iqn.2003-01.org.x:disk1 -node.conn[0].address = 192.168.6.32 -node.conn[0].port = 3260 -node.boot_lun = 01000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/meson.build b/src/settings/plugins/ibft/tests/meson.build deleted file mode 100644 index e2f9ca7e..00000000 --- a/src/settings/plugins/ibft/tests/meson.build +++ /dev/null @@ -1,16 +0,0 @@ -test_unit = 'test-ibft' - -test_ibft_dir = meson.current_source_dir() - -exe = executable( - test_unit, - test_unit + '.c', - dependencies: test_nm_dep, - link_with: libnms_ibft_core, -) - -test( - 'ibft/' + test_unit, - test_script, - args: test_args + [exe.full_path()], -) diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c deleted file mode 100644 index f5b584a1..00000000 --- a/src/settings/plugins/ibft/tests/test-ibft.c +++ /dev/null @@ -1,292 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include <stdio.h> -#include <stdarg.h> -#include <unistd.h> -#include <netinet/ether.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/socket.h> - -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" - -#include "settings/plugins/ibft/nms-ibft-reader.h" - -#include "nm-test-utils-core.h" - -#define TEST_DIR NM_BUILD_SRCDIR"/src/settings/plugins/ibft/tests" - -static GPtrArray * -read_block (const char *iscsiadm_path, const char *expected_mac) -{ - nm_auto_free_ibft_blocks GSList *blocks = NULL; - GSList *iter; - GPtrArray *block = NULL; - GError *error = NULL; - gboolean success; - - success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error); - nmtst_assert_success (success, error); - - g_assert (blocks); - for (iter = blocks; iter; iter = iter->next) { - const char *s_hwaddr = NULL; - - if (!nms_ibft_reader_parse_block (iter->data, NULL, "iface.hwaddress", &s_hwaddr, NULL)) - continue; - g_assert (s_hwaddr); - if (nm_utils_hwaddr_matches (s_hwaddr, -1, expected_mac, -1)) { - block = g_ptr_array_ref (iter->data); - break; - } - } - g_assert (block); - - return block; -} - -static void -test_read_ibft_dhcp (void) -{ - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - GError *error = NULL; - const char *mac_address; - const char *expected_mac_address = "00:33:21:98:b9:f1"; - GPtrArray *block; - - block = read_block (TEST_DIR "/iscsiadm-test-dhcp", expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_no_error (error); - nmtst_assert_connection_verifies_without_normalization (connection); - - g_assert (!nm_connection_get_setting_vlan (connection)); - - /* ===== CONNECTION SETTING ===== */ - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "iBFT eth1"); - g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0); - g_assert (nm_setting_connection_get_autoconnect (s_con)); - g_assert (nm_setting_connection_get_read_only (s_con)); - - /* ===== WIRED SETTING ===== */ - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac_address = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac_address); - g_assert (nm_utils_hwaddr_matches (mac_address, -1, expected_mac_address, -1)); - g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0); - - /* ===== IPv4 SETTING ===== */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - - g_object_unref (connection); - g_ptr_array_unref (block); -} - -static void -test_read_ibft_static (void) -{ - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - GError *error = NULL; - const char *mac_address; - const char *expected_mac_address = "00:33:21:98:b9:f0"; - NMIPAddress *ip4_addr; - GPtrArray *block; - - block = read_block (TEST_DIR "/iscsiadm-test-static", expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_no_error (error); - nmtst_assert_connection_verifies_without_normalization (connection); - - g_assert (!nm_connection_get_setting_vlan (connection)); - - /* ===== CONNECTION SETTING ===== */ - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "iBFT eth0"); - g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0); - g_assert (nm_setting_connection_get_autoconnect (s_con)); - g_assert (nm_setting_connection_get_read_only (s_con)); - - /* ===== WIRED SETTING ===== */ - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac_address = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac_address); - g_assert (nm_utils_hwaddr_matches (mac_address, -1, expected_mac_address, -1)); - g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0); - - /* ===== IPv4 SETTING ===== */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); - - g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 2); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "10.16.255.2"); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 1), ==, "10.16.255.3"); - - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1); - ip4_addr = nm_setting_ip_config_get_address (s_ip4, 0); - g_assert (ip4_addr); - g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "192.168.32.72"); - g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 22); - - g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.168.35.254"); - - g_object_unref (connection); - g_ptr_array_unref (block); -} - -static void -test_read_ibft_malformed (gconstpointer user_data) -{ - const char *iscsiadm_path = user_data; - nm_auto_free_ibft_blocks GSList *blocks = NULL; - GError *error = NULL; - gboolean success; - - g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS)); - - NMTST_EXPECT_NM_WARN ("*malformed iscsiadm record*"); - - success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error); - nmtst_assert_success (success, error); - - g_assert (!blocks); - - g_test_assert_expected_messages (); -} - -static void -test_read_ibft_bad_address (gconstpointer user_data) -{ - const char *iscsiadm_path = user_data; - NMConnection *connection; - const char *expected_mac_address = "00:33:21:98:b9:f0"; - GPtrArray *block; - GError *error = NULL; - - g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS)); - - block = read_block (iscsiadm_path, expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); - g_assert (strstr (error->message, "iBFT: malformed iscsiadm record: invalid")); - g_clear_error (&error); - g_assert (connection == NULL); - - g_ptr_array_unref (block); -} - -static void -test_read_ibft_vlan (void) -{ - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingVlan *s_vlan; - NMSettingIPConfig *s_ip4; - const char *mac_address; - const char *expected_mac_address = "00:33:21:98:b9:f0"; - NMIPAddress *ip4_addr; - GError *error = NULL; - GPtrArray *block; - - block = read_block (TEST_DIR "/iscsiadm-test-vlan", expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_no_error (error); - nmtst_assert_connection_verifies_without_normalization (connection); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_VLAN_SETTING_NAME); - - /* ===== WIRED SETTING ===== */ - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac_address = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac_address); - g_assert (nm_utils_hwaddr_matches (mac_address, -1, expected_mac_address, -1)); - - /* ===== VLAN SETTING ===== */ - s_vlan = nm_connection_get_setting_vlan (connection); - g_assert (s_vlan); - g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 123); - g_assert_cmpstr (nm_setting_vlan_get_parent (s_vlan), ==, NULL); - - /* ===== IPv4 SETTING ===== */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); - - g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0); - - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1); - ip4_addr = nm_setting_ip_config_get_address (s_ip4, 0); - g_assert (ip4_addr); - g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "192.168.6.200"); - g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 24); - - g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, NULL); - - g_object_unref (connection); - g_ptr_array_unref (block); -} - -NMTST_DEFINE (); - -#define TPATH "/settings/plugins/ibft/" - -int main (int argc, char **argv) -{ - nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); - - g_test_add_func (TPATH "ibft/dhcp", test_read_ibft_dhcp); - g_test_add_func (TPATH "ibft/static", test_read_ibft_static); - g_test_add_func (TPATH "ibft/vlan", test_read_ibft_vlan); - g_test_add_data_func (TPATH "ibft/bad-record-read", TEST_DIR "/iscsiadm-test-bad-record", test_read_ibft_malformed); - g_test_add_data_func (TPATH "ibft/bad-entry-read", TEST_DIR "/iscsiadm-test-bad-entry", test_read_ibft_malformed); - g_test_add_data_func (TPATH "ibft/bad-ipaddr-read", TEST_DIR "/iscsiadm-test-bad-ipaddr", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-gateway-read", TEST_DIR "/iscsiadm-test-bad-gateway", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-dns1-read", TEST_DIR "/iscsiadm-test-bad-dns1", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-dns2-read", TEST_DIR "/iscsiadm-test-bad-dns2", test_read_ibft_bad_address); - - return g_test_run (); -} - diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build index 9024782a..58acdcfc 100644 --- a/src/settings/plugins/ifcfg-rh/meson.build +++ b/src/settings/plugins/ifcfg-rh/meson.build @@ -19,7 +19,6 @@ libnmdbus_ifcfg_rh = static_library( ) core_sources = files( - 'nm-inotify-helper.c', 'nms-ifcfg-rh-reader.c', 'nms-ifcfg-rh-utils.c', 'nms-ifcfg-rh-writer.c', @@ -36,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-storage.c', '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/nm-inotify-helper.c b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c deleted file mode 100644 index 04cbb5bc..00000000 --- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c +++ /dev/null @@ -1,213 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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. - * - * (C) Copyright 2008 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-inotify-helper.h" - -#include <unistd.h> -#include <sys/inotify.h> - -#include "NetworkManagerUtils.h" - -/* NOTE: this code should be killed once we depend on a new enough glib to - * include the patches from https://bugzilla.gnome.org/show_bug.cgi?id=532815 - */ - -/*****************************************************************************/ - -enum { - EVENT, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct { - int ifd; - guint inotify_id; - GHashTable *wd_refs; -} NMInotifyHelperPrivate; - -struct _NMInotifyHelper { - GObject parent; - NMInotifyHelperPrivate _priv; -}; - -struct _NMInotifyHelperClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMInotifyHelper, nm_inotify_helper, G_TYPE_OBJECT) - -#define NM_INOTIFY_HELPER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMInotifyHelper, NM_IS_INOTIFY_HELPER) - -/*****************************************************************************/ - -NM_DEFINE_SINGLETON_GETTER (NMInotifyHelper, nm_inotify_helper_get, NM_TYPE_INOTIFY_HELPER); - -/*****************************************************************************/ - -int -nm_inotify_helper_add_watch (NMInotifyHelper *self, const char *path) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - int wd; - guint refcount; - - if (priv->ifd < 0) - return -1; - - /* We only care about modifications since we're just trying to get change - * notifications on hardlinks. - */ - - wd = inotify_add_watch (priv->ifd, path, IN_CLOSE_WRITE); - if (wd < 0) - return -1; - - refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd))); - refcount++; - g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount)); - - return wd; -} - -void -nm_inotify_helper_remove_watch (NMInotifyHelper *self, int wd) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - guint refcount; - - if (priv->ifd < 0) - return; - - refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd))); - if (!refcount) - return; - - refcount--; - if (!refcount) { - g_hash_table_remove (priv->wd_refs, GINT_TO_POINTER (wd)); - inotify_rm_watch (priv->ifd, wd); - } else - g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount)); -} - -static gboolean -inotify_event_handler (GIOChannel *channel, GIOCondition cond, gpointer user_data) -{ - NMInotifyHelper *self = NM_INOTIFY_HELPER (user_data); - struct inotify_event evt; - - /* read the notifications from the watch descriptor */ - while (g_io_channel_read_chars (channel, (char *) &evt, sizeof (struct inotify_event), NULL, NULL) == G_IO_STATUS_NORMAL) { - char filename[PATH_MAX + 1]; - - filename[0] = '\0'; - if (evt.len > 0) { - g_io_channel_read_chars (channel, - filename, - evt.len > PATH_MAX ? PATH_MAX : evt.len, - NULL, NULL); - } - - if (!(evt.mask & IN_IGNORED)) - g_signal_emit (self, signals[EVENT], 0, &evt, &filename[0]); - } - - return TRUE; -} - -static gboolean -init_inotify (NMInotifyHelper *self) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - GIOChannel *channel; - int errsv; - - priv->ifd = inotify_init1 (IN_CLOEXEC); - if (priv->ifd == -1) { - errsv = errno; - nm_log_warn (LOGD_SETTINGS, "couldn't initialize inotify: %s (%d)", nm_strerror_native (errsv), errsv); - return FALSE; - } - - /* Watch the inotify descriptor for file/directory change events */ - channel = g_io_channel_unix_new (priv->ifd); - g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL); - g_io_channel_set_encoding (channel, NULL, NULL); - - priv->inotify_id = g_io_add_watch (channel, - G_IO_IN | G_IO_ERR, - (GIOFunc) inotify_event_handler, - (gpointer) self); - g_io_channel_unref (channel); - return TRUE; -} - -/*****************************************************************************/ - -static void -nm_inotify_helper_init (NMInotifyHelper *self) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - - priv->wd_refs = g_hash_table_new (nm_direct_hash, NULL); -} - -static void -constructed (GObject *object) -{ - G_OBJECT_CLASS (nm_inotify_helper_parent_class)->constructed (object); - - init_inotify (NM_INOTIFY_HELPER (object)); -} - -static void -finalize (GObject *object) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE ((NMInotifyHelper *) object); - - nm_clear_g_source (&priv->inotify_id); - nm_close (priv->ifd); - - g_hash_table_destroy (priv->wd_refs); - - G_OBJECT_CLASS (nm_inotify_helper_parent_class)->finalize (object); -} - -static void -nm_inotify_helper_class_init (NMInotifyHelperClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = constructed; - object_class->finalize = finalize; - - signals[EVENT] = - g_signal_new ("event", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_STRING); -} - diff --git a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h deleted file mode 100644 index b887ae37..00000000 --- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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. - * - * (C) Copyright 2008 Red Hat, Inc. - */ - -#ifndef __NM_INOTIFY_HELPER_H__ -#define __NM_INOTIFY_HELPER_H__ - -/* NOTE: this code should be killed once we depend on a new enough glib to - * include the patches from https://bugzilla.gnome.org/show_bug.cgi?id=532815 - */ - -#define NM_TYPE_INOTIFY_HELPER (nm_inotify_helper_get_type ()) -#define NM_INOTIFY_HELPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelper)) -#define NM_INOTIFY_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass)) -#define NM_IS_INOTIFY_HELPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_INOTIFY_HELPER)) -#define NM_IS_INOTIFY_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_INOTIFY_HELPER)) -#define NM_INOTIFY_HELPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass)) - -typedef struct _NMInotifyHelper NMInotifyHelper; -typedef struct _NMInotifyHelperClass NMInotifyHelperClass; - -GType nm_inotify_helper_get_type (void); - -NMInotifyHelper * nm_inotify_helper_get (void); - -int nm_inotify_helper_add_watch (NMInotifyHelper *helper, const char *path); - -void nm_inotify_helper_remove_watch (NMInotifyHelper *helper, int wd); - -static inline gboolean -nm_inotify_helper_clear_watch (NMInotifyHelper *helper, int *wd) -{ - int x; - - if (wd && ((x = *wd) >= 0)) { - *wd = -1; - nm_inotify_helper_remove_watch (helper, x); - return TRUE; - } - return FALSE; -} - -#endif /* __NM_INOTIFY_HELPER_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h index eaff2db4..ff01fc7a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify 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 4f769c5f..00000000 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c +++ /dev/null @@ -1,511 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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" -#include "nm-inotify-helper.h" - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_UNMANAGED_SPEC, - PROP_UNRECOGNIZED_SPEC, -); - -enum { - IFCFG_CHANGED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct { - gulong ih_event_id; - - int file_wd; - - char *keyfile; - int keyfile_wd; - - char *routefile; - int routefile_wd; - - char *route6file; - int route6file_wd; - - char *unmanaged_spec; - char *unrecognized_spec; - - gulong devtimeout_link_changed_handler; - guint devtimeout_timeout_id; - - NMInotifyHelper *inotify_helper; -} 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); -} - -static void -files_changed_cb (NMInotifyHelper *ih, - struct inotify_event *evt, - const char *path, - gpointer user_data) -{ - NMIfcfgConnection *self = NM_IFCFG_CONNECTION (user_data); - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - if ( (evt->wd != priv->file_wd) - && (evt->wd != priv->keyfile_wd) - && (evt->wd != priv->routefile_wd) - && (evt->wd != priv->route6file_wd)) - return; - - /* push the event up to the plugin */ - g_signal_emit (self, signals[IFCFG_CHANGED], 0); -} - -static void -path_watch_stop (NMIfcfgConnection *self) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - nm_clear_g_signal_handler (priv->inotify_helper, &priv->ih_event_id); - - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->file_wd); - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->keyfile_wd); - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->routefile_wd); - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->route6file_wd); - - nm_clear_g_free (&priv->keyfile); - nm_clear_g_free (&priv->routefile); - nm_clear_g_free (&priv->route6file); -} - -static void -filename_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - NMIfcfgConnection *self = NM_IFCFG_CONNECTION (object); - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - const char *ifcfg_path; - - path_watch_stop (self); - - ifcfg_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (self)); - if (!ifcfg_path) - return; - - priv->keyfile = utils_get_keys_path (ifcfg_path); - priv->routefile = utils_get_route_path (ifcfg_path); - priv->route6file = utils_get_route6_path (ifcfg_path); - - if (nm_config_get_monitor_connection_files (nm_config_get ())) { - NMInotifyHelper *ih; - - if (!priv->inotify_helper) - priv->inotify_helper = g_object_ref (nm_inotify_helper_get ()); - ih = priv->inotify_helper; - - priv->ih_event_id = g_signal_connect (ih, "event", G_CALLBACK (files_changed_cb), self); - priv->file_wd = nm_inotify_helper_add_watch (ih, ifcfg_path); - priv->keyfile_wd = nm_inotify_helper_add_watch (ih, priv->keyfile); - priv->routefile_wd = nm_inotify_helper_add_watch (ih, priv->routefile); - priv->route6file_wd = nm_inotify_helper_add_watch (ih, priv->route6file); - } -} - -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, - &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) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) connection); - const char *filename; - - filename = nm_settings_connection_get_filename (connection); - if (filename) { - g_unlink (filename); - if (priv->keyfile) - g_unlink (priv->keyfile); - if (priv->routefile) - g_unlink (priv->routefile); - if (priv->route6file) - g_unlink (priv->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 *connection) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (connection); - - priv->file_wd = -1; - priv->keyfile_wd = -1; - priv->routefile_wd = -1; - priv->route6file_wd = -1; - - g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_FILENAME, - G_CALLBACK (filename_changed), NULL); -} - -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); - - path_watch_stop (NM_IFCFG_CONNECTION (object)); - - nm_clear_g_signal_handler (NM_PLATFORM_GET, &priv->devtimeout_link_changed_handler); - nm_clear_g_source (&priv->devtimeout_timeout_id); - - g_clear_object (&priv->inotify_helper); - - 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); - - signals[IFCFG_CHANGED] = - g_signal_new ("ifcfg-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - 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 fd68f2d3..00000000 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 5160dbf0..cc4fe4ce 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Dan Williams <dcbw@redhat.com> @@ -25,23 +24,26 @@ #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 "settings/nm-settings-utils.h" #include "NetworkManagerUtils.h" -#include "nms-ifcfg-rh-connection.h" +#include "nms-ifcfg-rh-storage.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" @@ -60,31 +62,25 @@ typedef struct { guint regist_id; } dbus; - GHashTable *connections; /* uuid::connection */ - gboolean initialized; + NMSettUtilStorages storages; - GFileMonitor *ifcfg_monitor; - gulong ifcfg_monitor_id; -} SettingsPluginIfcfgPrivate; + GHashTable *unmanaged_specs; + GHashTable *unrecognized_specs; -struct _SettingsPluginIfcfg { +} NMSIfcfgRHPluginPrivate; + +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) - -/*****************************************************************************/ - -static SettingsPluginIfcfg *settings_plugin_ifcfg_get (void); - -NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfcfg, settings_plugin_ifcfg_get, SETTINGS_TYPE_PLUGIN_IFCFG); +#define NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfcfgRHPlugin, NMS_IS_IFCFG_RH_PLUGIN, NMSettingsPlugin) /*****************************************************************************/ @@ -99,610 +95,811 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfcfg, settings_plugin_ifcfg_get, SETT /*****************************************************************************/ -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_storages (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages); /*****************************************************************************/ static void -connection_ifcfg_changed (NMIfcfgConnection *connection, gpointer user_data) +nm_assert_self (NMSIfcfgRHPlugin *self, gboolean unhandled_specs_consistent) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - const char *path; + nm_assert (NMS_IS_IFCFG_RH_PLUGIN (self)); - path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection)); - g_return_if_fail (path != NULL); +#if NM_MORE_ASSERTS > 5 + { + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + NMSIfcfgRHStorage *storage; + gsize n_uuid; + gs_unref_hashtable GHashTable *h_unmanaged = NULL; + gs_unref_hashtable GHashTable *h_unrecognized = NULL; - if (!priv->ifcfg_monitor) { - _LOGD ("connection_ifcfg_changed("NM_IFCFG_CONNECTION_LOG_FMTD"): %s", NM_IFCFG_CONNECTION_LOG_ARGD (connection), "ignore event"); - return; - } + nm_assert (g_hash_table_size (priv->storages.idx_by_filename) == c_list_length (&priv->storages._storage_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 (storage, &priv->storages._storage_lst_head, parent._storage_lst) { + const char *uuid; + const char *filename; + + filename = nms_ifcfg_rh_storage_get_filename (storage); + + nm_assert (filename && NM_STR_HAS_PREFIX (filename, IFCFG_DIR"/")); + + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + + nm_assert ((!!uuid) + (!!storage->unmanaged_spec) + (!!storage->unrecognized_spec) == 1); - _LOGD ("connection_ifcfg_changed("NM_IFCFG_CONNECTION_LOG_FMTD"): %s", NM_IFCFG_CONNECTION_LOG_ARGD (connection), "reload"); + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, filename)); + + if (uuid) { + NMSettUtilStorageByUuidHead *sbuh; + NMSettUtilStorageByUuidHead *sbuh2; + + if (storage->connection) + nm_assert (nm_streq0 (nm_connection_get_uuid (storage->connection), uuid)); + + if (!g_hash_table_lookup_extended (priv->storages.idx_by_uuid, &uuid, (gpointer *) &sbuh, (gpointer *) &sbuh2)) + nm_assert_not_reached (); + + nm_assert (sbuh); + nm_assert (nm_streq (uuid, sbuh->uuid)); + nm_assert (sbuh == sbuh2); + nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &storage->parent._storage_by_uuid_lst)); + + if (c_list_first (&sbuh->_storage_by_uuid_lst_head) == &storage->parent._storage_by_uuid_lst) + n_uuid++; + } else if (storage->unmanaged_spec) { + nm_assert (strlen (storage->unmanaged_spec) > 0); + g_hash_table_add (h_unmanaged, storage->unmanaged_spec); + } else if (storage->unrecognized_spec) { + nm_assert (strlen (storage->unrecognized_spec) > 0); + g_hash_table_add (h_unrecognized, storage->unrecognized_spec); + } else + nm_assert_not_reached (); + + nm_assert (!storage->connection); + } - update_connection (self, NULL, path, connection, TRUE, NULL, NULL); + nm_assert (g_hash_table_size (priv->storages.idx_by_uuid) == n_uuid); + + if (unhandled_specs_consistent) { + 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 } -static void -connection_removed_cb (NMSettingsConnection *obj, gpointer user_data) +/*****************************************************************************/ + +static NMSIfcfgRHStorage * +_load_file (NMSIfcfgRHPlugin *self, + const char *filename, + GError **error) { - g_hash_table_remove (SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) user_data)->connections, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (obj))); + gs_unref_object NMConnection *connection = NULL; + gs_free_error GError *load_error = NULL; + gs_free char *unhandled_spec = NULL; + gboolean load_error_ignore; + struct stat st; + + if (stat (filename, &st) != 0) { + int errsv = errno; + + if (error) { + nm_utils_error_set_errno (error, errsv, + "failure to stat file \%s\": %s", + filename); + } else + _LOGT ("load[%s]: failure to stat file: %s", filename, nm_strerror_native (errsv)); + return NULL; + } + + connection = connection_from_file (filename, + &unhandled_spec, + &load_error, + &load_error_ignore); + if (load_error) { + if (error) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, + "failure to read file \"%s\": %s", + filename, load_error->message); + } else { + _NMLOG (load_error_ignore ? LOGL_TRACE : LOGL_WARN, + "load[%s]: failure to read file: %s", filename, load_error->message); + } + return NULL; + } + + if (unhandled_spec) { + const char *unmanaged_spec; + const char *unrecognized_spec; + + if (!nms_ifcfg_rh_util_parse_unhandled_spec (unhandled_spec, + &unmanaged_spec, + &unrecognized_spec)) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, + "invalid unhandled spec \"%s\"", + unhandled_spec); + nm_assert_not_reached (); + return NULL; + } + return nms_ifcfg_rh_storage_new_unhandled (self, + filename, + unmanaged_spec, + unrecognized_spec); + } + + return nms_ifcfg_rh_storage_new_connection (self, + filename, + g_steal_pointer (&connection), + &st.st_mtim); } static void -remove_connection (SettingsPluginIfcfg *self, NMIfcfgConnection *connection) +_load_dir (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - gboolean unmanaged, unrecognized; + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_free_error GError *local = NULL; + const char *f_filename; + GDir *dir; - g_return_if_fail (self != NULL); - g_return_if_fail (connection != NULL); + dir = g_dir_open (IFCFG_DIR, 0, &local); + if (!dir) { + _LOGT ("Could not read directory '%s': %s", IFCFG_DIR, local->message); + return; + } - _LOGI ("remove "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection)); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); - unmanaged = !!nm_ifcfg_connection_get_unmanaged_spec (connection); - unrecognized = !!nm_ifcfg_connection_get_unrecognized_spec (connection); + while ((f_filename = g_dir_read_name (dir))) { + gs_free char *full_path = NULL; + NMSIfcfgRHStorage *storage; + char *full_filename; - 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); + full_path = g_build_filename (IFCFG_DIR, f_filename, NULL); + full_filename = utils_detect_ifcfg_path (full_path, TRUE); + if (!full_filename) + continue; - /* 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)); + if (!g_hash_table_add (dupl_filenames, full_filename)) + continue; + + nm_assert (!nm_sett_util_storages_lookup_by_filename (storages, full_filename)); + + storage = _load_file (self, + full_filename, + NULL); + if (storage) + nm_sett_util_storages_add_take (storages, storage); + } + g_dir_close (dir); } -static NMIfcfgConnection * -find_by_path (SettingsPluginIfcfg *self, const char *path) +static void +_storages_consolidate (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages_new, + gboolean replace_all, + GHashTable *storages_replaced, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - GHashTableIter iter; - NMSettingsConnection *candidate = NULL; + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted); + gs_unref_ptrarray GPtrArray *storages_modified = NULL; + CList storages_deleted; + NMSIfcfgRHStorage *storage_safe; + NMSIfcfgRHStorage *storage_new; + NMSIfcfgRHStorage *storage_old; + NMSIfcfgRHStorage *storage; + guint i; - g_return_val_if_fail (path != NULL, NULL); + /* 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. + * + * 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_storages (self, storages_new); + + storages_modified = g_ptr_array_new_with_free_func (g_object_unref); + c_list_init (&storages_deleted); + + c_list_for_each_entry (storage_old, &priv->storages._storage_lst_head, parent._storage_lst) + storage_old->dirty = TRUE; + + c_list_for_each_entry_safe (storage_new, storage_safe, &storages_new->_storage_lst_head, parent._storage_lst) { + storage_old = nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_ifcfg_rh_storage_get_filename (storage_new)); + + nm_sett_util_storages_steal (storages_new, storage_new); + + if ( !storage_old + || !nms_ifcfg_rh_storage_equal_type (storage_new, storage_old)) { + if (storage_old) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + if (nms_ifcfg_rh_storage_get_uuid_opt (storage_old)) + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst); + else + nms_ifcfg_rh_storage_destroy (storage_old); + } + storage_new->dirty = FALSE; + nm_sett_util_storages_add_take (&priv->storages, storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_new)); + continue; + } - 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); + storage_old->dirty = FALSE; + nms_ifcfg_rh_storage_copy_content (storage_old, storage_new); + nms_ifcfg_rh_storage_destroy (storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_old)); } - return NULL; -} -static NMIfcfgConnection * -update_connection (SettingsPluginIfcfg *self, - NMConnection *source, - const char *full_path, - NMIfcfgConnection *connection, - gboolean protect_existing_connection, - GHashTable *protected_connections, - GError **error) -{ - 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)"); + c_list_for_each_entry_safe (storage_old, storage_safe, &priv->storages._storage_lst_head, parent._storage_lst) { + if (!storage_old->dirty) + continue; + if ( replace_all + || ( storages_replaced + && g_hash_table_contains (storages_replaced, storage_old))) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + if (nms_ifcfg_rh_storage_get_uuid_opt (storage_old)) + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst); + else + nms_ifcfg_rh_storage_destroy (storage_old); } - g_propagate_error (error, local); - return NULL; } - uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new)); - connection_by_uuid = g_hash_table_lookup (priv->connections, uuid); + /* raise events. */ - if ( connection - && connection != connection_by_uuid) { + for (i = 0; i < storages_modified->len; i++) { + storage = storages_modified->pdata[i]; + storage->dirty = TRUE; + } - 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; + for (i = 0; i < storages_modified->len; i++) { + gs_unref_object NMConnection *connection = NULL; + storage = storages_modified->pdata[i]; - if (source) - _LOGW ("cannot update protected connection "NM_IFCFG_CONNECTION_LOG_FMT" due to conflicting UUID %s", NM_IFCFG_CONNECTION_LOG_ARG (conflicting), uuid); - 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; + if (!storage->dirty) { + /* the entry is no longer dirty. In the meantime we already emited + * another signal for it. */ + continue; + } + storage->dirty = FALSE; + if (storage != nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_ifcfg_rh_storage_get_filename (storage))) { + /* 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; } - /* The new connection has a different UUID then the original one that we - * are about to update. Remove @connection. */ - remove_connection (self, connection); + connection = nms_ifcfg_rh_storage_steal_connection (storage); + if (!connection) { + nm_assert (!nms_ifcfg_rh_storage_get_uuid_opt (storage)); + continue; + } + + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (nms_ifcfg_rh_storage_get_uuid_opt (storage)); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + connection, + user_data); } - /* 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; + while ((storage = c_list_first_entry (&storages_deleted, NMSIfcfgRHStorage, parent._storage_lst))) { + c_list_unlink (&storage->parent._storage_lst); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + NULL, + user_data); + nms_ifcfg_rh_storage_destroy (storage); } +} - /* 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 { +/*****************************************************************************/ + +static void +load_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_ifcfg_rh_storage_destroy); + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_unref_hashtable GHashTable *storages_replaced = NULL; + gs_unref_hashtable GHashTable *loaded_uuids = NULL; + const char *loaded_uuid; + GHashTableIter h_iter; + gsize i; + + if (n_entries == 0) + return; - /******************************************************* - * UPDATE - *******************************************************/ + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - 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 */); - } - } 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)); - } - } + loaded_uuids = g_hash_table_new (nm_str_hash, g_str_equal); + + storages_replaced = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL); + + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *const entry = &entries[i]; + gs_free_error GError *local = NULL; + const char *full_filename; + const char *uuid; + gs_free char *full_filename_keep = NULL; + NMSettingsPluginConnectionLoadEntry *dupl_content_entry; + gs_unref_object NMSIfcfgRHStorage *storage = NULL; + + if (entry->handled) + continue; - 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 (entry->filename[0] != '/') + continue; + + full_filename_keep = utils_detect_ifcfg_path (entry->filename, FALSE); + + if (!full_filename_keep) { + if (nm_utils_file_is_in_path (entry->filename, IFCFG_DIR)) { + nm_utils_error_set (&entry->error, + NM_UTILS_ERROR_UNKNOWN, + ("path is not a valid name for an ifcfg-rh file")); + entry->handled = TRUE; + } + continue; } - 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 ((dupl_content_entry = g_hash_table_lookup (dupl_filenames, full_filename_keep))) { + /* we already visited this file. */ + entry->handled = dupl_content_entry->handled; + if (dupl_content_entry->error) { + g_set_error_literal (&entry->error, + dupl_content_entry->error->domain, + dupl_content_entry->error->code, + dupl_content_entry->error->message); + } + continue; + } - 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)); - - /* watch changes of ifcfg hardlinks */ - g_signal_connect (G_OBJECT (connection_new), "ifcfg-changed", - G_CALLBACK (connection_ifcfg_changed), 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. */ - 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)); - else { - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (connection_new)); + entry->handled = TRUE; + + full_filename = full_filename_keep; + if (!g_hash_table_insert (dupl_filenames, g_steal_pointer (&full_filename_keep), entry)) + nm_assert_not_reached (); + + storage = _load_file (self, + full_filename, + &local); + if (!storage) { + if (nm_utils_file_stat (full_filename, NULL) == -ENOENT) { + NMSIfcfgRHStorage *storage2; + + /* the file does not exist. We take that as indication to unload the file + * that was previously loaded... */ + storage2 = nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename); + if (storage2) + g_hash_table_add (storages_replaced, g_object_ref (storage2)); + continue; } + g_propagate_error (&entry->error, g_steal_pointer (&local)); + continue; } - return connection_new; + + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + if (uuid) + g_hash_table_add (loaded_uuids, (char *) uuid); + + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage)); } -} -static void -ifcfg_dir_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) -{ - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (user_data); - char *path, *ifcfg_path; - NMIfcfgConnection *connection; - - path = g_file_get_path (file); - - ifcfg_path = utils_detect_ifcfg_path (path, FALSE); - _LOGD ("ifcfg_dir_changed(%s) = %d // %s", path, event_type, ifcfg_path ?: "(none)"); - if (ifcfg_path) { - connection = find_by_path (plugin, ifcfg_path); - switch (event_type) { - case G_FILE_MONITOR_EVENT_DELETED: - if (connection) - remove_connection (plugin, connection); - break; - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - /* Update or new */ - update_connection (plugin, NULL, ifcfg_path, connection, TRUE, NULL, NULL); - break; - default: - break; + /* now we visit all UUIDs that are about to change... */ + g_hash_table_iter_init (&h_iter, loaded_uuids); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &loaded_uuid, NULL)) { + NMSIfcfgRHStorage *storage; + NMSettUtilStorageByUuidHead *sbuh; + + sbuh = nm_sett_util_storages_lookup_by_uuid (&priv->storages, loaded_uuid); + if (!sbuh) + continue; + + c_list_for_each_entry (storage, &sbuh->_storage_by_uuid_lst_head, parent._storage_by_uuid_lst) { + const char *full_filename = nms_ifcfg_rh_storage_get_filename (storage); + gs_unref_object NMSIfcfgRHStorage *storage_new = NULL; + gs_free_error GError *local = NULL; + + if (g_hash_table_contains (dupl_filenames, full_filename)) { + /* already re-loaded. */ + continue; + } + + /* @storage has a UUID that was just loaded from disk, but we have an entry in cache. + * Reload that file too despite not being told to do so. The reason is to get + * the latest file timestamp so that we get the priorities right. */ + + storage_new = _load_file (self, + full_filename, + &local); + if ( storage_new + && !nm_streq0 (loaded_uuid, nms_ifcfg_rh_storage_get_uuid_opt (storage_new))) { + /* the file now references a different UUID. We are not told to reload + * that file, so this means the existing storage (with the previous + * filename and UUID tuple) is no longer valid. */ + g_clear_object (&storage_new); + } + + g_hash_table_add (storages_replaced, g_object_ref (storage)); + if (storage_new) + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage_new)); } - g_free (ifcfg_path); } - g_free (path); + + nm_clear_pointer (&loaded_uuids, g_hash_table_destroy); + nm_clear_pointer (&dupl_filenames, g_hash_table_destroy); + + _storages_consolidate (self, + &storages_new, + FALSE, + storages_replaced, + callback, + user_data); } static void -setup_ifcfg_monitoring (SettingsPluginIfcfg *plugin) +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin); - GFile *file; - GFileMonitor *monitor; - - file = g_file_new_for_path (IFCFG_DIR "/"); - monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - - if (monitor) { - priv->ifcfg_monitor_id = g_signal_connect (monitor, "changed", - G_CALLBACK (ifcfg_dir_changed), plugin); - priv->ifcfg_monitor = monitor; - } -} + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_ifcfg_rh_storage_destroy); -static GHashTable * -_paths_from_connections (GHashTable *connections) -{ - GHashTableIter iter; - NMIfcfgConnection *connection; - GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); + nm_assert_self (self, 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)); + _load_dir (self, &storages_new); - if (path) - g_hash_table_add (paths, (void *) path); - } - return paths; + _storages_consolidate (self, + &storages_new, + TRUE, + NULL, + callback, + user_data); + + nm_assert_self (self, FALSE); } -static int -_sort_paths (const char **f1, const char **f2, GHashTable *paths) +static void +load_connections_done (NMSettingsPlugin *plugin) { - struct stat st; - gboolean c1, c2; - gint64 m1, m2; - - c1 = !!g_hash_table_contains (paths, *f1); - c2 = !!g_hash_table_contains (paths, *f2); - if (c1 != c2) - return c1 ? -1 : 1; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); - 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; + /* at the beginning of a load, we emit a change signal for unmanaged/unrecognized + * specs that contain the sum of before and after (_unhandled_specs_merge_storages()). + * + * The idea is that while we emit signals about changes to connection, we have + * the sum of all unmanaged/unrecognized devices from before and after. + * + * This if triggered at the end, to reset the specs. */ + _unhandled_specs_reset (self); - return strcmp (*f1, *f2); + nm_assert_self (self, TRUE); } -static void -read_connections (SettingsPluginIfcfg *plugin) -{ - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin); - 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); - if (!dir) { - _LOGW ("Could not read directory '%s': %s", IFCFG_DIR, err->message); - g_error_free (err); - return; +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 NMSIfcfgRHStorage *storage = NULL; + gs_unref_object NMConnection *reread = NULL; + gs_free char *full_filename = NULL; + GError *local = NULL; + gboolean reread_same; + struct timespec mtime; + + nm_assert_self (self, TRUE); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (out_storage && !*out_storage); + nm_assert (out_connection && !*out_connection); + + if (!nms_ifcfg_rh_writer_write_connection (connection, + IFCFG_DIR, + NULL, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, NULL), + &full_filename, + &reread, + &reread_same, + &local)) { + _LOGT ("commit: %s (%s): failed to add: %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + local->message); + g_propagate_error (error, local); + return FALSE; } - alive_connections = g_hash_table_new (nm_direct_hash, NULL); + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - filenames = g_ptr_array_new_with_free_func (g_free); - while ((item = g_dir_read_name (dir))) { - char *full_path, *real_path; + nm_assert (full_filename && full_filename[0] == '/'); - full_path = g_build_filename (IFCFG_DIR, item, NULL); - real_path = utils_detect_ifcfg_path (full_path, TRUE); + _LOGT ("commit: %s (%s) added as \"%s\"", + nm_connection_get_uuid (reread), + nm_connection_get_id (reread), + full_filename); - if (real_path) - g_ptr_array_add (filenames, real_path); - g_free (full_path); - } - g_dir_close (dir); + storage = nms_ifcfg_rh_storage_new_connection (self, + full_filename, + g_steal_pointer (&reread), + nm_sett_util_stat_mtime (full_filename, FALSE, &mtime)); - /* 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 (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); - } - } - g_hash_table_destroy (alive_connections); + nm_sett_util_storages_add_take (&priv->storages, g_object_ref (storage)); - 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); - } + *out_connection = nms_ifcfg_rh_storage_steal_connection (storage); + *out_storage = NM_SETTINGS_STORAGE (g_steal_pointer (&storage)); + + nm_assert_self (self, TRUE); + + return TRUE; } -static GSList * -get_connections (NMSettingsPlugin *config) +static gboolean +update_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage_x, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) { - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin); - GSList *list = NULL; - GHashTableIter iter; - NMIfcfgConnection *connection; - - if (!priv->initialized) { - if (nm_config_get_monitor_connection_files (nm_config_get ())) - setup_ifcfg_monitoring (plugin); - read_connections (plugin); - priv->initialized = TRUE; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + NMSIfcfgRHStorage *storage = NMS_IFCFG_RH_STORAGE (storage_x); + const char *full_filename; + const char *uuid; + GError *local = NULL; + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same; + struct timespec mtime; + + nm_assert_self (self, TRUE); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (NMS_IS_IFCFG_RH_STORAGE (storage)); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (!error || !*error); + + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + + nm_assert (uuid && nm_streq0 (uuid, nm_connection_get_uuid (connection))); + + full_filename = nms_ifcfg_rh_storage_get_filename (storage); + + nm_assert (full_filename); + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); + + if (!nms_ifcfg_rh_writer_write_connection (connection, + IFCFG_DIR, + full_filename, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, full_filename), + NULL, + &reread, + &reread_same, + &local)) { + _LOGT ("commit: failure to write %s (%s) to \"%s\": %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + full_filename, + local->message); + g_propagate_error (error, local); + 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 ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - return list; + _LOGT ("commit: \"%s\": profile %s (%s) written", + full_filename, + uuid, + nm_connection_get_id (connection)); + + storage->stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime); + + *out_storage = NM_SETTINGS_STORAGE (g_object_ref (storage)); + *out_connection = g_steal_pointer (&reread); + + nm_assert_self (self, TRUE); + + return TRUE; } static gboolean -load_connection (NMSettingsPlugin *config, - const char *filename) +delete_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage_x, + 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); + NMSIfcfgRHStorage *storage = NMS_IFCFG_RH_STORAGE (storage_x); + const char *operation_message; + const char *full_filename; - if (!nm_utils_file_is_in_path (filename, IFCFG_DIR)) - return FALSE; + nm_assert_self (self, TRUE); + nm_assert (!error || !*error); + nm_assert (NMS_IS_IFCFG_RH_STORAGE (storage)); - /* 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) - return FALSE; + full_filename = nms_ifcfg_rh_storage_get_filename (storage); + nm_assert (full_filename); + + nm_assert (nms_ifcfg_rh_storage_get_uuid_opt (storage)); + + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); + + { + gs_free char *keyfile = utils_get_keys_path (full_filename); + gs_free char *routefile = utils_get_route_path (full_filename); + gs_free char *route6file = utils_get_route6_path (full_filename); + const char *const files[] = { full_filename, keyfile, routefile, route6file }; + gboolean any_deleted = FALSE; + gboolean any_failure = FALSE; + int i; + + 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 ("commit: failure to delete file \"%s\": %s", + 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 ("commit: deleted \"%s\", profile %s (%s)", + full_filename, + nms_ifcfg_rh_storage_get_uuid_opt (storage), + operation_message); - 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); + nm_sett_util_storages_steal (&priv->storages, storage); + nms_ifcfg_rh_storage_destroy (storage); - g_free (ifcfg_path); - return (connection != NULL); + nm_assert_self (self, TRUE); + + 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; + NMSIfcfgRHStorage *storage; + + 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 (storage, &priv->storages._storage_lst_head, parent._storage_lst) { + if (storage->unmanaged_spec) + g_hash_table_add (unmanaged_specs, g_strdup (storage->unmanaged_spec)); + if (storage->unrecognized_spec) + g_hash_table_add (unrecognized_specs, g_strdup (storage->unrecognized_spec)); + } - read_connections (plugin); + 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); + } + + 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) +static void +_unhandled_specs_merge_storages (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages) { - 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); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + gboolean unmanaged_changed = FALSE; + gboolean unrecognized_changed = FALSE; + NMSIfcfgRHStorage *storage; + + c_list_for_each_entry (storage, &storages->_storage_lst_head, parent._storage_lst) { + if ( storage->unmanaged_spec + && !g_hash_table_contains (priv->unmanaged_specs, storage->unmanaged_spec)) { + unmanaged_changed = TRUE; + g_hash_table_add (priv->unmanaged_specs, g_strdup (storage->unmanaged_spec)); + } + if ( storage->unrecognized_spec + && !g_hash_table_contains (priv->unrecognized_specs, storage->unrecognized_spec)) { + unrecognized_changed = TRUE; + g_hash_table_add (priv->unrecognized_specs, g_strdup (storage->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, &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; + NMSIfcfgRHStorage *storage; 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, @@ -719,10 +916,8 @@ 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)) { + storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, ifcfg_path); + if (!storage) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -730,25 +925,23 @@ 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_setting_connection_get_uuid (s_con); + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); if (!uuid) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "unable to get the UUID"); + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "ifcfg file '%s' not managed by NetworkManager", in_ifcfg); return; } - path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); + /* 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); + if (!path) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, @@ -764,9 +957,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); @@ -788,7 +981,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? */ } @@ -803,25 +996,27 @@ _method_call (GDBusConnection *connection, GDBusMethodInvocation *invocation, gpointer user_data) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data); - const char *ifcfg; - - 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; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (user_data); + + 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 ( - IFCFGRH1_BUS_NAME, + IFCFGRH1_IFACE1_NAME, .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( NM_DEFINE_GDBUS_METHOD_INFO ( IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS, @@ -842,8 +1037,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; @@ -852,8 +1047,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); @@ -900,8 +1095,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; @@ -909,8 +1104,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); @@ -944,9 +1139,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; @@ -974,9 +1169,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. @@ -988,7 +1183,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); @@ -997,23 +1192,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->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); - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); + priv->storages = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT (priv->storages, nms_ifcfg_rh_storage_destroy); } 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), @@ -1025,48 +1223,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_sett_util_storages_clear (&priv->storages); - if (priv->ifcfg_monitor) { - if (priv->ifcfg_monitor_id) - g_signal_handler_disconnect (priv->ifcfg_monitor, priv->ifcfg_monitor_id); + g_clear_object (&priv->config); - g_file_monitor_cancel (priv->ifcfg_monitor); - g_object_unref (priv->ifcfg_monitor); - } + 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->reload_connections = reload_connections; + plugin_class->load_connections = load_connections; + plugin_class->load_connections_done = load_connections_done; + plugin_class->add_connection = add_connection; + plugin_class->update_connection = update_connection; + plugin_class->delete_connection = delete_connection; } /*****************************************************************************/ @@ -1074,5 +1268,5 @@ settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass) G_MODULE_EXPORT NMSettingsPlugin * nm_settings_plugin_factory (void) { - return NM_SETTINGS_PLUGIN (g_object_ref (settings_plugin_ifcfg_get ())); + 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 d815cc51..1db36083 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Dan Williams <dcbw@redhat.com> @@ -21,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 e5423b18..900a3fc1 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -543,6 +542,37 @@ make_connection_setting (const char *file, vint64 = svGetValueInt64 (ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1); g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (int) vint64, NULL); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DEVTIMEOUT", &value); + if (v) { + vint64 = _nm_utils_ascii_str_to_int64 (v, 10, 0, ((gint64) G_MAXINT32) / 1000, -1); + if (vint64 != -1) + vint64 *= 1000; + else { + char *endptr; + double d; + + d = g_ascii_strtod (v, &endptr); + if ( errno == 0 + && endptr[0] == '\0' + && d >= 0.0) { + d *= 1000.0; + + /* We round. Yes, this is not correct to round IEEE 754 floats in general, + * but sufficient for our case where we know that NetworkManager wrote the + * setting with up to 3 digits for the milliseconds. */ + d += 0.5; + if ( d >= 0.0 + && d <= (double) G_MAXINT32) + vint64 = (gint64) d; + } + } + if (vint64 == -1) + PARSE_WARNING ("invalid DEVTIMEOUT setting"); + else + g_object_set (s_con, NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, (int) vint64, NULL); + } + i_val = NM_SETTING_CONNECTION_MDNS_DEFAULT; if (!svGetValueEnum (ifcfg, "MDNS", nm_setting_connection_mdns_get_type (), @@ -1906,7 +1936,10 @@ make_ip6_setting (shvarFile *ifcfg, gs_unref_object NMSettingIPConfig *s_ip6 = NULL; const char *v; gs_free char *value = NULL; - gboolean ipv6init, ipv6forwarding, dhcp6 = FALSE; + gboolean ipv6init; + gboolean ipv6forwarding; + gboolean disabled; + gboolean dhcp6 = FALSE; char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; const char *ipv6addr, *ipv6addr_secondaries; gs_free char *ipv6addr_to_free = NULL; @@ -1964,6 +1997,7 @@ make_ip6_setting (shvarFile *ifcfg, /* Find out method property */ /* Is IPV6 enabled? Set method to "ignored", when not enabled */ + disabled = svGetValueBoolean(ifcfg, "IPV6_DISABLED", FALSE); nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "IPV6INIT", &value); ipv6init = svGetValueBoolean (ifcfg, "IPV6INIT", FALSE); @@ -1972,8 +2006,10 @@ make_ip6_setting (shvarFile *ifcfg, ipv6init = svGetValueBoolean (network_ifcfg, "IPV6INIT", FALSE); } - if (!ipv6init) - method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; /* IPv6 is disabled */ + if (disabled) + method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED; + else if (!ipv6init) + method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; else { ipv6forwarding = svGetValueBoolean (ifcfg, "IPV6FORWARDING", FALSE); nm_clear_g_free (&value); @@ -2039,7 +2075,8 @@ make_ip6_setting (shvarFile *ifcfg, NULL); /* Don't bother to read IP, DNS and routes when IPv6 is disabled */ - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) return NM_SETTING (g_steal_pointer (&s_ip6)); nm_clear_g_free (&value); @@ -3071,12 +3108,17 @@ eap_tls_reader (const char *eap_method, gs_unref_bytes GBytes *privkey = NULL; gs_unref_bytes GBytes *client_cert = NULL; gs_free char *identity_free = NULL; + gs_free char *value_to_free = NULL; + const char *client_cert_var; + const char *client_cert_prop; + NMSetting8021xCKFormat format; g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free), NULL); + /* CA certificate */ if (!_cert_set_from_ifcfg (s_8021x, ifcfg, phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT", @@ -3090,6 +3132,7 @@ eap_tls_reader (const char *eap_method, phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD", phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD); + /* Private key */ if (!_cert_set_from_ifcfg (s_8021x, ifcfg, phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", @@ -3102,35 +3145,39 @@ eap_tls_reader (const char *eap_method, keys_ifcfg, phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : "IEEE_8021X_PRIVATE_KEY_PASSWORD", phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); - if (!privkey) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing %s for EAP method '%s'.", - phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", - eap_method); - return FALSE; - } + /* Client certificate */ + client_cert_var = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT"; + client_cert_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT; if (!_cert_set_from_ifcfg (s_8021x, ifcfg, - phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", - phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT, + client_cert_var, + client_cert_prop, &client_cert, error)) return FALSE; - /* FIXME: writer does not actually write IEEE_8021X_CLIENT_CERT_PASSWORD and other - * certificate related passwords. It should, because otherwise persisting such profiles - * to ifcfg looses information. As this currently only matters for PKCS11 URIs, it seems - * a seldom used feature so that it is not fixed yet. */ _secret_set_from_ifcfg (s_8021x, ifcfg, keys_ifcfg, phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD", phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD); - if (!client_cert) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing certificate for EAP method '%s'.", - eap_method); - return FALSE; + + /* In the past when the private key and client certificate + * were the same PKCS #12 file we used to write only the + * private key variable. Still support that even if it means + * that we have to look into the file content, which makes + * the connection not self-contained. + */ + if ( !client_cert + && privkey + && !svGetValue (ifcfg, client_cert_var, &value_to_free)) { + if (phase2) + format = nm_setting_802_1x_get_phase2_private_key_format (s_8021x); + else + format = nm_setting_802_1x_get_private_key_format (s_8021x); + + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) + g_object_set (s_8021x, client_cert_prop, privkey, NULL); } return TRUE; @@ -4369,9 +4416,11 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) gboolean autoneg = FALSE; guint32 speed = 0; const char *duplex = NULL; + gboolean wired_found = FALSE; ethtool_opts = svGetValue (ifcfg, "ETHTOOL_OPTS", ðtool_opts_free); if (ethtool_opts) { + wired_found = TRUE; /* WAKE_ON_LAN_IGNORE is inferred from a specified but empty ETHTOOL_OPTS */ if (!ethtool_opts[0]) wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; @@ -4395,7 +4444,9 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) } /* ETHTOOL_WAKE_ON_LAN = ignore overrides WoL settings in ETHTOOL_OPTS */ - tmp = svGetValueStr (ifcfg, "ETHTOOL_WAKE_ON_LAN", &wol_value_free); + tmp = svGetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN", &wol_value_free); + if (tmp) + wired_found = TRUE; if (nm_streq0 (tmp, "ignore")) wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; else if (tmp) @@ -4408,6 +4459,10 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) } s_wired = nm_connection_get_setting_wired (connection); + if (!s_wired && wired_found) { + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + } if (s_wired) { g_object_set (s_wired, NM_SETTING_WIRED_WAKE_ON_LAN, wol_flags, @@ -4433,86 +4488,100 @@ make_wired_setting (shvarFile *ifcfg, gs_unref_object NMSettingWired *s_wired = NULL; const char *cvalue; gs_free char *value = NULL; - char *nettype; + gboolean found = FALSE; s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); - value = svGetValueStr_cp (ifcfg, "MTU"); - if (value) { + cvalue = svGetValue (ifcfg, "MTU", &value); + if (cvalue) { int mtu; - mtu = _nm_utils_ascii_str_to_int64 (value, 0, 0, 65535, -1); + mtu = _nm_utils_ascii_str_to_int64 (cvalue, 0, 0, 65535, -1); if (mtu >= 0) g_object_set (s_wired, NM_SETTING_WIRED_MTU, (guint) mtu, NULL); else - PARSE_WARNING ("invalid MTU '%s'", value); + PARSE_WARNING ("invalid MTU '%s'", cvalue); nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "HWADDR"); + value = svGetValue_cp (ifcfg, "HWADDR"); if (value) { - value = g_strstrip (value); - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL); + if (value[0] != '\0') { + value = g_strstrip (value); + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL); + } nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "SUBCHANNELS"); - if (value) { - const char *p = value; - gboolean success = TRUE; - - /* basic sanity checks */ - while (*p) { - if (!g_ascii_isxdigit (*p) && (*p != ',') && (*p != '.')) { - PARSE_WARNING ("invalid SUBCHANNELS '%s'", value); - success = FALSE; - break; + cvalue = svGetValue (ifcfg, "SUBCHANNELS", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + const char *p = cvalue; + gboolean success = TRUE; + + /* basic sanity checks */ + while (*p) { + if (!g_ascii_isxdigit (*p) && (*p != ',') && (*p != '.')) { + PARSE_WARNING ("invalid SUBCHANNELS '%s'", cvalue); + success = FALSE; + break; + } + p++; } - p++; - } - if (success) { - gs_free const char **chans = NULL; - guint32 num_chans; + if (success) { + gs_free const char **chans = NULL; + guint32 num_chans; - chans = nm_utils_strsplit_set (value, ","); - num_chans = NM_PTRARRAY_LEN (chans); - if (num_chans < 2 || num_chans > 3) { - PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", - value, (unsigned) NM_PTRARRAY_LEN (chans)); - } else - g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, chans, NULL); + chans = nm_utils_strsplit_set (cvalue, ","); + num_chans = NM_PTRARRAY_LEN (chans); + if (num_chans < 2 || num_chans > 3) { + PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", + cvalue, (unsigned) NM_PTRARRAY_LEN (chans)); + } else + g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, chans, NULL); + } } nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "PORTNAME"); - if (value) { - nm_setting_wired_add_s390_option (s_wired, "portname", value); + cvalue = svGetValue (ifcfg, "PORTNAME", &value); + if (cvalue) { + if (cvalue[0] != '\0') + nm_setting_wired_add_s390_option (s_wired, "portname", cvalue); + found = TRUE; nm_clear_g_free (&value); } - value = svGetValueStr_cp (ifcfg, "CTCPROT"); - if (value) { - nm_setting_wired_add_s390_option (s_wired, "ctcprot", value); + cvalue = svGetValue (ifcfg, "CTCPROT", &value); + if (cvalue) { + if (cvalue[0] != '\0') + nm_setting_wired_add_s390_option (s_wired, "ctcprot", cvalue); nm_clear_g_free (&value); + found = TRUE; } - nettype = svGetValueStr_cp (ifcfg, "NETTYPE"); - if (nettype) { - if (!strcmp (nettype, "qeth") || !strcmp (nettype, "lcs") || !strcmp (nettype, "ctc")) - g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, nettype, NULL); + cvalue = svGetValue (ifcfg, "NETTYPE", &value); + if (cvalue) { + if (NM_IN_STRSET (cvalue, "qeth", "lcs", "ctc")) + g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, cvalue, NULL); else - PARSE_WARNING ("unknown s390 NETTYPE '%s'", nettype); - g_free (nettype); + PARSE_WARNING ("unknown s390 NETTYPE '%s'", cvalue); + nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "OPTIONS"); - if (value) { + cvalue = svGetValue (ifcfg, "OPTIONS", &value); + if (cvalue) + found = TRUE; + if (cvalue && cvalue[0]) { gs_free const char **options = NULL; gsize i; - options = nm_utils_strsplit_set_with_empty (value, " "); + options = nm_utils_escaped_tokens_split (cvalue, NM_ASCII_SPACES); for (i = 0; options && options[i]; i++) { const char *line = options[i]; const char *equals; @@ -4526,20 +4595,33 @@ make_wired_setting (shvarFile *ifcfg, if (!valid) PARSE_WARNING ("invalid s390 OPTION '%s'", line); } - nm_clear_g_free (&value); + found = TRUE; } - - g_object_set (s_wired, - NM_SETTING_WIRED_CLONED_MAC_ADDRESS, - svGetValueStr (ifcfg, "MACADDR", &value), - NULL); nm_clear_g_free (&value); - g_object_set (s_wired, - NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, - svGetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK", &value), - NULL); - nm_clear_g_free (&value); + cvalue = svGetValueStr (ifcfg, "MACADDR", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + g_object_set (s_wired, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + cvalue, + NULL); + } + nm_clear_g_free (&value); + found = TRUE; + } + + cvalue = svGetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + g_object_set (s_wired, + NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, + cvalue, + NULL); + } + nm_clear_g_free (&value); + found = TRUE; + } cvalue = svGetValueStr (ifcfg, "HWADDR_BLACKLIST", &value); if (cvalue) { @@ -4548,20 +4630,31 @@ make_wired_setting (shvarFile *ifcfg, strv = transform_hwaddr_blacklist (cvalue); g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, strv, NULL); nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "KEY_MGMT"); - if (value) { - if (!strcmp (value, "IEEE8021X")) { - *s_8021x = fill_8021x (ifcfg, file, value, FALSE, error); + cvalue = svGetValue (ifcfg, "KEY_MGMT", &value); + if (cvalue) + found = TRUE; + if (cvalue && cvalue[0] != '\0') { + if (!strcmp (cvalue, "IEEE8021X")) { + *s_8021x = fill_8021x (ifcfg, file, cvalue, FALSE, error); if (!*s_8021x) return NULL; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown wired KEY_MGMT type '%s'", value); + "Unknown wired KEY_MGMT type '%s'", cvalue); return NULL; } - nm_clear_g_free (&value); + } + nm_clear_g_free (&value); + + if (!found) { + g_set_error (error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_SETTING_MISSING, + "The setting is missing."); + return NULL; } return (NMSetting *) g_steal_pointer (&s_wired); @@ -4576,6 +4669,7 @@ wired_connection_from_ifcfg (const char *file, NMSetting *con_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -4591,12 +4685,16 @@ wired_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, con_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -4699,8 +4797,8 @@ make_infiniband_setting (shvarFile *ifcfg, g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL); if (svGetValueBoolean (ifcfg, "PKEY", FALSE)) { + gs_free char *parent = NULL; int p_key; - char *parent; if (!parse_infiniband_p_key (ifcfg, &p_key, &parent, error)) { g_object_unref (s_infiniband); @@ -4827,6 +4925,7 @@ bond_connection_from_ifcfg (const char *file, NMSetting *bond_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -4849,12 +4948,16 @@ bond_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, bond_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -4862,62 +4965,27 @@ bond_connection_from_ifcfg (const char *file, return connection; } -/* Check 'error' for errors. Missing config (NULL return value) is a valid case. */ -static char * -read_team_config (shvarFile *ifcfg, const char *key, GError **error) -{ - gs_free_error GError *local_error = NULL; - gs_free char *value = NULL; - size_t l; - - value = svGetValueStr_cp (ifcfg, key); - if (!value) - return NULL; - - l = strlen (value); - if (l > 1*1024*1024) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "%s too long (size %zd)", key, l); - return NULL; - } - - if (!nm_utils_is_json_object (value, &local_error)) { - PARSE_WARNING ("ignoring invalid team configuration: %s", local_error->message); - return NULL; - } - - return g_steal_pointer (&value); -} - static NMSetting * make_team_setting (shvarFile *ifcfg, const char *file, GError **error) { - NMSettingTeam *s_team; - char *value; - GError *local_err = NULL; + NMSetting *s_team; + gs_free char *value_device = NULL; + gs_free char *value = NULL; - value = svGetValueStr_cp (ifcfg, "DEVICE"); - if (!value) { + if (!svGetValueStr (ifcfg, "DEVICE", &value_device)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "mandatory DEVICE keyword missing"); return NULL; } - g_free (value); - - value = read_team_config (ifcfg, "TEAM_CONFIG", &local_err); - if (local_err) { - g_propagate_error (error, local_err); - return NULL; - } - s_team = NM_SETTING_TEAM (nm_setting_team_new ()); - - g_object_set (s_team, NM_SETTING_TEAM_CONFIG, value, NULL); - g_free (value); - - return (NMSetting *) s_team; + s_team = nm_setting_team_new (); + g_object_set (s_team, + NM_SETTING_TEAM_CONFIG, + svGetValue (ifcfg, "TEAM_CONFIG", &value), + NULL); + return s_team; } static NMConnection * @@ -4930,6 +4998,7 @@ team_connection_from_ifcfg (const char *file, NMSetting *team_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -4952,12 +5021,16 @@ team_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, team_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -5207,6 +5280,7 @@ bridge_connection_from_ifcfg (const char *file, NMSetting *bridge_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -5229,12 +5303,16 @@ bridge_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, bridge_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -5276,20 +5354,18 @@ make_bridge_port_setting (shvarFile *ifcfg) static NMSetting * make_team_port_setting (shvarFile *ifcfg) { - NMSetting *s_port = NULL; - char *value; - GError *error = NULL; + NMSetting *s_port; + gs_free char *value = NULL; - value = read_team_config (ifcfg, "TEAM_PORT_CONFIG", &error); - if (value) { - s_port = nm_setting_team_port_new (); - g_object_set (s_port, NM_SETTING_TEAM_PORT_CONFIG, value, NULL); - g_free (value); - } else if (error) { - PARSE_WARNING ("%s", error->message); - g_error_free (error); - } + value = svGetValueStr_cp (ifcfg, "TEAM_PORT_CONFIG"); + if (!value) + return NULL; + s_port = nm_setting_team_port_new (); + g_object_set (s_port, + NM_SETTING_TEAM_PORT_CONFIG, + value, + NULL); return s_port; } @@ -5486,6 +5562,7 @@ vlan_connection_from_ifcfg (const char *file, NMSetting *wired_setting = NULL; NMSetting *vlan_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -5508,12 +5585,16 @@ vlan_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, vlan_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -5549,21 +5630,21 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg, if (v) { gs_free char *lower = g_ascii_strdown (v, -1); - *out_spec = g_strdup_printf ("%s:mac:%s", type, lower); + *out_spec = g_strdup_printf ("%s:"NM_MATCH_SPEC_MAC_TAG"%s", type, lower); return connection; } nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "SUBCHANNELS", &value); if (v) { - *out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, v); + *out_spec = g_strdup_printf ("%s:"NM_MATCH_SPEC_S390_SUBCHANNELS_TAG"%s", type, v); return connection; } nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "DEVICE", &value); if (v) { - *out_spec = g_strdup_printf ("%s:interface-name:%s", type, v); + *out_spec = g_strdup_printf ("%s:"NM_MATCH_SPEC_INTERFACE_NAME_TAG"=%s", type, v); return connection; } @@ -5654,7 +5735,7 @@ connection_from_file_full (const char *filename, return g_steal_pointer (&connection); } - /* iBFT is handled by the iBFT settings plugin */ + /* iBFT is handled by nm-initrd-generator during boot. */ bootproto = svGetValueStr_cp (main_ifcfg, "BOOTPROTO"); if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) { NM_SET_OUT (out_ignore_error, TRUE); @@ -5902,12 +5983,10 @@ connection_from_file_full (const char *filename, if (s_match) nm_connection_add_setting (connection, s_match); - /* Bridge port? */ s_port = make_bridge_port_setting (main_ifcfg); if (s_port) nm_connection_add_setting (connection, s_port); - /* Team port? */ s_port = make_team_port_setting (main_ifcfg); if (s_port) nm_connection_add_setting (connection, s_port); @@ -5949,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 a8937ac8..8008e052 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -18,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" @@ -28,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/ifcfg-rh/nms-ifcfg-rh-storage.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c new file mode 100644 index 00000000..2841bedb --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c @@ -0,0 +1,198 @@ +/* NetworkManager + * + * 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-ifcfg-rh-storage.h" + +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "nm-connection.h" +#include "nms-ifcfg-rh-plugin.h" + +/*****************************************************************************/ + +struct _NMSIfcfgRHStorageClass { + NMSettingsStorageClass parent; +}; + +G_DEFINE_TYPE (NMSIfcfgRHStorage, nms_ifcfg_rh_storage, NM_TYPE_SETTINGS_STORAGE) + +/*****************************************************************************/ + +gboolean +nms_ifcfg_rh_storage_equal_type (const NMSIfcfgRHStorage *self_a, + const NMSIfcfgRHStorage *self_b) +{ + return (self_a == self_b) + || ( self_a + && self_b + && nm_streq0 (nms_ifcfg_rh_storage_get_uuid_opt (self_a), + nms_ifcfg_rh_storage_get_uuid_opt (self_b)) + && nm_streq0 (self_a->unmanaged_spec, + self_b->unmanaged_spec) + && nm_streq0 (self_a->unrecognized_spec, + self_b->unrecognized_spec)); +} + +void +nms_ifcfg_rh_storage_copy_content (NMSIfcfgRHStorage *dst, + const NMSIfcfgRHStorage *src) +{ + nm_assert (src != dst); + nm_assert (src && dst); + nm_assert (nms_ifcfg_rh_storage_equal_type (dst, src)); + nm_assert ( nms_ifcfg_rh_storage_get_filename (dst) + && nm_streq (nms_ifcfg_rh_storage_get_filename (dst), + nms_ifcfg_rh_storage_get_filename (src))); + + nm_g_object_ref_set (&dst->connection, src->connection); + g_free (dst->unmanaged_spec); + g_free (dst->unrecognized_spec); + dst->unmanaged_spec = g_strdup (src->unmanaged_spec); + dst->unrecognized_spec = g_strdup (src->unrecognized_spec); + dst->stat_mtime = src->stat_mtime; +} + +NMConnection * +nms_ifcfg_rh_storage_steal_connection (NMSIfcfgRHStorage *self) +{ + nm_assert (NMS_IS_IFCFG_RH_STORAGE (self)); + + return g_steal_pointer (&self->connection); +} + +/*****************************************************************************/ + +static int +cmp_fcn (const NMSIfcfgRHStorage *a, + const NMSIfcfgRHStorage *b) +{ + nm_assert (NMS_IS_IFCFG_RH_STORAGE (a)); + nm_assert (NMS_IS_IFCFG_RH_STORAGE (b)); + nm_assert (a != b); + + /* newer files are more important. */ + NM_CMP_FIELD (a, b, stat_mtime.tv_sec); + NM_CMP_FIELD (a, b, stat_mtime.tv_nsec); + + NM_CMP_DIRECT_STRCMP (nms_ifcfg_rh_storage_get_filename (a), nms_ifcfg_rh_storage_get_filename (b)); + + return 0; +} + +/*****************************************************************************/ + +static void +nms_ifcfg_rh_storage_init (NMSIfcfgRHStorage *self) +{ +} + +static NMSIfcfgRHStorage * +_storage_new (NMSIfcfgRHPlugin *plugin, + const char *uuid, + const char *filename) +{ + nm_assert (NMS_IS_IFCFG_RH_PLUGIN (plugin)); + nm_assert (!uuid || nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + + return g_object_new (NMS_TYPE_IFCFG_RH_STORAGE, + NM_SETTINGS_STORAGE_PLUGIN, plugin, + NM_SETTINGS_STORAGE_UUID, uuid, + NM_SETTINGS_STORAGE_FILENAME, filename, + NULL); +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_connection (NMSIfcfgRHPlugin *plugin, + const char *filename, + NMConnection *connection_take, + const struct timespec *mtime) +{ + NMSIfcfgRHStorage *self; + + nm_assert (NM_IS_CONNECTION (connection_take)); + nm_assert (_nm_connection_verify (connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS); + nmtst_connection_assert_unchanging (connection_take); + + self = _storage_new (plugin, + nm_connection_get_uuid (connection_take), + filename); + self->connection = connection_take; + if (mtime) + self->stat_mtime = *mtime; + return self; +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_unhandled (NMSIfcfgRHPlugin *plugin, + const char *filename, + const char *unmanaged_spec, + const char *unrecognized_spec) +{ + NMSIfcfgRHStorage *self; + + nm_assert (unmanaged_spec || unrecognized_spec); + + self = _storage_new (plugin, + NULL, + filename); + self->unmanaged_spec = g_strdup (unmanaged_spec); + self->unrecognized_spec = g_strdup (unrecognized_spec); + return self; +} + +static void +_storage_clear (NMSIfcfgRHStorage *self) +{ + c_list_unlink (&self->parent._storage_lst); + c_list_unlink (&self->parent._storage_by_uuid_lst); + nm_clear_g_free (&self->unmanaged_spec); + nm_clear_g_free (&self->unrecognized_spec); + g_clear_object (&self->connection); +} + +static void +dispose (GObject *object) +{ + NMSIfcfgRHStorage *self = NMS_IFCFG_RH_STORAGE (object); + + _storage_clear (self); + + G_OBJECT_CLASS (nms_ifcfg_rh_storage_parent_class)->dispose (object); +} + +void +nms_ifcfg_rh_storage_destroy (NMSIfcfgRHStorage *self) +{ + _storage_clear (self); + g_object_unref (self); +} + +static void +nms_ifcfg_rh_storage_class_init (NMSIfcfgRHStorageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingsStorageClass *storage_class = NM_SETTINGS_STORAGE_CLASS (klass); + + object_class->dispose = dispose; + + storage_class->cmp_fcn = (int (*) (NMSettingsStorage *, NMSettingsStorage *)) cmp_fcn; +} diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h new file mode 100644 index 00000000..e1165f50 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h @@ -0,0 +1,93 @@ +/* NetworkManager + * + * 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) 2019 Red Hat, Inc. + */ + +#ifndef __NMS_IFCFG_RH_STORAGE_H__ +#define __NMS_IFCFG_RH_STORAGE_H__ + +#include "c-list/src/c-list.h" +#include "settings/nm-settings-storage.h" + +/*****************************************************************************/ + +#define NMS_TYPE_IFCFG_RH_STORAGE (nms_ifcfg_rh_storage_get_type ()) +#define NMS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorage)) +#define NMS_IFCFG_RH_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) +#define NMS_IS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IS_IFCFG_RH_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IFCFG_RH_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) + +typedef struct { + NMSettingsStorage parent; + + NMConnection *connection; + + char *unmanaged_spec; + char *unrecognized_spec; + + /* The timestamp (stat's mtime) of the file. Newer files have + * higher priority. */ + struct timespec stat_mtime; + + bool dirty:1; + +} NMSIfcfgRHStorage; + +typedef struct _NMSIfcfgRHStorageClass NMSIfcfgRHStorageClass; + +GType nms_ifcfg_rh_storage_get_type (void); + +struct _NMSIfcfgRHPlugin; + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_connection (struct _NMSIfcfgRHPlugin *plugin, + const char *filename, + NMConnection *connection_take, + const struct timespec *mtime); + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_unhandled (struct _NMSIfcfgRHPlugin *plugin, + const char *filename, + const char *unmanaged_spec, + const char *unrecognized_spec); + +void nms_ifcfg_rh_storage_destroy (NMSIfcfgRHStorage *self); + +/*****************************************************************************/ + +gboolean nms_ifcfg_rh_storage_equal_type (const NMSIfcfgRHStorage *self_a, + const NMSIfcfgRHStorage *self_b); + +void nms_ifcfg_rh_storage_copy_content (NMSIfcfgRHStorage *dst, + const NMSIfcfgRHStorage *src); + +NMConnection *nms_ifcfg_rh_storage_steal_connection (NMSIfcfgRHStorage *self); + +/*****************************************************************************/ + +static inline const char * +nms_ifcfg_rh_storage_get_uuid_opt (const NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_uuid_opt ((const NMSettingsStorage *) self); +} + +static inline const char * +nms_ifcfg_rh_storage_get_filename (const NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_filename ((const NMSettingsStorage *) self); +} + +#endif /* __NMS_IFCFG_RH_STORAGE_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index 22c9061b..cb1fc23a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -29,6 +28,32 @@ #include "nms-ifcfg-rh-common.h" +/*****************************************************************************/ + +gboolean +nms_ifcfg_rh_util_parse_unhandled_spec (const char *unhandled_spec, + const char **out_unmanaged_spec, + const char **out_unrecognized_spec) +{ + if (unhandled_spec) { + if (NM_STR_HAS_PREFIX (unhandled_spec, "unmanaged:")) { + NM_SET_OUT (out_unmanaged_spec, &unhandled_spec[NM_STRLEN ("unmanaged:")]); + NM_SET_OUT (out_unrecognized_spec, NULL); + return TRUE; + } + if (NM_STR_HAS_PREFIX (unhandled_spec, "unrecognized:")) { + NM_SET_OUT (out_unmanaged_spec, NULL); + NM_SET_OUT (out_unrecognized_spec, &unhandled_spec[NM_STRLEN ("unrecognized:")]); + return TRUE; + } + } + NM_SET_OUT (out_unmanaged_spec, NULL); + NM_SET_OUT (out_unrecognized_spec, NULL); + return FALSE; +} + +/*****************************************************************************/ + /* * Check ';[a-fA-F0-9]{8}' file suffix used for temporary files by rpm when * installing packages. @@ -317,36 +342,41 @@ utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg) char * utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg) { - gs_free char *base = NULL; - char *ptr, *ifcfg = NULL; + const char *base; g_return_val_if_fail (path != NULL, NULL); if (utils_should_ignore_file (path, only_ifcfg)) return NULL; - base = g_path_get_basename (path); + base = strrchr (path, '/'); + if (!base) + base = path; + else + base += 1; - if (strncmp (base, IFCFG_TAG, NM_STRLEN (IFCFG_TAG)) == 0) { + if (NM_STR_HAS_PREFIX (base, IFCFG_TAG)) { if (base[NM_STRLEN (IFCFG_TAG)] == '\0') return NULL; if (utils_is_ifcfg_alias_file (base, NULL)) { + gs_free char *ifcfg = NULL; + char *ptr; + ifcfg = g_strdup (path); ptr = strrchr (ifcfg, ':'); - if (ptr && ptr > ifcfg) { + if ( ptr + && ptr > ifcfg + && !strchr (ptr, '/')) { *ptr = '\0'; if (g_file_test (ifcfg, G_FILE_TEST_EXISTS)) { /* the file has a colon, so it is probably an alias. * To be ~more~ certain that this is an alias file, * check whether a corresponding base file exists. */ - if (only_ifcfg) { - g_free (ifcfg); + if (only_ifcfg) return NULL; - } - return ifcfg; + return g_steal_pointer (&ifcfg); } } - g_free (ifcfg); } return g_strdup (path); } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index c7729df5..20d6f72d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -26,6 +25,10 @@ #include "shvar.h" +gboolean nms_ifcfg_rh_util_parse_unhandled_spec (const char *unhandled_spec, + const char **out_unmanaged_spec, + const char **out_unrecognized_spec); + #define NM_IFCFG_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory") #define NM_IFCFG_CONNECTION_LOG_FMT "%s (%s,\"%s\")" #define NM_IFCFG_CONNECTION_LOG_ARG(con) NM_IFCFG_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 80b1bffe..38dc5c8d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -177,30 +176,18 @@ typedef struct { } Setting8021xSchemeVtable; static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = { - [NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], - .ifcfg_rh_key = "IEEE_8021X_CA_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT], - .ifcfg_rh_key = "IEEE_8021X_INNER_CA_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], - .ifcfg_rh_key = "IEEE_8021X_CLIENT_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT], - .ifcfg_rh_key = "IEEE_8021X_INNER_CLIENT_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY], - .ifcfg_rh_key = "IEEE_8021X_PRIVATE_KEY", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY], - .ifcfg_rh_key = "IEEE_8021X_INNER_PRIVATE_KEY", - }, +#define _D(_scheme_type, _ifcfg_rh_key) \ + [(_scheme_type)] = { \ + .vtable = &nm_setting_8021x_scheme_vtable[(_scheme_type)], \ + .ifcfg_rh_key = ""_ifcfg_rh_key"", \ + } + _D (NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, "IEEE_8021X_CA_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, "IEEE_8021X_INNER_CA_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, "IEEE_8021X_CLIENT_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, "IEEE_8021X_INNER_CLIENT_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, "IEEE_8021X_PRIVATE_KEY"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, "IEEE_8021X_INNER_PRIVATE_KEY"), +#undef _D }; static gboolean @@ -209,6 +196,7 @@ write_object (NMSetting8021x *s_8021x, GHashTable *secrets, GHashTable *blobs, const Setting8021xSchemeVtable *objtype, + gboolean force_write, GError **error) { NMSetting8021xCKScheme scheme; @@ -216,7 +204,8 @@ write_object (NMSetting8021x *s_8021x, GBytes *blob = NULL; const char *password = NULL; NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; - char *secret_name, *secret_flags; + char secret_name[100]; + char secret_flags[sizeof (secret_name) + NM_STRLEN ("_FLAGS")]; const char *extension; char *standard_file; @@ -243,13 +232,11 @@ write_object (NMSetting8021x *s_8021x, } /* Set the password for certificate/private key. */ - secret_name = g_strdup_printf ("%s_PASSWORD", objtype->ifcfg_rh_key); - secret_flags = g_strdup_printf ("%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key); + nm_sprintf_buf (secret_name, "%s_PASSWORD", objtype->ifcfg_rh_key); + nm_sprintf_buf (secret_flags, "%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key); password = (*(objtype->vtable->passwd_func))(s_8021x); flags = (*(objtype->vtable->pwflag_func))(s_8021x); set_secret (ifcfg, secrets, secret_name, password, secret_flags, flags); - g_free (secret_name); - g_free (secret_flags); if (!objtype->vtable->format_func) extension = "der"; @@ -287,7 +274,7 @@ write_object (NMSetting8021x *s_8021x, */ standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension); g_hash_table_replace (blobs, standard_file, NULL); - svUnsetValue (ifcfg, objtype->ifcfg_rh_key); + svSetValue (ifcfg, objtype->ifcfg_rh_key, force_write ? "" : NULL); return TRUE; } @@ -338,43 +325,41 @@ write_8021x_certs (NMSetting8021x *s_8021x, shvarFile *ifcfg, GError **error) { - const Setting8021xSchemeVtable *otype = NULL; + const Setting8021xSchemeVtable *pk_otype = NULL; + gs_free char *value_to_free = NULL; /* CA certificate */ if (!write_object (s_8021x, ifcfg, secrets, blobs, phase2 ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], + FALSE, error)) return FALSE; /* Private key */ if (phase2) - otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]; + pk_otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]; else - otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; + pk_otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; /* Save the private key */ - if (!write_object (s_8021x, ifcfg, secrets, blobs, otype, error)) + if (!write_object (s_8021x, ifcfg, secrets, blobs, pk_otype, FALSE, error)) return FALSE; - /* Client certificate */ - if (otype->vtable->format_func (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { - /* Don't need a client certificate with PKCS#12 since the file is both - * the client certificate and the private key in one file. - */ - svSetValueStr (ifcfg, - phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", - NULL); - } else { - /* Save the client certificate */ - if (!write_object (s_8021x, ifcfg, secrets, blobs, - phase2 - ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] - : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], - error)) - return FALSE; - } + /* Save the client certificate. + * If there is a private key, always write a property for the + * client certificate even if it is empty, so that the reader + * doesn't have to read the private key file to determine if it + * is a PKCS #12 one which serves also as client certificate. + */ + if (!write_object (s_8021x, ifcfg, secrets, blobs, + phase2 + ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], + !!svGetValue (ifcfg, pk_otype->ifcfg_rh_key, &value_to_free), + error)) + return FALSE; return TRUE; } @@ -1118,14 +1103,22 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) nm_setting_wired_get_s390_option (s_wired, i, &s390_key, &s390_val); /* portname is handled separately */ - if (!strcmp (s390_key, "portname") || !strcmp (s390_key, "ctcprot")) + if (NM_IN_STRSET (s390_key, "portname", "ctcprot")) continue; + if (strchr (s390_key, '=')) { + /* this key cannot be expressed. But after all, it's not valid anyway + * and the connection shouldn't even verify. */ + continue; + } + if (!tmp) tmp = g_string_sized_new (30); else g_string_append_c (tmp, ' '); - g_string_append_printf (tmp, "%s=%s", s390_key, s390_val); + nm_utils_escaped_tokens_escape_gstr (s390_key, NM_ASCII_SPACES, tmp); + g_string_append_c (tmp, '='); + nm_utils_escaped_tokens_escape_gstr (s390_val, NM_ASCII_SPACES, tmp); } if (tmp) svSetValueStr (ifcfg, "OPTIONS", tmp->str); @@ -1302,7 +1295,7 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) has_wired = TRUE; device_mac = nm_setting_wired_get_mac_address (s_wired); - svSetValueStr (ifcfg, "HWADDR", device_mac); + svSetValue (ifcfg, "HWADDR", device_mac ?: ""); cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); svSetValueStr (ifcfg, "MACADDR", cloned_mac); @@ -1856,6 +1849,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) GString *str; const char *master, *master_iface = NULL, *type; int vint; + gint32 vint32; NMSettingConnectionMdns mdns; NMSettingConnectionLlmnr llmnr; guint32 vuint32; @@ -2022,6 +2016,19 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) vint = nm_setting_connection_get_auth_retries (s_con); svSetValueInt64_cond (ifcfg, "AUTH_RETRIES", vint >= 0, vint); + vint32 = nm_setting_connection_get_wait_device_timeout (s_con); + if (vint32 == -1) + svUnsetValue (ifcfg, "DEVTIMEOUT"); + else if ((vint32 % 1000) == 0) + svSetValueInt64 (ifcfg, "DEVTIMEOUT", vint32 / 1000); + else { + char b[100]; + + svSetValueStr (ifcfg, + "DEVTIMEOUT", + nm_sprintf_buf (b, "%.3f", ((double) vint) / 1000.0)); + } + mdns = nm_setting_connection_get_mdns (s_con); if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) { svSetValueEnum (ifcfg, "MDNS", nm_setting_connection_mdns_get_type (), @@ -2797,10 +2804,17 @@ write_ip6_setting (NMConnection *connection, value = nm_setting_ip_config_get_method (s_ip6); g_assert (value); + svUnsetValue (ifcfg, "IPV6_DISABLED"); if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { svSetValueStr (ifcfg, "IPV6INIT", "no"); svUnsetValue (ifcfg, "DHCPV6C"); return TRUE; + } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { + svSetValueStr (ifcfg, "IPV6_DISABLED", "yes"); + svSetValueStr (ifcfg, "IPV6INIT", "no"); + svUnsetValue (ifcfg, "DHCPV6C"); + svUnsetValue (ifcfg, "IPV6_AUTOCONF"); + return TRUE; } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { svSetValueStr (ifcfg, "IPV6INIT", "yes"); svSetValueStr (ifcfg, "IPV6_AUTOCONF", "yes"); @@ -3024,6 +3038,8 @@ static gboolean do_write_construct (NMConnection *connection, const char *ifcfg_dir, const char *filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, shvarFile **out_ifcfg, GHashTable **out_blobs, GHashTable **out_secrets, @@ -3067,30 +3083,31 @@ do_write_construct (NMConnection *connection, ifcfg_name = g_strdup (filename); } else if (ifcfg_dir) { - char *escaped; + gs_free char *escaped = NULL; + int i_path; escaped = escape_id (nm_setting_connection_get_id (s_con)); - ifcfg_name = g_strdup_printf ("%s/ifcfg-%s", ifcfg_dir, escaped); - /* If a file with this path already exists then we need another name. - * Multiple connections can have the same ID (ie if two connections with - * the same ID are visible to different users) but of course can't have - * the same path. - */ - if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS)) { - guint32 idx = 0; - - nm_clear_g_free (&ifcfg_name); - while (idx++ < 500) { - ifcfg_name = g_strdup_printf ("%s/ifcfg-%s-%u", ifcfg_dir, escaped, idx); - if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS) == FALSE) - break; - nm_clear_g_free (&ifcfg_name); - } + for (i_path = 0; i_path < 10000; i_path++) { + gs_free char *path_candidate = NULL; + + if (i_path == 0) + path_candidate = g_strdup_printf ("%s/ifcfg-%s", ifcfg_dir, escaped); + else + path_candidate = g_strdup_printf ("%s/ifcfg-%s-%d", ifcfg_dir, escaped, i_path); + + if ( allow_filename_cb + && !allow_filename_cb (path_candidate, allow_filename_user_data)) + continue; + + if (g_file_test (path_candidate, G_FILE_TEST_EXISTS)) + continue; + + ifcfg_name = g_steal_pointer (&path_candidate); + break; } - g_free (escaped); - if (ifcfg_name == NULL) { + if (!ifcfg_name) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Failed to find usable ifcfg file name"); return FALSE; @@ -3329,54 +3346,12 @@ do_write_to_disk (NMConnection *connection, return TRUE; } -static gboolean -do_write_reread (NMConnection *connection, - const char *ifcfg_name, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error) -{ - gs_unref_object NMConnection *reread = NULL; - gs_free_error GError *local = NULL; - gs_free char *unhandled = NULL; - gboolean reread_same = FALSE; - - nm_assert (!out_reread || !*out_reread); - - reread = connection_from_file (ifcfg_name, &unhandled, &local, NULL); - - if (!reread) { - g_propagate_error (error, local); - local = NULL; - return FALSE; - } - if (unhandled) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "connection is unhandled"); - return FALSE; - } - if (out_reread_same) { - if (nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT)) - reread_same = TRUE; - - nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); - nm_assert (reread_same == ({ - gs_unref_hashtable GHashTable *_settings = NULL; - - ( nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings) - && !_settings); - })); - } - - NM_SET_OUT (out_reread, g_steal_pointer (&reread)); - NM_SET_OUT (out_reread_same, reread_same); - return TRUE; -} - gboolean nms_ifcfg_rh_writer_write_connection (NMConnection *connection, const char *ifcfg_dir, const char *filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_filename, NMConnection **out_reread, gboolean *out_reread_same, @@ -3389,13 +3364,14 @@ nms_ifcfg_rh_writer_write_connection (NMConnection *connection, nm_auto_free_gstring GString *route6_content = NULL; gs_unref_hashtable GHashTable *secrets = NULL; gs_unref_hashtable GHashTable *blobs = NULL; - GError *local = NULL; nm_assert (!out_reread || !*out_reread); if (!do_write_construct (connection, ifcfg_dir, filename, + allow_filename_cb, + allow_filename_user_data, &ifcfg, &blobs, &secrets, @@ -3424,28 +3400,46 @@ nms_ifcfg_rh_writer_write_connection (NMConnection *connection, /* Note that we just wrote the connection to disk, and re-read it from there. * That is racy if somebody else modifies the connection. + * That race is why we must not tread a failure to re-read the profile + * as an error. * - * A better solution might be, to re-read the connection only based on the - * in-memory representation of what we collected above. But the reader + * FIXME: a much better solution might be, to re-read the connection only based + * on the in-memory representation of what we collected above. But the reader * does not yet allow to inject the configuration. */ - if (out_reread || out_reread_same) { - if (!do_write_reread (connection, - svFileGetName (ifcfg), - out_reread, - out_reread_same, - &local)) { + if ( out_reread + || out_reread_same) { + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = FALSE; + gs_free_error GError *local = NULL; + gs_free char *unhandled = NULL; + + reread = connection_from_file (svFileGetName (ifcfg), + &unhandled, + &local, + NULL); + nm_assert ((NM_IS_CONNECTION (reread) && !local) || (!reread && local)); + + if (!reread) { _LOGW ("write: failure to re-read connection \"%s\": %s", svFileGetName (ifcfg), local->message); - g_clear_error (&local); + } else if (unhandled) { + g_clear_object (&reread); + _LOGW ("write: failure to re-read connection \"%s\": %s", + svFileGetName (ifcfg), "connection is unhandled"); } else { - if ( out_reread_same - && !*out_reread_same) { - _LOGD ("write: connection %s (%s) was modified by persisting it to \"%s\" ", - nm_connection_get_id (connection), - nm_connection_get_uuid (connection), - svFileGetName (ifcfg)); + if (out_reread_same) { + reread_same = nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT); + if (!reread_same) { + _LOGD ("write: connection %s (%s) was modified by persisting it to \"%s\" ", + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + svFileGetName (ifcfg)); + } } } + + NM_SET_OUT (out_reread, g_steal_pointer (&reread)); + NM_SET_OUT (out_reread_same, reread_same); } /* Only return the filename if this was a newly written ifcfg */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h index d7a255a9..0902daee 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -23,12 +22,18 @@ #include "nm-connection.h" + +typedef gboolean (*NMSIfcfgRHWriterAllowFilenameCb) (const char *check_filename, + gpointer allow_filename_user_data); + gboolean nms_ifcfg_rh_writer_can_write_connection (NMConnection *connection, GError **error); gboolean nms_ifcfg_rh_writer_write_connection (NMConnection *connection, const char *ifcfg_dir, const char *filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_filename, NMConnection **out_reread, gboolean *out_reread_same, diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index b399a17f..94e31aac 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * shvar.c * diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index b38a8557..67fb5404 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * shvar.h * diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected index 5d81dfef..36df7712 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected @@ -1,6 +1,7 @@ BONDING_OPTS="downdelay=5 miimon=100 mode=balance-rr updelay=10" TYPE=Bond BONDING_MASTER=yes +HWADDR= PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=none diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected index 339f8107..9c2a1ff0 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected @@ -5,6 +5,7 @@ VLAN_ID=444 REORDER_HDR=yes GVRP=no MVRP=no +HWADDR= PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=dhcp diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected index 793713ea..44eb777c 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected @@ -8,6 +8,7 @@ VLAN_FLAGS=LOOSE_BINDING MVRP=no VLAN_INGRESS_PRIORITY_MAP=0:1,2:5 VLAN_EGRESS_PRIORITY_MAP=3:1,12:3,14:7 +HWADDR= PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=none diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected index 87980dc4..b01372af 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected @@ -4,6 +4,7 @@ VLAN_ID=142 REORDER_HDR=yes GVRP=no MVRP=no +HWADDR= NAME=team-slave-enp31s0f1-142 UUID=74f435bb-ede4-415a-9d48-f580b60eba04 DEVICE=enp31s0f1-142 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected new file mode 100644 index 00000000..cae51b89 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected @@ -0,0 +1,11 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6_DISABLED=yes +IPV6INIT=no +NAME="Test Write Wired Disabled IP6" +UUID=${UUID} +ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert new file mode 100644 index 00000000..24397473 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert @@ -0,0 +1,13 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TLS +IEEE_8021X_IDENTITY="David Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_PRIVATE_KEY=test_client.p12 +IEEE_8021X_PRIVATE_KEY_PASSWORD="test1" diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 new file mode 100644 index 00000000..edc2af75 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 Binary files differdiff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 49ab04d4..45e90b91 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -239,7 +238,7 @@ _assert_expected_content (NMConnection *connection, const char *filename, const g_assert (_ifcfg_dir && _ifcfg_dir[0]); \ g_assert (_filename && _filename[0]); \ \ - _success = nms_ifcfg_rh_writer_write_connection (_connection, _ifcfg_dir, _filename, NULL, _out_reread, _out_reread_same, &_error); \ + _success = nms_ifcfg_rh_writer_write_connection (_connection, _ifcfg_dir, _filename, NULL, NULL, NULL, _out_reread, _out_reread_same, &_error); \ nmtst_assert_success (_success, _error); \ _assert_expected_content (_connection, _filename, _expected); \ } G_STMT_END @@ -310,7 +309,7 @@ _writer_new_connection_reread (NMConnection *connection, char *filename = NULL; gs_unref_object NMConnection *con_verified = NULL; gs_unref_object NMConnection *reread_copy = NULL; - NMConnection **reread = out_reread ?: ((nmtst_get_rand_int () % 2) ? &reread_copy : NULL); + NMConnection **reread = out_reread ?: ((nmtst_get_rand_uint32 () % 2) ? &reread_copy : NULL); g_assert (NM_IS_CONNECTION (connection)); g_assert (ifcfg_dir); @@ -320,6 +319,8 @@ _writer_new_connection_reread (NMConnection *connection, success = nms_ifcfg_rh_writer_write_connection (con_verified, ifcfg_dir, NULL, + NULL, + NULL, &filename, reread, out_reread_same, @@ -395,6 +396,8 @@ _writer_new_connection_fail (NMConnection *connection, success = nms_ifcfg_rh_writer_write_connection (connection_normalized, ifcfg_dir, NULL, + NULL, + NULL, &filename, &reread, NULL, @@ -718,7 +721,7 @@ test_read_unmanaged_unrecognized (void) connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-nm-controlled-unrecognized", NULL, NULL, &unhandled_spec); - g_assert_cmpstr (unhandled_spec, ==, "unmanaged:interface-name:ipoac0"); + g_assert_cmpstr (unhandled_spec, ==, "unmanaged:interface-name:=ipoac0"); /* ===== CONNECTION SETTING ===== */ s_con = nm_connection_get_setting_connection (connection); @@ -1974,6 +1977,27 @@ test_read_802_1x_ttls_eapgtc (void) } static void +test_read_802_1x_tls_p12_no_client_cert (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSetting8021x *s_8021x; + const char *path; + + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-8021x-tls-p12-no-client-cert", + NULL, TYPE_ETHERNET, NULL); + + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x); + + g_assert_cmpint (nm_setting_802_1x_get_private_key_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH); + path = nm_setting_802_1x_get_private_key_path (s_8021x); + g_assert (path); + + g_assert_cmpint (nm_setting_802_1x_get_client_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH); + g_assert_cmpstr (path, ==, nm_setting_802_1x_get_client_cert_path (s_8021x)); +} + +static void test_read_write_802_1x_password_raw (void) { nmtst_auto_unlinkfile char *testfile = NULL; @@ -4768,6 +4792,48 @@ test_write_wired_static_ip6_only (void) nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); } +static void +test_write_ip6_disabled (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + + connection = nmtst_create_minimal_connection ("Test Write Wired Disabled IP6", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED, + NULL); + + nmtst_assert_connection_verifies (connection); + + _writer_new_connec_exp (connection, + TEST_SCRATCH_DIR_TMP, + TEST_IFCFG_DIR"/ifcfg-test-ip6-disabled.cexpected", + &testfile); + + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + /* Test writing an IPv6 config with varying gateway address. * For missing gateway (::), we expect no IPV6_DEFAULTGW to be written * to ifcfg-rh. @@ -8938,25 +9004,16 @@ static void test_read_team_master_invalid (gconstpointer user_data) { const char *const PATH_NAME = user_data; - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingTeam *s_team; - - NMTST_EXPECT_NM_WARN ("*ignoring invalid team configuration*"); - connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL); - g_test_assert_expected_messages (); - - g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "team0"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_TEAM_SETTING_NAME); + gs_free_error GError *error = NULL; + gs_unref_object NMConnection *connection = NULL; - s_team = nm_connection_get_setting_team (connection); - g_assert (s_team); - g_assert (nm_setting_team_get_config (s_team) == NULL); + if (WITH_JSON_VALIDATION) { + _connection_from_file_fail (PATH_NAME, NULL, TYPE_ETHERNET, &error); - g_object_unref (connection); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + g_assert (strstr (error->message, _("invalid json"))); + } else + connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL); } static void @@ -9237,19 +9294,19 @@ test_team_reread_slave (void) nmtst_assert_connection_equals (connection_1, FALSE, connection_2, FALSE); - _writer_new_connection_reread ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, + _writer_new_connection_reread ((nmtst_get_rand_uint32 () % 2) ? connection_1 : connection_2, TEST_SCRATCH_DIR, &testfile, TEST_IFCFG_DIR"/ifcfg-team-slave-enp31s0f1-142.cexpected", &reread, &reread_same); - _assert_reread_same ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, reread); + _assert_reread_same ((nmtst_get_rand_uint32 () % 2) ? connection_1 : connection_2, reread); g_assert (reread_same); g_clear_object (&reread); reread = _connection_from_file (testfile, NULL, TYPE_VLAN, NULL); - nmtst_assert_connection_equals ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, FALSE, + nmtst_assert_connection_equals ((nmtst_get_rand_uint32 () % 2) ? connection_1 : connection_2, FALSE, reread, FALSE); } @@ -9414,7 +9471,7 @@ do_svUnescape_combine_ansi (GString *str_val, GString *str_exp, const UnescapeTe g_string_append (str_val, "$'"); if (idx < 0) { for (i = -idx; i > 0; i--) { - j = nmtst_get_rand_int () % data_len; + j = nmtst_get_rand_uint32 () % data_len; if (!data_ansi[j].can_concat) { i++; continue; @@ -9613,7 +9670,7 @@ test_svUnescape (void) /* different values can be just concatenated... */ for (i = 0; i < 200; i++) { - gsize num_concat = (nmtst_get_rand_int () % 5) + 2; + gsize num_concat = (nmtst_get_rand_uint32 () % 5) + 2; g_string_set_size (str_val, 0); g_string_set_size (str_exp, 0); @@ -9621,12 +9678,12 @@ test_svUnescape (void) while (num_concat > 0) { gsize idx; - if ((nmtst_get_rand_int () % 3 == 0)) { - do_svUnescape_combine_ansi (str_val2, str_exp2, data_ansi, G_N_ELEMENTS (data_ansi), -((int) ((nmtst_get_rand_int () % 5) + 1))); + if ((nmtst_get_rand_uint32 () % 3 == 0)) { + do_svUnescape_combine_ansi (str_val2, str_exp2, data_ansi, G_N_ELEMENTS (data_ansi), -((int) ((nmtst_get_rand_uint32 () % 5) + 1))); continue; } - idx = nmtst_get_rand_int () % G_N_ELEMENTS (data_full); + idx = nmtst_get_rand_uint32 () % G_N_ELEMENTS (data_full); if (!data_full[idx].can_concat) continue; g_string_append (str_val, data_full[idx].val); @@ -9634,7 +9691,7 @@ test_svUnescape (void) num_concat--; } - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: g_string_append (str_val, " "); break; @@ -9642,7 +9699,7 @@ test_svUnescape (void) g_string_append (str_val, " "); break; } - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: g_string_append (str_val, " #"); break; @@ -10177,6 +10234,7 @@ int main (int argc, char **argv) g_test_add_data_func (TPATH "static-ip6-only-gw/::", "::", test_write_wired_static_ip6_only_gw); g_test_add_data_func (TPATH "static-ip6-only-gw/2001:db8:8:4::2", "2001:db8:8:4::2", test_write_wired_static_ip6_only_gw); g_test_add_data_func (TPATH "static-ip6-only-gw/::ffff:255.255.255.255", "::ffff:255.255.255.255", test_write_wired_static_ip6_only_gw); + g_test_add_func (TPATH "ip6/disabled", test_write_ip6_disabled); g_test_add_func (TPATH "read-dns-options", test_read_dns_options); g_test_add_func (TPATH "clear-master", test_clear_master); @@ -10223,6 +10281,8 @@ int main (int argc, char **argv) g_test_add_func (TPATH "802-1x/subj-matches", test_read_write_802_1X_subj_matches); g_test_add_func (TPATH "802-1x/ttls-eapgtc", test_read_802_1x_ttls_eapgtc); g_test_add_func (TPATH "802-1x/password_raw", test_read_write_802_1x_password_raw); + g_test_add_func (TPATH "802-1x/tls-p12-no-client-cert", test_read_802_1x_tls_p12_no_client_cert); + g_test_add_data_func (TPATH "wired/read/aliases/good/0", GINT_TO_POINTER (0), test_read_wired_aliases_good); g_test_add_data_func (TPATH "wired/read/aliases/good/3", GINT_TO_POINTER (3), test_read_wired_aliases_good); g_test_add_func (TPATH "wired/read/aliases/bad1", test_read_wired_aliases_bad_1); diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build index 42edd438..365ae1a9 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 d06078a9..00000000 --- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 gboolean -supports_secrets (NMSettingsConnection *connection, const char *setting_name) -{ - _LOGI ("supports_secrets() for setting_name: '%s'", setting_name); - - return (strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0); -} - -/*****************************************************************************/ - -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) -{ - NMSettingsConnectionClass *connection_class = NM_SETTINGS_CONNECTION_CLASS (ifupdown_connection_class); - - connection_class->supports_secrets = supports_secrets; -} - 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 7536c23e..00000000 --- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - -/* 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-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c index 6587fc84..75f29878 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * Tom Parker <palfrey@tevp.net> diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h index f367f626..308228a4 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * Tom Parker <palfrey@tevp.net> diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c index 239f6415..41b20850 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack <asac@ubuntu.com> @@ -653,22 +651,21 @@ 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, + gboolean autoconnect, + 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); @@ -679,11 +676,10 @@ ifupdown_update_connection_from_if_block (NMConnection *connection, NM_SETTING_CONNECTION_INTERFACE_NAME, block->name, NM_SETTING_CONNECTION_ID, idstr, NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_READ_ONLY, TRUE, - NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_AUTOCONNECT, (gboolean) (!!autoconnect), 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)) @@ -693,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 6a86bf86..7569648f 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack <asac@ubuntu.com> @@ -21,16 +19,14 @@ * (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, + gboolean autoconnect, + 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 04281f00..93f1813c 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack <asac@ubuntu.com> @@ -26,23 +24,13 @@ #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-core-utils.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" @@ -52,31 +40,35 @@ /*****************************************************************************/ 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; -} SettingsPluginIfupdownPrivate; + bool ifupdown_managed:1; -struct _SettingsPluginIfupdown { + bool initialized:1; + + bool already_reloaded:1; +} NMSIfupdownPluginPrivate; + +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) - -#define SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfupdown, SETTINGS_IS_PLUGIN_IFUPDOWN) +G_DEFINE_TYPE (NMSIfupdownPlugin, nms_ifupdown_plugin, NM_TYPE_SETTINGS_PLUGIN) -/*****************************************************************************/ - -static SettingsPluginIfupdown *settings_plugin_ifupdown_get (void); -NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfupdown, settings_plugin_ifupdown_get, SETTINGS_TYPE_PLUGIN_IFUPDOWN); +#define NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfupdownPlugin, NMS_IS_IFUPDOWN_PLUGIN) /*****************************************************************************/ @@ -90,46 +82,146 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfupdown, settings_plugin_ifupdown_get /*****************************************************************************/ -/* 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 GHashTable *load_eni_ifaces (NMSIfupdownPlugin *self); + +/*****************************************************************************/ + +static void +_storage_data_destroy (StorageData *sd) +{ + nm_g_object_unref (sd->connection); + nm_g_object_unref (sd->storage); + g_slice_free (StorageData, sd); +} + +/*****************************************************************************/ + +static void +initialize (NMSIfupdownPlugin *self) { - SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); - GSList *list = NULL; + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self); + gboolean ifupdown_managed; + + nm_assert (!priv->initialized); + + priv->initialized = TRUE; + + ifupdown_managed = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, + NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED, + !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT); + _LOGI ("management mode: %s", ifupdown_managed ? "managed" : "unmanaged"); + priv->ifupdown_managed = ifupdown_managed; + + priv->eni_ifaces = load_eni_ifaces (self); +} + +static void +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self); + gs_unref_hashtable GHashTable *eni_ifaces_old = NULL; GHashTableIter iter; - void *value; + StorageData *sd; + StorageData *sd2; + const char *block_name; - if (!priv->ifupdown_managed) { - _LOGD ("get_connections: not connections due to managed=false"); - return NULL; + if (!priv->initialized) + initialize (self); + else if (!priv->already_reloaded) { + /* This is the first call to reload, but we are already initialized. + * + * This happens because during start NMSettings first queries unmanaged-specs, + * and then issues a reload call right away. + * + * On future reloads, we really want to load /e/n/i again. */ + priv->already_reloaded = TRUE; + } else { + eni_ifaces_old = priv->eni_ifaces; + priv->eni_ifaces = load_eni_ifaces (self); + + g_hash_table_iter_init (&iter, eni_ifaces_old); + while (g_hash_table_iter_next (&iter, (gpointer *) &block_name, (gpointer *) &sd)) { + if (!sd) + continue; + + sd2 = g_hash_table_lookup (priv->eni_ifaces, block_name); + if (!sd2) + continue; + + nm_assert (nm_streq (nm_settings_storage_get_uuid (sd->storage), nm_settings_storage_get_uuid (sd2->storage))); + nm_g_object_ref_set (&sd2->storage, sd->storage); + g_hash_table_iter_remove (&iter); + } } + if (!priv->ifupdown_managed) + _LOGD ("load: no connections due to managed=false"); + 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)) { + gs_unref_object NMConnection *connection = NULL; + + if (!sd) + continue; + + connection = g_steal_pointer (&sd->connection); + + if (!priv->ifupdown_managed) + continue; + + _LOGD ("load: %s (%s)", + nm_settings_storage_get_uuid (sd->storage), + nm_connection_get_id (connection)); + callback (plugin, + sd->storage, + connection, + user_data); } + if ( eni_ifaces_old + && priv->ifupdown_managed) { + g_hash_table_iter_init (&iter, eni_ifaces_old); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &sd)) { + if (!sd) + continue; + _LOGD ("unload: %s", + nm_settings_storage_get_uuid (sd->storage)); + callback (plugin, + sd->storage, + NULL, + user_data); + } + } +} + +/*****************************************************************************/ - _LOGD ("get_connections: %u connections", g_slist_length (list)); - return list; +static GSList * +_unmanaged_specs (GHashTable *eni_ifaces) +{ + gs_free const char **keys = NULL; + GSList *specs = NULL; + guint i, len; + + keys = nm_utils_strdict_get_keys (eni_ifaces, TRUE, &len); + for (i = len; i > 0; ) { + i--; + specs = g_slist_prepend (specs, g_strdup_printf (NM_MATCH_SPEC_INTERFACE_NAME_TAG"=%s", keys[i])); + } + return specs; } -/* - * 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); - GSList *specs = NULL; - GHashTableIter iter; - const char *iface; + NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self); + + if (G_UNLIKELY (!priv->initialized)) + initialize (self); if (priv->ifupdown_managed) return NULL; @@ -137,38 +229,46 @@ get_unmanaged_specs (NMSettingsPlugin *plugin) _LOGD ("unmanaged-specs: unmanaged devices count %u", g_hash_table_size (priv->eni_ifaces)); - g_hash_table_iter_init (&iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&iter, (gpointer) &iface, NULL)) - specs = g_slist_append (specs, g_strdup_printf ("interface-name:=%s", iface)); - return specs; + return _unmanaged_specs (priv->eni_ifaces); } /*****************************************************************************/ -static void -initialize (NMSettingsPlugin *plugin) +static GHashTable * +load_eni_ifaces (NMSIfupdownPlugin *self) { - SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); + gs_unref_hashtable GHashTable *eni_ifaces = NULL; gs_unref_hashtable GHashTable *auto_ifaces = NULL; nm_auto_ifparser if_parser *parser = NULL; if_block *block; - GHashTableIter con_iter; - const char *block_name; - NMIfupdownConnection *conn; + StorageData *sd; + + eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) _storage_data_destroy); parser = ifparser_parse (ENI_INTERFACES_FILE, 0); c_list_for_each_entry (block, &parser->block_lst_head, block_lst) { - - if (NM_IN_STRSET (block->type, "auto", "allow-hotplug")) { + if (NM_IN_STRSET (block->type, "auto", + "allow-hotplug")) { if (!auto_ifaces) - auto_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - g_hash_table_add (auto_ifaces, g_strdup (block->name)); - continue; + auto_ifaces = g_hash_table_new (nm_str_hash, g_str_equal); + g_hash_table_add (auto_ifaces, (char *) block->name); } + } + + c_list_for_each_entry (block, &parser->block_lst_head, block_lst) { + + if (NM_IN_STRSET (block->type, "auto", + "allow-hotplug")) + continue; 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 */ @@ -201,13 +301,13 @@ initialize (NMSettingsPlugin *plugin) 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 (eni_ifaces, block->name); + if (!sd) { _LOGD ("parse: adding bridge port \"%s\"", token); - g_hash_table_insert (priv->eni_ifaces, g_strdup (token), NULL); + g_hash_table_insert (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)); } } } @@ -219,107 +319,91 @@ initialize (NMSettingsPlugin *plugin) 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 (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); - g_hash_table_remove (priv->eni_ifaces, block->name); + nm_settings_storage_get_uuid (sd_repl->storage)); + g_hash_table_remove (eni_ifaces, block->name); + } + + connection = ifupdown_new_connection_from_if_block (block, + auto_ifaces + && g_hash_table_contains (auto_ifaces, block->name), + &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 { + + nmtst_connection_assert_unchanging (connection); + uuid = nm_connection_get_uuid (connection); + + if (!storage) + storage = nm_settings_storage_new (NM_SETTINGS_PLUGIN (self), uuid, NULL); + + sd = g_slice_new (StorageData); + *sd = (StorageData) { + .connection = g_steal_pointer (&connection), + .storage = g_steal_pointer (&storage), + }; + _LOGD ("parse: adding connection \"%s\" (%s)", block->name, uuid); } - /* 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); + g_hash_table_replace (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 (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); + g_hash_table_insert (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; + nm_clear_pointer (&auto_ifaces, g_hash_table_destroy); - if ( !conn - || !auto_ifaces - || !g_hash_table_contains (auto_ifaces, block_name)) - continue; - - /* 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); - } - - /* 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, - NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, - 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)); - } - } - } + return g_steal_pointer (&eni_ifaces); } /*****************************************************************************/ static void -settings_plugin_ifupdown_init (SettingsPluginIfupdown *self) +nms_ifupdown_plugin_init (NMSIfupdownPlugin *self) { - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); - - priv->eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } 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->initialize = initialize; - 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; } @@ -328,5 +412,5 @@ settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass) G_MODULE_EXPORT NMSettingsPlugin * nm_settings_plugin_factory (void) { - return NM_SETTINGS_PLUGIN (g_object_ref (settings_plugin_ifupdown_get ())); + 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 c2a3639c..10ea2be4 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack <asac@ubuntu.com> @@ -21,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 674cb19c..4adcf085 100644 --- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c +++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * 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 @@ -31,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, FALSE, &_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; @@ -453,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); @@ -501,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); @@ -555,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); @@ -589,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 @@ -671,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 3b362978..00000000 --- a/src/settings/plugins/keyfile/nms-keyfile-connection.c +++ /dev/null @@ -1,186 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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), - &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 0773ced0..00000000 --- a/src/settings/plugins/keyfile/nms-keyfile-connection.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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 c13cc1ff..fbe70ef4 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -16,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" @@ -26,7 +25,11 @@ #include <sys/stat.h> #include <unistd.h> #include <sys/types.h> -#include <glib/gstdio.h> +#include <sys/time.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" @@ -36,22 +39,42 @@ #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 "settings/nm-settings-utils.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 */ - - gboolean initialized; - GFileMonitor *monitor; - gulong monitor_id; 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; + + NMSettUtilStorages storages; + } NMSKeyfilePluginPrivate; struct _NMSKeyfilePlugin { @@ -65,7 +88,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) /*****************************************************************************/ @@ -79,494 +102,1112 @@ G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN) /*****************************************************************************/ -static void -connection_removed_cb (NMSettingsConnection *sett_conn, NMSKeyfilePlugin *self) +static const char * +_extra_flags_to_string (char *str, gsize str_len, gboolean is_nm_generated, gboolean is_volatile) { - g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections, - nm_settings_connection_get_uuid (sett_conn)); + const char *str0 = str; + + if ( !is_nm_generated + && !is_volatile) + nm_utils_strbuf_append_str (&str, &str_len, ""); + else { + nm_utils_strbuf_append_str (&str, &str_len, " ("); + if (is_nm_generated) { + nm_utils_strbuf_append_str (&str, &str_len, "nm-generated"); + if (is_volatile) + nm_utils_strbuf_append_c (&str, &str_len, ','); + } + if (is_volatile) + nm_utils_strbuf_append_str (&str, &str_len, "volatile"); + nm_utils_strbuf_append_c (&str, &str_len, ')'); + } + + return str0; } -/* Monitoring */ +static gboolean +_ignore_filename (NMSKeyfileStorageType storage_type, + const char *filename) +{ + /* 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)); +} -static void -remove_connection (NMSKeyfilePlugin *self, NMSKeyfileConnection *connection) +static const char * +_get_plugin_dir (NMSKeyfilePluginPrivate *priv) { - gboolean removed; + /* 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; +} - g_return_if_fail (connection != NULL); +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, + gboolean *out_is_nmmeta_file, + gboolean *out_failed_due_to_invalid_filename) +{ + NMSKeyfileStorageType storage_type; + const char *filename = NULL; + const char *dirname = NULL; + guint i; + gboolean is_nmmeta_file = FALSE; + + NM_SET_OUT (out_failed_due_to_invalid_filename, FALSE); + + if (full_filename[0] != '/') + return FALSE; + + 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 (i); + dirname = dirname_libs[i]; + break; + } + } + if (!dirname) + return FALSE; + } + + if (_ignore_filename (storage_type, filename)) { - _LOGI ("removed " NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection)); + /* we accept nmmeta files, but only in /etc and /run directories. */ - /* 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 ( !NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC) + || !nms_keyfile_nmmeta_check_filename (filename, NULL)) { + NM_SET_OUT (out_failed_due_to_invalid_filename, TRUE); + return FALSE; + } + + is_nmmeta_file = TRUE; + } - 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); + NM_SET_OUT (out_is_nmmeta_file, is_nmmeta_file); + 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, + char **out_shadowed_storage, + NMTernary *out_shadowed_owned, + GError **error) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - GHashTableIter iter; - NMSettingsConnection *candidate = NULL; + NMConnection *connection; - g_return_val_if_fail (path != NULL, NULL); + nm_assert (full_filename && full_filename[0] == '/'); - 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); - } - return NULL; + connection = nms_keyfile_reader_from_file (full_filename, + plugin_dir, + out_stat, + out_is_nm_generated, + out_is_volatile, + out_shadowed_storage, + out_shadowed_owned, + 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; } -/* 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) +/*****************************************************************************/ + +static void +_nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */, + gpointer storage /* NMSKeyfileStorage */, + gboolean tracked) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - NMSKeyfileConnection *connection_new; - NMSKeyfileConnection *connection_by_uuid; - GError *local = NULL; +#if NM_MORE_ASSERTS + NMSettUtilStorageByUuidHead *sbuh; const char *uuid; - g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL); - g_return_val_if_fail (full_path || source, NULL); + 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)); - if (full_path) - _LOGD ("loading from file \"%s\"...", full_path); + nm_assert (({ + const char *f = nms_keyfile_storage_get_filename (storage); + f && f[0] == '/'; + })); - 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"); - return FALSE; - } + uuid = nms_keyfile_storage_get_uuid (storage); - 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; + nm_assert (nm_utils_is_uuid (uuid)); + + nm_assert ( ((NMSKeyfileStorage *) storage)->is_meta_data + || !(((NMSKeyfileStorage *) storage)->u.conn_data.connection) + || ( NM_IS_CONNECTION ((((NMSKeyfileStorage *) storage)->u.conn_data.connection)) + && nm_streq0 (uuid, nm_connection_get_uuid ((((NMSKeyfileStorage *) storage)->u.conn_data.connection))))); + + nm_assert ( !tracked + || !plugin + || c_list_contains (&NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages._storage_lst_head, + &NMS_KEYFILE_STORAGE (storage)->parent._storage_lst)); + + nm_assert ( !tracked + || !plugin + || storage == g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx_by_filename, + nms_keyfile_storage_get_filename (storage))); + + if ( tracked + && plugin) { + sbuh = g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx_by_uuid, &uuid); + nm_assert (sbuh); + nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &((NMSKeyfileStorage *) storage)->parent._storage_by_uuid_lst)); } +#endif +} - uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new)); - connection_by_uuid = g_hash_table_lookup (priv->connections, uuid); +/*****************************************************************************/ - if ( connection - && connection != connection_by_uuid) { +static NMSKeyfileStorage * +_load_file (NMSKeyfilePlugin *self, + const char *dirname, + const char *filename, + NMSKeyfileStorageType storage_type, + GError **error) +{ + NMSKeyfilePluginPrivate *priv; + gs_unref_object NMConnection *connection = NULL; + NMTernary is_volatile_opt; + NMTernary is_nm_generated_opt; + NMTernary shadowed_owned_opt; + gs_free char *shadowed_storage = NULL; + gs_free_error GError *local = NULL; + gs_free char *full_filename = NULL; + struct stat st; - 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; + if (_ignore_filename (storage_type, filename)) { + gs_free char *nmmeta = NULL; + gs_free char *loaded_path = NULL; + gs_free char *shadowed_storage_filename = NULL; - if (source) - _LOGW ("cannot update protected "NMS_KEYFILE_CONNECTION_LOG_FMT" connection due to conflicting UUID %s", NMS_KEYFILE_CONNECTION_LOG_ARG (conflicting), uuid); + if (!nms_keyfile_nmmeta_check_filename (filename, NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip due to invalid filename"); + else + _LOGT ("load: \"%s/%s\": skip file due to invalid filename", dirname, filename); + return NULL; + } + if (!nms_keyfile_nmmeta_read (dirname, + filename, + &full_filename, + &nmmeta, + &loaded_path, + &shadowed_storage_filename, + NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip unreadable nmmeta file"); 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"); + _LOGT ("load: \"%s/%s\": skip unreadable nmmeta file", dirname, filename); + return NULL; + } + nm_assert (loaded_path); + if (!NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip nmmeta file from read-only directory"); + else + _LOGT ("load: \"%s/%s\": skip nmmeta file from read-only directory", dirname, filename); + return NULL; + } + if (!nm_streq (loaded_path, NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip nmmeta file not symlinking %s", NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL); + else + _LOGT ("load: \"%s/%s\": skip nmmeta file not symlinking to %s", dirname, filename, NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL); return NULL; } - /* The new connection has a different UUID then the original one. - * Remove @connection. */ - remove_connection (self, connection); + return nms_keyfile_storage_new_tombstone (self, + nmmeta, + full_filename, + storage_type, + shadowed_storage_filename); } - 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)); + full_filename = g_build_filename (dirname, filename, NULL); + + priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + + connection = _read_from_file (full_filename, + _get_plugin_dir (priv), + &st, + &is_nm_generated_opt, + &is_volatile_opt, + &shadowed_storage, + &shadowed_owned_opt, + &local); + if (!connection) { + if (error) + g_propagate_error (error, local); 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"); + _LOGW ("load: \"%s\": failed to load connection: %s", full_filename, local->message); return NULL; } - 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)); - } + return nms_keyfile_storage_new_connection (self, + g_steal_pointer (&connection), + full_filename, + storage_type, + is_nm_generated_opt, + is_volatile_opt, + shadowed_storage, + shadowed_owned_opt, + &st.st_mtim); +} - return connection_new; - } +static NMSKeyfileStorage * +_load_file_from_path (NMSKeyfilePlugin *self, + const char *full_filename, + NMSKeyfileStorageType storage_type, + GError **error) +{ + gs_free char *f_dirname_free = NULL; + const char *f_filename; + const char *f_dirname; + + nm_assert (full_filename && full_filename[0] == '/'); + + f_filename = strrchr (full_filename, '/'); + f_dirname = nm_strndup_a (300, full_filename, f_filename - full_filename, &f_dirname_free); + f_filename++; + return _load_file (self, + f_dirname, + f_filename, + storage_type, + error); } static void -dir_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) +_load_dir (NMSKeyfilePlugin *self, + NMSKeyfileStorageType storage_type, + const char *dirname, + NMSettUtilStorages *storages) { - NMSettingsPlugin *config = NM_SETTINGS_PLUGIN (user_data); - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config); - NMSKeyfileConnection *connection; - char *full_path; - gboolean exists; - - full_path = g_file_get_path (file); - if (nm_keyfile_utils_ignore_filename (full_path, FALSE)) { - g_free (full_path); + const char *filename; + GDir *dir; + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + + dir = g_dir_open (dirname, 0, NULL); + if (!dir) return; - } - exists = g_file_test (full_path, G_FILE_TEST_EXISTS); - - _LOGD ("dir_changed(%s) = %d; file %s", full_path, event_type, exists ? "exists" : "does not exist"); - - connection = find_by_path (self, full_path); - - switch (event_type) { - case G_FILE_MONITOR_EVENT_DELETED: - if (!exists && connection) - remove_connection (NMS_KEYFILE_PLUGIN (config), connection); - break; - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - if (exists) - update_connection (NMS_KEYFILE_PLUGIN (config), NULL, full_path, connection, TRUE, NULL, NULL); - break; - default: - break; + + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); + + while ((filename = g_dir_read_name (dir))) { + gs_unref_object NMSKeyfileStorage *storage = NULL; + + filename = g_strdup (filename); + if (!g_hash_table_add (dupl_filenames, (char *) filename)) + continue; + + storage = _load_file (self, + dirname, + filename, + storage_type, + NULL); + if (!storage) + continue; + + nm_sett_util_storages_add_take (storages, g_steal_pointer (&storage)); } - g_free (full_path); + g_dir_close (dir); + +#if NM_MORE_ASSERTS + { + NMSKeyfileStorage *storage; + + c_list_for_each_entry (storage, &storages->_storage_lst_head, parent._storage_lst) + nm_assert (NMS_IS_KEYFILE_STORAGE (storage)); + } +#endif } +/*****************************************************************************/ + static void -config_changed_cb (NMConfig *config, - NMConfigData *config_data, - NMConfigChangeFlags changes, - NMConfigData *old_data, - NMSKeyfilePlugin *self) +_storages_consolidate (NMSKeyfilePlugin *self, + NMSettUtilStorages *storages_new, + gboolean replace_all, + GHashTable *storages_replaced, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - gs_free char *old_value = NULL; - gs_free char *new_value = NULL; + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted); + gs_unref_ptrarray GPtrArray *storages_modified = NULL; + CList storages_deleted; + NMSKeyfileStorage *storage_safe; + NMSKeyfileStorage *storage_new; + NMSKeyfileStorage *storage_old; + NMSKeyfileStorage *storage; + guint i; - 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); + storages_modified = g_ptr_array_new_with_free_func (g_object_unref); + c_list_init (&storages_deleted); - if (!nm_streq0 (old_value, new_value)) - _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); -} + c_list_for_each_entry (storage_old, &priv->storages._storage_lst_head, parent._storage_lst) + storage_old->is_dirty = TRUE; -static void -setup_monitoring (NMSettingsPlugin *config) -{ - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config); - GFile *file; - GFileMonitor *monitor; - - if (nm_config_get_monitor_connection_files (priv->config)) { - file = g_file_new_for_path (nms_keyfile_utils_get_path ()); - monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - - if (monitor) { - priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), config); - priv->monitor = monitor; + c_list_for_each_entry_safe (storage_new, storage_safe, &storages_new->_storage_lst_head, parent._storage_lst) { + storage_old = nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage_new)); + + nm_sett_util_storages_steal (storages_new, storage_new); + + if ( !storage_old + || !nm_streq (nms_keyfile_storage_get_uuid (storage_new), nms_keyfile_storage_get_uuid (storage_old))) { + if (storage_old) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_by_uuid_lst); + } + storage_new->is_dirty = FALSE; + nm_sett_util_storages_add_take (&priv->storages, storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_new)); + continue; } - } - g_signal_connect (G_OBJECT (priv->config), - NM_CONFIG_SIGNAL_CONFIG_CHANGED, - G_CALLBACK (config_changed_cb), - config); -} + storage_old->is_dirty = FALSE; + nms_keyfile_storage_copy_content (storage_old, storage_new); + nms_keyfile_storage_destroy (storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_old)); + } -static GHashTable * -_paths_from_connections (GHashTable *connections) -{ - GHashTableIter iter; - NMSKeyfileConnection *connection; - GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); + c_list_for_each_entry_safe (storage_old, storage_safe, &priv->storages._storage_lst_head, parent._storage_lst) { + if (!storage_old->is_dirty) + continue; + if ( replace_all + || ( storages_replaced + && g_hash_table_contains (storages_replaced, storage_old))) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_by_uuid_lst); + } + } - 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)); + /* raise events. */ - if (path) - g_hash_table_add (paths, (void *) path); + for (i = 0; i < storages_modified->len; i++) { + storage = storages_modified->pdata[i]; + storage->is_dirty = TRUE; } - return paths; -} -static int -_sort_paths (const char **f1, const char **f2, GHashTable *paths) -{ - struct stat st; - gboolean c1, c2; - gint64 m1, m2; + for (i = 0; i < storages_modified->len; i++) { + gs_unref_object NMConnection *connection = NULL; - c1 = !!g_hash_table_contains (paths, *f1); - c2 = !!g_hash_table_contains (paths, *f2); - if (c1 != c2) - return c1 ? -1 : 1; + storage = storages_modified->pdata[i]; - 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; + if (!storage->is_dirty) { + /* the entry is no longer is_dirty. In the meantime we already emited + * another signal for it. */ + continue; + } + storage->is_dirty = FALSE; - return strcmp (*f1, *f2); -} + if (c_list_is_empty (&storage->parent._storage_lst)) { + /* 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; + } -static void -_read_dir (GPtrArray *filenames, - const char *path, - gboolean require_extension) -{ - GDir *dir; - const char *item; - GError *error = NULL; + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage))); - dir = g_dir_open (path, 0, &error); - if (!dir) { - _LOGD ("cannot read directory '%s': %s", path, error->message); - g_clear_error (&error); - return; + connection = nms_keyfile_storage_steal_connection (storage); + + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + connection, + user_data); } - while ((item = g_dir_read_name (dir))) { - if (nm_keyfile_utils_ignore_filename (item, require_extension)) - continue; - g_ptr_array_add (filenames, g_build_filename (path, item, NULL)); + while ((storage = c_list_first_entry (&storages_deleted, NMSKeyfileStorage, parent._storage_by_uuid_lst))) { + c_list_unlink (&storage->parent._storage_by_uuid_lst); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + NULL, + user_data); + nms_keyfile_storage_destroy (storage); } - g_dir_close (dir); } +static void +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_keyfile_storage_destroy); + int i; + + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_RUN, priv->dirname_run, &storages_new); + if (priv->dirname_etc) + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_ETC, priv->dirname_etc, &storages_new); + for (i = 0; priv->dirname_libs[i]; i++) + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_LIB (i), priv->dirname_libs[i], &storages_new); + + _storages_consolidate (self, + &storages_new, + TRUE, + NULL, + callback, + user_data); +} static void -read_connections (NMSettingsPlugin *config) +load_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback 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; - guint i; - GPtrArray *filenames; - GHashTable *paths; + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_keyfile_storage_destroy); + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_unref_hashtable GHashTable *storages_replaced = NULL; + gs_unref_hashtable GHashTable *loaded_uuids = NULL; + const char *loaded_uuid; + GHashTableIter h_iter; + gsize i; + + if (n_entries == 0) + return; - filenames = g_ptr_array_new_with_free_func (g_free); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - _read_dir (filenames, NM_KEYFILE_PATH_NAME_RUN, TRUE); - _read_dir (filenames, nms_keyfile_utils_get_path (), FALSE); + loaded_uuids = g_hash_table_new (nm_str_hash, g_str_equal); - alive_connections = g_hash_table_new (nm_direct_hash, NULL); + storages_replaced = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL); - /* 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); + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *const entry = &entries[i]; + NMSKeyfileStorageType storage_type; + gs_free_error GError *local = NULL; + const char *f_filename; + const char *f_dirname; + const char *full_filename; + gs_free char *full_filename_keep = NULL; + gboolean is_nmmeta_file; + NMSettingsPluginConnectionLoadEntry *dupl_content_entry; + gboolean failed_due_to_invalid_filename; + gs_unref_object NMSKeyfileStorage *storage = NULL; + + if (entry->handled) + continue; + + if (!_path_detect_storage_type (entry->filename, + (const char *const*) priv->dirname_libs, + priv->dirname_etc, + priv->dirname_run, + &storage_type, + &f_dirname, + &f_filename, + &is_nmmeta_file, + &failed_due_to_invalid_filename)) { + if (failed_due_to_invalid_filename) { + entry->handled = TRUE; + nm_utils_error_set (&entry->error, NM_UTILS_ERROR_UNKNOWN, "filename is not valid for a keyfile"); + } + continue; } - } - 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); + full_filename_keep = g_build_filename (f_dirname, f_filename, NULL); + + if ((dupl_content_entry = g_hash_table_lookup (dupl_filenames, full_filename_keep))) { + /* we already visited this file. */ + entry->handled = dupl_content_entry->handled; + if (dupl_content_entry->error) { + g_set_error_literal (&entry->error, + dupl_content_entry->error->domain, + dupl_content_entry->error->code, + dupl_content_entry->error->message); + } + continue; + } + + entry->handled = TRUE; + + full_filename = full_filename_keep; + if (!g_hash_table_insert (dupl_filenames, g_steal_pointer (&full_filename_keep), entry)) + nm_assert_not_reached (); + + storage = _load_file (self, + f_dirname, + f_filename, + storage_type, + &local); + if (!storage) { + if (nm_utils_file_stat (full_filename, NULL) == -ENOENT) { + NMSKeyfileStorage *storage2; + + /* the file does not exist. We take that as indication to unload the file + * that was previously loaded... */ + storage2 = nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename); + if (storage2) + g_hash_table_add (storages_replaced, g_object_ref (storage2)); + continue; + } + g_propagate_error (&entry->error, g_steal_pointer (&local)); + continue; + } + + g_hash_table_add (loaded_uuids, (char *) nms_keyfile_storage_get_uuid (storage)); + + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage)); } -} -/*****************************************************************************/ + /* now we visit all UUIDs that are about to change... */ + g_hash_table_iter_init (&h_iter, loaded_uuids); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &loaded_uuid, NULL)) { + NMSKeyfileStorage *storage; + NMSettUtilStorageByUuidHead *sbuh; -static GSList * -get_connections (NMSettingsPlugin *config) -{ - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config); + sbuh = nm_sett_util_storages_lookup_by_uuid (&priv->storages, loaded_uuid); + if (!sbuh) + continue; + + c_list_for_each_entry (storage, &sbuh->_storage_by_uuid_lst_head, parent._storage_by_uuid_lst) { + const char *full_filename = nms_keyfile_storage_get_filename (storage); + gs_unref_object NMSKeyfileStorage *storage_new = NULL; + gs_free_error GError *local = NULL; + + if (g_hash_table_contains (dupl_filenames, full_filename)) { + /* already re-loaded. */ + continue; + } + + /* @storage has a UUID that was just loaded from disk, but we have an entry in cache. + * Reload that file too despite not being told to do so. The reason is to get + * the latest file timestamp so that we get the priorities right. */ + + storage_new = _load_file_from_path (self, + full_filename, + storage->storage_type, + &local); + if ( storage_new + && !nm_streq (loaded_uuid, nms_keyfile_storage_get_uuid (storage_new))) { + /* the file now references a different UUID. We are not told to reload + * that file, so this means the existing storage (with the previous + * filename and UUID tuple) is no longer valid. */ + g_clear_object (&storage_new); + } - if (!priv->initialized) { - setup_monitoring (config); - read_connections (config); - priv->initialized = TRUE; + g_hash_table_add (storages_replaced, g_object_ref (storage)); + if (storage_new) + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage_new)); + } } - return _nm_utils_hash_values_to_slist (priv->connections); + + nm_clear_pointer (&loaded_uuids, g_hash_table_destroy); + nm_clear_pointer (&dupl_filenames, g_hash_table_destroy); + + _storages_consolidate (self, + &storages_new, + FALSE, + storages_replaced, + callback, + user_data); } -static gboolean -load_connection (NMSettingsPlugin *config, - const char *filename) +gboolean +nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, + NMConnection *connection, + gboolean in_memory, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) { - 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 - return FALSE; + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + gs_unref_object NMConnection *reread = NULL; + gs_free char *full_filename = NULL; + NMSKeyfileStorageType storage_type; + gs_unref_object NMSKeyfileStorage *storage = NULL; + GError *local = NULL; + const char *uuid; + gboolean reread_same; + struct timespec mtime; + char strbuf[100]; + + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (out_storage && !*out_storage); + nm_assert (out_connection && !*out_connection); + + nm_assert ( in_memory + || ( !is_nm_generated + && !is_volatile + && !shadowed_storage + && !shadowed_owned)); - if (nm_keyfile_utils_ignore_filename (filename, require_extension)) + uuid = nm_connection_get_uuid (connection); + + /* Note that even if the caller requests persistent storage, we may switch to in-memory, if + * no /etc directory is configured. */ + 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, + shadowed_storage, + shadowed_owned, + storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC + ? priv->dirname_etc + : priv->dirname_run, + _get_plugin_dir (priv), + NULL, + FALSE, + FALSE, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, NULL), + &full_filename, + &reread, + &reread_same, + &local)) { + _LOGT ("commit: %s (%s) failed to add: %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + local->message); + g_propagate_error (error, local); return FALSE; + } - connection = update_connection (self, NULL, filename, find_by_path (self, filename), TRUE, NULL, NULL); + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - return (connection != NULL); -} + 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))); -static void -reload_connections (NMSettingsPlugin *config) -{ - read_connections (config); + nm_assert (full_filename && full_filename[0] == '/'); + nm_assert (!nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); + + _LOGT ("commit: %s (%s) added as \"%s\"%s%s%s%s", + uuid, + nm_connection_get_id (connection), + full_filename, + _extra_flags_to_string (strbuf, sizeof (strbuf), is_nm_generated, is_volatile), + NM_PRINT_FMT_QUOTED (shadowed_storage, " (shadows \"", shadowed_storage, shadowed_owned ? "\", owned)" : "\")", "")); + + storage = nms_keyfile_storage_new_connection (self, + g_steal_pointer (&reread), + full_filename, + storage_type, + is_nm_generated ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + is_volatile ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + shadowed_storage, + shadowed_owned ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + nm_sett_util_stat_mtime (full_filename, FALSE, &mtime)); + + nm_sett_util_storages_add_take (&priv->storages, g_object_ref (storage)); + + *out_connection = nms_keyfile_storage_steal_connection (storage); + *out_storage = NM_SETTINGS_STORAGE (g_steal_pointer (&storage)); + + 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, + NULL, + 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, + const char *shadowed_storage, + gboolean shadowed_owned, + 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; + gs_free char *full_filename = NULL; + gs_free_error GError *local = NULL; + struct timespec mtime; + const char *previous_filename; + gboolean reread_same; + const char *uuid; + char strbuf[100]; + + _nm_assert_storage (self, storage, TRUE); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (nm_streq (nms_keyfile_storage_get_uuid (storage), nm_connection_get_uuid (connection))); + nm_assert (!error || !*error); + nm_assert (NM_IN_SET (storage->storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)); + nm_assert (!storage->is_meta_data); + nm_assert ( storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN + || ( !is_nm_generated + && !is_volatile + && !shadowed_storage + && !shadowed_owned)); + nm_assert (!shadowed_owned || shadowed_storage); + nm_assert ( priv->dirname_etc + || storage->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, + is_nm_generated, + is_volatile, + shadowed_storage, + shadowed_owned, + storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC + ? priv->dirname_etc + : priv->dirname_run, + _get_plugin_dir (priv), + previous_filename, + FALSE, FALSE, - &path, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, previous_filename), + &full_filename, &reread, - NULL, - error)) - return NULL; + &reread_same, + &local)) { + _LOGW ("commit: failure to write %s (%s) to \"%s\": %s", + uuid, + nm_connection_get_id (connection_clone), + previous_filename, + local->message); + g_propagate_error (error, g_steal_pointer (&local)); + return FALSE; + } + + nm_assert ( full_filename + && nm_streq (full_filename, previous_filename)); + + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error)); + nm_assert (_nm_connection_verify (reread, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (nm_streq (nm_connection_get_uuid (reread), uuid)); + + _LOGT ("commit: \"%s\": profile %s (%s) written%s%s%s%s", + full_filename, + uuid, + nm_connection_get_id (connection), + _extra_flags_to_string (strbuf, sizeof (strbuf), is_nm_generated, is_volatile), + NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", "")); + + storage->u.conn_data.is_nm_generated = is_nm_generated; + storage->u.conn_data.is_volatile = is_volatile; + storage->u.conn_data.stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime); + storage->u.conn_data.shadowed_owned = shadowed_owned; + + *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage)); + *out_connection = g_steal_pointer (&reread); + 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, + NULL, + FALSE, + FALSE, + out_storage, + out_connection, + error); +} + +static gboolean +delete_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage_x, + 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)); + const char *remove_from_disk_errmsg = NULL; + const char *operation_message; + const char *previous_filename; + const char *uuid; + gboolean success = TRUE; + + _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 (!NM_IN_SET (storage->storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)) { + nm_utils_error_set (error, + NM_UTILS_ERROR_UNKNOWN, + "profile in read-only storage cannot be deleted"); + success = FALSE; + 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"; + success = FALSE; + nm_utils_error_set_errno (error, + errsv, + "failure to delete \"%s\": %s", + previous_filename); + } else + operation_message = "does not exist on disk"; + } else + operation_message = "deleted from disk"; + + _LOGT ("commit: deleted \"%s\", %s %s (%s%s%s%s)", + previous_filename, + storage->is_meta_data ? "meta-data" : "profile", + uuid, + operation_message, + NM_PRINT_FMT_QUOTED (remove_from_disk_errmsg, ": ", remove_from_disk_errmsg, "", "")); + + if (success) { + nm_sett_util_storages_steal (&priv->storages, storage); + nms_keyfile_storage_destroy (storage); + } + + return success; +} + +/** + * nms_keyfile_plugin_set_nmmeta_tombstone: + * @self: the #NMSKeyfilePlugin instance + * @simulate: if %TRUE, don't do anything on the filename but just pretend + * that the loaded UUID file gets tracked/untracked. In this mode, the function + * cannot fail (except on hard-failure, see below). + * The idea is that you first try without simulate to write to disk. + * If that fails, you might still want to forcefully pretend (in-memory + * only) that this uuid is marked as tombstone (or not), as desired. + * So you repeate the call with @simulate %TRUE. + * @uuid: the UUID for which to write/delete the nmmeta file + * @in_memory: the storage type, either /etc or /run. Note that if @self + * has no /etc directory configured, this results in a hard failure. + * @set: if %TRUE, write the symlink to point to /dev/null. If %FALSE, + * delete the nmmeta file (if it exists). + * @shadowed_storage: a tombstone can also shadow an existing storage. + * In combination with @set and @in_memory, this is allowed to store + * the shadowed storage filename. + * @out_storage: (transfer full) (allow-none): the storage element that changes, or + * NULL if nothing changed. Note that the file on disk is already as + * we want to write it, then this still counts as a change. No change only + * means if we try to delete a storage (@set %FALSE) that did not + * exist previously. + * @out_hard_failure: (allow-none): on failure, indicate that this is a hard failure. + * + * The function writes or deletes nmmeta files to/from filesystem. In this case, + * the nmmeta files can only be symlinks to /dev/null (to indicate tombstones). + * + * A hard failure can only happen if @self has no /etc directory configured + * and @in_memory is FALSE. In such case even @simulate call fails (which + * otherwise would always succeed). + * Also, if you get a hard-failure (with @simulate %FALSE) there is no point + * in retrying with @simulate %TRUE (contrary to all other cases!). + * + * Returns: %TRUE on success. + */ +gboolean +nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, + gboolean simulate, + const char *uuid, + gboolean in_memory, + gboolean set, + const char *shadowed_storage, + NMSettingsStorage **out_storage, + gboolean *out_hard_failure) +{ + NMSKeyfilePluginPrivate *priv; + gboolean hard_failure = FALSE; + NMSKeyfileStorage *storage; + gs_unref_object NMSKeyfileStorage *storage_result = NULL; + gboolean nmmeta_success = FALSE; + gs_free char *nmmeta_filename = NULL; + NMSKeyfileStorageType storage_type; + const char *loaded_path; + const char *dirname; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (self)); + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (!out_storage || !*out_storage); + nm_assert (!shadowed_storage || (set && in_memory)); + + priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + + loaded_path = set + ? NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL + : NULL; + + if (in_memory) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN; + dirname = priv->dirname_run; + } else { + if (!priv->dirname_etc) { + _LOGT ("commit: cannot %s%s nmmeta file for %s as there is no /etc directory", + simulate ? "simulate " : "", + loaded_path ? "write" : "delete", + uuid); + hard_failure = TRUE; + goto out; + } + storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC; + dirname = priv->dirname_etc; + } + + if (simulate) { + nmmeta_success = TRUE; + nmmeta_filename = nms_keyfile_nmmeta_filename (dirname, uuid, FALSE); + } else { + nmmeta_success = nms_keyfile_nmmeta_write (dirname, + uuid, + loaded_path, + FALSE, + shadowed_storage, + &nmmeta_filename); + } + + _LOGT ("commit: %s nmmeta file \"%s\"%s%s%s%s%s%s %s", + loaded_path ? "writing" : "deleting", + nmmeta_filename, + NM_PRINT_FMT_QUOTED (loaded_path, " (pointing to \"", loaded_path, "\")", ""), + NM_PRINT_FMT_QUOTED (shadowed_storage, " (shadows \"", shadowed_storage, "\")", ""), + simulate + ? "simulated" + : ( nmmeta_success + ? "succeeded" + : "failed")); + + if (!nmmeta_success) + goto out; + + storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, nmmeta_filename); + + nm_assert ( !storage + || ( storage->is_meta_data + && storage->storage_type == storage_type + && nm_streq (nms_keyfile_storage_get_uuid (storage), uuid))); + + if (loaded_path) { + + if (!storage) { + storage = nms_keyfile_storage_new_tombstone (self, + uuid, + nmmeta_filename, + storage_type, + shadowed_storage); + nm_sett_util_storages_add_take (&priv->storages, storage); + } else { + g_free (storage->u.meta_data.shadowed_storage); + storage->u.meta_data.shadowed_storage = g_strdup (shadowed_storage); + } + + storage_result = g_object_ref (storage); + } else { + if (storage) + storage_result = nm_sett_util_storages_steal (&priv->storages, storage); + } + +out: + nm_assert (!nmmeta_success || !hard_failure); + nm_assert (nmmeta_success || !storage_result); + + NM_SET_OUT (out_hard_failure, hard_failure); + NM_SET_OUT (out_storage, (NMSettingsStorage *) g_steal_pointer (&storage_result)); + return nmmeta_success; +} + +/*****************************************************************************/ + +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; + + 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), @@ -584,13 +1225,48 @@ 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); + + priv->storages = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT (priv->storages, nms_keyfile_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 constructed (GObject *object) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) object); + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (object); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); G_OBJECT_CLASS (nms_keyfile_plugin_parent_class)->constructed (object); @@ -599,6 +1275,17 @@ constructed (GObject *object) NM_CONFIG_KEYFILE_KEY_KEYFILE_HOSTNAME, NM_CONFIG_GET_VALUE_RAW)) _LOGW ("'hostname' option is deprecated and has no effect"); + + if (nm_config_data_has_value (nm_config_get_data_orig (priv->config), + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES, + NM_CONFIG_GET_VALUE_RAW)) + _LOGW ("'monitor-connection-files' option is deprecated and has no effect"); + + g_signal_connect (G_OBJECT (priv->config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (config_changed_cb), + self); } NMSKeyfilePlugin * @@ -610,24 +1297,19 @@ nms_keyfile_plugin_new (void) static void dispose (GObject *object) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) object); + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (object); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - if (priv->monitor) { - nm_clear_g_signal_handler (priv->monitor, &priv->monitor_id); + if (priv->config) + g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, object); - g_file_monitor_cancel (priv->monitor); - g_clear_object (&priv->monitor); - } + nm_sett_util_storages_clear (&priv->storages); - if (priv->connections) { - g_hash_table_destroy (priv->connections); - priv->connections = NULL; - } + nm_clear_g_free (&priv->dirname_libs[0]); + nm_clear_g_free (&priv->dirname_etc); + nm_clear_g_free (&priv->dirname_run); - if (priv->config) { - g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, object); - g_clear_object (&priv->config); - } + g_clear_object (&priv->config); G_OBJECT_CLASS (nms_keyfile_plugin_parent_class)->dispose (object); } @@ -641,9 +1323,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_connections = load_connections; 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 41f47aac..48440964 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.h +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -22,6 +21,11 @@ #ifndef __NMS_KEYFILE_PLUGIN_H__ #define __NMS_KEYFILE_PLUGIN_H__ +#include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-storage.h" + +#include "nms-keyfile-utils.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)) @@ -36,4 +40,36 @@ GType nms_keyfile_plugin_get_type (void); NMSKeyfilePlugin *nms_keyfile_plugin_new (void); +gboolean nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, + NMConnection *connection, + gboolean in_memory, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + 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, + const char *shadowed_storage, + gboolean shadowed_owned, + gboolean force_rename, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error); + +gboolean nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, + gboolean simulate, + const char *uuid, + gboolean in_memory, + gboolean set, + const char *shadowed_storage, + NMSettingsStorage **out_storage, + gboolean *out_hard_failure); + #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 5778f13c..8d1f5599 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-reader.c +++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -162,6 +161,11 @@ 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, + char **out_shadowed_storage, + NMTernary *out_shadowed_owned, GError **error) { gs_unref_keyfile GKeyFile *key_file = NULL; @@ -171,9 +175,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; @@ -195,6 +202,26 @@ 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)); + + NM_SET_OUT (out_shadowed_storage, g_key_file_get_string (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE, + NULL)); + + NM_SET_OUT (out_shadowed_owned, nm_key_file_get_boolean (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED, + 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 c0fb06d1..f20e6d93 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-reader.h +++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -31,8 +30,15 @@ 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, + char **out_shadowed_storage, + NMTernary *out_shadowed_owned, 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 00000000..d68d60c8 --- /dev/null +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.c @@ -0,0 +1,277 @@ +/* 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 "nm-core-internal.h" +#include "nms-keyfile-plugin.h" + +/*****************************************************************************/ + +struct _NMSKeyfileStorageClass { + NMSettingsStorageClass parent; +}; + +G_DEFINE_TYPE (NMSKeyfileStorage, nms_keyfile_storage, NM_TYPE_SETTINGS_STORAGE) + +/*****************************************************************************/ + +void +nms_keyfile_storage_copy_content (NMSKeyfileStorage *dst, + const NMSKeyfileStorage *src) +{ + nm_assert (src != dst); + nm_assert (nm_streq (nms_keyfile_storage_get_uuid (dst), nms_keyfile_storage_get_uuid (src))); + nm_assert ( nms_keyfile_storage_get_filename (dst) + && nm_streq (nms_keyfile_storage_get_filename (dst), nms_keyfile_storage_get_filename (src))); + nm_assert (dst->storage_type == src->storage_type); + nm_assert (dst->is_meta_data == src->is_meta_data); + + if (dst->is_meta_data) { + gs_free char *shadowed_storage_to_free = NULL; + + shadowed_storage_to_free = g_steal_pointer (&dst->u.meta_data.shadowed_storage); + dst->u.meta_data = src->u.meta_data; + dst->u.meta_data.shadowed_storage = g_strdup (dst->u.meta_data.shadowed_storage); + } else { + gs_unref_object NMConnection *connection_to_free = NULL; + gs_free char *shadowed_storage_to_free = NULL; + + connection_to_free = g_steal_pointer (&dst->u.conn_data.connection); + shadowed_storage_to_free = g_steal_pointer (&dst->u.conn_data.shadowed_storage); + dst->u.conn_data = src->u.conn_data; + nm_g_object_ref (dst->u.conn_data.connection); + dst->u.conn_data.shadowed_storage = g_strdup (dst->u.conn_data.shadowed_storage); + } +} + +NMConnection * +nms_keyfile_storage_steal_connection (NMSKeyfileStorage *self) +{ + nm_assert (NMS_IS_KEYFILE_STORAGE (self)); + nm_assert ( self->is_meta_data + || NM_IS_CONNECTION (self->u.conn_data.connection)); + + return self->is_meta_data + ? NULL + : g_steal_pointer (&self->u.conn_data.connection); +} + +/*****************************************************************************/ + +static int +cmp_fcn (const NMSKeyfileStorage *a, + const NMSKeyfileStorage *b) +{ + nm_assert (NMS_IS_KEYFILE_STORAGE (a)); + nm_assert (NMS_IS_KEYFILE_STORAGE (b)); + nm_assert (a != b); + + /* sort by storage-type, which also has a numeric value according to their + * (inverse) priority. */ + NM_CMP_FIELD_UNSAFE (b, a, storage_type); + + /* meta-data is more important. */ + NM_CMP_FIELD_UNSAFE (a, b, is_meta_data); + + if (a->is_meta_data) { + nm_assert (nm_streq (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b))); + NM_CMP_FIELD_UNSAFE (a, b, u.meta_data.is_tombstone); + } else { + /* newer files are more important. */ + NM_CMP_FIELD (a, b, u.conn_data.stat_mtime.tv_sec); + NM_CMP_FIELD (a, b, u.conn_data.stat_mtime.tv_nsec); + + NM_CMP_DIRECT_STRCMP (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b)); + } + + return 0; +} + +/*****************************************************************************/ + +static void +nms_keyfile_storage_init (NMSKeyfileStorage *self) +{ +} + +static NMSKeyfileStorage * +_storage_new (NMSKeyfilePlugin *plugin, + const char *uuid, + const char *filename, + gboolean is_meta_data, + NMSKeyfileStorageType storage_type) + +{ + NMSKeyfileStorage *self; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin)); + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + + self = g_object_new (NMS_TYPE_KEYFILE_STORAGE, + NM_SETTINGS_STORAGE_PLUGIN, plugin, + NM_SETTINGS_STORAGE_UUID, uuid, + NM_SETTINGS_STORAGE_FILENAME, filename, + NULL); + + *((bool *) &self->is_meta_data) = is_meta_data; + *((NMSKeyfileStorageType *) &self->storage_type) = storage_type; + + return self; +} + +NMSKeyfileStorage * +nms_keyfile_storage_new_tombstone (NMSKeyfilePlugin *plugin, + const char *uuid, + const char *filename, + NMSKeyfileStorageType storage_type, + const char *shadowed_storage) +{ + NMSKeyfileStorage *self; + + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + nm_assert (nms_keyfile_nmmeta_check_filename (filename, NULL)); + nm_assert (NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)); + + self = _storage_new (plugin, uuid, filename, TRUE, storage_type); + self->u.meta_data.is_tombstone = TRUE; + if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) + self->u.meta_data.shadowed_storage = g_strdup (shadowed_storage); + return self; +} + +NMSKeyfileStorage * +nms_keyfile_storage_new_connection (NMSKeyfilePlugin *plugin, + NMConnection *connection_take /* pass reference */, + const char *filename, + NMSKeyfileStorageType storage_type, + NMTernary is_nm_generated_opt, + NMTernary is_volatile_opt, + const char *shadowed_storage, + NMTernary shadowed_owned_opt, + const struct timespec *stat_mtime) +{ + NMSKeyfileStorage *self; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin)); + nm_assert (NM_IS_CONNECTION (connection_take)); + nm_assert (_nm_connection_verify (connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (filename && filename[0] == '/'); + nm_assert ( storage_type >= NMS_KEYFILE_STORAGE_TYPE_RUN + && storage_type <= _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST); + nmtst_connection_assert_unchanging (connection_take); + + self = _storage_new (plugin, nm_connection_get_uuid (connection_take), filename, FALSE, storage_type); + + self->u.conn_data.connection = connection_take; /* take reference. */ + + self->u.conn_data.shadowed_storage = g_strdup (shadowed_storage); + + if (stat_mtime) + self->u.conn_data.stat_mtime = *stat_mtime; + + if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) { + self->u.conn_data.is_nm_generated = (is_nm_generated_opt == NM_TERNARY_TRUE); + self->u.conn_data.is_volatile = (is_volatile_opt == NM_TERNARY_TRUE); + self->u.conn_data.shadowed_owned = shadowed_storage + && (shadowed_owned_opt == NM_TERNARY_TRUE); + } + + return self; +} + +static void +_storage_clear (NMSKeyfileStorage *self) +{ + c_list_unlink (&self->parent._storage_lst); + c_list_unlink (&self->parent._storage_by_uuid_lst); + if (self->is_meta_data) + nm_clear_g_free (&self->u.meta_data.shadowed_storage); + else { + g_clear_object (&self->u.conn_data.connection); + nm_clear_g_free (&self->u.conn_data.shadowed_storage); + self->u.conn_data.shadowed_owned = FALSE; + } +} + +static void +dispose (GObject *object) +{ + NMSKeyfileStorage *self = NMS_KEYFILE_STORAGE (object); + + _storage_clear (self); + + G_OBJECT_CLASS (nms_keyfile_storage_parent_class)->dispose (object); +} + +void +nms_keyfile_storage_destroy (NMSKeyfileStorage *self) +{ + _storage_clear (self); + g_object_unref (self); +} + +static void +nms_keyfile_storage_class_init (NMSKeyfileStorageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingsStorageClass *storage_class = NM_SETTINGS_STORAGE_CLASS (klass); + + object_class->dispose = dispose; + + storage_class->cmp_fcn = (int (*) (NMSettingsStorage *, NMSettingsStorage *)) cmp_fcn; +} + +/*****************************************************************************/ + +#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->is_meta_data) + return; + if (s->storage_type != NMS_KEYFILE_STORAGE_TYPE_RUN) + return; + + if (s->u.conn_data.is_nm_generated) + *sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED; + + if (s->u.conn_data.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 00000000..2252b47b --- /dev/null +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.h @@ -0,0 +1,264 @@ +/* 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 { + /* whether this is a tombstone to hide a UUID (via symlink to /dev/null). */ + char *shadowed_storage; + bool is_tombstone:1; +} NMSettingsMetaData; + +typedef struct { + NMSettingsStorage parent; + + /* The connection. Note that there are tombstones (loaded-uuid files to /dev/null) + * that don't have a connection. + * + * Also, we don't actually remember the loaded connection after returning it + * to NMSettings. So, also for regular storages (non-tombstones) this field + * is often cleared. */ + union { + struct { + NMConnection *connection; + + /* when we move a profile from permanent storage to unsaved (/run), then + * we may leave the profile on disk (depending on options for Update2()). + * + * Later, when we save the profile again to disk, we want to re-use that filename. + * Likewise, we delete the (now in-memory) profile, we may want to also delete + * the original filename. + * + * This is the original filename, and we store it inside [.nmmeta] in the + * keyfile in /run. Note that we don't store this in the .nmmeta file, because + * the information is tied to the particular keyfile in /run, not to all UUIDs + * in general. */ + char *shadowed_storage; + + /* the timestamp (stat's mtime) of the keyfile. For meta-data this + * is irrelevant. The purpose is that if the same storage type (directory) has + * multiple files with the same UUID, then the newer file gets preferred. */ + struct timespec stat_mtime; + + /* these flags are only relevant for storages with %NMS_KEYFILE_STORAGE_TYPE_RUN + * (and non-metadata). This is to persist and reload these settings flags to + * /run. + * + * Note that these flags are not stored in as meta-data. The reason is that meta-data + * is per UUID. But these flags are only relevant for a particular keyfile on disk. + * That is, it must be tied to the actual keyfile, and not to the UUID. */ + bool is_nm_generated:1; + bool is_volatile:1; + + /* if shadowed_storage is set, then this flag indicates whether the file + * is owned. The difference comes into play when deleting the in-memory, + * shadowing profile: a owned profile will also be deleted. */ + bool shadowed_owned:1; + + } conn_data; + + /* the content from the .nmmeta file. Note that the nmmeta file has the UUID + * in the filename, that means there can be only two variants of this file: + * in /etc and in /run. As such, this is really meta-data about the entire profile + * (the UUID), and not about the individual keyfile. */ + NMSettingsMetaData meta_data; + + } u; + + /* The storage type. This is directly related to the filename. Since + * the filename cannot change, this value is unchanging. */ + const NMSKeyfileStorageType storage_type; + + /* whether union "u" has meta_data or conn_data. Since the type of the storage + * depends on the (immutable) filename, this is also const. */ + const bool is_meta_data; + + /* this flag is only used during reload to mark and prune old entries. */ + bool is_dirty:1; + +} NMSKeyfileStorage; + +typedef struct _NMSKeyfileStorageClass NMSKeyfileStorageClass; + +GType nms_keyfile_storage_get_type (void); + +struct _NMSKeyfilePlugin; + +NMSKeyfileStorage *nms_keyfile_storage_new_tombstone (struct _NMSKeyfilePlugin *self, + const char *uuid, + const char *filename, + NMSKeyfileStorageType storage_type, + const char *shadowed_storage); + +NMSKeyfileStorage *nms_keyfile_storage_new_connection (struct _NMSKeyfilePlugin *self, + NMConnection *connection_take /* pass reference */, + const char *filename, + NMSKeyfileStorageType storage_type, + NMTernary is_nm_generated_opt, + NMTernary is_volatile_opt, + const char *shadowed_storage, + NMTernary shadowed_owned_opt, + const struct timespec *stat_mtime); + +void nms_keyfile_storage_destroy (NMSKeyfileStorage *storage); + +/*****************************************************************************/ + +void nms_keyfile_storage_copy_content (NMSKeyfileStorage *dst, + const NMSKeyfileStorage *src); + +NMConnection *nms_keyfile_storage_steal_connection (NMSKeyfileStorage *storage); + +/*****************************************************************************/ + +static inline const char * +nms_keyfile_storage_get_uuid (const NMSKeyfileStorage *self) +{ + return nm_settings_storage_get_uuid ((const NMSettingsStorage *) self); +} + +static inline const char * +nms_keyfile_storage_get_filename (const NMSKeyfileStorage *self) +{ + return nm_settings_storage_get_filename ((const NMSettingsStorage *) self); +} + +/*****************************************************************************/ + +static inline gboolean +nm_settings_storage_is_keyfile_run (const NMSettingsStorage *self) +{ + return NMS_IS_KEYFILE_STORAGE (self) + && (((NMSKeyfileStorage *) self)->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN); +} + +static inline gboolean +nm_settings_storage_is_keyfile_lib (const NMSettingsStorage *self) +{ + return NMS_IS_KEYFILE_STORAGE (self) + && (((NMSKeyfileStorage *) self)->storage_type >= NMS_KEYFILE_STORAGE_TYPE_LIB_BASE); +} + +static inline const NMSettingsMetaData * +nm_settings_storage_is_meta_data (const NMSettingsStorage *storage) +{ + const NMSKeyfileStorage *self; + + if (!NMS_IS_KEYFILE_STORAGE (storage)) + return NULL; + + self = (NMSKeyfileStorage *) storage; + + if (!self->is_meta_data) + return NULL; + + return &self->u.meta_data; +} + +static inline const NMSettingsMetaData * +nm_settings_storage_is_meta_data_alive (const NMSettingsStorage *storage) +{ + const NMSettingsMetaData *meta_data; + + meta_data = nm_settings_storage_is_meta_data (storage); + + if (!meta_data) + return NULL; + + /* Regular (all other) storages are alive as long as they report a NMConnection, and + * they will be dropped, once they have no more connection. + * + * Meta-data storages are special: they never report a NMConnection. + * So, a meta-data storage is alive as long as it is tracked by the + * settings plugin. + * + * This function is used to ckeck for that. */ + + if (c_list_is_empty (&storage->_storage_lst)) + return NULL; + + return meta_data; +} + +static inline const char * +nm_settings_storage_get_shadowed_storage (const NMSettingsStorage *storage, + gboolean *out_shadowed_owned) +{ + if (NMS_IS_KEYFILE_STORAGE (storage)) { + const NMSKeyfileStorage *self = (const NMSKeyfileStorage *) storage; + + if (self->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) { + if (!self->is_meta_data) { + if (self->u.conn_data.shadowed_storage) { + NM_SET_OUT (out_shadowed_owned, self->u.conn_data.shadowed_owned); + return self->u.conn_data.shadowed_storage; + } + } else { + NM_SET_OUT (out_shadowed_owned, FALSE); + return self->u.meta_data.shadowed_storage; + } + } + } + + NM_SET_OUT (out_shadowed_owned, FALSE); + return NULL; +} + +static inline const char * +nm_settings_storage_get_filename_for_shadowed_storage (const NMSettingsStorage *storage) +{ + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), NULL); + + if (!storage->_filename) + return NULL; + + if (NMS_IS_KEYFILE_STORAGE (storage)) { + const NMSKeyfileStorage *self = (const NMSKeyfileStorage *) storage; + + if ( self->is_meta_data + || self->storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC) + return NULL; + } + + return storage->_filename; +} + +/*****************************************************************************/ + +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 3c4b0288..ea03e1b6 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.c +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -15,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" @@ -25,6 +24,7 @@ #include <stdlib.h> #include <sys/stat.h> +#include "nm-glib-aux/nm-io-utils.h" #include "nm-keyfile-internal.h" #include "nm-utils.h" #include "nm-setting-wired.h" @@ -34,108 +34,155 @@ /*****************************************************************************/ +#define NMMETA_KF_GROUP_NAME_NMMETA "nmmeta" +#define NMMETA_KF_KEY_NAME_NMMETA_UUID "uuid" +#define NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH "loaded-path" +#define NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE "shadowed-storage" + +/*****************************************************************************/ + +const char * +nms_keyfile_nmmeta_check_filename (const char *filename, + guint *out_uuid_len) +{ + const char *uuid; + const char *s; + gsize len; + + s = strrchr (filename, '/'); + if (s) + filename = &s[1]; + + len = strlen (filename); + if ( len <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA) + || memcmp (&filename[len - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA)], + NM_KEYFILE_PATH_SUFFIX_NMMETA, + NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA)) != 0) { + /* the filename does not have the right suffix. */ + return NULL; + } + + len -= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA); + + if (!NM_IN_SET (len, 36, 40)) { + /* the remaining part of the filename has not the right length to + * contain a UUID (according to nm_utils_is_uuid()). */ + return NULL; + } + + uuid = nm_strndup_a (100, filename, len, NULL); + if (!nm_utils_is_uuid (uuid)) + return NULL; + + NM_SET_OUT (out_uuid_len, len); + return filename; +} + char * -nms_keyfile_loaded_uuid_filename (const char *dirname, - const char *uuid, - gboolean temporary) +nms_keyfile_nmmeta_filename (const char *dirname, + const char *uuid, + gboolean temporary) { char filename[250]; + char *s; 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), - "%s%s%s%s", - NM_KEYFILE_PATH_PREFIX_NMLOADED, + "%s%s%s", uuid, - NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, + NM_KEYFILE_PATH_SUFFIX_NMMETA, temporary ? "~" : "") >= sizeof (filename)) { - /* valid uuids are limited in length. The buffer should always be large - * enough. */ + /* valid uuids are limited in length (nm_utils_is_uuid). The buffer should always + * be large enough. */ nm_assert_not_reached (); - return NULL; } - return g_build_filename (dirname, filename, NULL); + s = g_build_filename (dirname, filename, NULL); + + nm_assert (nm_keyfile_utils_ignore_filename (s, FALSE)); + + return s; } gboolean -nms_keyfile_loaded_uuid_read (const char *dirname, - const char *filename, - char **out_full_filename, - char **out_uuid, - char **out_loaded_path) +nms_keyfile_nmmeta_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage, + struct stat *out_st) { const char *uuid; - const char *tmp; - gsize len; + guint uuid_len; gs_free char *full_filename = NULL; - gs_free char *ln = NULL; + gs_free char *loaded_path = NULL; + gs_free char *shadowed_storage = NULL; + struct stat st_stack; + struct stat *st = out_st ?: &st_stack; nm_assert (dirname && dirname[0] == '/'); nm_assert (filename && filename[0] && !strchr (filename, '/')); - if (filename[0] != '.') { - /* the hidden-uuid filename must start with '.'. That is, - * so that it does not conflict with regular keyfiles according - * to nm_keyfile_utils_ignore_filename(). */ + uuid = nms_keyfile_nmmeta_check_filename (filename, &uuid_len); + if (!uuid) return FALSE; - } - len = strlen (filename); - if ( len <= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED) - || memcmp (filename, NM_KEYFILE_PATH_PREFIX_NMLOADED, NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)) != 0) { - /* the filename does not have the right prefix. */ + full_filename = g_build_filename (dirname, filename, NULL); + + if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMMETA, + full_filename, + st, + NULL)) return FALSE; - } - tmp = &filename[NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)]; - len -= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED); + if (S_ISREG (st->st_mode)) { + gs_unref_keyfile GKeyFile *kf = NULL; + gs_free char *v_uuid = NULL; - if ( len <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION) - || memcmp (&tmp[len - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)], - NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) != 0) { - /* the file does not have the right suffix. */ - return FALSE; - } - len -= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); + kf = g_key_file_new (); - if (!NM_IN_SET (len, 36, 40)) { - /* the remaining part of the filename has not the right length to - * contain a UUID (according to nm_utils_is_uuid()). */ - return FALSE; - } + if (!g_key_file_load_from_file (kf, full_filename, G_KEY_FILE_NONE, NULL)) + return FALSE; - uuid = nm_strndup_a (100, tmp, len, NULL); - if (!nm_utils_is_uuid (uuid)) - return FALSE; + v_uuid = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_UUID, NULL); + if (!nm_streq0 (v_uuid, uuid)) + return FALSE; - full_filename = g_build_filename (dirname, filename, NULL); + loaded_path = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH, NULL); + shadowed_storage = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE, NULL); - if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMLOADED, - full_filename, - NULL, - NULL)) - return FALSE; + if ( !loaded_path + && !shadowed_storage) { + /* if there is no useful information in the file, it is the same as if + * the file is not present. Signal failure. */ + return FALSE; + } - ln = nm_utils_read_link_absolute (full_filename, NULL); - if (!ln) - return FALSE; + } else { + loaded_path = nm_utils_read_link_absolute (full_filename, NULL); + if (!loaded_path) + return FALSE; + } - NM_SET_OUT (out_uuid, g_strdup (uuid)); + NM_SET_OUT (out_uuid, g_strndup (uuid, uuid_len)); NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); - NM_SET_OUT (out_loaded_path, g_steal_pointer (&ln)); + NM_SET_OUT (out_loaded_path, g_steal_pointer (&loaded_path)); + NM_SET_OUT (out_shadowed_storage, g_steal_pointer (&shadowed_storage)); return TRUE; } gboolean -nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, - char **out_dirname, - char **out_filename, - char **out_uuid, - char **out_loaded_path) +nms_keyfile_nmmeta_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage) { gs_free char *dirname = NULL; gs_free char *filename = NULL; @@ -145,11 +192,13 @@ nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, filename = g_path_get_basename (full_filename); dirname = g_path_get_dirname (full_filename); - if (!nms_keyfile_loaded_uuid_read (dirname, - filename, - NULL, - out_uuid, - out_loaded_path)) + if (!nms_keyfile_nmmeta_read (dirname, + filename, + NULL, + out_uuid, + out_loaded_path, + out_shadowed_storage, + NULL)) return FALSE; NM_SET_OUT (out_dirname, g_steal_pointer (&dirname)); @@ -158,20 +207,23 @@ nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, } gboolean -nms_keyfile_loaded_uuid_write (const char *dirname, - const char *uuid, - const char *loaded_path, - gboolean allow_relative, - char **out_full_filename) +nms_keyfile_nmmeta_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean loaded_path_allow_relative, + const char *shadowed_storage, + char **out_full_filename) { gs_free char *full_filename_tmp = NULL; 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] == '/'); + nm_assert (!shadowed_storage || loaded_path); - full_filename_tmp = nms_keyfile_loaded_uuid_filename (dirname, uuid, TRUE); + full_filename_tmp = nms_keyfile_nmmeta_filename (dirname, uuid, TRUE); nm_assert (g_str_has_suffix (full_filename_tmp, "~")); nm_assert (nm_utils_file_is_in_path (full_filename_tmp, dirname)); @@ -188,7 +240,7 @@ nms_keyfile_loaded_uuid_write (const char *dirname, return success; } - if (allow_relative) { + if (loaded_path_allow_relative) { const char *f; f = nm_utils_file_is_in_path (loaded_path, dirname); @@ -199,18 +251,40 @@ nms_keyfile_loaded_uuid_write (const char *dirname, } } - if (symlink (loaded_path, full_filename_tmp) != 0) { - full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; - NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); - return FALSE; - } + full_filename = g_strndup (full_filename_tmp, strlen (full_filename_tmp) - 1); - full_filename = g_strdup (full_filename_tmp); - full_filename[strlen (full_filename) - 1] = '\0'; - if (rename (full_filename_tmp, full_filename) != 0) { - (void) unlink (full_filename_tmp); - NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); - return FALSE; + if (shadowed_storage) { + gs_unref_keyfile GKeyFile *kf = NULL; + gs_free char *contents = NULL; + gsize length; + + kf = g_key_file_new (); + + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_UUID, uuid); + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH, loaded_path); + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE, shadowed_storage); + + contents = g_key_file_to_data (kf, &length, NULL); + + if (!nm_utils_file_set_contents (full_filename, contents, length, 0600, NULL)) { + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return FALSE; + } + } else { + /* we only have the "loaded_path" to store. That is commonly used for the tombstones to + * link to /dev/null. A symlink is sufficient to store that ammount of information. + * No need to bother with a keyfile. */ + if (symlink (loaded_path, full_filename_tmp) != 0) { + full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return FALSE; + } + + if (rename (full_filename_tmp, full_filename) != 0) { + (void) unlink (full_filename_tmp); + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); + return FALSE; + } } NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); @@ -232,10 +306,11 @@ nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, "file is not a regular file"); return FALSE; } - } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { - if (!S_ISLNK (st->st_mode)) { + } else if (filetype == NMS_KEYFILE_FILETYPE_NMMETA) { + if ( !S_ISLNK (st->st_mode) + && !S_ISREG (st->st_mode)) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "file is not a slink"); + "file is neither a symlink nor a regular file"); return FALSE; } } else @@ -249,7 +324,7 @@ nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, return FALSE; } - if ( filetype == NMS_KEYFILE_FILETYPE_KEYFILE + if ( S_ISREG (st->st_mode) && (st->st_mode & 0077)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "File permissions (%03o) are insecure", @@ -279,7 +354,7 @@ nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype, "cannot access file: %s", nm_strerror_native (errsv)); return FALSE; } - } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { + } else if (filetype == NMS_KEYFILE_FILETYPE_NMMETA) { if (lstat (filename, &st) != 0) { errsv = errno; g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -295,22 +370,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 bc601dad..723c4436 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.h +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -15,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__ @@ -23,42 +22,56 @@ #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, + NMS_KEYFILE_FILETYPE_NMMETA, } NMSKeyfileFiletype; -const char *nms_keyfile_utils_get_path (void); +typedef enum { + NMS_KEYFILE_STORAGE_TYPE_RUN = 1, /* read-write, runtime only, e.g. /run */ + NMS_KEYFILE_STORAGE_TYPE_ETC = 2, /* read-write, persistent, e.g. /etc */ + NMS_KEYFILE_STORAGE_TYPE_LIB_BASE = 3, /* read-only, e.g. /usr/lib */ + + _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST = 1000, +} NMSKeyfileStorageType; + +static inline NMSKeyfileStorageType +NMS_KEYFILE_STORAGE_TYPE_LIB (guint run_idx) +{ + nm_assert (run_idx <= (_NMS_KEYFILE_STORAGE_TYPE_LIB_LAST - NMS_KEYFILE_STORAGE_TYPE_LIB_BASE)); + return NMS_KEYFILE_STORAGE_TYPE_LIB_BASE + run_idx; +} /*****************************************************************************/ -char *nms_keyfile_loaded_uuid_filename (const char *dirname, - const char *uuid, - gboolean temporary); - -gboolean nms_keyfile_loaded_uuid_read (const char *dirname, - const char *filename, - char **out_full_filename, - char **out_uuid, - char **out_loaded_path); - -gboolean nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, - char **out_dirname, - char **out_filename, - char **out_uuid, - char **out_loaded_path); - -gboolean nms_keyfile_loaded_uuid_write (const char *dirname, - const char *uuid, - const char *loaded_path, - gboolean allow_relative, - char **out_full_filename); +const char *nms_keyfile_nmmeta_check_filename (const char *filename, + guint *out_uuid_len); + +char *nms_keyfile_nmmeta_filename (const char *dirname, + const char *uuid, + gboolean temporary); + +gboolean nms_keyfile_nmmeta_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage, + struct stat *out_st); + +gboolean nms_keyfile_nmmeta_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage); + +gboolean nms_keyfile_nmmeta_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean loaded_path_allow_relative, + const char *shadowed_storage, + char **out_full_filename); /*****************************************************************************/ diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c index 2e06aeb9..5fbbb7a1 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.c +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -169,6 +168,10 @@ _handler_write (NMConnection *connection, static gboolean _internal_write_connection (NMConnection *connection, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, const char *keyfile_dir, const char *profile_dir, gboolean with_extension, @@ -177,6 +180,8 @@ _internal_write_connection (NMConnection *connection, const char *existing_path, gboolean existing_path_read_only, gboolean force_rename, + NMSKeyfileWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_path, NMConnection **out_reread, gboolean *out_reread_same, @@ -191,33 +196,57 @@ _internal_write_connection (NMConnection *connection, GError *local_err = NULL; int errsv; gboolean rename; + int i_path; g_return_val_if_fail (!out_path || !*out_path, FALSE); g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + + nm_assert (!shadowed_owned || shadowed_storage); + rename = force_rename || existing_path_read_only || ( existing_path && !nm_utils_file_is_in_path (existing_path, keyfile_dir)); - switch (_nm_connection_verify (connection, error)) { - case NM_SETTING_VERIFY_NORMALIZABLE: - nm_assert_not_reached (); - /* fall-through */ - case NM_SETTING_VERIFY_SUCCESS: - break; - default: - g_return_val_if_reached (FALSE); - } - id = nm_connection_get_id (connection); - g_assert (id && *id); + nm_assert (id && *id); info.keyfile_dir = keyfile_dir; 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); + } + + if (shadowed_storage) { + g_key_file_set_string (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE, + shadowed_storage); + } + + if (shadowed_owned) { + g_key_file_set_boolean (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED, + TRUE); + } + kf_content_buf = g_key_file_to_data (kf_file, &kf_content_len, error); if (!kf_content_buf) return FALSE; @@ -225,64 +254,59 @@ _internal_write_connection (NMConnection *connection, if (!g_file_test (keyfile_dir, G_FILE_TEST_IS_DIR)) (void) g_mkdir_with_parents (keyfile_dir, 0755); - /* If we have existing file path, use it. Else generate one from - * connection's ID. - */ - if ( existing_path - && !rename) - path = g_strdup (existing_path); - else { - gs_free char *filename_escaped = NULL; + for (i_path = -2; i_path < 10000; i_path++) { + gs_free char *path_candidate = NULL; + gboolean is_existing_path; - filename_escaped = nm_keyfile_utils_create_filename (id, with_extension); - path = g_build_filename (keyfile_dir, filename_escaped, NULL); - } - - /* If a file with this path already exists (but isn't the existing path - * of the connection) then we need another name. Multiple connections - * can have the same ID (ie if two connections with the same ID are visible - * to different users) but of course can't have the same path. Yeah, - * there's a race here, but there's not a lot we can do about it, and - * we shouldn't get more than one connection with the same UUID either. - */ - if ( !nm_streq0 (path, existing_path) - && g_file_test (path, G_FILE_TEST_EXISTS)) { - guint i; - gboolean name_found = FALSE; + if (i_path == -2) { + if ( !existing_path + || rename) + continue; + path_candidate = g_strdup (existing_path); + } else if (i_path == -1) { + gs_free char *filename_escaped = NULL; - /* A keyfile with this connection's ID already exists. Pick another name. */ - for (i = 0; i < 100; i++) { + filename_escaped = nm_keyfile_utils_create_filename (id, with_extension); + path_candidate = g_build_filename (keyfile_dir, filename_escaped, NULL); + } else { gs_free char *filename_escaped = NULL; gs_free char *filename = NULL; - if (i == 0) + if (i_path == 0) filename = g_strdup_printf ("%s-%s", id, nm_connection_get_uuid (connection)); else - filename = g_strdup_printf ("%s-%s-%u", id, nm_connection_get_uuid (connection), i); + filename = g_strdup_printf ("%s-%s-%d", id, nm_connection_get_uuid (connection), i_path); filename_escaped = nm_keyfile_utils_create_filename (filename, with_extension); - g_free (path); - path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); - - if ( nm_streq0 (path, existing_path) - || !g_file_test (path, G_FILE_TEST_EXISTS)) { - name_found = TRUE; - break; - } + path_candidate = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); } - if (!name_found) { - if (existing_path_read_only || !existing_path) { - /* this really should not happen, we tried hard to find an unused name... bail out. */ - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "could not find suitable keyfile file name (%s already used)", path); - return FALSE; - } - /* Both our preferred path based on connection id and id-uuid are taken. - * Fallback to @existing_path */ - g_free (path); - path = g_strdup (existing_path); + + is_existing_path = existing_path + && nm_streq (existing_path, path_candidate); + + if ( is_existing_path + && rename) + continue; + + if ( allow_filename_cb + && !allow_filename_cb (path_candidate, allow_filename_user_data)) + continue; + + if (!is_existing_path) { + if (g_file_test (path_candidate, G_FILE_TEST_EXISTS)) + continue; } + + path = g_steal_pointer (&path_candidate); + break; + } + + if (!path) { + /* this really should not happen, we tried hard to find an unused name... bail out. */ + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "could not find suitable keyfile file name (%s already used)", path); + return FALSE; } nm_utils_file_set_contents (path, kf_content_buf, kf_content_len, 0600, &local_err); @@ -348,30 +372,37 @@ _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 *shadowed_storage, + gboolean shadowed_owned, + 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, char **out_path, NMConnection **out_reread, 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, + shadowed_storage, + shadowed_owned, 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, out_path, out_reread, out_reread_same, @@ -389,6 +420,10 @@ nms_keyfile_writer_test_connection (NMConnection *connection, GError **error) { return _internal_write_connection (connection, + FALSE, + FALSE, + NULL, + FALSE, keyfile_dir, keyfile_dir, FALSE, @@ -397,6 +432,8 @@ nms_keyfile_writer_test_connection (NMConnection *connection, NULL, FALSE, FALSE, + NULL, + NULL, out_path, out_reread, out_reread_same, diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h index 030fb7cc..99e86025 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.h +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -24,10 +23,21 @@ #include "nm-connection.h" +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 *shadowed_storage, + gboolean shadowed_owned, + 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, char **out_path, NMConnection **out_reread, gboolean *out_reread_same, diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build index 752b6d7b..f1e96bdf 100644 --- a/src/settings/plugins/keyfile/tests/meson.build +++ b/src/settings/plugins/keyfile/tests/meson.build @@ -1,6 +1,4 @@ -test_unit = 'test-keyfile' - -test_keyfiles_dir = join_paths(meson.current_source_dir(), 'keyfiles') +test_unit = 'test-keyfile-settings' exe = executable( test_unit, @@ -9,7 +7,7 @@ exe = executable( ) test( - 'keyfile/' + test_unit, + test_unit, test_script, args: test_args + [exe.full_path()], timeout: default_test_timeout, diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c index baecac13..f96111a2 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -73,7 +72,12 @@ check_ip_route (NMSettingIPConfig *config, int idx, const char *destination, int \ _connection = nms_keyfile_reader_from_file (full_filename, \ NULL, \ - (nmtst_get_rand_int () % 2) ? &_error : NULL); \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + (nmtst_get_rand_uint32 () % 2) ? &_error : NULL); \ nmtst_assert_success (_connection, _error); \ nmtst_assert_connection_verifies_without_normalization (_connection); \ \ @@ -128,7 +132,7 @@ write_test_connection_reread (NMConnection *connection, gid_t owner_grp; gboolean success; GError *error = NULL; - GError **p_error = (nmtst_get_rand_int () % 2) ? &error : NULL; + GError **p_error = (nmtst_get_rand_uint32 () % 2) ? &error : NULL; gs_unref_object NMConnection *connection_normalized = NULL; g_assert (NM_IS_CONNECTION (connection)); @@ -2508,14 +2512,14 @@ test_nm_keyfile_plugin_utils_escape_filename (void) /*****************************************************************************/ static void -_assert_keyfile_loaded_uuid (const char *dirname, - const char *uuid, - const char *loaded_path, - gboolean allow_relative, - const char *exp_full_filename, - const char *exp_uuid, - const char *exp_symlink_target, - const char *exp_loaded_path) +_assert_keyfile_nmmeta (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean allow_relative, + const char *exp_full_filename, + const char *exp_uuid, + const char *exp_symlink_target, + const char *exp_loaded_path) { gs_free char *full_filename = NULL; gs_free char *symlink_target = NULL; @@ -2534,12 +2538,12 @@ _assert_keyfile_loaded_uuid (const char *dirname, filename = g_path_get_basename (exp_full_filename); - full_filename = nms_keyfile_loaded_uuid_filename (dirname, uuid, FALSE); + full_filename = nms_keyfile_nmmeta_filename (dirname, uuid, FALSE); g_assert_cmpstr (full_filename, ==, full_filename); nm_clear_g_free (&full_filename); - g_assert (nms_keyfile_loaded_uuid_write (dirname, uuid, loaded_path, allow_relative, &full_filename)); + g_assert (nms_keyfile_nmmeta_write (dirname, uuid, loaded_path, allow_relative, NULL, &full_filename)); g_assert_cmpstr (full_filename, ==, exp_full_filename); nm_clear_g_free (&full_filename); @@ -2551,7 +2555,7 @@ _assert_keyfile_loaded_uuid (const char *dirname, g_assert_cmpstr (symlink_target, ==, exp_symlink_target); - success = nms_keyfile_loaded_uuid_read (dirname, filename, &full_filename, &uuid2, &loaded_path2); + success = nms_keyfile_nmmeta_read (dirname, filename, &full_filename, &uuid2, &loaded_path2, NULL, NULL); g_assert_cmpint (!!exp_uuid, ==, success); if (success) g_assert_cmpstr (full_filename, ==, exp_full_filename); @@ -2562,7 +2566,7 @@ _assert_keyfile_loaded_uuid (const char *dirname, g_assert_cmpstr (loaded_path2, ==, exp_loaded_path); - success = nms_keyfile_loaded_uuid_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3); + success = nms_keyfile_nmmeta_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3, NULL); g_assert_cmpint (!!exp_uuid, ==, success); if (success) { g_assert_cmpstr (dirname3, ==, dirname); @@ -2576,34 +2580,33 @@ _assert_keyfile_loaded_uuid (const char *dirname, } static void -test_loaded_uuid (void) +test_nmmeta (void) { const char *uuid = "3c03fd17-ddc3-4100-a954-88b6fafff959"; - gs_free char *filename = g_strdup_printf ("%s%s%s", - NM_KEYFILE_PATH_PREFIX_NMLOADED, + gs_free char *filename = g_strdup_printf ("%s%s", uuid, - NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); + NM_KEYFILE_PATH_SUFFIX_NMMETA); gs_free char *full_filename = g_strdup_printf ("%s/%s", TEST_SCRATCH_DIR, filename); - const char *loaded_path0 = NM_KEYFILE_PATH_NMLOADED_NULL; + const char *loaded_path0 = NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL; const char *loaded_path1 = "/some/where/but/not/scratch/dir"; const char *filename2 = "foo1"; gs_free char *loaded_path2 = g_strdup_printf ("%s/%s", TEST_SCRATCH_DIR, filename2); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, NULL, FALSE, full_filename, NULL, NULL, NULL); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, NULL, TRUE, full_filename, NULL, NULL, NULL); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, NULL, FALSE, full_filename, NULL, NULL, NULL); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, NULL, TRUE, full_filename, NULL, NULL, NULL); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path0, FALSE, full_filename, uuid, loaded_path0, loaded_path0); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path0, TRUE, full_filename, uuid, loaded_path0, loaded_path0); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path0, FALSE, full_filename, uuid, loaded_path0, loaded_path0); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path0, TRUE, full_filename, uuid, loaded_path0, loaded_path0); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path1, FALSE, full_filename, uuid, loaded_path1, loaded_path1); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path1, TRUE, full_filename, uuid, loaded_path1, loaded_path1); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path1, FALSE, full_filename, uuid, loaded_path1, loaded_path1); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path1, TRUE, full_filename, uuid, loaded_path1, loaded_path1); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path2, FALSE, full_filename, uuid, loaded_path2, loaded_path2); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path2, TRUE, full_filename, uuid, filename2, loaded_path2); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path2, FALSE, full_filename, uuid, loaded_path2, loaded_path2); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path2, TRUE, full_filename, uuid, filename2, loaded_path2); (void) unlink (full_filename); } @@ -2697,7 +2700,7 @@ int main (int argc, char **argv) g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename", test_nm_keyfile_plugin_utils_escape_filename); - g_test_add_func ("/keyfile/test_loaded_uuid", test_loaded_uuid); + g_test_add_func ("/keyfile/test_nmmeta", test_nmmeta); return g_test_run (); } diff --git a/src/settings/plugins/meson.build b/src/settings/plugins/meson.build index a1aa7823..83981aab 100644 --- a/src/settings/plugins/meson.build +++ b/src/settings/plugins/meson.build @@ -1,7 +1,3 @@ -if enable_ibft - subdir('ibft') -endif - if enable_ifcfg_rh subdir('ifcfg-rh') endif |