diff options
Diffstat (limited to 'src/settings')
37 files changed, 3011 insertions, 3115 deletions
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 3d6b1cfb..bcd17843 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -36,6 +36,7 @@ #include "nm-simple-connection.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" +#include "nm-utils/c-list.h" #include "introspection/org.freedesktop.NetworkManager.AgentManager.h" @@ -50,6 +51,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { NMAuthManager *auth_mgr; + NMSessionMonitor *session_monitor; /* Auth chains for checking agent permissions */ GSList *chains; @@ -59,7 +61,7 @@ typedef struct { */ GHashTable *agents; - GHashTable *requests; + CList requests; } NMAgentManagerPrivate; struct _NMAgentManager { @@ -123,7 +125,7 @@ typedef struct _NMAgentManagerCallId Request; static void request_add_agent (Request *req, NMSecretAgent *agent); -static void request_remove_agent (Request *req, NMSecretAgent *agent, GSList **pending_reqs); +static void request_remove_agent (Request *req, NMSecretAgent *agent); static void request_next_agent (Request *req); @@ -155,14 +157,62 @@ _request_type_to_string (RequestType request_type, gboolean verbose) /*****************************************************************************/ +struct _NMAgentManagerCallId { + CList lst_request; + + NMAgentManager *self; + + RequestType request_type; + + char *detail; + + NMAuthSubject *subject; + + /* Current agent being asked for secrets */ + NMSecretAgent *current; + NMSecretAgentCallId current_call_id; + + /* Stores the sorted list of NMSecretAgents which will be asked for secrets */ + GSList *pending; + + guint idle_id; + + union { + struct { + char *path; + NMConnection *connection; + + NMAuthChain *chain; + + /* Whether the agent currently being asked for secrets + * has the system.modify privilege. + */ + gboolean current_has_modify; + + union { + struct { + NMSecretAgentGetSecretsFlags flags; + char *setting_name; + char **hints; + + GVariant *existing_secrets; + + NMAgentSecretsResultFunc callback; + gpointer callback_data; + } get; + }; + } con; + }; +}; + +/*****************************************************************************/ + static gboolean remove_agent (NMAgentManager *self, const char *owner) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); NMSecretAgent *agent; - GHashTableIter iter; - gpointer data; - GSList *pending_reqs = NULL; + CList *iter, *safe; g_return_val_if_fail (owner != NULL, FALSE); @@ -174,16 +224,8 @@ remove_agent (NMAgentManager *self, const char *owner) _LOGD (agent, "agent unregistered or disappeared"); /* Remove this agent from any in-progress secrets requests */ - g_hash_table_iter_init (&iter, priv->requests); - while (g_hash_table_iter_next (&iter, &data, NULL)) - request_remove_agent ((Request *) data, agent, &pending_reqs); - - /* We cannot call request_next_agent() from within hash iterating loop, - * because it may remove the request from the hash table, which invalidates - * the iterator. So, only remove the agent from requests. And store the requests - * that should be sent to other agent to a temporary list to proceed afterwards. - */ - g_slist_free_full (pending_reqs, (GDestroyNotify) request_next_agent); + c_list_for_each_safe (iter, safe, &priv->requests) + request_remove_agent (c_list_entry (iter, Request, lst_request), agent); /* And dispose of the agent */ g_hash_table_remove (priv->agents, owner); @@ -270,8 +312,7 @@ agent_register_permissions_done (NMAuthChain *chain, const char *sender; GError *local = NULL; NMAuthCallResult result; - GHashTableIter iter; - Request *req; + CList *iter; g_assert (context); @@ -304,9 +345,8 @@ agent_register_permissions_done (NMAuthChain *chain, g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent); /* Add this agent to any in-progress secrets requests */ - g_hash_table_iter_init (&iter, priv->requests); - while (g_hash_table_iter_next (&iter, (gpointer) &req, NULL)) - request_add_agent (req, agent); + c_list_for_each (iter, &priv->requests) + request_add_agent (c_list_entry (iter, Request, lst_request), agent); } nm_auth_chain_unref (chain); @@ -450,52 +490,6 @@ done: /*****************************************************************************/ -struct _NMAgentManagerCallId { - NMAgentManager *self; - - RequestType request_type; - - char *detail; - - NMAuthSubject *subject; - - /* Current agent being asked for secrets */ - NMSecretAgent *current; - NMSecretAgentCallId current_call_id; - - /* Stores the sorted list of NMSecretAgents which will be asked for secrets */ - GSList *pending; - - guint idle_id; - - union { - struct { - char *path; - NMConnection *connection; - - NMAuthChain *chain; - - /* Whether the agent currently being asked for secrets - * has the system.modify privilege. - */ - gboolean current_has_modify; - - union { - struct { - NMSecretAgentGetSecretsFlags flags; - char *setting_name; - char **hints; - - GVariant *existing_secrets; - - NMAgentSecretsResultFunc callback; - gpointer callback_data; - } get; - }; - } con; - }; -}; - static Request * request_new (NMAgentManager *self, RequestType request_type, @@ -509,6 +503,7 @@ request_new (NMAgentManager *self, req->request_type = request_type; req->detail = g_strdup (detail); req->subject = g_object_ref (subject); + c_list_link_tail (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &req->lst_request); return req; } @@ -597,7 +592,7 @@ req_complete_cancel (Request *req, gboolean is_disposing) gs_free_error GError *error = NULL; nm_assert (req && req->self); - nm_assert (!g_hash_table_contains (req->self->_priv.requests, req)); + nm_assert (!c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (req->self)->requests, &req->lst_request)); nm_utils_error_set_cancelled (&error, is_disposing, "NMAgentManager"); req_complete_release (req, NULL, NULL, NULL, error); @@ -611,10 +606,11 @@ req_complete (Request *req, GError *error) { NMAgentManager *self = req->self; - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - if (!g_hash_table_remove (priv->requests, req)) - g_return_if_reached (); + nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &req->lst_request)); + + c_list_unlink_init (&req->lst_request); + req_complete_release (req, secrets, agent_dbus_owner, agent_username, error); } @@ -630,6 +626,7 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data) NMSecretAgent *a = (NMSecretAgent *)aa; NMSecretAgent *b = (NMSecretAgent *)bb; Request *req = user_data; + NMSessionMonitor *sm; gboolean a_active, b_active; gulong a_pid, b_pid, requester; @@ -648,8 +645,9 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data) } /* Prefer agents in active sessions */ - a_active = nm_session_monitor_session_exists (nm_session_monitor_get (), nm_secret_agent_get_owner_uid (a), TRUE); - b_active = nm_session_monitor_session_exists (nm_session_monitor_get (), nm_secret_agent_get_owner_uid (b), TRUE); + sm = NM_AGENT_MANAGER_GET_PRIVATE (req->self)->session_monitor; + a_active = nm_session_monitor_session_exists (sm, nm_secret_agent_get_owner_uid (a), TRUE); + b_active = nm_session_monitor_session_exists (sm, nm_secret_agent_get_owner_uid (b), TRUE); if (a_active && !b_active) return -1; else if (a_active == b_active) @@ -734,7 +732,7 @@ request_next_agent (Request *req) nm_secret_agent_cancel_secrets (req->current, req->current_call_id); g_clear_object (&req->current); } - g_warn_if_fail (!req->current_call_id); + nm_assert (!req->current_call_id); if (req->pending) { /* Send the request to the next agent */ @@ -769,7 +767,7 @@ request_next_agent (Request *req) } static void -request_remove_agent (Request *req, NMSecretAgent *agent, GSList **pending_reqs) +request_remove_agent (Request *req, NMSecretAgent *agent) { NMAgentManager *self; @@ -798,7 +796,7 @@ request_remove_agent (Request *req, NMSecretAgent *agent, GSList **pending_reqs) g_assert_not_reached (); } - *pending_reqs = g_slist_prepend (*pending_reqs, req); + request_next_agent (req); } else if (g_slist_find (req->pending, agent)) { req->pending = g_slist_remove (req->pending, agent); @@ -1219,7 +1217,6 @@ nm_agent_manager_get_secrets (NMAgentManager *self, NMAgentSecretsResultFunc callback, gpointer callback_data) { - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); Request *req; g_return_val_if_fail (self != NULL, NULL); @@ -1253,9 +1250,6 @@ nm_agent_manager_get_secrets (NMAgentManager *self, req->con.get.callback = callback; req->con.get.callback_data = callback_data; - if (!nm_g_hash_table_add (priv->requests, req)) - g_assert_not_reached (); - /* Kick off the request */ if (!(req->con.get.flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM)) request_add_agents (self, req); @@ -1271,9 +1265,9 @@ nm_agent_manager_cancel_secrets (NMAgentManager *self, g_return_if_fail (request_id); g_return_if_fail (request_id->request_type == REQUEST_TYPE_CON_GET); - if (!g_hash_table_remove (NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, - request_id)) - g_return_if_reached (); + nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &request_id->lst_request)); + + c_list_unlink_init (&request_id->lst_request); req_complete_cancel (request_id, FALSE); } @@ -1341,7 +1335,6 @@ nm_agent_manager_save_secrets (NMAgentManager *self, NMConnection *connection, NMAuthSubject *subject) { - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); Request *req; g_return_if_fail (self); @@ -1359,8 +1352,6 @@ nm_agent_manager_save_secrets (NMAgentManager *self, subject); req->con.path = g_strdup (path); req->con.connection = g_object_ref (connection); - if (!nm_g_hash_table_add (priv->requests, req)) - g_assert_not_reached (); /* Kick off the request */ request_add_agents (self, req); @@ -1426,7 +1417,6 @@ nm_agent_manager_delete_secrets (NMAgentManager *self, const char *path, NMConnection *connection) { - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); NMAuthSubject *subject; Request *req; @@ -1447,8 +1437,6 @@ nm_agent_manager_delete_secrets (NMAgentManager *self, req->con.path = g_strdup (path); req->con.connection = g_object_ref (connection); g_object_unref (subject); - if (!nm_g_hash_table_add (priv->requests, req)) - g_assert_not_reached (); /* Kick off the request */ request_add_agents (self, req); @@ -1570,8 +1558,8 @@ nm_agent_manager_init (NMAgentManager *self) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - priv->agents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - priv->requests = g_hash_table_new (g_direct_hash, g_direct_equal); + c_list_init (&priv->requests); + priv->agents = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void @@ -1582,6 +1570,7 @@ constructed (GObject *object) G_OBJECT_CLASS (nm_agent_manager_parent_class)->constructed (object); priv->auth_mgr = g_object_ref (nm_auth_manager_get ()); + priv->session_monitor = g_object_ref (nm_session_monitor_get ()); nm_exported_object_export (NM_EXPORTED_OBJECT (object)); @@ -1589,28 +1578,19 @@ constructed (GObject *object) NM_AUTH_MANAGER_SIGNAL_CHANGED, G_CALLBACK (authority_changed_cb), object); - - NM_UTILS_KEEP_ALIVE (object, nm_session_monitor_get (), "NMAgentManager-depends-on-NMSessionMonitor"); } static void dispose (GObject *object) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE ((NMAgentManager *) object); - - if (priv->requests) { - GHashTableIter iter; - Request *req; + CList *iter; cancel_more: - g_hash_table_iter_init (&iter, priv->requests); - if (g_hash_table_iter_next (&iter, (gpointer *) &req, NULL)) { - g_hash_table_iter_remove (&iter); - req_complete_cancel (req, TRUE); - goto cancel_more; - } - g_hash_table_unref (priv->requests); - priv->requests = NULL; + c_list_for_each (iter, &priv->requests) { + c_list_unlink_init (iter); + req_complete_cancel (c_list_entry (iter, Request, lst_request), TRUE); + goto cancel_more; } g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_unref); @@ -1630,6 +1610,8 @@ cancel_more: nm_exported_object_unexport (NM_EXPORTED_OBJECT (object)); + g_clear_object (&priv->session_monitor); + G_OBJECT_CLASS (nm_agent_manager_parent_class)->dispose (object); } diff --git a/src/settings/nm-inotify-helper.c b/src/settings/nm-inotify-helper.c index a0432a25..4c65b02d 100644 --- a/src/settings/nm-inotify-helper.c +++ b/src/settings/nm-inotify-helper.c @@ -188,8 +188,7 @@ finalize (GObject *object) { NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE ((NMInotifyHelper *) object); - if (priv->ifd >= 0) - close (priv->ifd); + nm_close (priv->ifd); g_hash_table_destroy (priv->wd_refs); diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c index b9ca34d0..5fe4dd17 100644 --- a/src/settings/nm-secret-agent.c +++ b/src/settings/nm-secret-agent.c @@ -30,6 +30,7 @@ #include "nm-auth-subject.h" #include "nm-simple-connection.h" #include "NetworkManagerUtils.h" +#include "nm-utils/c-list.h" #include "introspection/org.freedesktop.NetworkManager.SecretAgent.h" @@ -58,7 +59,7 @@ typedef struct { gboolean connection_is_private; gulong on_disconnected_id; - GHashTable *requests; + CList requests; } NMSecretAgentPrivate; struct _NMSecretAgent { @@ -99,6 +100,7 @@ G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT) /*****************************************************************************/ struct _NMSecretAgentCallId { + CList lst; NMSecretAgent *agent; GCancellable *cancellable; char *path; @@ -129,6 +131,8 @@ request_new (NMSecretAgent *self, r->callback = callback; r->callback_data = callback_data; r->cancellable = g_cancellable_new (); + c_list_link_tail (&NM_SECRET_AGENT_GET_PRIVATE (self)->requests, + &r->lst); _LOGt ("request "LOG_REQ_FMT": created", LOG_REQ_ARG (r)); return r; } @@ -140,6 +144,7 @@ request_free (Request *r) NMSecretAgent *self = r->agent; _LOGt ("request "LOG_REQ_FMT": destroyed", LOG_REQ_ARG (r)); + c_list_unlink (&r->lst); g_free (r->path); g_free (r->setting_name); if (r->cancellable) @@ -150,17 +155,15 @@ request_free (Request *r) static gboolean request_check_return (Request *r) { - NMSecretAgentPrivate *priv; - if (!r->cancellable) return FALSE; g_return_val_if_fail (NM_IS_SECRET_AGENT (r->agent), FALSE); - priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent); + nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (r->agent)->requests, + &r->lst)); - if (!g_hash_table_remove (priv->requests, r)) - g_return_val_if_reached (FALSE); + c_list_unlink_init (&r->lst); return TRUE; } @@ -373,7 +376,6 @@ nm_secret_agent_get_secrets (NMSecretAgent *self, r = request_new (self, "GetSecrets", path, setting_name, callback, callback_data); r->is_get_secrets = TRUE; - g_hash_table_add (priv->requests, r); /* Increase the timeout only for this call */ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->proxy), 120000); @@ -467,15 +469,15 @@ do_cancel_secrets (NMSecretAgent *self, Request *r, gboolean disposing) void nm_secret_agent_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId call_id) { - NMSecretAgentPrivate *priv; Request *r = call_id; g_return_if_fail (NM_IS_SECRET_AGENT (self)); g_return_if_fail (r); - priv = NM_SECRET_AGENT_GET_PRIVATE (self); - if (!g_hash_table_remove (priv->requests, r)) - g_return_if_reached (); + nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (self)->requests, + &r->lst)); + + c_list_unlink_init (&r->lst); do_cancel_secrets (self, r, FALSE); } @@ -523,7 +525,6 @@ nm_secret_agent_save_secrets (NMSecretAgent *self, dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL); r = request_new (self, "SaveSecrets", path, NULL, callback, callback_data); - g_hash_table_add (priv->requests, r); nmdbus_secret_agent_call_save_secrets (priv->proxy, dict, path, @@ -576,7 +577,6 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self, dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_NO_SECRETS); r = request_new (self, "DeleteSecrets", path, NULL, callback, callback_data); - g_hash_table_add (priv->requests, r); nmdbus_secret_agent_call_delete_secrets (priv->proxy, dict, path, @@ -634,7 +634,7 @@ _on_disconnected_name_owner_changed (GDBusConnection *connection, { NMSecretAgent *self = NM_SECRET_AGENT (user_data); NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - const char *old_owner, *new_owner; + const char *old_owner = NULL, *new_owner = NULL; g_variant_get (parameters, "(&s&s&s)", @@ -747,7 +747,7 @@ nm_secret_agent_init (NMSecretAgent *self) { NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - priv->requests = g_hash_table_new (g_direct_hash, g_direct_equal); + c_list_init (&priv->requests); } static void @@ -755,13 +755,13 @@ dispose (GObject *object) { NMSecretAgent *self = NM_SECRET_AGENT (object); NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - GHashTableIter iter; - Request *r; + CList *iter; - g_hash_table_iter_init (&iter, priv->requests); - while (g_hash_table_iter_next (&iter, (gpointer *) &r, NULL)) { - g_hash_table_iter_remove (&iter); - do_cancel_secrets (self, r, TRUE); +again: + c_list_for_each (iter, &priv->requests) { + c_list_unlink_init (iter); + do_cancel_secrets (self, c_list_entry (iter, Request, lst), TRUE); + goto again; } _on_disconnected_cleanup (priv); @@ -783,7 +783,6 @@ finalize (GObject *object) g_free (priv->dbus_owner); g_slist_free_full (priv->permissions, g_free); - g_hash_table_destroy (priv->requests); G_OBJECT_CLASS (nm_secret_agent_parent_class)->finalize (object); diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 45a1b664..ed69115c 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -109,7 +109,7 @@ typedef struct _NMSettingsConnectionPrivate { GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */ int autoconnect_retries; - gint32 autoconnect_retry_time; + gint32 autoconnect_blocked_until; char *filename; } NMSettingsConnectionPrivate; @@ -510,18 +510,12 @@ connection_changed_cb (NMSettingsConnection *self, gpointer unused) _emit_updated (self, FALSE); } -/* Update the settings of this connection to match that of 'new_connection', - * taking care to make a private copy of secrets. - */ gboolean -nm_settings_connection_replace_settings (NMSettingsConnection *self, - NMConnection *new_connection, - gboolean update_unsaved, - const char *log_diff_name, - GError **error) +nm_settings_connection_replace_settings_prepare (NMSettingsConnection *self, + NMConnection *new_connection, + GError **error) { NMSettingsConnectionPrivate *priv; - gboolean success = FALSE; g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE); @@ -540,6 +534,30 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self, return FALSE; } + return TRUE; +} + +gboolean +nm_settings_connection_replace_settings_full (NMSettingsConnection *self, + NMConnection *new_connection, + gboolean prepare_new_connection, + gboolean update_unsaved, + const char *log_diff_name, + GError **error) +{ + NMSettingsConnectionPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE); + + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + + if ( prepare_new_connection + && !nm_settings_connection_replace_settings_prepare (self, + new_connection, + error)) + return FALSE; + /* Do nothing if there's nothing to update */ if (nm_connection_compare (NM_CONNECTION (self), new_connection, @@ -567,7 +585,6 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self, * nm_connection_clear_secrets() and clears them. */ update_system_secrets_cache (self); - success = TRUE; /* Add agent and always-ask secrets back; they won't necessarily be * in the replacement connection data if it was eg reread from disk. @@ -594,114 +611,101 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self, _emit_updated (self, TRUE); - return success; -} - -static void -ignore_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ + return TRUE; } -/* Replaces the settings in this connection with those in 'new_connection'. If - * any changes are made, commits them to permanent storage and to any other - * subsystems watching this connection. Before returning, 'callback' is run - * with the given 'user_data' along with any errors encountered. +/* Update the settings of this connection to match that of 'new_connection', + * taking care to make a private copy of secrets. */ -static void -replace_and_commit (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) +gboolean +nm_settings_connection_replace_settings (NMSettingsConnection *self, + NMConnection *new_connection, + gboolean update_unsaved, + const char *log_diff_name, + GError **error) { - GError *error = NULL; - NMSettingsConnectionCommitReason commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION; - - if (g_strcmp0 (nm_connection_get_id (NM_CONNECTION (self)), - nm_connection_get_id (new_connection)) != 0) - commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED; - - if (nm_settings_connection_replace_settings (self, new_connection, TRUE, "replace-and-commit-disk", &error)) - nm_settings_connection_commit_changes (self, commit_reason, callback, user_data); - else { - g_assert (error); - if (callback) - callback (self, error, user_data); - g_clear_error (&error); - } + return nm_settings_connection_replace_settings_full (self, + new_connection, + TRUE, + update_unsaved, + log_diff_name, + error); } -void -nm_settings_connection_replace_and_commit (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) +gboolean +nm_settings_connection_commit_changes (NMSettingsConnection *self, + NMConnection *new_connection, + NMSettingsConnectionCommitReason commit_reason, + GError **error) { - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); - g_return_if_fail (NM_IS_CONNECTION (new_connection)); + NMSettingsConnectionClass *klass; + gs_free_error GError *local = NULL; + gs_unref_object NMConnection *reread_connection = NULL; + gs_free char *logmsg_change = NULL; - NM_SETTINGS_CONNECTION_GET_CLASS (self)->replace_and_commit (self, new_connection, callback, user_data); -} + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); -static void -commit_changes (NMSettingsConnection *self, - NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) -{ - /* Subclasses only call this function if the save was successful, so at - * this point the connection is synced to disk and no longer unsaved. - */ - set_unsaved (self, FALSE); + klass = NM_SETTINGS_CONNECTION_GET_CLASS (self); + if (!klass->commit_changes) { + _LOGW ("write: setting plugin %s does not support to write connection", + G_OBJECT_TYPE_NAME (self)); + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "writing settings not supported"); + return FALSE; + } - g_object_ref (self); - callback (self, NULL, user_data); - g_object_unref (self); -} + if ( new_connection + && !nm_settings_connection_replace_settings_prepare (self, + new_connection, + &local)) { + _LOGW ("write: failed to prepare connection for writing: %s", + local->message); + g_propagate_error (error, g_steal_pointer (&local)); + return FALSE; + } -void -nm_settings_connection_commit_changes (NMSettingsConnection *self, - NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) -{ - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); + if (!klass->commit_changes (self, + new_connection, + commit_reason, + &reread_connection, + &logmsg_change, + &local)) { + _LOGW ("write: failure to write setting: %s", + local->message); + g_propagate_error (error, g_steal_pointer (&local)); + return FALSE; + } - if (NM_SETTINGS_CONNECTION_GET_CLASS (self)->commit_changes) { - NM_SETTINGS_CONNECTION_GET_CLASS (self)->commit_changes (self, - commit_reason, - callback ? callback : ignore_cb, - user_data); - } else { - GError *error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "%s: %s:%d commit_changes() unimplemented", __func__, __FILE__, __LINE__); - if (callback) - callback (self, error, user_data); - g_error_free (error); + if (reread_connection || new_connection) { + if (!nm_settings_connection_replace_settings_full (self, + reread_connection ?: new_connection, + !reread_connection, + FALSE, + new_connection + ? "update-during-write" + : "replace-and-commit-disk", + &local)) { + /* this can't really happen, because at this point replace-settings + * is no longer supposed to fail. It's a bug. */ + _LOGE ("write: replacing setting failed: %s", + local->message); + g_propagate_error (error, g_steal_pointer (&local)); + g_return_val_if_reached (FALSE); + } } -} -void -nm_settings_connection_delete (NMSettingsConnection *self, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) -{ - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); + set_unsaved (self, FALSE); - if (NM_SETTINGS_CONNECTION_GET_CLASS (self)->delete) { - NM_SETTINGS_CONNECTION_GET_CLASS (self)->delete (self, - callback ? callback : ignore_cb, - user_data); - } else { - GError *error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "%s: %s:%d delete() unimplemented", __func__, __FILE__, __LINE__); - if (callback) - callback (self, error, user_data); - g_error_free (error); - } + 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 commited (%s)", logmsg_change); + + return TRUE; } static void @@ -740,15 +744,32 @@ remove_entry_from_db (NMSettingsConnection *self, const char* db_name) g_key_file_free (key_file); } -static void -do_delete (NMSettingsConnection *self, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) +gboolean +nm_settings_connection_delete (NMSettingsConnection *self, + GError **error) { + gs_unref_object NMSettingsConnection *self_keep_alive = NULL; + NMSettingsConnectionClass *klass; NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); NMConnection *for_agents; - g_object_ref (self); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); + + klass = NM_SETTINGS_CONNECTION_GET_CLASS (self); + + self_keep_alive = g_object_ref (self); + + if (!klass->delete) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "delete not supported"); + return FALSE; + } + if (!klass->delete (self, + error)) + return FALSE; + set_visible (self, FALSE); /* Tell agents to remove secrets for this connection */ @@ -766,12 +787,10 @@ do_delete (NMSettingsConnection *self, remove_entry_from_db (self, "seen-bssids"); nm_settings_connection_signal_remove (self, FALSE); - - callback (self, NULL, user_data); - - g_object_unref (self); + return TRUE; } + /*****************************************************************************/ @@ -887,15 +906,6 @@ secret_is_system_owned (NMSettingSecretFlags flags, } static void -new_secrets_commit_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ - if (error) - _LOGW ("Error saving new secrets to backing storage: %s", error->message); -} - -static void get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */ GetSecretsInfo *info, /* only needed for logging */ NMConnection *connection, @@ -974,6 +984,33 @@ get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */ } } +gboolean +nm_settings_connection_new_secrets (NMSettingsConnection *self, + NMConnection *applied_connection, + const char *setting_name, + GVariant *secrets, + GError **error) +{ + 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_CONNECTION (self), setting_name, secrets, error)) + return FALSE; + + update_system_secrets_cache (self); + update_agent_secrets_cache (self, NULL); + + nm_settings_connection_commit_changes (self, + NULL, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + NULL); + return TRUE; +} + static void get_secrets_done_cb (NMAgentManager *manager, NMAgentManagerCallId call_id_a, @@ -1084,7 +1121,10 @@ get_secrets_done_cb (NMAgentManager *manager, setting_name, info); - nm_settings_connection_commit_changes (self, NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, new_secrets_commit_cb, NULL); + nm_settings_connection_commit_changes (self, + NULL, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + NULL); } else { _LOGD ("(%s:%p) new agent secrets processed", setting_name, @@ -1545,11 +1585,6 @@ typedef struct { char *audit_args; } UpdateInfo; -typedef struct { - GDBusMethodInvocation *context; - NMAuthSubject *subject; -} CallbackInfo; - static void has_some_secrets_cb (NMSetting *setting, const char *key, @@ -1626,33 +1661,6 @@ update_complete (NMSettingsConnection *self, } static void -con_update_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ - UpdateInfo *info = user_data; - NMConnection *for_agent; - - if (!error) { - /* Dupe the connection so we can clear out non-agent-owned secrets, - * as agent-owned secrets are the only ones we send back be saved. - * Only send secrets to agents of the same UID that called update too. - */ - for_agent = nm_simple_connection_new_clone (NM_CONNECTION (self)); - nm_connection_clear_secrets_with_flags (for_agent, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - nm_agent_manager_save_secrets (info->agent_mgr, - nm_connection_get_path (NM_CONNECTION (self)), - for_agent, - info->subject); - g_object_unref (for_agent); - } - - update_complete (self, info, error); -} - -static void update_auth_cb (NMSettingsConnection *self, GDBusMethodInvocation *context, NMAuthSubject *subject, @@ -1660,63 +1668,93 @@ update_auth_cb (NMSettingsConnection *self, gpointer data) { UpdateInfo *info = data; - GError *local = NULL; + NMSettingsConnectionCommitReason commit_reason; + gs_free_error GError *local = NULL; if (error) { update_complete (self, info, error); return; } - if (!info->new_settings) { - /* We're just calling Save(). Just commit the existing connection. */ - if (info->save_to_disk) { - nm_settings_connection_commit_changes (self, - NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION, - con_update_cb, - info); + if (info->new_settings) { + if (!any_secrets_present (info->new_settings)) { + /* 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); + } 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 + * they're in the main connection. + */ + update_agent_secrets_cache (self, info->new_settings); } - return; - } - if (!any_secrets_present (info->new_settings)) { - /* 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); - } 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 - * they're in the main connection. - */ - update_agent_secrets_cache (self, info->new_settings); + if (nm_audit_manager_audit_enabled (nm_audit_manager_get ())) { + gs_unref_hashtable GHashTable *diff = NULL; + gboolean same; + + same = nm_connection_diff (NM_CONNECTION (self), info->new_settings, + NM_SETTING_COMPARE_FLAG_EXACT | + NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT, + &diff); + if (!same && diff) + info->audit_args = nm_utils_format_con_diff_for_audit (diff); + } } - if (nm_audit_manager_audit_enabled (nm_audit_manager_get ())) { - gs_unref_hashtable GHashTable *diff = NULL; - gboolean same; + if (!info->save_to_disk) { + if (info->new_settings) { + nm_settings_connection_replace_settings (self, + info->new_settings, + TRUE, + "replace-unsaved", + &local); + } + goto out; + } - same = nm_connection_diff (NM_CONNECTION (self), info->new_settings, - NM_SETTING_COMPARE_FLAG_EXACT | - NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT, - &diff); - if (!same && diff) - info->audit_args = nm_utils_format_con_diff_for_audit (diff); + if (info->new_settings) { + if (!nm_settings_connection_replace_settings_prepare (self, + info->new_settings, + &local)) + goto out; } - if (info->save_to_disk) { - nm_settings_connection_replace_and_commit (self, - info->new_settings, - con_update_cb, - info); - } else { - if (!nm_settings_connection_replace_settings (self, info->new_settings, TRUE, "replace-and-commit-memory", &local)) - g_assert (local); - con_update_cb (self, local, info); - g_clear_error (&local); + commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION; + if ( info->new_settings + && !nm_streq0 (nm_connection_get_id (NM_CONNECTION (self)), + nm_connection_get_id (info->new_settings))) + commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED; + + nm_settings_connection_commit_changes (self, + info->new_settings, + commit_reason, + &local); + +out: + if (!local) { + gs_unref_object NMConnection *for_agent = NULL; + + /* Dupe the connection so we can clear out non-agent-owned secrets, + * as agent-owned secrets are the only ones we send back be saved. + * Only send secrets to agents of the same UID that called update too. + */ + for_agent = nm_simple_connection_new_clone (NM_CONNECTION (self)); + nm_connection_clear_secrets_with_flags (for_agent, + secrets_filter_cb, + GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + nm_agent_manager_save_secrets (info->agent_mgr, + nm_connection_get_path (NM_CONNECTION (self)), + for_agent, + info->subject); } + + update_complete (self, info, local); } + static const char * get_update_modify_permission (NMConnection *old, NMConnection *new) { @@ -1840,30 +1878,16 @@ impl_settings_connection_save (NMSettingsConnection *self, } static void -con_delete_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ - CallbackInfo *info = user_data; - - if (error) - g_dbus_method_invocation_return_gerror (info->context, error); - else - g_dbus_method_invocation_return_value (info->context, NULL); - - nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, - !error, NULL, info->subject, error ? error->message : NULL); - g_free (info); -} - -static void delete_auth_cb (NMSettingsConnection *self, GDBusMethodInvocation *context, NMAuthSubject *subject, GError *error, gpointer data) { - CallbackInfo *info; + gs_unref_object NMSettingsConnection *self_keep_alive = NULL; + gs_free_error GError *local = NULL; + + self_keep_alive = g_object_ref (self); if (error) { nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, NULL, subject, @@ -1872,11 +1896,15 @@ delete_auth_cb (NMSettingsConnection *self, return; } - info = g_malloc0 (sizeof (*info)); - info->context = context; - info->subject = subject; + nm_settings_connection_delete (self, &local); - nm_settings_connection_delete (self, con_delete_cb, info); + 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); } static const char * @@ -1996,23 +2024,6 @@ impl_settings_connection_get_secrets (NMSettingsConnection *self, } static void -clear_secrets_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ - CallbackInfo *info = user_data; - - if (error) - g_dbus_method_invocation_return_gerror (info->context, error); - else - g_dbus_method_invocation_return_value (info->context, NULL); - - nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self, - !error, NULL, info->subject, error ? error->message : NULL); - g_free (info); -} - -static void dbus_clear_secrets_auth_cb (NMSettingsConnection *self, GDBusMethodInvocation *context, NMAuthSubject *subject, @@ -2020,31 +2031,39 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self, gpointer user_data) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - CallbackInfo *info; + gs_free_error GError *local = NULL; if (error) { g_dbus_method_invocation_return_gerror (context, error); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self, FALSE, NULL, subject, error->message); - } else { - /* Clear secrets in connection and caches */ - nm_connection_clear_secrets (NM_CONNECTION (self)); - if (priv->system_secrets) - nm_connection_clear_secrets (priv->system_secrets); - if (priv->agent_secrets) - nm_connection_clear_secrets (priv->agent_secrets); + return; + } + + /* Clear secrets in connection and caches */ + nm_connection_clear_secrets (NM_CONNECTION (self)); + if (priv->system_secrets) + nm_connection_clear_secrets (priv->system_secrets); + if (priv->agent_secrets) + nm_connection_clear_secrets (priv->agent_secrets); - /* Tell agents to remove secrets for this connection */ - nm_agent_manager_delete_secrets (priv->agent_mgr, - nm_connection_get_path (NM_CONNECTION (self)), - NM_CONNECTION (self)); + /* Tell agents to remove secrets for this connection */ + nm_agent_manager_delete_secrets (priv->agent_mgr, + nm_connection_get_path (NM_CONNECTION (self)), + NM_CONNECTION (self)); - info = g_malloc0 (sizeof (*info)); - info->context = context; - info->subject = subject; + nm_settings_connection_commit_changes (self, + NULL, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + &local); - nm_settings_connection_commit_changes (self, NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, clear_secrets_cb, info); - } + nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, 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); } static void @@ -2512,8 +2531,10 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self) } } +/*****************************************************************************/ + /** - * nm_settings_connection_get_autoconnect_retries: + * nm_settings_connection_autoconnect_retries_get: * @self: the settings connection * * Returns the number of autoconnect retries left. If the value is @@ -2521,14 +2542,13 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self) * with the global default. */ int -nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self) +nm_settings_connection_autoconnect_retries_get (NMSettingsConnection *self) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - if (priv->autoconnect_retries == AUTOCONNECT_RETRIES_UNSET) { + if (G_UNLIKELY (priv->autoconnect_retries == AUTOCONNECT_RETRIES_UNSET)) { NMSettingConnection *s_con; int retries = -1; - const char *value; s_con = nm_connection_get_setting_connection ((NMConnection *) self); if (s_con) @@ -2536,20 +2556,18 @@ nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self) /* -1 means 'default' */ if (retries == -1) { - value = nm_config_data_get_value_cached (NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_GROUP_MAIN, - "autoconnect-retries-default", - NM_CONFIG_GET_VALUE_STRIP); - - retries = _nm_utils_ascii_str_to_int64 (value, - 10, 0, G_MAXINT32, - AUTOCONNECT_RETRIES_DEFAULT); + retries = nm_config_data_get_value_int64 (NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_GROUP_MAIN, + "autoconnect-retries-default", + 10, 0, G_MAXINT32, + AUTOCONNECT_RETRIES_DEFAULT); } /* 0 means 'forever', which is translated to a retry count of -1 */ if (retries == 0) retries = AUTOCONNECT_RETRIES_FOREVER; + _LOGT ("autoconnect-retries: init %d", retries); priv->autoconnect_retries = retries; } @@ -2557,74 +2575,57 @@ nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self) } void -nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self, +nm_settings_connection_autoconnect_retries_set (NMSettingsConnection *self, int retries) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + NMSettingsConnectionPrivate *priv; + + g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); + nm_assert (retries == AUTOCONNECT_RETRIES_UNSET || retries >= 0); + + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); if (priv->autoconnect_retries != retries) { _LOGT ("autoconnect-retries: set %d", retries); priv->autoconnect_retries = retries; } if (retries) - priv->autoconnect_retry_time = 0; + priv->autoconnect_blocked_until = 0; else - priv->autoconnect_retry_time = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER; + priv->autoconnect_blocked_until = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER; } void -nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *self) +nm_settings_connection_autoconnect_retries_reset (NMSettingsConnection *self) { - nm_settings_connection_set_autoconnect_retries (self, AUTOCONNECT_RETRIES_UNSET); + nm_settings_connection_autoconnect_retries_set (self, AUTOCONNECT_RETRIES_UNSET); } gint32 -nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self) +nm_settings_connection_autoconnect_blocked_until_get (NMSettingsConnection *self) { - return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retry_time; + return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_until; } NMSettingsAutoconnectBlockedReason -nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self) +nm_settings_connection_autoconnect_blocked_reason_get (NMSettingsConnection *self) { return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason; } void -nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self, +nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *self, NMSettingsAutoconnectBlockedReason reason) { g_return_if_fail (NM_IN_SET (reason, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS)); NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason = reason; } -gboolean -nm_settings_connection_can_autoconnect (NMSettingsConnection *self) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMSettingConnection *s_con; - const char *permission; - - if ( !priv->visible - || priv->autoconnect_retries == 0 - || priv->autoconnect_blocked_reason != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED) - return FALSE; - - s_con = nm_connection_get_setting_connection (NM_CONNECTION (self)); - if (!nm_setting_connection_get_autoconnect (s_con)) - return FALSE; - - permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (self)); - if (permission) { - if (nm_settings_connection_check_permission (self, permission) == FALSE) - return FALSE; - } - - return TRUE; -} +/*****************************************************************************/ /** * nm_settings_connection_get_nm_generated: @@ -2747,7 +2748,7 @@ nm_settings_connection_init (NMSettingsConnection *self) priv->agent_mgr = g_object_ref (nm_agent_manager_get ()); - priv->seen_bssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + priv->seen_bssids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); priv->autoconnect_retries = AUTOCONNECT_RETRIES_UNSET; @@ -2873,9 +2874,6 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class) object_class->get_property = get_property; object_class->set_property = set_property; - class->replace_and_commit = replace_and_commit; - class->commit_changes = commit_changes; - class->delete = do_delete; class->supports_secrets = supports_secrets; obj_properties[PROP_VISIBLE] = diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index b449e2bd..faacd949 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -83,9 +83,10 @@ typedef enum { /*< skip >*/ } NMSettingsConnectionCommitReason; typedef enum { - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED = 0, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED = 1, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS = 2, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE = 0, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST = 1, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED = 2, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS = 3, } NMSettingsAutoconnectBlockedReason; struct _NMSettingsConnectionCallId; @@ -93,14 +94,6 @@ typedef struct _NMSettingsConnectionCallId *NMSettingsConnectionCallId; typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass; -typedef void (*NMSettingsConnectionCommitFunc) (NMSettingsConnection *self, - GError *error, - gpointer user_data); - -typedef void (*NMSettingsConnectionDeleteFunc) (NMSettingsConnection *self, - GError *error, - gpointer user_data); - struct _NMSettingsConnectionPrivate; struct _NMSettingsConnection { @@ -111,20 +104,15 @@ struct _NMSettingsConnection { struct _NMSettingsConnectionClass { NMExportedObjectClass parent; - /* virtual methods */ - void (*replace_and_commit) (NMSettingsConnection *self, + gboolean (*commit_changes) (NMSettingsConnection *self, NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data); - - void (*commit_changes) (NMSettingsConnection *self, - NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data); + NMSettingsConnectionCommitReason commit_reason, + NMConnection **out_reread_connection, + char **out_logmsg_change, + GError **error); - void (*delete) (NMSettingsConnection *self, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data); + gboolean (*delete) (NMSettingsConnection *self, + GError **error); gboolean (*supports_secrets) (NMSettingsConnection *self, const char *setting_name); @@ -136,10 +124,14 @@ gboolean nm_settings_connection_has_unmodified_applied_connection (NMSettingsCon NMConnection *applied_connection, NMSettingCompareFlags compare_flage); -void nm_settings_connection_commit_changes (NMSettingsConnection *self, - NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data); +gboolean nm_settings_connection_commit_changes (NMSettingsConnection *self, + NMConnection *new_connection, + NMSettingsConnectionCommitReason commit_reason, + GError **error); + +gboolean nm_settings_connection_replace_settings_prepare (NMSettingsConnection *self, + NMConnection *new_connection, + GError **error); gboolean nm_settings_connection_replace_settings (NMSettingsConnection *self, NMConnection *new_connection, @@ -147,14 +139,15 @@ gboolean nm_settings_connection_replace_settings (NMSettingsConnection *self, const char *log_diff_name, GError **error); -void nm_settings_connection_replace_and_commit (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data); +gboolean nm_settings_connection_replace_settings_full (NMSettingsConnection *self, + NMConnection *new_connection, + gboolean prepare_new_connection, + gboolean update_unsaved, + const char *log_diff_name, + GError **error); -void nm_settings_connection_delete (NMSettingsConnection *self, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data); +gboolean nm_settings_connection_delete (NMSettingsConnection *self, + GError **error); typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *self, NMSettingsConnectionCallId call_id, @@ -163,6 +156,12 @@ typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *self, GError *error, gpointer user_data); +gboolean nm_settings_connection_new_secrets (NMSettingsConnection *self, + NMConnection *applied_connection, + const char *setting_name, + GVariant *secrets, + GError **error); + NMSettingsConnectionCallId nm_settings_connection_get_secrets (NMSettingsConnection *self, NMConnection *applied_connection, NMAuthSubject *subject, @@ -214,19 +213,17 @@ void nm_settings_connection_add_seen_bssid (NMSettingsConnection *self, void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self); -int nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self); -void nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self, +int nm_settings_connection_autoconnect_retries_get (NMSettingsConnection *self); +void nm_settings_connection_autoconnect_retries_set (NMSettingsConnection *self, int retries); -void nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *self); +void nm_settings_connection_autoconnect_retries_reset (NMSettingsConnection *self); -gint32 nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self); +gint32 nm_settings_connection_autoconnect_blocked_until_get (NMSettingsConnection *self); -NMSettingsAutoconnectBlockedReason nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self); -void nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self, +NMSettingsAutoconnectBlockedReason nm_settings_connection_autoconnect_blocked_reason_get (NMSettingsConnection *self); +void nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *self, NMSettingsAutoconnectBlockedReason reason); -gboolean nm_settings_connection_can_autoconnect (NMSettingsConnection *self); - gboolean nm_settings_connection_get_nm_generated (NMSettingsConnection *self); gboolean nm_settings_connection_get_volatile (NMSettingsConnection *self); diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c index 7de7e597..234e345f 100644 --- a/src/settings/nm-settings-plugin.c +++ b/src/settings/nm-settings-plugin.c @@ -169,13 +169,17 @@ nm_settings_plugin_add_connection (NMSettingsPlugin *config, gboolean save_to_disk, GError **error) { + NMSettingsPluginInterface *config_interface; + g_return_val_if_fail (config != NULL, NULL); g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - if (NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->add_connection) - return NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->add_connection (config, connection, save_to_disk, error); + config_interface = NM_SETTINGS_PLUGIN_GET_INTERFACE (config); + if (!config_interface->add_connection) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "Plugin does not support adding connections"); + return NULL; + } - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, - "Plugin does not support adding connections"); - return NULL; + return config_interface->add_connection (config, connection, save_to_disk, error); } diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index afd1b084..e2b467a2 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -76,6 +76,7 @@ #include "NetworkManagerUtils.h" #include "nm-dispatcher.h" #include "nm-inotify-helper.h" +#include "nm-hostname-manager.h" #include "introspection/org.freedesktop.NetworkManager.Settings.h" @@ -90,20 +91,9 @@ EXPORT(nm_inotify_helper_remove_watch) EXPORT(nm_settings_connection_get_type) EXPORT(nm_settings_connection_replace_settings) -EXPORT(nm_settings_connection_replace_and_commit) /*****************************************************************************/ -#define HOSTNAMED_SERVICE_NAME "org.freedesktop.hostname1" -#define HOSTNAMED_SERVICE_PATH "/org/freedesktop/hostname1" -#define HOSTNAMED_SERVICE_INTERFACE "org.freedesktop.hostname1" - -#define HOSTNAME_FILE_DEFAULT "/etc/hostname" -#define HOSTNAME_FILE_UCASE_HOSTNAME "/etc/HOSTNAME" -#define HOSTNAME_FILE_GENTOO "/etc/conf.d/hostname" -#define IFCFG_DIR SYSCONFDIR "/sysconfig/network" -#define CONF_DHCP IFCFG_DIR "/dhcp" - static NM_CACHED_QUARK_FCN ("plugin-module-path", plugin_module_path_quark) #if (defined(HOSTNAME_PERSIST_SUSE) + defined(HOSTNAME_PERSIST_SLACKWARE) + defined(HOSTNAME_PERSIST_GENTOO)) > 1 @@ -162,14 +152,8 @@ typedef struct { gboolean started; gboolean startup_complete; - struct { - char *value; - GFileMonitor *monitor; - GFileMonitor *dhcp_monitor; - gulong monitor_id; - gulong dhcp_monitor_id; - GDBusProxy *hostnamed_proxy; - } hostname; + NMHostnameManager *hostname_manager; + } NMSettingsPrivate; struct _NMSettings { @@ -568,131 +552,6 @@ get_plugin (NMSettings *self, guint32 capability) return NULL; } -#if defined(HOSTNAME_PERSIST_GENTOO) -static gchar * -read_hostname_gentoo (const char *path) -{ - gs_free char *contents = NULL; - gs_strfreev char **all_lines = NULL; - const char *tmp; - guint i; - - if (!g_file_get_contents (path, &contents, NULL, NULL)) - return NULL; - - all_lines = g_strsplit (contents, "\n", 0); - for (i = 0; all_lines[i]; i++) { - g_strstrip (all_lines[i]); - if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') - continue; - if (g_str_has_prefix (all_lines[i], "hostname=")) { - tmp = &all_lines[i][NM_STRLEN ("hostname=")]; - return g_shell_unquote (tmp, NULL); - } - } - return NULL; -} -#endif - -#if defined(HOSTNAME_PERSIST_SLACKWARE) -static gchar * -read_hostname_slackware (const char *path) -{ - gs_free char *contents = NULL; - gs_strfreev char **all_lines = NULL; - char *tmp; - guint i, j = 0; - - if (!g_file_get_contents (path, &contents, NULL, NULL)) - return NULL; - - all_lines = g_strsplit (contents, "\n", 0); - for (i = 0; all_lines[i]; i++) { - g_strstrip (all_lines[i]); - if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') - continue; - tmp = &all_lines[i][0]; - /* We only want up to the first '.' -- the rest of the */ - /* fqdn is defined in /etc/hosts */ - while (tmp[j] != '\0') { - if (tmp[j] == '.') { - tmp[j] = '\0'; - break; - } - j++; - } - return g_shell_unquote (tmp, NULL); - } - return NULL; -} -#endif - -#if defined(HOSTNAME_PERSIST_SUSE) -static gboolean -hostname_is_dynamic (void) -{ - GIOChannel *channel; - char *str = NULL; - gboolean dynamic = FALSE; - - channel = g_io_channel_new_file (CONF_DHCP, "r", NULL); - if (!channel) - return dynamic; - - while (g_io_channel_read_line (channel, &str, NULL, NULL, NULL) != G_IO_STATUS_EOF) { - if (str) { - g_strstrip (str); - if (g_str_has_prefix (str, "DHCLIENT_SET_HOSTNAME=")) - dynamic = strcmp (&str[NM_STRLEN ("DHCLIENT_SET_HOSTNAME=")], "\"yes\"") == 0; - g_free (str); - } - } - - g_io_channel_shutdown (channel, FALSE, NULL); - g_io_channel_unref (channel); - - return dynamic; -} -#endif - -/* Returns an allocated string which the caller owns and must eventually free */ -char * -nm_settings_get_hostname (NMSettings *self) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - char *hostname = NULL; - - if (!priv->started) - return NULL; - - if (priv->hostname.hostnamed_proxy) { - hostname = g_strdup (priv->hostname.value); - goto out; - } - -#if defined(HOSTNAME_PERSIST_SUSE) - if (priv->hostname.dhcp_monitor_id && hostname_is_dynamic ()) - return NULL; -#endif - -#if defined(HOSTNAME_PERSIST_GENTOO) - hostname = read_hostname_gentoo (HOSTNAME_FILE); -#elif defined(HOSTNAME_PERSIST_SLACKWARE) - hostname = read_hostname_slackware (HOSTNAME_FILE); -#else - if (g_file_get_contents (HOSTNAME_FILE, &hostname, NULL, NULL)) - g_strchomp (hostname); -#endif - -out: - if (hostname && !hostname[0]) { - g_free (hostname); - hostname = NULL; - } - - return hostname; -} - static gboolean find_spec (GSList *spec_list, const char *spec) { @@ -1626,160 +1485,7 @@ impl_settings_reload_connections (NMSettings *self, g_dbus_method_invocation_return_value (context, g_variant_new ("(b)", TRUE)); } -typedef struct { - char *hostname; - NMSettingsSetHostnameCb cb; - gpointer user_data; -} SetHostnameInfo; - -static void -set_transient_hostname_done (GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - GDBusProxy *proxy = G_DBUS_PROXY (object); - gs_free SetHostnameInfo *info = user_data; - gs_unref_variant GVariant *result = NULL; - gs_free_error GError *error = NULL; - - result = g_dbus_proxy_call_finish (proxy, res, &error); - - if (error) { - _LOGW ("couldn't set the system hostname to '%s' using hostnamed: %s", - info->hostname, error->message); - } - - info->cb (info->hostname, !error, info->user_data); - g_free (info->hostname); -} - -void -nm_settings_set_transient_hostname (NMSettings *self, - const char *hostname, - NMSettingsSetHostnameCb cb, - gpointer user_data) -{ - NMSettingsPrivate *priv; - SetHostnameInfo *info; - - g_return_if_fail (NM_IS_SETTINGS (self)); - priv = NM_SETTINGS_GET_PRIVATE (self); - - if (!priv->hostname.hostnamed_proxy) { - cb (hostname, FALSE, user_data); - return; - } - - info = g_new0 (SetHostnameInfo, 1); - info->hostname = g_strdup (hostname); - info->cb = cb; - info->user_data = user_data; - - g_dbus_proxy_call (priv->hostname.hostnamed_proxy, - "SetHostname", - g_variant_new ("(sb)", hostname, FALSE), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - set_transient_hostname_done, - info); -} - -gboolean -nm_settings_get_transient_hostname (NMSettings *self, char **hostname) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GVariant *v_hostname; - - if (!priv->hostname.hostnamed_proxy) - return FALSE; - - v_hostname = g_dbus_proxy_get_cached_property (priv->hostname.hostnamed_proxy, - "Hostname"); - if (!v_hostname) { - _LOGT ("transient hostname retrieval failed"); - return FALSE; - } - - *hostname = g_variant_dup_string (v_hostname, NULL); - g_variant_unref (v_hostname); - - return TRUE; -} - -static gboolean -write_hostname (NMSettingsPrivate *priv, const char *hostname) -{ - char *hostname_eol; - gboolean ret; - gs_free_error GError *error = NULL; - const char *file = HOSTNAME_FILE; - gs_free char *link_path = NULL; - gs_unref_variant GVariant *var = NULL; - struct stat file_stat; -#if HAVE_SELINUX - security_context_t se_ctx_prev = NULL, se_ctx = NULL; - mode_t st_mode = 0; -#endif - - if (priv->hostname.hostnamed_proxy) { - var = g_dbus_proxy_call_sync (priv->hostname.hostnamed_proxy, - "SetStaticHostname", - g_variant_new ("(sb)", hostname, FALSE), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - &error); - if (error) - _LOGW ("could not set hostname: %s", error->message); - - return !error; - } - - /* If the hostname file is a symbolic link, follow it to find where the - * real file is located, otherwise g_file_set_contents will attempt to - * replace the link with a plain file. - */ - if ( lstat (file, &file_stat) == 0 - && S_ISLNK (file_stat.st_mode) - && (link_path = nm_utils_read_link_absolute (file, NULL))) - file = link_path; - -#if HAVE_SELINUX - /* Get default context for hostname file and set it for fscreate */ - if (stat (file, &file_stat) == 0) - st_mode = file_stat.st_mode; - matchpathcon (file, st_mode, &se_ctx); - matchpathcon_fini (); - getfscreatecon (&se_ctx_prev); - setfscreatecon (se_ctx); -#endif - -#if defined (HOSTNAME_PERSIST_GENTOO) - hostname_eol = g_strdup_printf ("#Generated by NetworkManager\n" - "hostname=\"%s\"\n", hostname); -#else - hostname_eol = g_strdup_printf ("%s\n", hostname); -#endif - - ret = g_file_set_contents (file, hostname_eol, -1, &error); - -#if HAVE_SELINUX - /* Restore previous context and cleanup */ - setfscreatecon (se_ctx_prev); - freecon (se_ctx); - freecon (se_ctx_prev); -#endif - - g_free (hostname_eol); - - if (!ret) { - _LOGW ("could not save hostname to %s: %s", file, error->message); - return FALSE; - } - - return TRUE; -} +/*****************************************************************************/ static void pk_hostname_cb (NMAuthChain *chain, @@ -1812,7 +1518,7 @@ pk_hostname_cb (NMAuthChain *chain, } else { hostname = nm_auth_chain_get_data (chain, "hostname"); - if (!write_hostname (priv, hostname)) { + if (!nm_hostname_manager_write_hostname (priv->hostname_manager, hostname)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Saving the hostname failed."); @@ -1827,33 +1533,6 @@ pk_hostname_cb (NMAuthChain *chain, nm_auth_chain_unref (chain); } -static gboolean -validate_hostname (const char *hostname) -{ - const char *p; - gboolean dot = TRUE; - - if (!hostname || !hostname[0]) - return FALSE; - - for (p = hostname; *p; p++) { - if (*p == '.') { - if (dot) - return FALSE; - dot = TRUE; - } else { - if (!g_ascii_isalnum (*p) && (*p != '-') && (*p != '_')) - return FALSE; - dot = FALSE; - } - } - - if (dot) - return FALSE; - - return (p - hostname <= HOST_NAME_MAX); -} - static void impl_settings_save_hostname (NMSettings *self, GDBusMethodInvocation *context, @@ -1864,7 +1543,7 @@ impl_settings_save_hostname (NMSettings *self, GError *error = NULL; /* Minimal validation of the hostname */ - if (!validate_hostname (hostname)) { + if (!nm_hostname_manager_validate_hostname (hostname)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_HOSTNAME, "The hostname was too long or contained invalid characters."); @@ -1888,37 +1567,7 @@ done: g_dbus_method_invocation_take_error (context, error); } -static void -hostname_maybe_changed (NMSettings *settings) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (settings); - char *new_hostname; - - new_hostname = nm_settings_get_hostname (settings); - - if ( (new_hostname && !priv->hostname.value) - || (!new_hostname && priv->hostname.value) - || (priv->hostname.value && new_hostname && strcmp (priv->hostname.value, new_hostname))) { - - _LOGI ("hostname changed from %s%s%s to %s%s%s", - NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"), - NM_PRINT_FMT_QUOTED (new_hostname, "\"", new_hostname, "\"", "(none)")); - g_free (priv->hostname.value); - priv->hostname.value = new_hostname; - _notify (settings, PROP_HOSTNAME); - } else - g_free (new_hostname); -} - -static void -hostname_file_changed_cb (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) -{ - hostname_maybe_changed (user_data); -} +/*****************************************************************************/ static gboolean have_connection_for_device (NMSettings *self, NMDevice *device) @@ -2124,7 +1773,7 @@ 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, NULL); + nm_settings_connection_delete (connection, NULL); } } @@ -2141,95 +1790,19 @@ nm_settings_get_startup_complete (NMSettings *self) /*****************************************************************************/ static void -hostnamed_properties_changed (GDBusProxy *proxy, - GVariant *changed_properties, - char **invalidated_properties, - gpointer user_data) +_hostname_changed_cb (NMHostnameManager *hostname_manager, + GParamSpec *pspec, + gpointer user_data) { - NMSettings *self = user_data; - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GVariant *v_hostname; - const char *hostname; - - v_hostname = g_dbus_proxy_get_cached_property (priv->hostname.hostnamed_proxy, - "StaticHostname"); - if (!v_hostname) - return; - - hostname = g_variant_get_string (v_hostname, NULL); - - if (g_strcmp0 (priv->hostname.value, hostname) != 0) { - _LOGI ("hostname changed from %s%s%s to %s%s%s", - NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"), - NM_PRINT_FMT_QUOTED (hostname, "\"", hostname, "\"", "(none)")); - g_free (priv->hostname.value); - priv->hostname.value = g_strdup (hostname); - _notify (self, PROP_HOSTNAME); - nm_dispatcher_call_hostname (NULL, NULL, NULL); - } - - g_variant_unref (v_hostname); + _notify (user_data, PROP_HOSTNAME); } -static void -setup_hostname_file_monitors (NMSettings *self) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GFileMonitor *monitor; - const char *path = HOSTNAME_FILE; - char *link_path = NULL; - struct stat file_stat; - GFile *file; - - priv->hostname.value = nm_settings_get_hostname (self); - - /* resolve the path to the hostname file if it is a symbolic link */ - if ( lstat(path, &file_stat) == 0 - && S_ISLNK (file_stat.st_mode) - && (link_path = nm_utils_read_link_absolute (path, NULL))) { - path = link_path; - if ( lstat(link_path, &file_stat) == 0 - && S_ISLNK (file_stat.st_mode)) { - _LOGW ("only one level of symbolic link indirection is allowed when monitoring " - HOSTNAME_FILE); - } - } - - /* monitor changes to hostname file */ - file = g_file_new_for_path (path); - monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - g_free(link_path); - if (monitor) { - priv->hostname.monitor_id = g_signal_connect (monitor, "changed", - G_CALLBACK (hostname_file_changed_cb), - self); - priv->hostname.monitor = monitor; - } - -#if defined (HOSTNAME_PERSIST_SUSE) - /* monitor changes to dhcp file to know whether the hostname is valid */ - file = g_file_new_for_path (CONF_DHCP); - monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - if (monitor) { - priv->hostname.dhcp_monitor_id = g_signal_connect (monitor, "changed", - G_CALLBACK (hostname_file_changed_cb), - self); - priv->hostname.dhcp_monitor = monitor; - } -#endif - - hostname_maybe_changed (self); -} +/*****************************************************************************/ gboolean nm_settings_start (NMSettings *self, GError **error) { NMSettingsPrivate *priv; - GDBusProxy *proxy; - GVariant *variant; - GError *local_error = NULL; gs_strfreev char **plugins = NULL; priv = NM_SETTINGS_GET_PRIVATE (self); @@ -2245,33 +1818,14 @@ nm_settings_start (NMSettings *self, GError **error) load_connections (self); check_startup_complete (self); - proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, 0, NULL, - HOSTNAMED_SERVICE_NAME, HOSTNAMED_SERVICE_PATH, - HOSTNAMED_SERVICE_INTERFACE, NULL, &local_error); - if (proxy) { - variant = g_dbus_proxy_get_cached_property (proxy, "StaticHostname"); - if (variant) { - _LOGI ("hostname: using hostnamed"); - priv->hostname.hostnamed_proxy = proxy; - g_signal_connect (proxy, "g-properties-changed", - G_CALLBACK (hostnamed_properties_changed), self); - hostnamed_properties_changed (proxy, NULL, NULL, self); - g_variant_unref (variant); - } else { - _LOGI ("hostname: couldn't get property from hostnamed"); - g_object_unref (proxy); - } - } else { - _LOGI ("hostname: hostnamed not used as proxy creation failed with: %s", - local_error->message); - g_clear_error (&local_error); - } - - if (!priv->hostname.hostnamed_proxy) - setup_hostname_file_monitors (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), + self); + if (nm_hostname_manager_get_hostname (priv->hostname_manager)) + _notify (self, PROP_HOSTNAME); - priv->started = TRUE; - _notify (self, PROP_HOSTNAME); return TRUE; } @@ -2298,11 +1852,10 @@ get_property (GObject *object, guint prop_id, g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE)); break; case PROP_HOSTNAME: - g_value_take_string (value, nm_settings_get_hostname (self)); - - /* Don't ever pass NULL through D-Bus */ - if (!g_value_get_string (value)) - g_value_set_static_string (value, ""); + g_value_set_string (value, + priv->hostname_manager + ? nm_hostname_manager_get_hostname (priv->hostname_manager) + : NULL); break; case PROP_CAN_MODIFY: g_value_set_boolean (value, !!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS)); @@ -2331,7 +1884,7 @@ nm_settings_init (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); /* Hold a reference to the agent manager so it stays alive; the only * other holders are NMSettingsConnection objects which are often @@ -2362,32 +1915,13 @@ dispose (GObject *object) g_object_unref (priv->agent_mgr); - if (priv->hostname.hostnamed_proxy) { - g_signal_handlers_disconnect_by_func (priv->hostname.hostnamed_proxy, - G_CALLBACK (hostnamed_properties_changed), + if (priv->hostname_manager) { + g_signal_handlers_disconnect_by_func (priv->hostname_manager, + G_CALLBACK (_hostname_changed_cb), self); - g_clear_object (&priv->hostname.hostnamed_proxy); - } - - if (priv->hostname.monitor) { - if (priv->hostname.monitor_id) - g_signal_handler_disconnect (priv->hostname.monitor, priv->hostname.monitor_id); - - g_file_monitor_cancel (priv->hostname.monitor); - g_clear_object (&priv->hostname.monitor); + g_clear_object (&priv->hostname_manager); } - if (priv->hostname.dhcp_monitor) { - if (priv->hostname.dhcp_monitor_id) - g_signal_handler_disconnect (priv->hostname.dhcp_monitor, - priv->hostname.dhcp_monitor_id); - - g_file_monitor_cancel (priv->hostname.dhcp_monitor); - g_clear_object (&priv->hostname.dhcp_monitor); - } - - g_clear_pointer (&priv->hostname.value, g_free); - G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object); } diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 7110a12b..eede76b0 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -119,20 +119,10 @@ gboolean nm_settings_has_connection (NMSettings *self, NMSettingsConnection *con const GSList *nm_settings_get_unmanaged_specs (NMSettings *self); -char *nm_settings_get_hostname (NMSettings *self); - void nm_settings_device_added (NMSettings *self, NMDevice *device); void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting); gboolean nm_settings_get_startup_complete (NMSettings *self); -void nm_settings_set_transient_hostname (NMSettings *self, - const char *hostname, - NMSettingsSetHostnameCb cb, - gpointer user_data); - -gboolean nm_settings_get_transient_hostname (NMSettings *self, - char **hostname); - #endif /* __NM_SETTINGS_H__ */ diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c index c9069dc7..9b1f5ccd 100644 --- a/src/settings/plugins/ibft/nms-ibft-plugin.c +++ b/src/settings/plugins/ibft/nms-ibft-plugin.c @@ -152,7 +152,7 @@ nms_ibft_plugin_init (NMSIbftPlugin *self) { NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf b/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf index 8fefaf18..cc6ccb5c 100644 --- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf +++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf @@ -2,16 +2,11 @@ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> - <policy user="root"> - <allow own="com.redhat.ifcfgrh1"/> - <allow send_destination="com.redhat.ifcfgrh1"/> - </policy> - <policy at_console="true"> - <allow send_destination="com.redhat.ifcfgrh1"/> - </policy> <policy context="default"> - <deny own="com.redhat.ifcfgrh1"/> <allow send_destination="com.redhat.ifcfgrh1"/> </policy> + <policy user="root"> + <allow own="com.redhat.ifcfgrh1"/> + </policy> </busconfig> diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c index b54f9549..4c1d02ae 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c @@ -306,75 +306,52 @@ nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self) return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unrecognized_spec; } -static void -replace_and_commit (NMSettingsConnection *connection, - NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) -{ - const char *filename; - GError *error = NULL; - - filename = nm_settings_connection_get_filename (connection); - if (filename && utils_has_complex_routes (filename)) { - if (callback) { - error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Cannot modify a connection that has an associated 'rule-' or 'rule6-' file"); - callback (connection, error, user_data); - g_clear_error (&error); - } - return; - } - - NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->replace_and_commit (connection, new_connection, callback, user_data); -} - -static void +static gboolean commit_changes (NMSettingsConnection *connection, + NMConnection *new_connection, NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) + NMConnection **out_reread_connection, + char **out_logmsg_change, + GError **error) { - GError *error = NULL; - gboolean success = FALSE; - char *ifcfg_path = NULL; const char *filename; + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = TRUE; + const char *operation_message; + gs_free char *ifcfg_path = NULL; - filename = nm_settings_connection_get_filename (connection); - if (filename) { - success = writer_update_connection (NM_CONNECTION (connection), - IFCFG_DIR, - filename, - NULL, - NULL, - &error); - } else { - success = writer_new_connection (NM_CONNECTION (connection), - IFCFG_DIR, - &ifcfg_path, - NULL, - NULL, - &error); - if (success) { - nm_settings_connection_set_filename (connection, ifcfg_path); - g_free (ifcfg_path); - } - } + nm_assert (out_reread_connection && !*out_reread_connection); + nm_assert (!out_logmsg_change || !*out_logmsg_change); - if (success) { - /* Chain up to parent to handle success */ - NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, commit_reason, callback, user_data); - } else { - /* Otherwise immediate error */ - callback (connection, error, user_data); - g_error_free (error); - } + filename = nm_settings_connection_get_filename (connection); + if (!nms_ifcfg_rh_writer_write_connection (new_connection ?: NM_CONNECTION (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 void -do_delete (NMSettingsConnection *connection, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) +static gboolean +delete (NMSettingsConnection *connection, + GError **error) { NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) connection); const char *filename; @@ -390,7 +367,7 @@ do_delete (NMSettingsConnection *connection, g_unlink (priv->route6file); } - NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->delete (connection, callback, user_data); + return TRUE; } /*****************************************************************************/ @@ -529,8 +506,7 @@ nm_ifcfg_connection_class_init (NMIfcfgConnectionClass *ifcfg_connection_class) object_class->get_property = get_property; object_class->dispose = dispose; - settings_class->delete = do_delete; - settings_class->replace_and_commit = replace_and_commit; + settings_class->delete = delete; settings_class->commit_changes = commit_changes; obj_properties[PROP_UNMANAGED_SPEC] = 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 a3092e71..da0920ef 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -461,7 +461,7 @@ _paths_from_connections (GHashTable *connections) { GHashTableIter iter; NMIfcfgConnection *connection; - GHashTable *paths = g_hash_table_new (g_str_hash, g_str_equal); + GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); g_hash_table_iter_init (&iter, connections); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { @@ -679,18 +679,16 @@ add_connection (NMSettingsPlugin *config, { SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (config); gs_free char *path = NULL; - - /* Ensure we reject attempts to add the connection long before we're - * asked to write it to disk. - */ - if (!writer_can_write_connection (connection, error)) - return NULL; + gs_unref_object NMConnection *reread = NULL; if (save_to_disk) { - if (!writer_new_connection (connection, IFCFG_DIR, &path, NULL, NULL, error)) + 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, connection, path, NULL, FALSE, NULL, error)); + return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error)); } static void @@ -991,7 +989,7 @@ settings_plugin_ifcfg_init (SettingsPluginIfcfg *plugin) { SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) plugin); - priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void 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 164f6844..4754bea5 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -89,6 +89,64 @@ get_uint (const char *str, guint32 *value) return TRUE; } +static void +check_if_bond_slave (shvarFile *ifcfg, + NMSettingConnection *s_con) +{ + gs_free char *value = NULL; + const char *v; + const char *master; + + v = svGetValueStr (ifcfg, "MASTER_UUID", &value); + if (!v) + v = svGetValueStr (ifcfg, "MASTER", &value); + + if (v) { + master = nm_setting_connection_get_master (s_con); + if (master) { + PARSE_WARNING ("Already configured as slave of %s. Ignoring MASTER{_UUID}=\"%s\"", + master, v); + return; + } + + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, v, + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, + NULL); + } + + /* We should be checking for SLAVE=yes as well, but NM used to not set that, + * so for backward-compatibility, we don't check. + */ +} + +static void +check_if_team_slave (shvarFile *ifcfg, + NMSettingConnection *s_con) +{ + gs_free char *value = NULL; + const char *v; + const char *master; + + v = svGetValueStr (ifcfg, "TEAM_MASTER_UUID", &value); + if (!v) + v = svGetValueStr (ifcfg, "TEAM_MASTER", &value); + if (!v) + return; + + master = nm_setting_connection_get_master (s_con); + if (master) { + PARSE_WARNING ("Already configured as slave of %s. Ignoring TEAM_MASTER{_UUID}=\"%s\"", + master, v); + return; + } + + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, v, + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, + NULL); +} + static char * make_connection_name (shvarFile *ifcfg, const char *ifcfg_name, @@ -128,8 +186,14 @@ make_connection_setting (const char *file, NMSettingConnection *s_con; NMSettingConnectionLldp lldp; const char *ifcfg_name = NULL; - char *new_id, *uuid = NULL, *zone = NULL, *value; + char *new_id; + const char *uuid; + gs_free char *uuid_free = NULL; + gs_free char *value = NULL; + const char *v; gs_free char *stable_id = NULL; + const char *const *iter; + int vint64; ifcfg_name = utils_get_ifcfg_name (file, TRUE); if (!ifcfg_name) @@ -142,38 +206,38 @@ make_connection_setting (const char *file, g_free (new_id); /* Try for a UUID key before falling back to hashing the file name */ - uuid = svGetValueStr_cp (ifcfg, "UUID"); - if (!uuid) - uuid = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); + uuid = svGetValueStr (ifcfg, "UUID", &uuid_free); + if (!uuid) { + uuid_free = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); + uuid = uuid_free; + } g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, type, NM_SETTING_CONNECTION_UUID, uuid, NM_SETTING_CONNECTION_STABLE_ID, svGetValue (ifcfg, "STABLE_ID", &stable_id), NULL); - g_free (uuid); - value = svGetValueStr_cp (ifcfg, "DEVICE"); - if (value) { + v = svGetValueStr (ifcfg, "DEVICE", &value); + if (v) { GError *error = NULL; - if (nm_utils_is_valid_iface_name (value, &error)) { + if (nm_utils_is_valid_iface_name (v, &error)) { g_object_set (s_con, - NM_SETTING_CONNECTION_INTERFACE_NAME, value, + NM_SETTING_CONNECTION_INTERFACE_NAME, v, NULL); } else { - PARSE_WARNING ("invalid DEVICE name '%s': %s", value, error->message); + PARSE_WARNING ("invalid DEVICE name '%s': %s", v, error->message); g_error_free (error); } - g_free (value); } - value = svGetValueStr_cp (ifcfg, "LLDP"); - if (!g_strcmp0 (value, "rx")) + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "LLDP", &value); + if (nm_streq0 (v, "rx")) lldp = NM_SETTING_CONNECTION_LLDP_ENABLE_RX; else - lldp = svParseBoolean (value, NM_SETTING_CONNECTION_LLDP_DEFAULT); - g_free (value); + lldp = svParseBoolean (v, NM_SETTING_CONNECTION_LLDP_DEFAULT); /* Missing ONBOOT is treated as "ONBOOT=true" by the old network service */ g_object_set (s_con, @@ -192,68 +256,69 @@ make_connection_setting (const char *file, NM_SETTING_CONNECTION_LLDP, lldp, NULL); - value = svGetValueStr_cp (ifcfg, "USERS"); - if (value) { - char **items, **iter; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "USERS", &value); + if (v) { + gs_free const char **items = NULL; - items = g_strsplit_set (value, " ", -1); + items = nm_utils_strsplit_set (v, " "); for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - if (!nm_setting_connection_add_permission (s_con, "user", *iter, NULL)) - PARSE_WARNING ("invalid USERS item '%s'", *iter); - } + if (!nm_setting_connection_add_permission (s_con, "user", *iter, NULL)) + PARSE_WARNING ("invalid USERS item '%s'", *iter); } - g_free (value); - g_strfreev (items); } - zone = svGetValueStr_cp (ifcfg, "ZONE"); - g_object_set (s_con, NM_SETTING_CONNECTION_ZONE, zone, NULL); - g_free (zone); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "ZONE", &value); + g_object_set (s_con, NM_SETTING_CONNECTION_ZONE, v, NULL); - value = svGetValueStr_cp (ifcfg, "SECONDARY_UUIDS"); - if (value) { - char **items, **iter; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "SECONDARY_UUIDS", &value); + if (v) { + gs_free const char **items = NULL; - items = g_strsplit_set (value, " \t", -1); + items = nm_utils_strsplit_set (v, " \t"); for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - if (!nm_setting_connection_add_secondary (s_con, *iter)) - PARSE_WARNING ("secondary connection UUID '%s' already added", *iter); - } + if (!nm_setting_connection_add_secondary (s_con, *iter)) + PARSE_WARNING ("secondary connection UUID '%s' already added", *iter); } - g_free (value); - g_strfreev (items); } - value = svGetValueStr_cp (ifcfg, "BRIDGE_UUID"); - if (!value) - value = svGetValueStr_cp (ifcfg, "BRIDGE"); - if (value) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "BRIDGE_UUID", &value); + if (!v) + v = svGetValueStr (ifcfg, "BRIDGE", &value); + if (v) { const char *old_value; if ((old_value = nm_setting_connection_get_master (s_con))) { PARSE_WARNING ("Already configured as slave of %s. Ignoring BRIDGE=\"%s\"", - old_value, value); + old_value, v); } else { - g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL); + g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, v, NULL); g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, NULL); } - g_free (value); } - value = svGetValueStr_cp (ifcfg, "GATEWAY_PING_TIMEOUT"); - if (value) { + check_if_bond_slave (ifcfg, s_con); + check_if_team_slave (ifcfg, s_con); + + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "GATEWAY_PING_TIMEOUT", &value); + if (v) { gint64 tmp; - tmp = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32 - 1, -1); - if (tmp >= 0) + tmp = _nm_utils_ascii_str_to_int64 (v, 10, 0, G_MAXINT32 - 1, -1); + if (tmp >= 0) { + if (tmp > 600) { + tmp = 600; + PARSE_WARNING ("invalid GATEWAY_PING_TIMEOUT time"); + } g_object_set (s_con, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, (guint) tmp, NULL); - else + } else PARSE_WARNING ("invalid GATEWAY_PING_TIMEOUT time"); - g_free (value); } switch (svGetValueBoolean (ifcfg, "CONNECTION_METERED", -1)) { @@ -265,6 +330,9 @@ make_connection_setting (const char *file, break; } + vint64 = svGetValueInt64 (ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1); + g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (gint) vint64, NULL); + return NM_SETTING (s_con); } @@ -302,30 +370,6 @@ read_ip4_address (shvarFile *ifcfg, return TRUE; } -static void -_numbered_tag (char *buf, gsize buf_len, const char *tag_name, int which) -{ - gsize l; - - l = g_strlcpy (buf, tag_name, buf_len); - nm_assert (l < buf_len); - if (which != -1) { - buf_len -= l; - l = g_snprintf (&buf[l], buf_len, "%d", which); - nm_assert (l < buf_len); - } -} -#define numbered_tag(buf, tag_name, which) \ - ({ \ - _nm_unused char *const _buf = (buf); \ - \ - /* some static assert trying to ensure that the buffer is statically allocated. - * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ - G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *) && sizeof (buf) < G_MAXINT); \ - _numbered_tag (buf, sizeof (buf), ""tag_name"", (which)); \ - buf; \ - }) - static gboolean is_any_ip4_address_defined (shvarFile *ifcfg, int *idx) { @@ -368,6 +412,7 @@ read_full_ip4_address (shvarFile *ifcfg, char prefix_tag[256]; guint32 ipaddr; gs_free char *value = NULL; + const char *v; int prefix = 0; gboolean has_key; guint32 a; @@ -402,12 +447,12 @@ read_full_ip4_address (shvarFile *ifcfg, /* Prefix */ numbered_tag (prefix_tag, "PREFIX", which); - value = svGetValueStr_cp (ifcfg, prefix_tag); - if (value) { - prefix = _nm_utils_ascii_str_to_int64 (value, 10, 0, 32, -1); + v = svGetValueStr (ifcfg, prefix_tag, &value); + if (v) { + prefix = _nm_utils_ascii_str_to_int64 (v, 10, 0, 32, -1); if (prefix < 0) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 prefix '%s'", value); + "Invalid IP4 prefix '%s'", v); return FALSE; } } else { @@ -423,7 +468,7 @@ read_full_ip4_address (shvarFile *ifcfg, prefix = nm_ip_address_get_prefix (base_addr); else { /* Try to autodetermine the prefix for the address' class */ - prefix = nm_utils_ip4_get_default_prefix (ipaddr); + prefix = _nm_utils_ip4_get_default_prefix (ipaddr); PARSE_WARNING ("missing %s, assuming %s/%d", prefix_tag, nm_utils_inet4_ntop (ipaddr, inet_buf), prefix); } } @@ -436,131 +481,448 @@ read_full_ip4_address (shvarFile *ifcfg, return FALSE; } -/* - * Use looser syntax to comprise all the possibilities. - * The validity must be checked after the match. - */ -#define IPV4_ADDR_REGEX "(?:[0-9]{1,3}\\.){3}[0-9]{1,3}" -#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+" - -/* - * NOTE: The regexes below don't describe all variants allowed by 'ip route add', - * namely destination IP without 'to' keyword is recognized just at line start. - */ +/*****************************************************************************/ static gboolean -parse_route_options (NMIPRoute *route, int family, const char *line, GError **error) +parse_route_line_is_comment (const char *line) { - GRegex *regex = NULL; - GMatchInfo *match_info = NULL; - gboolean success = FALSE; - static const char *metrics[] = { NM_IP_ROUTE_ATTRIBUTE_WINDOW, NM_IP_ROUTE_ATTRIBUTE_CWND, - NM_IP_ROUTE_ATTRIBUTE_INITCWND, NM_IP_ROUTE_ATTRIBUTE_INITRWND, - NM_IP_ROUTE_ATTRIBUTE_MTU, NULL }; - char buffer[1024]; - int i; + /* we obtained the line from a legacy route file. Here we skip + * empty lines and comments. + * + * initscripts compares: "$line" =~ '^[[:space:]]*(\#.*)?$' + */ + while (NM_IN_SET (line[0], ' ', '\t')) + line++; + if (NM_IN_SET (line[0], '\0', '#')) + return TRUE; + return FALSE; +} + +/*****************************************************************************/ + +typedef struct { + const char *key; + + /* the element is not available in this case. */ + bool disabled:1; + + /* whether the element is to be ignored. Ignord is different from + * "disabled", because we still parse the option, but don't use it. */ + bool ignore:1; + + bool int_base_16:1; + + /* the type, one of PARSE_LINE_TYPE_* */ + char type; - g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); + /* whether the command line option was found, and @v is + * initialized. */ + bool has:1; - for (i = 0; metrics[i]; i++) { - nm_sprintf_buf (buffer, "(?:\\s|^)%s\\s+(lock\\s+)?(\\d+)(?:$|\\s)", metrics[i]); - regex = g_regex_new (buffer, 0, 0, NULL); - g_regex_match (regex, line, 0, &match_info); - if (g_match_info_matches (match_info)) { - gs_free char *lock = g_match_info_fetch (match_info, 1); - gs_free char *str = g_match_info_fetch (match_info, 2); - gint64 num = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT32, -1); + union { + guint8 uint8; + guint32 uint32; + struct { + guint32 uint32; + bool lock:1; + } uint32_with_lock; + struct { + NMIPAddr addr; + guint8 plen; + bool has_plen:1; + } addr; + } v; - if (num == -1) { - g_match_info_free (match_info); +} ParseLineInfo; + +enum { + /* route attributes */ + PARSE_LINE_ATTR_ROUTE_TABLE, + PARSE_LINE_ATTR_ROUTE_SRC, + PARSE_LINE_ATTR_ROUTE_FROM, + PARSE_LINE_ATTR_ROUTE_TOS, + PARSE_LINE_ATTR_ROUTE_WINDOW, + PARSE_LINE_ATTR_ROUTE_CWND, + PARSE_LINE_ATTR_ROUTE_INITCWND, + PARSE_LINE_ATTR_ROUTE_INITRWND, + PARSE_LINE_ATTR_ROUTE_MTU, + + /* iproute2 arguments that only matter when parsing the file. */ + PARSE_LINE_ATTR_ROUTE_TO, + PARSE_LINE_ATTR_ROUTE_VIA, + PARSE_LINE_ATTR_ROUTE_METRIC, + + /* iproute2 paramters that are well known and that we silently ignore. */ + PARSE_LINE_ATTR_ROUTE_DEV, +}; + +#define PARSE_LINE_TYPE_UINT8 '8' +#define PARSE_LINE_TYPE_UINT32 'u' +#define PARSE_LINE_TYPE_UINT32_WITH_LOCK 'l' +#define PARSE_LINE_TYPE_ADDR 'a' +#define PARSE_LINE_TYPE_ADDR_WITH_PREFIX 'p' +#define PARSE_LINE_TYPE_IFNAME 'i' + +/** + * parse_route_line: + * @line: the line to parse. This is either a line from the route-* or route6-* file, + * or the numbered OPTIONS setting. + * @addr_family: the address family. + * @options_route: (in-out): when line is from the OPTIONS setting, this is a pre-created + * route object that is completed with the settings from options. Otherwise, + * it shall point to %NULL and a new route is created and returned. + * @out_route: (out): (transfer-full): (allow-none): the parsed %NMIPRoute instance. + * In case a @options_route is passed in, it returns the input route that was modified + * in-place. But the caller must unref the returned route in either case. + * @error: the failure description. + * + * Parsing the route options line has two modes: one for the numbered OPTIONS + * setting, and one for initscript's handle_ip_file(), which takes the lines + * and passes them to `ip route add`. The modes are similar, but certain properties + * are not allowed for OPTIONS. + * The mode is differenciated by having an @options_route argument. + * + * Returns: returns a negative errno on failure. On success, it returns 0 + * and @out_route. + */ +static int +parse_route_line (const char *line, + int addr_family, + NMIPRoute *options_route, + NMIPRoute **out_route, + GError **error) +{ + nm_auto_ip_route_unref NMIPRoute *route = NULL; + gs_free const char **words_free = NULL; + const char *const*words; + const char *s; + gsize i_words; + guint i; + char buf1[256]; + char buf2[256]; + ParseLineInfo infos[] = { + [PARSE_LINE_ATTR_ROUTE_TABLE] = { .key = NM_IP_ROUTE_ATTRIBUTE_TABLE, + .type = PARSE_LINE_TYPE_UINT32, }, + [PARSE_LINE_ATTR_ROUTE_SRC] = { .key = NM_IP_ROUTE_ATTRIBUTE_SRC, + .type = PARSE_LINE_TYPE_ADDR, }, + [PARSE_LINE_ATTR_ROUTE_FROM] = { .key = NM_IP_ROUTE_ATTRIBUTE_FROM, + .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX, + .disabled = (addr_family != AF_INET6), }, + [PARSE_LINE_ATTR_ROUTE_TOS] = { .key = NM_IP_ROUTE_ATTRIBUTE_TOS, + .type = PARSE_LINE_TYPE_UINT8, + .int_base_16 = TRUE, + .ignore = (addr_family != AF_INET), }, + [PARSE_LINE_ATTR_ROUTE_WINDOW] = { .key = NM_IP_ROUTE_ATTRIBUTE_WINDOW, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + [PARSE_LINE_ATTR_ROUTE_CWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_CWND, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + [PARSE_LINE_ATTR_ROUTE_INITCWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITCWND, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + [PARSE_LINE_ATTR_ROUTE_INITRWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITRWND, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + [PARSE_LINE_ATTR_ROUTE_MTU] = { .key = NM_IP_ROUTE_ATTRIBUTE_MTU, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + + [PARSE_LINE_ATTR_ROUTE_TO] = { .key = "to", + .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX, + .disabled = (options_route != NULL), }, + [PARSE_LINE_ATTR_ROUTE_VIA] = { .key = "via", + .type = PARSE_LINE_TYPE_ADDR, + .disabled = (options_route != NULL), }, + [PARSE_LINE_ATTR_ROUTE_METRIC] = { .key = "metric", + .type = PARSE_LINE_TYPE_UINT32, + .disabled = (options_route != NULL), }, + + [PARSE_LINE_ATTR_ROUTE_DEV] = { .key = "dev", + .type = PARSE_LINE_TYPE_IFNAME, + .ignore = TRUE, + .disabled = (options_route != NULL), }, + }; + + nm_assert (line); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (!options_route || nm_ip_route_get_family (options_route) == addr_family); + + /* initscripts read the legacy route file line-by-line and + * use it as `ip route add $line`, thus doing split+glob. + * Splitting on IFS (which we consider '<space><tab><newline>') + * and globbing (which we obviously don't do). + * + * I think it's a mess, because it doesn't support escaping or + * quoting. In fact, it can only encode benign values. + * + * We also use the same form for the numbered OPTIONS + * variable. I think it's bad not to support any form of + * escaping. But do that for now. + * + * Maybe later we want to support some form of quotation here. + * Which of course, would be incompatible with initscripts. + */ + words_free = nm_utils_strsplit_set (line, " \t\n"); + + words = words_free ?: NM_PTRARRAY_EMPTY (const char *); + + for (i_words = 0; words[i_words]; ) { + const gsize i_words0 = i_words; + const char *const w = words[i_words0]; + ParseLineInfo *info; + gboolean unqualified_addr = FALSE; + + for (i = 0; i < G_N_ELEMENTS (infos); i++) { + info = &infos[i]; + + if (info->disabled) + continue; + + if (!nm_streq (w, info->key)) + continue; + + if (info->has) { + /* iproute2 for most arguments allows specifying them multiple times. + * Let's not do that. */ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid route %s '%s'", metrics[i], str); - goto out; + "Duplicate option \"%s\"", w); + return -EINVAL; } - nm_ip_route_set_attribute (route, metrics[i], - g_variant_new_uint32 (num)); - if (lock && lock[0]) { - nm_sprintf_buf (buffer, "lock-%s", metrics[i]); - nm_ip_route_set_attribute (route, buffer, - g_variant_new_boolean (TRUE)); + info->has = TRUE; + switch (info->type) { + case PARSE_LINE_TYPE_UINT8: + i_words++; + goto parse_line_type_uint8; + case PARSE_LINE_TYPE_UINT32: + i_words++; + goto parse_line_type_uint32; + case PARSE_LINE_TYPE_UINT32_WITH_LOCK: + i_words++; + goto parse_line_type_uint32_with_lock; + case PARSE_LINE_TYPE_ADDR: + i_words++; + goto parse_line_type_addr; + case PARSE_LINE_TYPE_ADDR_WITH_PREFIX: + i_words++; + goto parse_line_type_addr_with_prefix; + case PARSE_LINE_TYPE_IFNAME: + i_words++; + goto parse_line_type_ifname; + default: + nm_assert_not_reached (); } } - g_clear_pointer (®ex, g_regex_unref); - g_clear_pointer (&match_info, g_match_info_free); - } - /* tos */ - regex = g_regex_new ("(?:\\s|^)tos\\s+(\\S+)(?:$|\\s)", 0, 0, NULL); - g_regex_match (regex, line, 0, &match_info); - if (g_match_info_matches (match_info)) { - gs_free char *str = g_match_info_fetch (match_info, 1); - gint64 num = _nm_utils_ascii_str_to_int64 (str, 0, 0, G_MAXUINT8, -1); + /* "to" is also accepted unqualified... (once) */ + info = &infos[PARSE_LINE_ATTR_ROUTE_TO]; + if (!info->has && !info->disabled) { + unqualified_addr = TRUE; + info->has = TRUE; + goto parse_line_type_addr; + } - if (num == -1) { - g_match_info_free (match_info); + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unrecognized argument (\"to\" is duplicate or \"%s\" is garbage)", w); + return -EINVAL; + +parse_line_type_uint8: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + info->v.uint8 = _nm_utils_ascii_str_to_int64 (s, + info->int_base_16 ? 16 : 10, + 0, + G_MAXUINT8, + 0);; + if (errno) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid route %s '%s'", "tos", str); - goto out; + "Argument for \"%s\" is not a valid number", w); + return -EINVAL; } - nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_TOS, - g_variant_new_byte ((guchar) num)); - } - g_clear_pointer (®ex, g_regex_unref); - g_clear_pointer (&match_info, g_match_info_free); - - /* from */ - if (family == AF_INET6) { - regex = g_regex_new ("(?:\\s|^)from\\s+(" IPV6_ADDR_REGEX "(?:/\\d{1,3})?)(?:$|\\s)", 0, 0, NULL); - g_regex_match (regex, line, 0, &match_info); - if (g_match_info_matches (match_info)) { - gs_free char *str = g_match_info_fetch (match_info, 1); - gs_free_error GError *local_error = NULL; - GVariant *variant = g_variant_new_string (str); - - if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_FROM, variant, family, NULL, &local_error)) { - g_match_info_free (match_info); - g_variant_unref (variant); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid route from '%s': %s", str, local_error->message); - goto out; + i_words++; + goto next; + +parse_line_type_uint32: +parse_line_type_uint32_with_lock: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + if (info->type == PARSE_LINE_TYPE_UINT32_WITH_LOCK) { + if (nm_streq (s, "lock")) { + s = words[++i_words]; + if (!s) + goto err_word_missing_argument; + info->v.uint32_with_lock.lock = TRUE; + } else + info->v.uint32_with_lock.lock = FALSE; + info->v.uint32_with_lock.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0);; + } else { + info->v.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0); + } + if (errno) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not a valid number", w); + return -EINVAL; + } + i_words++; + goto next; + +parse_line_type_ifname: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + i_words++; + goto next; + +parse_line_type_addr: +parse_line_type_addr_with_prefix: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + { + int prefix = -1; + + if (info->type == PARSE_LINE_TYPE_ADDR) { + if (!nm_utils_parse_inaddr_bin (addr_family, + s, + &info->v.addr.addr)) { + if ( info == &infos[PARSE_LINE_ATTR_ROUTE_VIA] + && nm_streq (s, "(null)")) { + /* Due to a bug, would older versions of NM write "via (null)" + * (rh#1452648). Workaround that, and accept it.*/ + memset (&info->v.addr.addr, 0, sizeof (info->v.addr.addr)); + } else { + if (unqualified_addr) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unrecognized argument (inet prefix is expected rather then \"%s\")", w); + return -EINVAL; + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not a valid IPv%c address", w, + addr_family == AF_INET ? '4' : '6'); + } + return -EINVAL; + } + } + } else { + nm_assert (info->type == PARSE_LINE_TYPE_ADDR_WITH_PREFIX); + if ( info == &infos[PARSE_LINE_ATTR_ROUTE_TO] + && nm_streq (s, "default")) { + memset (&info->v.addr.addr, 0, sizeof (info->v.addr.addr)); + prefix = 0; + } else if (!nm_utils_parse_inaddr_prefix_bin (addr_family, + s, + &info->v.addr.addr, + &prefix)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not ADDR/PREFIX format", w); + return -EINVAL; + } + } + if (prefix == -1) + info->v.addr.has_plen = FALSE; + else { + info->v.addr.has_plen = TRUE; + info->v.addr.plen = prefix; } - nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_FROM, variant); } - g_clear_pointer (®ex, g_regex_unref); - g_clear_pointer (&match_info, g_match_info_free); + i_words++; + goto next; + +err_word_missing_argument: + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing argument for \"%s\"", w); + return -EINVAL; +next: + ; } - if (family == AF_INET) - regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV4_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL); - else - regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV6_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL); - g_regex_match (regex, line, 0, &match_info); - if (g_match_info_matches (match_info)) { - gs_free char *str = g_match_info_fetch (match_info, 1); - gs_free_error GError *local_error = NULL; - GVariant *variant = g_variant_new_string (str); - - if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_SRC, variant, family, - NULL, &local_error)) { - g_match_info_free (match_info); - g_variant_unref (variant); + if (options_route) { + route = options_route; + nm_ip_route_ref (route); + } else { + ParseLineInfo *info_to = &infos[PARSE_LINE_ATTR_ROUTE_TO]; + ParseLineInfo *info_via = &infos[PARSE_LINE_ATTR_ROUTE_VIA]; + ParseLineInfo *info_metric = &infos[PARSE_LINE_ATTR_ROUTE_METRIC]; + guint prefix; + + if (!info_to->has) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid route src '%s': %s", str, local_error->message); - goto out; + "Missing destination prefix"); + return -EINVAL; } - nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_SRC, variant); + prefix = info_to->v.addr.has_plen + ? info_to->v.addr.plen + : (addr_family == AF_INET ? 32 : 128); + + if ( ( (addr_family == AF_INET && !info_to->v.addr.addr.addr4) + || (addr_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED (&info_to->v.addr.addr.addr6))) + && prefix == 0) { + /* we ignore default routes by returning -ERANGE. */ + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ignore manual default route"); + return -ERANGE; + } + + route = nm_ip_route_new_binary (addr_family, + &info_to->v.addr.addr, + prefix, + info_via->has ? &info_via->v.addr.addr : NULL, + info_metric->has ? (gint64) info_metric->v.uint32 : (gint64) -1, + error); + info_to->has = FALSE; + info_via->has = FALSE; + info_metric->has = FALSE; + if (!route) + return -EINVAL; } - success = TRUE; -out: - if (regex) - g_regex_unref (regex); - if (match_info) - g_match_info_free (match_info); + for (i = 0; i < G_N_ELEMENTS (infos); i++) { + ParseLineInfo *info = &infos[i]; - return success; + if (!info->has) + continue; + if (info->ignore || info->disabled) + continue; + switch (info->type) { + case PARSE_LINE_TYPE_UINT8: + nm_ip_route_set_attribute (route, + info->key, + g_variant_new_byte (info->v.uint8)); + break; + case PARSE_LINE_TYPE_UINT32: + nm_ip_route_set_attribute (route, + info->key, + g_variant_new_uint32 (info->v.uint32)); + break; + case PARSE_LINE_TYPE_UINT32_WITH_LOCK: + if (info->v.uint32_with_lock.lock) { + nm_ip_route_set_attribute (route, + nm_sprintf_buf (buf1, "lock-%s", info->key), + g_variant_new_boolean (TRUE)); + } + nm_ip_route_set_attribute (route, + info->key, + g_variant_new_uint32 (info->v.uint32_with_lock.uint32)); + break; + case PARSE_LINE_TYPE_ADDR: + case PARSE_LINE_TYPE_ADDR_WITH_PREFIX: + nm_ip_route_set_attribute (route, + info->key, + g_variant_new_printf ("%s%s", + inet_ntop (addr_family, &info->v.addr.addr, buf1, sizeof (buf1)), + info->v.addr.has_plen + ? nm_sprintf_buf (buf2, "/%u", (unsigned) info->v.addr.plen) + : "")); + break; + default: + nm_assert_not_reached (); + break; + } + } + + nm_assert (_nm_ip_route_attribute_validate_all (route)); + + NM_SET_OUT (out_route, g_steal_pointer (&route)); + return 0; } /* Returns TRUE on missing route or valid route */ @@ -576,13 +938,13 @@ read_one_ip4_route (shvarFile *ifcfg, guint32 next_hop; guint32 netmask; gboolean has_key; + const char *v; gs_free char *value = NULL; gint64 prefix, metric; char inet_buf[NM_UTILS_INET_ADDRSTRLEN]; g_return_val_if_fail (ifcfg != NULL, FALSE); - g_return_val_if_fail (out_route != NULL, FALSE); - g_return_val_if_fail (*out_route == NULL, FALSE); + g_return_val_if_fail (out_route && !*out_route, FALSE); g_return_val_if_fail (!error || !*error, FALSE); /* Destination */ @@ -610,7 +972,7 @@ read_one_ip4_route (shvarFile *ifcfg, return FALSE; if (has_key) { prefix = nm_utils_ip4_netmask_to_prefix (netmask); - if (prefix == 0 || netmask != nm_utils_ip4_prefix_to_netmask (prefix)) { + if (prefix == 0 || netmask != _nm_utils_ip4_prefix_to_netmask (prefix)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, inet_buf)); return FALSE; @@ -623,12 +985,12 @@ read_one_ip4_route (shvarFile *ifcfg, /* Metric */ nm_clear_g_free (&value); - value = svGetValueStr_cp (ifcfg, numbered_tag (tag, "METRIC", which)); - if (value) { - metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1); + v = svGetValueStr (ifcfg, numbered_tag (tag, "METRIC", which), &value); + if (v) { + metric = _nm_utils_ascii_str_to_int64 (v, 10, 0, G_MAXUINT32, -1); if (metric < 0) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route metric '%s'", value); + "Invalid IP4 route metric '%s'", v); return FALSE; } } else @@ -640,9 +1002,9 @@ read_one_ip4_route (shvarFile *ifcfg, /* Options */ nm_clear_g_free (&value); - value = svGetValueStr_cp (ifcfg, numbered_tag (tag, "OPTIONS", which)); - if (value) { - if (!parse_route_options (*out_route, AF_INET, value, error)) { + v = svGetValueStr (ifcfg, numbered_tag (tag, "OPTIONS", which), &value); + if (v) { + if (parse_route_line (v, AF_INET, *out_route, NULL, error) < 0) { g_clear_pointer (out_route, nm_ip_route_unref); return FALSE; } @@ -652,156 +1014,65 @@ read_one_ip4_route (shvarFile *ifcfg, } static gboolean -read_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError **error) +read_route_file (int addr_family, + const char *filename, + NMSettingIPConfig *s_ip, + GError **error) { - char *contents = NULL; + gs_free char *contents = NULL; + char *contents_rest = NULL; + const char *line; gsize len = 0; - char **lines = NULL, **iter; - GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric; - GMatchInfo *match_info; - int prefix_int; - gint64 metric_int; - gboolean success = FALSE; - - const char *pattern_empty = "^\\s*(\\#.*)?$"; - const char *pattern_to1 = "^\\s*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|default)" /* IP or 'default' keyword */ - "(?:/(\\d{1,2}))?"; /* optional prefix */ - const char *pattern_to2 = "to\\s+(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|default)" /* IP or 'default' keyword */ - "(?:/(\\d{1,2}))?"; /* optional prefix */ - const char *pattern_via = "via\\s+(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})"; /* IP of gateway */ - const char *pattern_metric = "metric\\s+(\\d+)"; /* metric */ + gsize line_num; - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (s_ip4 != NULL, FALSE); + g_return_val_if_fail (filename, FALSE); + g_return_val_if_fail ( (addr_family == AF_INET && NM_IS_SETTING_IP4_CONFIG (s_ip)) + || (addr_family == AF_INET6 && NM_IS_SETTING_IP6_CONFIG (s_ip)), FALSE); g_return_val_if_fail (!error || !*error, FALSE); - /* Read the route file */ - if (!g_file_get_contents (filename, &contents, &len, NULL) || !len) { - g_free (contents); + if ( !g_file_get_contents (filename, &contents, &len, NULL) + || !len) { return TRUE; /* missing/empty = success */ } - /* Create regexes for pieces to be matched */ - regex_to1 = g_regex_new (pattern_to1, 0, 0, NULL); - regex_to2 = g_regex_new (pattern_to2, 0, 0, NULL); - regex_via = g_regex_new (pattern_via, 0, 0, NULL); - regex_metric = g_regex_new (pattern_metric, 0, 0, NULL); + line_num = 0; + for (line = strtok_r (contents, "\n", &contents_rest); + line; + line = strtok_r (NULL, "\n", &contents_rest)) { + nm_auto_ip_route_unref NMIPRoute *route = NULL; + gs_free_error GError *local = NULL; + int e; - /* Iterate through file lines */ - lines = g_strsplit_set (contents, "\n\r", -1); - for (iter = lines; iter && *iter; iter++) { - gs_free char *next_hop = NULL, *dest = NULL; - char *prefix, *metric; - NMIPRoute *route; + line_num++; - /* Skip empty lines */ - if (g_regex_match_simple (pattern_empty, *iter, 0, 0)) + if (parse_route_line_is_comment (line)) continue; - /* Destination */ - g_regex_match (regex_to1, *iter, 0, &match_info); - if (!g_match_info_matches (match_info)) { - g_match_info_free (match_info); - g_regex_match (regex_to2, *iter, 0, &match_info); - if (!g_match_info_matches (match_info)) { - g_match_info_free (match_info); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IP4 route destination address in record: '%s'", *iter); - goto error; - } - } - dest = g_match_info_fetch (match_info, 1); - if (!strcmp (dest, "default")) { - g_match_info_free (match_info); - PARSE_WARNING ("ignoring manual default route: '%s' (%s)", *iter, filename); - continue; - } - if (!nm_utils_ipaddr_valid (AF_INET, dest)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route destination address '%s'", dest); - g_match_info_free (match_info); - goto error; - } - - /* Prefix - is optional; 32 if missing */ - prefix = g_match_info_fetch (match_info, 2); - g_match_info_free (match_info); - prefix_int = 32; - if (prefix) { - prefix_int = _nm_utils_ascii_str_to_int64 (prefix, 10, 1, 32, -1); - if (prefix_int == -1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route destination prefix '%s'", prefix); - g_free (prefix); - goto error; - } - } - g_free (prefix); + e = parse_route_line (line, addr_family, NULL, &route, &local); - /* Next hop */ - g_regex_match (regex_via, *iter, 0, &match_info); - if (g_match_info_matches (match_info)) { - next_hop = g_match_info_fetch (match_info, 1); - if (!nm_utils_ipaddr_valid (AF_INET, next_hop)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route gateway address '%s'", - next_hop); - g_match_info_free (match_info); - goto error; - } - } else { - /* we don't make distinction between missing GATEWAY IP and 0.0.0.0 */ - } - g_match_info_free (match_info); - - /* Metric */ - g_regex_match (regex_metric, *iter, 0, &match_info); - metric_int = -1; - if (g_match_info_matches (match_info)) { - metric = g_match_info_fetch (match_info, 1); - metric_int = _nm_utils_ascii_str_to_int64 (metric, 10, 0, G_MAXUINT32, -1); - if (metric_int == -1) { - g_match_info_free (match_info); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route metric '%s'", metric); - g_free (metric); - goto error; + if (e < 0) { + if (e == -ERANGE) + PARSE_WARNING ("ignoring manual default route: '%s' (%s)", line, filename); + else { + /* we accept all unrecognized lines, because otherwise we would reject the + * entire connection. */ + PARSE_WARNING ("ignoring invalid route at \"%s\" (%s:%lu): %s", line, filename, (long unsigned) line_num, local->message); } - g_free (metric); - } - g_match_info_free (match_info); - - route = nm_ip_route_new (AF_INET, dest, prefix_int, next_hop, metric_int, error); - if (!route) - goto error; - - if (!parse_route_options (route, AF_INET, *iter, error)) { - nm_ip_route_unref (route); - goto error; + continue; } - if (!nm_setting_ip_config_add_route (s_ip4, route)) - PARSE_WARNING ("duplicate IP4 route"); - nm_ip_route_unref (route); + if (!nm_setting_ip_config_add_route (s_ip, route)) + PARSE_WARNING ("duplicate IPv%c route", addr_family == AF_INET ? '4' : '6'); } - success = TRUE; - -error: - g_free (contents); - g_strfreev (lines); - g_regex_unref (regex_to1); - g_regex_unref (regex_to2); - g_regex_unref (regex_via); - g_regex_unref (regex_metric); - - return success; + return TRUE; } static void parse_dns_options (NMSettingIPConfig *ip_config, const char *value) { - char **options = NULL; + gs_free const char **options = NULL; + const char *const *item; g_return_if_fail (ip_config); @@ -811,16 +1082,12 @@ parse_dns_options (NMSettingIPConfig *ip_config, const char *value) if (!nm_setting_ip_config_has_dns_options (ip_config)) nm_setting_ip_config_clear_dns_options (ip_config, TRUE); - options = g_strsplit (value, " ", 0); + options = nm_utils_strsplit_set (value, " "); if (options) { - char **item; for (item = options; *item; item++) { - if (strlen (*item)) { - if (!nm_setting_ip_config_add_dns_option (ip_config, *item)) - PARSE_WARNING ("can't add DNS option '%s'", *item); - } + if (!nm_setting_ip_config_add_dns_option (ip_config, *item)) + PARSE_WARNING ("can't add DNS option '%s'", *item); } - g_strfreev (options); } } @@ -873,157 +1140,6 @@ error: return success; } -static gboolean -read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error) -{ - char *contents = NULL; - gsize len = 0; - char **lines = NULL, **iter; - GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric; - GMatchInfo *match_info; - char *dest = NULL, *prefix = NULL, *next_hop = NULL, *metric = NULL; - int prefix_int; - gint64 metric_int; - gboolean success = FALSE; - - const char *pattern_empty = "^\\s*(\\#.*)?$"; - const char *pattern_to1 = "^\\s*(default|" IPV6_ADDR_REGEX ")" /* IPv6 or 'default' keyword */ - "(?:/(\\d{1,3}))?"; /* optional prefix */ - const char *pattern_to2 = "to\\s+(default|" IPV6_ADDR_REGEX ")" /* IPv6 or 'default' keyword */ - "(?:/(\\d{1,3}))?"; /* optional prefix */ - const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")"; /* IPv6 of gateway */ - const char *pattern_metric = "metric\\s+(\\d+)"; /* metric */ - - - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (s_ip6 != NULL, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - - /* Read the route file */ - if (!g_file_get_contents (filename, &contents, &len, NULL) || !len) { - g_free (contents); - return TRUE; /* missing/empty = success */ - } - - /* Create regexes for pieces to be matched */ - regex_to1 = g_regex_new (pattern_to1, 0, 0, NULL); - regex_to2 = g_regex_new (pattern_to2, 0, 0, NULL); - regex_via = g_regex_new (pattern_via, 0, 0, NULL); - regex_metric = g_regex_new (pattern_metric, 0, 0, NULL); - - /* Iterate through file lines */ - lines = g_strsplit_set (contents, "\n\r", -1); - for (iter = lines; iter && *iter; iter++) { - NMIPRoute *route; - - /* Skip empty lines */ - if (g_regex_match_simple (pattern_empty, *iter, 0, 0)) - continue; - - /* Destination */ - g_regex_match (regex_to1, *iter, 0, &match_info); - if (!g_match_info_matches (match_info)) { - g_match_info_free (match_info); - g_regex_match (regex_to2, *iter, 0, &match_info); - if (!g_match_info_matches (match_info)) { - g_match_info_free (match_info); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IP6 route destination address in record: '%s'", *iter); - goto error; - } - } - dest = g_match_info_fetch (match_info, 1); - if (!g_strcmp0 (dest, "default")) { - /* Ignore default route - NM handles it internally */ - g_clear_pointer (&dest, g_free); - g_match_info_free (match_info); - PARSE_WARNING ("ignoring manual default route: '%s' (%s)", *iter, filename); - continue; - } - - /* Prefix - is optional; 128 if missing */ - prefix = g_match_info_fetch (match_info, 2); - g_match_info_free (match_info); - prefix_int = 128; - if (prefix) { - prefix_int = _nm_utils_ascii_str_to_int64 (prefix, 10, 1, 128, -1); - if (prefix_int == -1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP6 route destination prefix '%s'", prefix); - g_free (dest); - g_free (prefix); - goto error; - } - } - g_free (prefix); - - /* Next hop */ - g_regex_match (regex_via, *iter, 0, &match_info); - if (g_match_info_matches (match_info)) { - next_hop = g_match_info_fetch (match_info, 1); - if (!nm_utils_ipaddr_valid (AF_INET6, next_hop)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IPv6 route nexthop address '%s'", - next_hop); - g_match_info_free (match_info); - g_free (dest); - g_free (next_hop); - goto error; - } - } else { - /* Missing "via" is taken as :: */ - next_hop = NULL; - } - g_match_info_free (match_info); - - /* Metric */ - g_regex_match (regex_metric, *iter, 0, &match_info); - metric_int = -1; - if (g_match_info_matches (match_info)) { - metric = g_match_info_fetch (match_info, 1); - metric_int = _nm_utils_ascii_str_to_int64 (metric, 10, 0, G_MAXUINT32, -1); - if (metric_int == -1) { - g_match_info_free (match_info); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP6 route metric '%s'", metric); - g_free (dest); - g_free (next_hop); - g_free (metric); - goto error; - } - g_free (metric); - } - g_match_info_free (match_info); - - route = nm_ip_route_new (AF_INET6, dest, prefix_int, next_hop, metric_int, error); - g_free (dest); - g_free (next_hop); - if (!route) - goto error; - - if (!parse_route_options (route, AF_INET6, *iter, error)) { - nm_ip_route_unref (route); - goto error; - } - - if (!nm_setting_ip_config_add_route (s_ip6, route)) - PARSE_WARNING ("duplicate IP6 route"); - nm_ip_route_unref (route); - } - - success = TRUE; - -error: - g_free (contents); - g_strfreev (lines); - g_regex_unref (regex_to1); - g_regex_unref (regex_to2); - g_regex_unref (regex_via); - g_regex_unref (regex_metric); - - return success; -} - static NMSetting * make_user_setting (shvarFile *ifcfg, GError **error) { @@ -1076,18 +1192,18 @@ static NMSetting * make_proxy_setting (shvarFile *ifcfg, GError **error) { NMSettingProxy *s_proxy = NULL; - char *value = NULL; + gs_free char *value = NULL; + const char *v; NMSettingProxyMethod method; - value = svGetValueStr_cp (ifcfg, "PROXY_METHOD"); - if (!value) + v = svGetValueStr (ifcfg, "PROXY_METHOD", &value); + if (!v) return NULL; - if (!g_ascii_strcasecmp (value, "auto")) + if (!g_ascii_strcasecmp (v, "auto")) method = NM_SETTING_PROXY_METHOD_AUTO; else method = NM_SETTING_PROXY_METHOD_NONE; - g_free (value); s_proxy = (NMSettingProxy *) nm_setting_proxy_new (); @@ -1097,19 +1213,15 @@ make_proxy_setting (shvarFile *ifcfg, GError **error) NM_SETTING_PROXY_METHOD, (int) NM_SETTING_PROXY_METHOD_AUTO, NULL); - value = svGetValueStr_cp (ifcfg, "PAC_URL"); - if (value) { - value = g_strstrip (value); - g_object_set (s_proxy, NM_SETTING_PROXY_PAC_URL, value, NULL); - g_free (value); - } + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "PAC_URL", &value); + if (v) + g_object_set (s_proxy, NM_SETTING_PROXY_PAC_URL, v, NULL); - value = svGetValueStr_cp (ifcfg, "PAC_SCRIPT"); - if (value) { - value = g_strstrip (value); - g_object_set (s_proxy, NM_SETTING_PROXY_PAC_SCRIPT, value, NULL); - g_free (value); - } + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "PAC_SCRIPT", &value); + if (v) + g_object_set (s_proxy, NM_SETTING_PROXY_PAC_SCRIPT, v, NULL); break; case NM_SETTING_PROXY_METHOD_NONE: @@ -1119,12 +1231,8 @@ make_proxy_setting (shvarFile *ifcfg, GError **error) break; } - value = svGetValueStr_cp (ifcfg, "BROWSER_ONLY"); - if (value) { - if (!g_ascii_strcasecmp (value, "yes")) - g_object_set (s_proxy, NM_SETTING_PROXY_BROWSER_ONLY, TRUE, NULL); - g_free (value); - } + if (svGetValueBoolean (ifcfg, "BROWSER_ONLY", FALSE)) + g_object_set (s_proxy, NM_SETTING_PROXY_BROWSER_ONLY, TRUE, NULL); return NM_SETTING (s_proxy); } @@ -1132,12 +1240,14 @@ make_proxy_setting (shvarFile *ifcfg, GError **error) static NMSetting * make_ip4_setting (shvarFile *ifcfg, const char *network_file, + gboolean routes_read, gboolean *out_has_defroute, GError **error) { gs_unref_object NMSettingIPConfig *s_ip4 = NULL; gs_free char *route_path = NULL; - char *value = NULL; + gs_free char *value = NULL; + const char *v; char *method; gs_free char *dns_options_free = NULL; const char *dns_options = NULL; @@ -1151,6 +1261,8 @@ make_ip4_setting (shvarFile *ifcfg, gint64 timeout; gint priority; char inet_buf[NM_UTILS_INET_ADDRSTRLEN]; + const char *const *item; + guint32 route_table; nm_assert (out_has_defroute && !*out_has_defroute); @@ -1172,44 +1284,43 @@ make_ip4_setting (shvarFile *ifcfg, /* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */ network_ifcfg = svOpenFile (network_file, NULL); if (network_ifcfg) { - char *gatewaydev; + gs_free char *gatewaydev_value = NULL; + const char *gatewaydev; /* Get the connection ifcfg device name and the global gateway device */ - value = svGetValueStr_cp (ifcfg, "DEVICE"); - gatewaydev = svGetValueStr_cp (network_ifcfg, "GATEWAYDEV"); + v = svGetValueStr (ifcfg, "DEVICE", &value); + gatewaydev = svGetValueStr (network_ifcfg, "GATEWAYDEV", &gatewaydev_value); dns_options = svGetValue (network_ifcfg, "RES_OPTIONS", &dns_options_free); /* If there was a global gateway device specified, then only connections * for that device can be the default connection. */ - if (gatewaydev && value) - never_default = !!strcmp (value, gatewaydev); + if (gatewaydev && v) + never_default = !!strcmp (v, gatewaydev); - g_free (gatewaydev); - g_free (value); + nm_clear_g_free (&value); svCloseFile (network_ifcfg); } - value = svGetValueStr_cp (ifcfg, "BOOTPROTO"); + v = svGetValueStr (ifcfg, "BOOTPROTO", &value); - if (!value || !*value || !g_ascii_strcasecmp (value, "none")) { + if (!v || !*v || !g_ascii_strcasecmp (v, "none")) { if (is_any_ip4_address_defined (ifcfg, NULL)) method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; else method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - } else if (!g_ascii_strcasecmp (value, "bootp") || !g_ascii_strcasecmp (value, "dhcp")) { + } else if (!g_ascii_strcasecmp (v, "bootp") || !g_ascii_strcasecmp (v, "dhcp")) { method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; - } else if (!g_ascii_strcasecmp (value, "static")) { + } else if (!g_ascii_strcasecmp (v, "static")) { if (is_any_ip4_address_defined (ifcfg, NULL)) method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; else method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - } else if (!g_ascii_strcasecmp (value, "autoip")) { + } else if (!g_ascii_strcasecmp (v, "autoip")) { method = NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL; - } else if (!g_ascii_strcasecmp (value, "shared")) { + } else if (!g_ascii_strcasecmp (v, "shared")) { int idx; - g_free (value); g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED, NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, @@ -1233,11 +1344,18 @@ make_ip4_setting (shvarFile *ifcfg, return g_steal_pointer (&s_ip4); } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown BOOTPROTO '%s'", value); - g_free (value); + "Unknown BOOTPROTO '%s'", v); return NULL; } - g_free (value); + + /* the route table (policy routing) is ignored if we don't handle routes. */ + route_table = svGetValueInt64 (ifcfg, "IPV4_ROUTE_TABLE", 10, + 0, G_MAXUINT32, 0); + if ( route_table != 0 + && !routes_read) { + PARSE_WARNING ("'rule-' or 'rule6-' files are present; Policy routing (IPV4_ROUTE_TABLE) is ignored"); + route_table = 0; + } g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, @@ -1247,44 +1365,43 @@ make_ip4_setting (shvarFile *ifcfg, NM_SETTING_IP_CONFIG_MAY_FAIL, !svGetValueBoolean (ifcfg, "IPV4_FAILURE_FATAL", FALSE), NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV4_ROUTE_METRIC", 10, -1, G_MAXUINT32, -1), + NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) route_table, NULL); if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) return g_steal_pointer (&s_ip4); /* Handle DHCP settings */ - value = svGetValueStr_cp (ifcfg, "DHCP_HOSTNAME"); - if (value) { - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, value, NULL); - g_free (value); - } + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DHCP_HOSTNAME", &value); + if (v) + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL); - value = svGetValueStr_cp (ifcfg, "DHCP_FQDN"); - if (value) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DHCP_FQDN", &value); + if (v) { g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, NULL, - NM_SETTING_IP4_CONFIG_DHCP_FQDN, value, + NM_SETTING_IP4_CONFIG_DHCP_FQDN, v, NULL); - g_free (value); } g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE), - NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0), - NULL); + NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE), + NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0), + NULL); - value = svGetValueStr_cp (ifcfg, "DHCP_CLIENT_ID"); - if (value) { - g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, value, NULL); - g_free (value); - } + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DHCP_CLIENT_ID", &value); + if (v) + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, v, NULL); /* Read static IP addresses. * Read them even for AUTO method - in this case the addresses are * added to the automatic ones. Note that this is not currently supported by * the legacy 'network' service (ifup-eth). */ - for (i = -1; i < 256; i++) { + for (i = -1;; i++) { NMIPAddress *addr = NULL; /* gateway will only be set if still unset. Hence, we don't leak gateway @@ -1337,46 +1454,40 @@ make_ip4_setting (shvarFile *ifcfg, char tag[256]; numbered_tag (tag, "DNS", i); - value = svGetValueStr_cp (ifcfg, tag); - if (value) { - if (nm_utils_ipaddr_valid (AF_INET, value)) { - if (!nm_setting_ip_config_add_dns (s_ip4, value)) + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, tag, &value); + if (v) { + if (nm_utils_ipaddr_valid (AF_INET, v)) { + if (!nm_setting_ip_config_add_dns (s_ip4, v)) PARSE_WARNING ("duplicate DNS server %s", tag); - } else if (nm_utils_ipaddr_valid (AF_INET6, value)) { + } else if (nm_utils_ipaddr_valid (AF_INET6, v)) { /* Ignore IPv6 addresses */ } else { - PARSE_WARNING ("invalid DNS server address %s", value); - g_free (value); + PARSE_WARNING ("invalid DNS server address %s", v); return NULL; } - - g_free (value); } } /* DNS searches */ - value = svGetValueStr_cp (ifcfg, "DOMAIN"); - if (value) { - char **searches = NULL; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DOMAIN", &value); + if (v) { + gs_free const char **searches = NULL; - searches = g_strsplit (value, " ", 0); + searches = nm_utils_strsplit_set (v, " "); if (searches) { - char **item; for (item = searches; *item; item++) { - if (strlen (*item)) { - if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) - PARSE_WARNING ("duplicate DNS domain '%s'", *item); - } + if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) + PARSE_WARNING ("duplicate DNS domain '%s'", *item); } - g_strfreev (searches); } - g_free (value); } /* DNS options */ + nm_clear_g_free (&value); parse_dns_options (s_ip4, svGetValue (ifcfg, "RES_OPTIONS", &value)); parse_dns_options (s_ip4, dns_options); - g_free (value); /* DNS priority */ priority = svGetValueInt64 (ifcfg, "IPV4_DNS_PRIORITY", 10, G_MININT32, G_MAXINT32, 0); @@ -1388,13 +1499,13 @@ make_ip4_setting (shvarFile *ifcfg, /* Static routes - route-<name> file */ route_path = utils_get_route_path (svFileGetName (ifcfg)); - if (utils_has_complex_routes (route_path)) { - PARSE_WARNING ("'rule-' or 'rule6-' file is present; you will need to use a dispatcher script to apply these routes"); + if (!routes_read) { + /* NOP */ } else if (utils_has_route_file_new_syntax (route_path)) { /* Parse route file in new syntax */ route_ifcfg = utils_get_route_ifcfg (svFileGetName (ifcfg), FALSE); if (route_ifcfg) { - for (i = 0; i < 256; i++) { + for (i = 0;; i++) { NMIPRoute *route = NULL; if (!read_one_ip4_route (route_ifcfg, i, &route, error)) { @@ -1412,28 +1523,24 @@ make_ip4_setting (shvarFile *ifcfg, svCloseFile (route_ifcfg); } } else { - if (!read_route_file_legacy (route_path, s_ip4, error)) + if (!read_route_file (AF_INET, route_path, s_ip4, error)) return NULL; } /* Legacy value NM used for a while but is incorrect (rh #459370) */ if (!nm_setting_ip_config_get_num_dns_searches (s_ip4)) { - value = svGetValueStr_cp (ifcfg, "SEARCH"); - if (value) { - char **searches = NULL; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "SEARCH", &value); + if (v) { + gs_free const char **searches = NULL; - searches = g_strsplit (value, " ", 0); + searches = nm_utils_strsplit_set (v, " "); if (searches) { - char **item; for (item = searches; *item; item++) { - if (strlen (*item)) { - if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) - PARSE_WARNING ("duplicate DNS search '%s'", *item); - } + if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) + PARSE_WARNING ("duplicate DNS search '%s'", *item); } - g_strfreev (searches); } - g_free (value); } } @@ -1450,7 +1557,6 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file { GDir *dir; char *dirname, *base; - shvarFile *parsed; NMIPAddress *base_addr = NULL; GError *err = NULL; @@ -1472,8 +1578,11 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file gboolean ok; while ((item = g_dir_read_name (dir))) { + nm_auto_shvar_file_close shvarFile *parsed = NULL; gs_free char *gateway = NULL; - char *full_path, *device; + gs_free char *device_value = NULL; + gs_free char *full_path = NULL; + const char *device; const char *p; if (!utils_is_ifcfg_alias_file (item, base)) @@ -1489,32 +1598,25 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file } if (*p) { PARSE_WARNING ("ignoring alias file '%s' with invalid name", full_path); - g_free (full_path); continue; } parsed = svOpenFile (full_path, &err); if (!parsed) { PARSE_WARNING ("couldn't parse alias file '%s': %s", full_path, err->message); - g_free (full_path); g_clear_error (&err); continue; } - device = svGetValueStr_cp (parsed, "DEVICE"); + device = svGetValueStr (parsed, "DEVICE", &device_value); if (!device) { PARSE_WARNING ("alias file '%s' has no DEVICE", full_path); - svCloseFile (parsed); - g_free (full_path); continue; } /* We know that item starts with IFCFG_TAG from utils_is_ifcfg_alias_file() */ if (strcmp (device, item + strlen (IFCFG_TAG)) != 0) { PARSE_WARNING ("alias file '%s' has invalid DEVICE (%s) for filename", full_path, device); - g_free (device); - svCloseFile (parsed); - g_free (full_path); continue; } @@ -1549,11 +1651,6 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file g_clear_error (&err); } nm_ip_address_unref (addr); - - svCloseFile (parsed); - - g_free (device); - g_free (full_path); } g_dir_close (dir); @@ -1569,6 +1666,7 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file static NMSetting * make_ip6_setting (shvarFile *ifcfg, const char *network_file, + gboolean routes_read, GError **error) { NMSettingIPConfig *s_ip6 = NULL; @@ -1580,14 +1678,17 @@ make_ip6_setting (shvarFile *ifcfg, gboolean ipv6init, ipv6forwarding, dhcp6 = FALSE; char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; char *ipv6addr, *ipv6addr_secondaries; - char **list = NULL, **iter; + gs_free const char **list = NULL; + const char *const *iter; guint32 i; + int i_val; + GError *local = NULL; gint priority; shvarFile *network_ifcfg; gboolean never_default = FALSE; gboolean ip6_privacy = FALSE, ip6_privacy_prefer_public_ip; NMSettingIP6ConfigPrivacy ip6_privacy_val; - NMSettingIP6ConfigAddrGenMode addr_gen_mode; + guint32 route_table; s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); @@ -1689,6 +1790,15 @@ make_ip6_setting (shvarFile *ifcfg, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; g_free (str_value); + /* the route table (policy routing) is ignored if we don't handle routes. */ + route_table = svGetValueInt64 (ifcfg, "IPV6_ROUTE_TABLE", 10, + 0, G_MAXUINT32, 0); + if ( route_table != 0 + && !routes_read) { + PARSE_WARNING ("'rule-' or 'rule6-' files are present; Policy routing (IPV6_ROUTE_TABLE) is ignored"); + route_table = 0; + } + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, method, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svGetValueBoolean (ifcfg, "IPV6_PEERDNS", TRUE), @@ -1697,6 +1807,7 @@ make_ip6_setting (shvarFile *ifcfg, NM_SETTING_IP_CONFIG_MAY_FAIL, !svGetValueBoolean (ifcfg, "IPV6_FAILURE_FATAL", FALSE), NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV6_ROUTE_METRIC", 10, -1, G_MAXUINT32, -1), + NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) route_table, NM_SETTING_IP6_CONFIG_IP6_PRIVACY, ip6_privacy_val, NULL); @@ -1736,21 +1847,18 @@ make_ip6_setting (shvarFile *ifcfg, g_free (ipv6addr); g_free (ipv6addr_secondaries); - list = g_strsplit_set (value, " ", 0); + list = nm_utils_strsplit_set (value, " "); g_free (value); for (iter = list, i = 0; iter && *iter; iter++, i++) { NMIPAddress *addr = NULL; - if (!parse_full_ip6_address (ifcfg, *iter, i, &addr, error)) { - g_strfreev (list); + if (!parse_full_ip6_address (ifcfg, *iter, i, &addr, error)) goto error; - } if (!nm_setting_ip_config_add_address (s_ip6, addr)) PARSE_WARNING ("duplicate IP6 address"); nm_ip_address_unref (addr); } - g_strfreev (list); /* Gateway */ if (nm_setting_ip_config_get_num_addresses (s_ip6)) { @@ -1779,21 +1887,14 @@ make_ip6_setting (shvarFile *ifcfg, } } - /* IPv6 addressing mode configuration */ - str_value = svGetValueStr_cp (ifcfg, "IPV6_ADDR_GEN_MODE"); - if (str_value) { - if (nm_utils_enum_from_str (nm_setting_ip6_config_addr_gen_mode_get_type (), str_value, - (int *) &addr_gen_mode, NULL)) - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, addr_gen_mode, NULL); - else - PARSE_WARNING ("Invalid IPV6_ADDR_GEN_MODE"); - g_free (str_value); - } else { - g_object_set (s_ip6, - NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, - NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, - NULL); + i_val = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; + if (!svGetValueEnum (ifcfg, "IPV6_ADDR_GEN_MODE", + nm_setting_ip6_config_addr_gen_mode_get_type (), + &i_val, &local)) { + PARSE_WARNING ("%s", local->message); + g_clear_error (&local); } + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, i_val, NULL); /* IPv6 tokenized interface identifier */ str_value = svGetValueStr_cp (ifcfg, "IPV6_TOKEN"); @@ -1831,12 +1932,13 @@ make_ip6_setting (shvarFile *ifcfg, /* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIPConfig */ - if (!utils_has_complex_routes (svFileGetName (ifcfg))) { + if (!routes_read) { + /* NOP */ + } else { /* Read static routes from route6-<interface> file */ route6_path = utils_get_route6_path (svFileGetName (ifcfg)); - if (!read_route6_file (route6_path, s_ip6, error)) + if (!read_route_file (AF_INET6, route6_path, s_ip6, error)) goto error; - g_free (route6_path); } @@ -1860,57 +1962,6 @@ error: return NULL; } -static void -check_if_bond_slave (shvarFile *ifcfg, - NMSettingConnection *s_con) -{ - char *value; - - value = svGetValueStr_cp (ifcfg, "MASTER_UUID"); - if (!value) - value = svGetValueStr_cp (ifcfg, "MASTER"); - - if (value) { - g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL); - g_object_set (s_con, - NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, - NULL); - g_free (value); - } - - /* We should be checking for SLAVE=yes as well, but NM used to not set that, - * so for backward-compatibility, we don't check. - */ -} - -static gboolean -check_if_team_slave (shvarFile *ifcfg, - NMSettingConnection *s_con) -{ - gs_free char *value = NULL; - - value = svGetValueStr_cp (ifcfg, "TEAM_MASTER_UUID"); - if (!value) - value = svGetValueStr_cp (ifcfg, "TEAM_MASTER"); - if (!value) - return FALSE; - - g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL); - g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, NULL); - return TRUE; -} - -static void -check_if_slave (shvarFile *ifcfg, - NMSettingConnection *s_con) -{ - g_return_if_fail (NM_IS_SETTING_CONNECTION (s_con)); - - if (check_if_team_slave (ifcfg, s_con)) - return; - check_if_bond_slave (ifcfg, s_con); -} - typedef struct { const char *enable_key; const char *advertise_key; @@ -1959,29 +2010,29 @@ read_dcb_app (shvarFile *ifcfg, GError **error) { NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; - char *tmp, *val; + gs_free char *value = NULL; + const char *v; gboolean success = TRUE; int priority = -1; + char key[255]; flags = read_dcb_flags (ifcfg, flags_prop); /* Priority */ - tmp = g_strdup_printf ("DCB_APP_%s_PRIORITY", app); - val = svGetValueStr_cp (ifcfg, tmp); - if (val) { - priority = _nm_utils_ascii_str_to_int64 (val, 0, 0, 7, -1); + nm_sprintf_buf (key, "DCB_APP_%s_PRIORITY", app); + v = svGetValueStr (ifcfg, key, &value); + if (v) { + priority = _nm_utils_ascii_str_to_int64 (v, 0, 0, 7, -1); if (priority < 0) { success = FALSE; g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid %s value '%s' (expected 0 - 7)", - tmp, val); + key, v); } - g_free (val); if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) PARSE_WARNING ("ignoring DCB %s priority; app not enabled", app); } - g_free (tmp); if (success) { g_object_set (G_OBJECT (s_dcb), @@ -2004,11 +2055,12 @@ read_dcb_bool_array (shvarFile *ifcfg, DcbSetBoolFunc set_func, GError **error) { - gs_free char *val = NULL; + gs_free char *value = NULL; + const char *v; guint i; - val = svGetValueStr_cp (ifcfg, prop); - if (!val) + v = svGetValueStr (ifcfg, prop, &value); + if (!v) return TRUE; if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) { @@ -2016,8 +2068,8 @@ read_dcb_bool_array (shvarFile *ifcfg, return TRUE; } - if (strlen (val) != 8) { - PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, val); + if (strlen (v) != 8) { + PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, v); g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "boolean array must be 8 characters"); return FALSE; @@ -2025,13 +2077,13 @@ read_dcb_bool_array (shvarFile *ifcfg, /* All characters must be either 0 or 1 */ for (i = 0; i < 8; i++) { - if (val[i] != '0' && val[i] != '1') { - PARSE_WARNING ("invalid %s value '%s': not all 0s and 1s", prop, val); + if (v[i] != '0' && v[i] != '1') { + PARSE_WARNING ("invalid %s value '%s': not all 0s and 1s", prop, v); g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "invalid boolean digit"); return FALSE; } - set_func (s_dcb, i, (val[i] == '1')); + set_func (s_dcb, i, (v[i] == '1')); } return TRUE; } @@ -2096,8 +2148,8 @@ read_dcb_percent_array (shvarFile *ifcfg, GError **error) { gs_free char *val = NULL; - gs_strfreev char **split = NULL; - char **iter; + gs_free const char **split = NULL; + const char *const *iter; guint i, sum = 0; val = svGetValueStr_cp (ifcfg, prop); @@ -2109,8 +2161,8 @@ read_dcb_percent_array (shvarFile *ifcfg, return TRUE; } - split = g_strsplit_set (val, ",", 0); - if (!split || (g_strv_length (split) != 8)) { + split = nm_utils_strsplit_set (val, ","); + if (NM_PTRARRAY_LEN (split) != 8) { PARSE_WARNING ("invalid %s percentage list value '%s'", prop, val); g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "percent array must be 8 elements"); @@ -2531,15 +2583,17 @@ fill_wpa_ciphers (shvarFile *ifcfg, gboolean group, gboolean adhoc) { - char *value = NULL, *p; - char **list = NULL, **iter; + gs_free char *value = NULL; + const char *p; + gs_free const char **list = NULL; + const char *const *iter; int i = 0; - p = value = svGetValueStr_cp (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE"); - if (!value) + p = svGetValueStr (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", &value); + if (!p) return TRUE; - list = g_strsplit_set (p, " ", 0); + list = nm_utils_strsplit_set (p, " "); for (iter = list; iter && *iter; iter++, i++) { /* Ad-Hoc configurations cannot have pairwise ciphers, and can only * have one group cipher. Ignore any additional group ciphers and @@ -2578,9 +2632,6 @@ fill_wpa_ciphers (shvarFile *ifcfg, } } - if (list) - g_strfreev (list); - g_free (value); return TRUE; } @@ -2637,6 +2688,23 @@ parse_wpa_psk (shvarFile *ifcfg, return g_steal_pointer (&psk); } +static void +read_8021x_password (shvarFile *ifcfg, shvarFile *keys_ifcfg, const char *name, + char **value, NMSettingSecretFlags *flags) +{ + gs_free char *flags_key = NULL; + + *value = NULL; + flags_key = g_strdup_printf ("%s_FLAGS", name); + *flags = read_secret_flags (ifcfg, flags_key); + + if (*flags == NM_SETTING_SECRET_FLAG_NONE) { + *value = svGetValueStr_cp (ifcfg, name); + if (!*value && keys_ifcfg) + *value = svGetValueStr_cp (keys_ifcfg, name); + } +} + static gboolean eap_simple_reader (const char *eap_method, shvarFile *ifcfg, @@ -2646,6 +2714,7 @@ eap_simple_reader (const char *eap_method, GError **error) { NMSettingSecretFlags flags; + GBytes *bytes; char *value; value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY"); @@ -2656,28 +2725,29 @@ eap_simple_reader (const char *eap_method, return FALSE; } g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); - g_free (value); + nm_clear_g_free (&value); - flags = read_secret_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS"); + read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD", &value, &flags); g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_FLAGS, flags, NULL); + if (value) { + g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL); + nm_clear_g_free (&value); + } - /* Only read the password if it's system-owned */ - if (flags == NM_SETTING_SECRET_FLAG_NONE) { - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PASSWORD"); - if (!value && keys) { - /* Try the lookaside keys file */ - value = svGetValueStr_cp (keys, "IEEE_8021X_PASSWORD"); - } - - if (!value) { + read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD_RAW", &value, &flags); + g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, flags, NULL); + if (value) { + bytes = nm_utils_hexstr2bin (value); + if (!bytes) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IEEE_8021X_PASSWORD for EAP method '%s'.", - eap_method); + "Invalid hex string '%s' in IEEE_8021X_PASSWORD_RAW.", + value); + g_free (value); return FALSE; } - - g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL); - g_free (value); + g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW, bytes, NULL); + g_bytes_unref (bytes); + nm_clear_g_free (&value); } return TRUE; @@ -2795,14 +2865,6 @@ eap_tls_reader (const char *eap_method, /* Try the lookaside keys file */ privkey_password = svGetValueStr_cp (keys, pk_pw_key); } - - if (!privkey_password) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing %s for EAP method '%s'.", - pk_pw_key, - eap_method); - return FALSE; - } } /* The private key itself */ @@ -2843,8 +2905,7 @@ eap_tls_reader (const char *eap_method, * then nm_setting_802_1x_set_private_key() already set the client certificate * to the same value as the private key. */ - if ( privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY - || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) { + if (privkey_format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) { gs_free char *real_cert_value = NULL; gs_free char *client_cert = NULL; @@ -2888,98 +2949,89 @@ eap_peap_reader (const char *eap_method, gboolean phase2, GError **error) { - char *anon_ident = NULL; - char *ca_cert = NULL; - char *real_cert_value = NULL; - char *inner_auth = NULL; - char *peapver = NULL; - char *lower; - char **list = NULL, **iter; - gboolean success = FALSE; + gs_free char *value = NULL; + const char *v; + gs_free const char **list = NULL; + const char *const *iter; NMSetting8021xCKScheme scheme; - ca_cert = svGetValueStr_cp (ifcfg, "IEEE_8021X_CA_CERT"); - if (ca_cert) { - real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme); + v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value); + if (v) { + gs_free char *real_cert_value = NULL; + + real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme); if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error)) - goto done; + return FALSE; } else { PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", eap_method); } - peapver = svGetValueStr_cp (ifcfg, "IEEE_8021X_PEAP_VERSION"); - if (peapver) { - if (!strcmp (peapver, "0")) + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", &value); + if (v) { + if (!strcmp (v, "0")) g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "0", NULL); - else if (!strcmp (peapver, "1")) + else if (!strcmp (v, "1")) g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "1", NULL); else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Unknown IEEE_8021X_PEAP_VERSION value '%s'", - peapver); - goto done; + v); + return FALSE; } } if (svGetValueBoolean (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE)) g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL); - anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY"); - if (anon_ident) - g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY", &value); + if (v) + g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL); - inner_auth = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS"); - if (!inner_auth) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &value); + if (!v) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Missing IEEE_8021X_INNER_AUTH_METHODS."); - goto done; + return FALSE; } /* Handle options for the inner auth method */ - list = g_strsplit (inner_auth, " ", 0); - for (iter = list; iter && *iter; iter++) { - if (!strlen (*iter)) - continue; - - if ( !strcmp (*iter, "MSCHAPV2") - || !strcmp (*iter, "MD5") - || !strcmp (*iter, "GTC")) { + list = nm_utils_strsplit_set (v, " "); + iter = list; + if (iter) { + if (NM_IN_STRSET (*iter, "MSCHAPV2", + "MD5", + "GTC")) { if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; - } else if (!strcmp (*iter, "TLS")) { + return FALSE; + } else if (nm_streq (*iter, "TLS")) { if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; + return FALSE; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", *iter); - goto done; + return FALSE; } - lower = g_ascii_strdown (*iter, -1); - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL); - g_free (lower); - break; + { + gs_free char *lower = NULL; + + lower = g_ascii_strdown (*iter, -1); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL); + } } if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "No valid IEEE_8021X_INNER_AUTH_METHODS found."); - goto done; + return FALSE; } - success = TRUE; - -done: - if (list) - g_strfreev (list); - g_free (inner_auth); - g_free (peapver); - g_free (real_cert_value); - g_free (ca_cert); - g_free (anon_ident); - return success; + return TRUE; } static gboolean @@ -2990,81 +3042,70 @@ eap_ttls_reader (const char *eap_method, gboolean phase2, GError **error) { - gboolean success = FALSE; - char *anon_ident = NULL; - char *ca_cert = NULL; - char *real_cert_value = NULL; - char *inner_auth = NULL; - char *tmp; - char **list = NULL, **iter; + gs_free char *inner_auth = NULL; + gs_free char *value = NULL; + const char *v; + gs_free const char **list = NULL; + const char *const *iter; NMSetting8021xCKScheme scheme; - ca_cert = svGetValueStr_cp (ifcfg, "IEEE_8021X_CA_CERT"); - if (ca_cert) { - real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme); + v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value); + if (v) { + gs_free char *real_cert_value = NULL; + + real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme); if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error)) - goto done; + return FALSE; } else { PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", eap_method); } - anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY"); - if (anon_ident) - g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY", &value); + if (v) + g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL); - tmp = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS"); - if (!tmp) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &value); + if (!v) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Missing IEEE_8021X_INNER_AUTH_METHODS."); - goto done; + return FALSE; } - inner_auth = g_ascii_strdown (tmp, -1); - g_free (tmp); + inner_auth = g_ascii_strdown (v, -1); /* Handle options for the inner auth method */ - list = g_strsplit (inner_auth, " ", 0); - for (iter = list; iter && *iter; iter++) { - if (!strlen (*iter)) - continue; - - if ( !strcmp (*iter, "mschapv2") - || !strcmp (*iter, "mschap") - || !strcmp (*iter, "pap") - || !strcmp (*iter, "chap")) { + list = nm_utils_strsplit_set (inner_auth, " "); + iter = list; + if (iter) { + if (NM_IN_STRSET (*iter, "mschapv2", + "mschap", + "pap", + "chap")) { if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; + return FALSE; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL); - } else if (!strcmp (*iter, "eap-tls")) { + } else if (nm_streq (*iter, "eap-tls")) { if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; + return FALSE; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL); - } else if ( !strcmp (*iter, "eap-mschapv2") - || !strcmp (*iter, "eap-md5") - || !strcmp (*iter, "eap-gtc")) { + } else if (NM_IN_STRSET (*iter, "eap-mschapv2", + "eap-md5", + "eap-gtc")) { if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; + return FALSE; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + NM_STRLEN ("eap-")), NULL); } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", *iter); - goto done; + return FALSE; } - break; } - success = TRUE; - -done: - if (list) - g_strfreev (list); - g_free (inner_auth); - g_free (real_cert_value); - g_free (ca_cert); - g_free (anon_ident); - return success; + return TRUE; } static gboolean @@ -3081,7 +3122,8 @@ eap_fast_reader (const char *eap_method, char *inner_auth = NULL; char *fast_provisioning = NULL; char *lower; - char **list = NULL, **iter; + gs_free const char **list = NULL; + const char *const *iter; const char *pac_prov_str; gboolean allow_unauth = FALSE, allow_auth = FALSE; gboolean success = FALSE; @@ -3094,10 +3136,10 @@ eap_fast_reader (const char *eap_method, fast_provisioning = svGetValueStr_cp (ifcfg, "IEEE_8021X_FAST_PROVISIONING"); if (fast_provisioning) { - list = g_strsplit_set (fast_provisioning, " \t", 0); - for (iter = list; iter && *iter; iter++) { - if (**iter == '\0') - continue; + gs_free const char **list1 = NULL; + + list1 = nm_utils_strsplit_set (fast_provisioning, " \t"); + for (iter = list1; iter && *iter; iter++) { if (strcmp (*iter, "allow-unauth") == 0) allow_unauth = TRUE; else if (strcmp (*iter, "allow-auth") == 0) @@ -3108,8 +3150,6 @@ eap_fast_reader (const char *eap_method, *iter); } } - g_strfreev (list); - list = NULL; } pac_prov_str = allow_unauth ? (allow_auth ? "3" : "1") : (allow_auth ? "2" : "0"); g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, pac_prov_str, NULL); @@ -3132,11 +3172,9 @@ eap_fast_reader (const char *eap_method, } /* Handle options for the inner auth method */ - list = g_strsplit (inner_auth, " ", 0); - for (iter = list; iter && *iter; iter++) { - if (!strlen (*iter)) - continue; - + list = nm_utils_strsplit_set (inner_auth, " "); + iter = list; + if (iter) { if ( !strcmp (*iter, "MSCHAPV2") || !strcmp (*iter, "GTC")) { if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) @@ -3151,7 +3189,6 @@ eap_fast_reader (const char *eap_method, lower = g_ascii_strdown (*iter, -1); g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL); g_free (lower); - break; } if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) { @@ -3163,7 +3200,6 @@ eap_fast_reader (const char *eap_method, success = TRUE; done: - g_strfreev (list); g_free (inner_auth); g_free (fast_provisioning); g_free (real_pac_path); @@ -3204,22 +3240,21 @@ read_8021x_list_value (shvarFile *ifcfg, NMSetting8021x *setting, const char *prop_name) { - char *value; - char **strv; + gs_free char *value = NULL; + gs_free const char **strv = NULL; + const char *v; g_return_if_fail (ifcfg != NULL); g_return_if_fail (ifcfg_var_name != NULL); g_return_if_fail (prop_name != NULL); - value = svGetValueStr_cp (ifcfg, ifcfg_var_name); - if (!value) + v = svGetValueStr (ifcfg, ifcfg_var_name, &value); + if (!v) return; - strv = g_strsplit_set (value, " \t", 0); - if (strv && strv[0]) + strv = nm_utils_strsplit_set (v, " \t"); + if (strv) g_object_set (setting, prop_name, strv, NULL); - g_strfreev (strv); - g_free (value); } static NMSetting8021x * @@ -3230,21 +3265,23 @@ fill_8021x (shvarFile *ifcfg, GError **error) { nm_auto_shvar_file_close shvarFile *keys = NULL; - NMSetting8021x *s_8021x; - char *value; - char **list = NULL, **iter; + gs_unref_object NMSetting8021x *s_8021x = NULL; + gs_free char *value = NULL; + const char *v; + gs_free const char **list = NULL; + const char *const *iter; gint64 timeout; + int i_val; - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_EAP_METHODS"); - if (!value) { + v = svGetValueStr (ifcfg, "IEEE_8021X_EAP_METHODS", &value); + if (!v) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Missing IEEE_8021X_EAP_METHODS for key management '%s'", key_mgmt); return NULL; } - list = g_strsplit (value, " ", 0); - g_free (value); + list = nm_utils_strsplit_set (v, " "); s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); @@ -3255,7 +3292,7 @@ fill_8021x (shvarFile *ifcfg, for (iter = list; iter && *iter; iter++) { EAPReader *eap = &eap_readers[0]; gboolean found = FALSE; - char *lower = NULL; + gs_free char *lower = NULL; lower = g_ascii_strdown (*iter, -1); while (eap->method) { @@ -3273,10 +3310,9 @@ fill_8021x (shvarFile *ifcfg, } /* Parse EAP method specific options */ - if (!(*eap->reader)(lower, ifcfg, keys, s_8021x, FALSE, error)) { - g_free (lower); - goto error; - } + if (!(*eap->reader)(lower, ifcfg, keys, s_8021x, FALSE, error)) + return NULL; + nm_setting_802_1x_add_eap_method (s_8021x, lower); found = TRUE; break; @@ -3287,65 +3323,46 @@ next: if (!found) PARSE_WARNING ("ignored unknown IEEE_8021X_EAP_METHOD '%s'.", lower); - g_free (lower); } if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "No valid EAP methods found in IEEE_8021X_EAP_METHODS."); - goto error; + return NULL; } - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_SUBJECT_MATCH"); - g_object_set (s_8021x, NM_SETTING_802_1X_SUBJECT_MATCH, value, NULL); - g_free (value); - - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH"); - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, value, NULL); - g_free (value); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_SUBJECT_MATCH", &value); + g_object_set (s_8021x, NM_SETTING_802_1X_SUBJECT_MATCH, v, NULL); - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS"); - if (value) { - NMSetting8021xAuthFlags flags; - char *token; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH", &value); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, v, NULL); - if (nm_utils_enum_from_str (nm_setting_802_1x_auth_flags_get_type (), value, - (int *) &flags, &token)) { - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, flags, NULL); - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IEEE_8021X_PHASE1_AUTH_FLAGS flag '%s'", token); - g_free (token); - g_free (value); - goto error; - } - g_free (value); - } + i_val = NM_SETTING_802_1X_AUTH_FLAGS_NONE; + if (!svGetValueEnum (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS", + nm_setting_802_1x_auth_flags_get_type (), + &i_val, error)) + return NULL; + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, (guint) i_val, NULL); read_8021x_list_value (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", s_8021x, NM_SETTING_802_1X_ALTSUBJECT_MATCHES); read_8021x_list_value (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", s_8021x, NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH"); - g_object_set (s_8021x, NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, value, NULL); - g_free (value); - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH"); - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, value, NULL); - g_free (value); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH", &value); + g_object_set (s_8021x, NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, v, NULL); - timeout = svGetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", 10, 0, G_MAXINT32, 0); - g_object_set (s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (gint32) timeout, NULL); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH", &value); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, v, NULL); - if (list) - g_strfreev (list); - return s_8021x; + timeout = svGetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", 10, 0, G_MAXINT32, 0); + g_object_set (s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (gint) timeout, NULL); -error: - if (list) - g_strfreev (list); - g_object_unref (s_8021x); - return NULL; + return g_steal_pointer (&s_8021x); } static NMSetting * @@ -3356,18 +3373,31 @@ make_wpa_setting (shvarFile *ifcfg, NMSetting8021x **s_8021x, GError **error) { - NMSettingWirelessSecurity *wsec; - char *value, *psk, *lower; + gs_unref_object NMSettingWirelessSecurity *wsec = NULL; + gs_free char *value = NULL; + const char *v; gboolean wpa_psk = FALSE, wpa_eap = FALSE, ieee8021x = FALSE; + int i_val; + GError *local = NULL; wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); - value = svGetValueStr_cp (ifcfg, "KEY_MGMT"); - wpa_psk = !g_strcmp0 (value, "WPA-PSK"); - wpa_eap = !g_strcmp0 (value, "WPA-EAP"); - ieee8021x = !g_strcmp0 (value, "IEEE8021X"); + v = svGetValueStr (ifcfg, "KEY_MGMT", &value); + wpa_psk = nm_streq0 (v, "WPA-PSK"); + wpa_eap = nm_streq0 (v, "WPA-EAP"); + ieee8021x = nm_streq0 (v, "IEEE8021X"); if (!wpa_psk && !wpa_eap && !ieee8021x) - goto error; /* Not WPA or Dynamic WEP */ + return NULL; /* Not WPA or Dynamic WEP */ + + /* WPS */ + i_val = NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT; + if (!svGetValueEnum (ifcfg, "WPS_METHOD", + nm_setting_wireless_security_wps_method_get_type (), + &i_val, error)) + return NULL; + g_object_set (wsec, + NM_SETTING_WIRELESS_SECURITY_WPS_METHOD, (guint) i_val, + NULL); /* Pairwise and Group ciphers (only relevant for WPA/RSN) */ if (wpa_psk || wpa_eap) { @@ -3380,18 +3410,17 @@ make_wpa_setting (shvarFile *ifcfg, /* Ad-Hoc mode only supports WPA proto for now */ nm_setting_wireless_security_add_proto (wsec, "wpa"); } else { - char *allow_wpa, *allow_rsn; + gs_free char *value2 = NULL; + const char *v2; - allow_wpa = svGetValueStr_cp (ifcfg, "WPA_ALLOW_WPA"); - allow_rsn = svGetValueStr_cp (ifcfg, "WPA_ALLOW_WPA2"); - - if (allow_wpa && svGetValueBoolean (ifcfg, "WPA_ALLOW_WPA", TRUE)) + v2 = svGetValueStr (ifcfg, "WPA_ALLOW_WPA", &value2); + if (v2 && svParseBoolean (v2, TRUE)) nm_setting_wireless_security_add_proto (wsec, "wpa"); - if (allow_rsn && svGetValueBoolean (ifcfg, "WPA_ALLOW_WPA2", TRUE)) - nm_setting_wireless_security_add_proto (wsec, "rsn"); - g_free (allow_wpa); - g_free (allow_rsn); + nm_clear_g_free (&value2); + v2 = svGetValueStr (ifcfg, "WPA_ALLOW_WPA2", &value2); + if (v2 && svParseBoolean (v2, TRUE)) + nm_setting_wireless_security_add_proto (wsec, "rsn"); } if (wpa_psk) { @@ -3402,12 +3431,15 @@ make_wpa_setting (shvarFile *ifcfg, /* Read PSK if it's system-owned */ if (psk_flags == NM_SETTING_SECRET_FLAG_NONE) { - psk = parse_wpa_psk (ifcfg, file, ssid, error); - if (psk) { + gs_free char *psk = NULL; + + psk = parse_wpa_psk (ifcfg, file, ssid, &local); + if (psk) g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk, NULL); - g_free (psk); - } else if (error) - goto error; + else if (local) { + g_propagate_error (error, local); + return NULL; + } } if (adhoc) @@ -3418,37 +3450,38 @@ make_wpa_setting (shvarFile *ifcfg, /* Adhoc mode is mutually exclusive with any 802.1x-based authentication */ if (adhoc) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", value); - goto error; + "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", v); + return NULL; } - *s_8021x = fill_8021x (ifcfg, file, value, TRUE, error); + *s_8021x = fill_8021x (ifcfg, file, v, TRUE, error); if (!*s_8021x) - goto error; + return NULL; - lower = g_ascii_strdown (value, -1); - g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL); - g_free (lower); + { + gs_free char *lower = g_ascii_strdown (v, -1); + + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL); + } } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown wireless KEY_MGMT type '%s'", value); - goto error; + "Unknown wireless KEY_MGMT type '%s'", v); + return NULL; } - g_free (value); - - value = svGetValueStr_cp (ifcfg, "SECURITYMODE"); - if (NM_IN_STRSET (value, NULL, "open")) - g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, value, NULL); + i_val = NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT; + if (!svGetValueEnum (ifcfg, "PMF", + nm_setting_wireless_security_pmf_get_type (), + &i_val, error)) + return NULL; + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PMF, i_val, NULL); - g_free (value); - return (NMSetting *) wsec; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "SECURITYMODE", &value); + if (NM_IN_STRSET (v, NULL, "open")) + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, v, NULL); -error: - g_free (value); - if (wsec) - g_object_unref (wsec); - return NULL; + return (NMSetting *) g_steal_pointer (&wsec); } static NMSetting * @@ -3456,23 +3489,22 @@ make_leap_setting (shvarFile *ifcfg, const char *file, GError **error) { - NMSettingWirelessSecurity *wsec; + gs_unref_object NMSettingWirelessSecurity *wsec = NULL; shvarFile *keys_ifcfg; - char *value; + gs_free char *value = NULL; NMSettingSecretFlags flags; wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); value = svGetValueStr_cp (ifcfg, "KEY_MGMT"); if (!value || strcmp (value, "IEEE8021X")) - goto error; /* Not LEAP */ + return NULL; + nm_clear_g_free (&value); - g_free (value); value = svGetValueStr_cp (ifcfg, "SECURITYMODE"); if (!value || strcasecmp (value, "leap")) - goto error; /* Not LEAP */ - - g_free (value); + return NULL; /* Not LEAP */ + nm_clear_g_free (&value); flags = read_secret_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS"); g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, flags, NULL); @@ -3490,30 +3522,24 @@ make_leap_setting (shvarFile *ifcfg, } if (value && strlen (value)) g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, value, NULL); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY"); if (!value) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Missing LEAP identity"); - goto error; + return NULL; } g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value, NULL); - g_free (value); + nm_clear_g_free (&value); g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", NULL); - return (NMSetting *) wsec; - -error: - g_free (value); - if (wsec) - g_object_unref (wsec); - return NULL; + return (NMSetting *) g_steal_pointer (&wsec); } static NMSetting * @@ -3980,39 +4006,29 @@ parse_ethtool_option (const char *value, guint32 *out_speed, const char **out_duplex) { - gs_strfreev char **words = NULL; - const char **iter = NULL, *opt_val, *opt; + gs_free const char **words = NULL; + const char *const *iter; + const char *opt_val, *opt; - if (!value || !value[0]) + words = nm_utils_strsplit_set (value, "\t "); + if (!words) return; - words = g_strsplit_set (value, "\t ", 0); - iter = (const char **) words; + iter = words; while (iter[0]) { - /* g_strsplit_set() returns empty tokens when extra spaces are found: skip them */ - if (!*iter[0]) { - iter++; - continue; - } - opt = iter++[0]; - - /* skip over repeated space characters like to parse "wol d". */ - while (iter[0] && !*iter[0]) - iter++; - opt_val = iter[0]; - if (g_str_equal (opt, "autoneg")) + if (nm_streq (opt, "autoneg")) parse_ethtool_option_autoneg (opt_val, out_autoneg); - else if (g_str_equal (opt, "speed")) + else if (nm_streq (opt, "speed")) parse_ethtool_option_speed (opt_val, out_speed); - else if (g_str_equal (opt, "duplex")) + else if (nm_streq (opt, "duplex")) parse_ethtool_option_duplex (opt_val, out_duplex); - else if (g_str_equal (opt, "wol")) + else if (nm_streq (opt, "wol")) parse_ethtool_option_wol (opt_val, out_flags); - else if (g_str_equal (opt, "sopass")) + else if (nm_streq (opt, "sopass")) parse_ethtool_option_sopass (opt_val, out_password); else { /* Silently skip unknown options */ @@ -4034,15 +4050,15 @@ parse_ethtool_options (shvarFile *ifcfg, NMSettingWired *s_wired, const char *va const char *duplex = NULL; if (value) { - gs_strfreev char **opts = NULL; - const char **iter; + gs_free const char **opts = NULL; + const char *const *iter; /* WAKE_ON_LAN_IGNORE is inferred from a specified but empty ETHTOOL_OPTS */ if (!value[0]) wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; - opts = g_strsplit_set (value, ";", 0); - for (iter = (const char **) opts; iter[0]; iter++) { + opts = nm_utils_strsplit_set (value, ";"); + for (iter = opts; iter && iter[0]; iter++) { /* in case of repeated wol_passwords, parse_ethtool_option() * will do the right thing and clear wol_password before resetting. */ parse_ethtool_option (iter[0], &wol_flags, &wol_password, &autoneg, &speed, &duplex); @@ -4079,8 +4095,8 @@ make_wired_setting (shvarFile *ifcfg, NMSetting8021x **s_8021x, GError **error) { - NMSettingWired *s_wired; - char *value = NULL; + gs_unref_object NMSettingWired *s_wired = NULL; + gs_free char *value = NULL; char *nettype; s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); @@ -4094,21 +4110,20 @@ make_wired_setting (shvarFile *ifcfg, g_object_set (s_wired, NM_SETTING_WIRED_MTU, (guint) mtu, NULL); else PARSE_WARNING ("invalid MTU '%s'", value); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "HWADDR"); if (value) { value = g_strstrip (value); g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "SUBCHANNELS"); if (value) { const char *p = value; gboolean success = TRUE; - char **chans = NULL; /* basic sanity checks */ while (*p) { @@ -4121,30 +4136,30 @@ make_wired_setting (shvarFile *ifcfg, } if (success) { + gs_free const char **chans = NULL; guint32 num_chans; - chans = g_strsplit_set (value, ",", 0); - num_chans = g_strv_length (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' (%d channels, 2 or 3 expected)", - value, g_strv_length (chans)); + 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); - g_strfreev (chans); } - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "PORTNAME"); if (value) { nm_setting_wired_add_s390_option (s_wired, "portname", value); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "CTCPROT"); if (value) { nm_setting_wired_add_s390_option (s_wired, "ctcprot", value); - g_free (value); + nm_clear_g_free (&value); } nettype = svGetValueStr_cp (ifcfg, "NETTYPE"); @@ -4174,28 +4189,28 @@ make_wired_setting (shvarFile *ifcfg, iter++; } g_strfreev (options); - g_free (value); + nm_clear_g_free (&value); } - value = svGetValueStr_cp (ifcfg, "MACADDR"); - if (value) { - value = g_strstrip (value); - g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, value, NULL); - g_free (value); - } + g_object_set (s_wired, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + svGetValueStr (ifcfg, "MACADDR", &value), + NULL); + nm_clear_g_free (&value); - value = svGetValueStr_cp (ifcfg, "GENERATE_MAC_ADDRESS_MASK"); - g_object_set (s_wired, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, value, NULL); - 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); value = svGetValueStr_cp (ifcfg, "HWADDR_BLACKLIST"); if (value) { - char **strv; + gs_strfreev char **strv = NULL; strv = transform_hwaddr_blacklist (value); g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, strv, NULL); - g_strfreev (strv); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "KEY_MGMT"); @@ -4203,25 +4218,20 @@ make_wired_setting (shvarFile *ifcfg, if (!strcmp (value, "IEEE8021X")) { *s_8021x = fill_8021x (ifcfg, file, value, FALSE, error); if (!*s_8021x) - goto error; + return NULL; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Unknown wired KEY_MGMT type '%s'", value); - goto error; + return NULL; } - g_free (value); + nm_clear_g_free (&value); } parse_ethtool_options (ifcfg, s_wired, svGetValue (ifcfg, "ETHTOOL_OPTS", &value)); - g_free (value); - - return (NMSetting *) s_wired; + nm_clear_g_free (&value); -error: - g_free (value); - g_object_unref (s_wired); - return NULL; + return (NMSetting *) g_steal_pointer (&s_wired); } static NMConnection * @@ -4246,7 +4256,6 @@ wired_connection_from_ifcfg (const char *file, g_object_unref (connection); return NULL; } - check_if_slave (ifcfg, (NMSettingConnection *) con_setting); nm_connection_add_setting (connection, con_setting); wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); @@ -4396,7 +4405,6 @@ infiniband_connection_from_ifcfg (const char *file, g_object_unref (connection); return NULL; } - check_if_slave (ifcfg, (NMSettingConnection *) con_setting); nm_connection_add_setting (connection, con_setting); infiniband_setting = make_infiniband_setting (ifcfg, file, error); @@ -4441,40 +4449,37 @@ make_bond_setting (shvarFile *ifcfg, GError **error) { NMSettingBond *s_bond; - char *value; + gs_free char *value = NULL; + const char *v; - value = svGetValueStr_cp (ifcfg, "DEVICE"); - if (!value) { + v = svGetValueStr (ifcfg, "DEVICE", &value); + if (!v) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "mandatory DEVICE keyword missing"); return NULL; } - g_free (value); s_bond = NM_SETTING_BOND (nm_setting_bond_new ()); - value = svGetValueStr_cp (ifcfg, "BONDING_OPTS"); - if (value) { - char **items, **iter; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "BONDING_OPTS", &value); + if (v) { + gs_free const char **items = NULL; + const char *const *iter; - items = g_strsplit_set (value, " ", -1); + items = nm_utils_strsplit_set (v, " "); for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - char **keys, *key, *val; - - keys = g_strsplit_set (*iter, "=", 2); - if (keys && *keys) { - key = *keys; - val = *(keys + 1); - if (val && key[0] && val[0]) - handle_bond_option (s_bond, key, val); - } + gs_strfreev char **keys = NULL; + const char *key, *val; - g_strfreev (keys); + keys = g_strsplit_set (*iter, "=", 2); + if (keys && *keys) { + key = *keys; + val = *(keys + 1); + if (val && key[0] && val[0]) + handle_bond_option (s_bond, key, val); } } - g_free (value); - g_strfreev (items); } return (NMSetting *) s_bond; @@ -4673,6 +4678,12 @@ handle_bridge_option (NMSetting *setting, (gboolean) u, NULL); else PARSE_WARNING ("invalid multicast_snooping value '%s'", value); + } else if (!strcmp (key, "group_fwd_mask")) { + if (get_uint (value, &u) && u <= 0xFFFF && !NM_FLAGS_ANY (u, 7)) + g_object_set (setting, NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, + (gboolean) u, NULL); + else + PARSE_WARNING ("invalid group_fwd_mask value '%s'", value); } else PARSE_WARNING ("unhandled bridge option '%s'", key); } @@ -4683,25 +4694,22 @@ handle_bridging_opts (NMSetting *setting, const char *value, BridgeOptFunc func) { - char **items, **iter; + gs_free const char **items = NULL; + const char *const *iter; - items = g_strsplit_set (value, " ", -1); + items = nm_utils_strsplit_set (value, " "); for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - char **keys, *key, *val; - - keys = g_strsplit_set (*iter, "=", 2); - if (keys && *keys) { - key = *keys; - val = *(keys + 1); - if (val && strlen (key) && strlen (val)) - func (setting, stp, key, val); - } - - g_strfreev (keys); + gs_strfreev char **keys = NULL; + const char *key, *val; + + keys = g_strsplit_set (*iter, "=", 2); + if (keys && *keys) { + key = *keys; + val = *(keys + 1); + if (val && key[0] && val[0]) + func (setting, stp, key, val); } } - g_strfreev (items); } static NMSetting * @@ -4923,24 +4931,22 @@ parse_prio_map_list (NMSettingVlan *s_vlan, const char *key, NMVlanPriorityMap map) { - char *value; - gchar **list = NULL, **iter; + gs_free char *value = NULL; + gs_free const char **list = NULL; + const char *const *iter; + const char *v; - value = svGetValueStr_cp (ifcfg, key); - if (!value) + v = svGetValueStr (ifcfg, key, &value); + if (!v) return; - - list = g_strsplit_set (value, ",", -1); - g_free (value); + list = nm_utils_strsplit_set (v, ","); for (iter = list; iter && *iter; iter++) { - if (!*iter || !strchr (*iter, ':')) + if (!strchr (*iter, ':')) continue; - if (!nm_setting_vlan_add_priority_str (s_vlan, map, *iter)) PARSE_WARNING ("invalid %s priority map item '%s'", key, *iter); } - g_strfreev (list); } static NMSetting * @@ -4951,22 +4957,20 @@ make_vlan_setting (shvarFile *ifcfg, gs_unref_object NMSettingVlan *s_vlan = NULL; gs_free char *parent = NULL; gs_free char *iface_name = NULL; - char *value = NULL; - const char *p = NULL; + gs_free char *value = NULL; + const char *v = NULL; int vlan_id = -1; guint32 vlan_flags = 0; gint gvrp, reorder_hdr; - value = svGetValueStr_cp (ifcfg, "VLAN_ID"); - if (value) { - vlan_id = _nm_utils_ascii_str_to_int64 (value, 10, 0, 4095, -1); + v = svGetValueStr (ifcfg, "VLAN_ID", &value); + if (v) { + vlan_id = _nm_utils_ascii_str_to_int64 (v, 10, 0, 4095, -1); if (vlan_id == -1) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid VLAN_ID '%s'", value); - g_free (value); + "Invalid VLAN_ID '%s'", v); return NULL; } - g_free (value); } /* Need DEVICE if we don't have a separate VLAN_ID property */ @@ -4983,11 +4987,11 @@ make_vlan_setting (shvarFile *ifcfg, parent = svGetValueStr_cp (ifcfg, "PHYSDEV"); if (iface_name) { - p = strchr (iface_name, '.'); - if (p) { + v = strchr (iface_name, '.'); + if (v) { /* eth0.43; PHYSDEV is assumed from it if unknown */ if (!parent) { - parent = g_strndup (iface_name, p - iface_name); + parent = g_strndup (iface_name, v - iface_name); if (g_str_has_prefix (parent, "vlan")) { /* Like initscripts, if no PHYSDEV and we get an obviously * invalid parent interface from DEVICE, fail. @@ -4995,20 +4999,20 @@ make_vlan_setting (shvarFile *ifcfg, nm_clear_g_free (&parent); } } - p++; + v++; } else { /* format like vlan43; PHYSDEV must be set */ if (g_str_has_prefix (iface_name, "vlan")) - p = iface_name + 4; + v = iface_name + 4; } - if (p) { + if (v) { int device_vlan_id; /* Grab VLAN ID from interface name; this takes precedence over the * separate VLAN_ID property for backwards compat. */ - device_vlan_id = _nm_utils_ascii_str_to_int64 (p, 10, 0, 4095, -1); + device_vlan_id = _nm_utils_ascii_str_to_int64 (v, 10, 0, 4095, -1); if (device_vlan_id != -1) vlan_id = device_vlan_id; } @@ -5034,13 +5038,13 @@ make_vlan_setting (shvarFile *ifcfg, if (gvrp > 0) vlan_flags |= NM_VLAN_FLAG_GVRP; - value = svGetValueStr_cp (ifcfg, "VLAN_FLAGS"); - if (value) { - gs_strfreev char **strv = NULL; - char **ptr; - - strv = g_strsplit_set (value, ", ", 0); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "VLAN_FLAGS", &value); + if (v) { + gs_free const char **strv = NULL; + const char *const *ptr; + strv = nm_utils_strsplit_set (v, ", "); for (ptr = strv; ptr && *ptr; ptr++) { if (nm_streq (*ptr, "GVRP") && gvrp == -1) vlan_flags |= NM_VLAN_FLAG_GVRP; @@ -5060,7 +5064,6 @@ make_vlan_setting (shvarFile *ifcfg, vlan_flags |= NM_VLAN_FLAG_MVRP; g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, vlan_flags, NULL); - g_free (value); parse_prio_map_list (s_vlan, ifcfg, "VLAN_INGRESS_PRIORITY_MAP", NM_VLAN_INGRESS_MAP); parse_prio_map_list (s_vlan, ifcfg, "VLAN_EGRESS_PRIORITY_MAP", NM_VLAN_EGRESS_MAP); @@ -5091,7 +5094,6 @@ vlan_connection_from_ifcfg (const char *file, g_object_unref (connection); return NULL; } - check_if_slave (ifcfg, (NMSettingConnection *) con_setting); nm_connection_add_setting (connection, con_setting); vlan_setting = make_vlan_setting (ifcfg, file, error); @@ -5120,7 +5122,8 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg, { NMConnection *connection; NMSetting *s_con; - char *value; + gs_free char *value = NULL; + const char *v; nm_assert (out_spec && !*out_spec); @@ -5137,26 +5140,25 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg, nm_connection_add_setting (connection, nm_setting_generic_new ()); /* Get a spec */ - value = svGetValueStr_cp (ifcfg, "HWADDR"); - if (value) { - char *lower = g_ascii_strdown (value, -1); + v = svGetValueStr (ifcfg, "HWADDR", &value); + if (v) { + gs_free char *lower = g_ascii_strdown (v, -1); + *out_spec = g_strdup_printf ("%s:mac:%s", type, lower); - g_free (lower); - g_free (value); return connection; } - value = svGetValueStr_cp (ifcfg, "SUBCHANNELS"); - if (value) { - *out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, value); - g_free (value); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "SUBCHANNELS", &value); + if (v) { + *out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, v); return connection; } - value = svGetValueStr_cp (ifcfg, "DEVICE"); - if (value) { - *out_spec = g_strdup_printf ("%s:interface-name:%s", type, value); - g_free (value); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DEVICE", &value); + if (v) { + *out_spec = g_strdup_printf ("%s:interface-name:%s", type, v); return connection; } @@ -5164,32 +5166,6 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg, return NULL; } -char * -uuid_from_file (const char *filename) -{ - const char *ifcfg_name = NULL; - shvarFile *ifcfg; - char *uuid; - - g_return_val_if_fail (filename != NULL, NULL); - - ifcfg_name = utils_get_ifcfg_name (filename, TRUE); - if (!ifcfg_name) - return NULL; - - ifcfg = svOpenFile (filename, NULL); - if (!ifcfg) - return NULL; - - /* Try for a UUID key before falling back to hashing the file name */ - uuid = svGetValueStr_cp (ifcfg, "UUID"); - if (!uuid) - uuid = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); - - svCloseFile (ifcfg); - return uuid; -} - static void check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6) { @@ -5199,23 +5175,24 @@ check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6) /* If there is no IPv4 config or it doesn't contain DNS searches, * read DOMAIN and put the domains into IPv6. */ - if (!s_ip4 || nm_setting_ip_config_get_num_dns_searches (NM_SETTING_IP_CONFIG (s_ip4)) == 0) { + if ( !s_ip4 + || nm_setting_ip_config_get_num_dns_searches (NM_SETTING_IP_CONFIG (s_ip4)) == 0) { /* DNS searches */ - char *value = svGetValueStr_cp (ifcfg, "DOMAIN"); + gs_free char *value = NULL; + const char *v; - if (value) { - char **searches = g_strsplit (value, " ", 0); + v = svGetValueStr (ifcfg, "DOMAIN", &value); + if (v) { + gs_free const char **searches = NULL; + const char *const *item; + + searches = nm_utils_strsplit_set (v, " "); if (searches) { - char **item; for (item = searches; *item; item++) { - if (strlen (*item)) { - if (!nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (s_ip6), *item)) - PARSE_WARNING ("duplicate DNS domain '%s'", *item); - } + if (!nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (s_ip6), *item)) + PARSE_WARNING ("duplicate DNS domain '%s'", *item); } - g_strfreev (searches); } - g_free (value); } } } @@ -5235,6 +5212,8 @@ connection_from_file_full (const char *filename, NMSetting *s_ip4, *s_ip6, *s_proxy, *s_port, *s_dcb = NULL, *s_user; const char *ifcfg_name = NULL; gboolean has_ip4_defroute = FALSE; + gboolean has_complex_routes_v4; + gboolean has_complex_routes_v6; g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (out_unhandled && !*out_unhandled, NULL); @@ -5446,13 +5425,32 @@ connection_from_file_full (const char *filename, if (!connection) return NULL; - s_ip6 = make_ip6_setting (parsed, network_file, error); + has_complex_routes_v4 = utils_has_complex_routes (filename, AF_INET); + has_complex_routes_v6 = utils_has_complex_routes (filename, AF_INET6); + + if (has_complex_routes_v4 || has_complex_routes_v6) { + if (has_complex_routes_v4 && !has_complex_routes_v6) + PARSE_WARNING ("'rule-' file is present; you will need to use a dispatcher script to apply these routes"); + else if (has_complex_routes_v6 && !has_complex_routes_v4) + PARSE_WARNING ("'rule6-' file is present; you will need to use a dispatcher script to apply these routes"); + else + PARSE_WARNING ("'rule-' and 'rule6-' files are present; you will need to use a dispatcher script to apply these routes"); + } + + s_ip6 = make_ip6_setting (parsed, + network_file, + !has_complex_routes_v4 && !has_complex_routes_v6, + error); if (!s_ip6) return NULL; else nm_connection_add_setting (connection, s_ip6); - s_ip4 = make_ip4_setting (parsed, network_file, &has_ip4_defroute, error); + s_ip4 = make_ip4_setting (parsed, + network_file, + !has_complex_routes_v4 && !has_complex_routes_v6, + &has_ip4_defroute, + error); if (!s_ip4) return NULL; else { @@ -5510,11 +5508,11 @@ connection_from_file (const char *filename, } NMConnection * -connection_from_file_test (const char *filename, - const char *network_file, - const char *test_type, - char **out_unhandled, - GError **error) +nmtst_connection_from_file (const char *filename, + const char *network_file, + const char *test_type, + char **out_unhandled, + GError **error) { return connection_from_file_full (filename, network_file, @@ -5528,7 +5526,6 @@ guint devtimeout_from_file (const char *filename) { shvarFile *ifcfg; - char *devtimeout_str; guint devtimeout; g_return_val_if_fail (filename != NULL, 0); @@ -5537,14 +5534,7 @@ devtimeout_from_file (const char *filename) if (!ifcfg) return 0; - devtimeout_str = svGetValueStr_cp (ifcfg, "DEVTIMEOUT"); - if (devtimeout_str) { - devtimeout = _nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0); - g_free (devtimeout_str); - } else - devtimeout = 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 35464474..a8937ac8 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h @@ -28,15 +28,12 @@ NMConnection *connection_from_file (const char *filename, GError **error, gboolean *out_ignore_error); -char *uuid_from_file (const char *filename); - guint devtimeout_from_file (const char *filename); -/* for test-ifcfg-rh */ -NMConnection *connection_from_file_test (const char *filename, - const char *network_file, - const char *test_type, - char **out_unhandled, - GError **error); +NMConnection *nmtst_connection_from_file (const char *filename, + const char *network_file, + const char *test_type, + char **out_unhandled, + GError **error); #endif /* __READER_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 e82ef60c..6434ad7d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -173,7 +173,7 @@ utils_get_extra_path (const char *parent, const char *tag) dirname = g_path_get_dirname (parent); if (!dirname) - return NULL; + g_return_val_if_reached (NULL); name = utils_get_ifcfg_name (parent, FALSE); if (name) { @@ -280,25 +280,22 @@ gone: } gboolean -utils_has_complex_routes (const char *filename) +utils_has_complex_routes (const char *filename, int addr_family) { - char *rules; + g_return_val_if_fail (filename, TRUE); - g_return_val_if_fail (filename != NULL, TRUE); + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) { + gs_free char *rules = utils_get_extra_path (filename, RULE_TAG); - rules = utils_get_extra_path (filename, RULE_TAG); - if (g_file_test (rules, G_FILE_TEST_EXISTS)) { - g_free (rules); - return TRUE; + if (g_file_test (rules, G_FILE_TEST_EXISTS)) + return TRUE; } - g_free (rules); - rules = utils_get_extra_path (filename, RULE6_TAG); - if (g_file_test (rules, G_FILE_TEST_EXISTS)) { - g_free (rules); - return TRUE; + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) { + gs_free char *rules = utils_get_extra_path (filename, RULE6_TAG); + if (g_file_test (rules, G_FILE_TEST_EXISTS)) + return TRUE; } - g_free (rules); return FALSE; } 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 2b2c2755..e7abf4d8 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -48,7 +48,7 @@ shvarFile *utils_get_route_ifcfg (const char *parent, gboolean should_create); shvarFile *utils_get_route6_ifcfg (const char *parent, gboolean should_create); gboolean utils_has_route_file_new_syntax (const char *filename); -gboolean utils_has_complex_routes (const char *filename); +gboolean utils_has_complex_routes (const char *filename, int addr_family); gboolean utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg); @@ -57,4 +57,28 @@ char *utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg); void nms_ifcfg_rh_utils_user_key_encode (const char *key, GString *str_buffer); gboolean nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer); +static inline const char * +_nms_ifcfg_rh_utils_numbered_tag (char *buf, gsize buf_len, const char *tag_name, int which) +{ + gsize l; + + l = g_strlcpy (buf, tag_name, buf_len); + nm_assert (l < buf_len); + if (which != -1) { + buf_len -= l; + l = g_snprintf (&buf[l], buf_len, "%d", which); + nm_assert (l < buf_len); + } + return buf; +} +#define numbered_tag(buf, tag_name, which) \ + ({ \ + _nm_unused char *const _buf = (buf); \ + \ + /* some static assert trying to ensure that the buffer is statically allocated. + * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ + G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *) && sizeof (buf) < G_MAXINT); \ + _nms_ifcfg_rh_utils_numbered_tag (buf, sizeof (buf), ""tag_name"", (which)); \ + }) + #endif /* _UTILS_H_ */ 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 d2b7ff67..d16f46be 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -32,6 +32,7 @@ #include <unistd.h> #include <stdio.h> +#include "nm-utils/nm-enum-utils.h" #include "nm-manager.h" #include "nm-setting-connection.h" #include "nm-setting-wired.h" @@ -48,7 +49,7 @@ #include "nm-utils.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" -#include "nm-setting-metadata.h" +#include "nm-meta-setting.h" #include "nms-ifcfg-rh-common.h" #include "nms-ifcfg-rh-reader.h" @@ -107,45 +108,75 @@ save_secret_flags (shvarFile *ifcfg, static void set_secret (shvarFile *ifcfg, + GHashTable *secrets, const char *key, const char *value, const char *flags_key, NMSettingSecretFlags flags) { - shvarFile *keyfile; - GError *error = NULL; - /* Clear the secret from the ifcfg and the associated "keys" file */ svUnsetValue (ifcfg, key); /* Save secret flags */ save_secret_flags (ifcfg, flags_key, flags); + /* Only write the secret if it's system owned and supposed to be saved */ + if (flags != NM_SETTING_SECRET_FLAG_NONE) + value = NULL; + + g_hash_table_replace (secrets, g_strdup (key), g_strdup (value)); +} + +static gboolean +write_secrets (shvarFile *ifcfg, + GHashTable *secrets, + GError **error) +{ + nm_auto_shvar_file_close shvarFile *keyfile = NULL; + gs_free const char **secrets_keys = NULL; + guint i, secrets_keys_n; + GError *local = NULL; + gboolean any_secrets = FALSE; + keyfile = utils_get_keys_ifcfg (svFileGetName (ifcfg), TRUE); if (!keyfile) { - _LOGW ("could not create ifcfg file for '%s'", svFileGetName (ifcfg)); - goto error; + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Failure to create secrets file for '%s'", svFileGetName (ifcfg)); + return FALSE; } - /* Only write the secret if it's system owned and supposed to be saved */ - if (flags == NM_SETTING_SECRET_FLAG_NONE) - svSetValueStr (keyfile, key, value); - else - svUnsetValue (keyfile, key); + /* we purge all existing secrets. */ + svUnsetAll (keyfile, SV_KEY_TYPE_ANY); + + /* sort the keys. */ + secrets_keys = (const char **) g_hash_table_get_keys_as_array (secrets, &secrets_keys_n); + if (secrets_keys) { + g_qsort_with_data (secrets_keys, + secrets_keys_n, + sizeof (const char *), + nm_strcmp_p_with_data, + NULL); + } + + for (i = 0; i < secrets_keys_n; i++) { + const char *k = secrets_keys[i]; + const char *v = g_hash_table_lookup (secrets, k); + + if (v) { + svSetValueStr (keyfile, k, v); + any_secrets = TRUE; + } + } - if (!svWriteFile (keyfile, 0600, &error)) { - _LOGW ("could not update ifcfg file '%s': %s", - svFileGetName (keyfile), error->message); - g_clear_error (&error); - svCloseFile (keyfile); - goto error; + if (!any_secrets) + (void) unlink (svFileGetName (keyfile)); + else if (!svWriteFile (keyfile, 0600, &local)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Failure to write secrets to '%s': %s", svFileGetName (keyfile), local->message); + return FALSE; } - svCloseFile (keyfile); - return; -error: - /* Try setting the secret in the actual ifcfg */ - svSetValueStr (ifcfg, key, value); + return TRUE; } typedef struct { @@ -183,6 +214,8 @@ static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = { static gboolean write_object (NMSetting8021x *s_8021x, shvarFile *ifcfg, + GHashTable *secrets, + GHashTable *blobs, const Setting8021xSchemeVtable *objtype, GError **error) { @@ -193,6 +226,7 @@ write_object (NMSetting8021x *s_8021x, NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; char *secret_name, *secret_flags; const char *extension; + char *standard_file; g_return_val_if_fail (ifcfg != NULL, FALSE); g_return_val_if_fail (objtype != NULL, FALSE); @@ -221,7 +255,7 @@ write_object (NMSetting8021x *s_8021x, secret_flags = g_strdup_printf ("%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key); password = (*(objtype->vtable->passwd_func))(s_8021x); flags = (*(objtype->vtable->pwflag_func))(s_8021x); - set_secret (ifcfg, secret_name, password, secret_flags, flags); + set_secret (ifcfg, secrets, secret_name, password, secret_flags, flags); g_free (secret_name); g_free (secret_flags); @@ -232,28 +266,6 @@ write_object (NMSetting8021x *s_8021x, else extension = "pem"; - /* If certificate/private key wasn't sent, the connection may no longer be - * 802.1x and thus we clear out the paths and certs. - */ - if (!value && !blob) { - char *standard_file; - int ignored; - - /* Since no cert/private key is now being used, delete any standard file - * that was created for this connection, but leave other files alone. - * Thus, for example, - * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der - * will be deleted, but /etc/pki/tls/cert.pem will not. - */ - standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension); - if (g_file_test (standard_file, G_FILE_TEST_EXISTS)) - ignored = unlink (standard_file); - g_free (standard_file); - - svUnsetValue (ifcfg, objtype->ifcfg_rh_key); - return TRUE; - } - /* If the object path was specified, prefer that over any raw cert data that * may have been sent. */ @@ -264,46 +276,72 @@ write_object (NMSetting8021x *s_8021x, /* If it's raw certificate data, write the data out to the standard file */ if (blob) { - gboolean success; char *new_file; - GError *write_error = NULL; new_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension); - if (!new_file) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not create file path for %s / %s", - NM_SETTING_802_1X_SETTING_NAME, objtype->vtable->setting_key); - return FALSE; + g_hash_table_replace (blobs, new_file, g_bytes_ref (blob)); + svSetValueStr (ifcfg, objtype->ifcfg_rh_key, new_file); + return TRUE; + } + + /* If certificate/private key wasn't sent, the connection may no longer be + * 802.1x and thus we clear out the paths and certs. + * + * Since no cert/private key is now being used, delete any standard file + * that was created for this connection, but leave other files alone. + * Thus, for example, + * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der + * will be deleted, but /etc/pki/tls/cert.pem will not. + */ + 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); + return TRUE; +} + +static gboolean +write_blobs (GHashTable *blobs, GError **error) +{ + GHashTableIter iter; + const char *filename; + GBytes *blob; + + if (!blobs) + return TRUE; + + g_hash_table_iter_init (&iter, blobs); + while (g_hash_table_iter_next (&iter, (gpointer *) &filename, (gpointer *) &blob)) { + GError *write_error = NULL; + + if (!blob) { + (void) unlink (filename); + continue; } /* Write the raw certificate data out to the standard file so that we * can use paths from now on instead of pushing around the certificate * data itself. */ - success = nm_utils_file_set_contents (new_file, - (const char *) g_bytes_get_data (blob, NULL), - g_bytes_get_size (blob), - 0600, - &write_error); - if (success) { - svSetValueStr (ifcfg, objtype->ifcfg_rh_key, new_file); - g_free (new_file); - return TRUE; - } else { + if (!nm_utils_file_set_contents (filename, + (const char *) g_bytes_get_data (blob, NULL), + g_bytes_get_size (blob), + 0600, + &write_error)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not write certificate/key for %s / %s: %s", - NM_SETTING_802_1X_SETTING_NAME, objtype->vtable->setting_key, - (write_error && write_error->message) ? write_error->message : "(unknown)"); - g_clear_error (&write_error); + "Could not write certificate to file \"%s\": %s", + filename, + write_error->message); + return FALSE; } - g_free (new_file); } - return FALSE; + return TRUE; } static gboolean write_8021x_certs (NMSetting8021x *s_8021x, + GHashTable *secrets, + GHashTable *blobs, gboolean phase2, shvarFile *ifcfg, GError **error) @@ -311,7 +349,7 @@ write_8021x_certs (NMSetting8021x *s_8021x, const Setting8021xSchemeVtable *otype = NULL; /* CA certificate */ - if (!write_object (s_8021x, ifcfg, + 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], @@ -325,7 +363,7 @@ write_8021x_certs (NMSetting8021x *s_8021x, otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; /* Save the private key */ - if (!write_object (s_8021x, ifcfg, otype, error)) + if (!write_object (s_8021x, ifcfg, secrets, blobs, otype, error)) return FALSE; /* Client certificate */ @@ -338,7 +376,7 @@ write_8021x_certs (NMSetting8021x *s_8021x, NULL); } else { /* Save the client certificate */ - if (!write_object (s_8021x, ifcfg, + 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], @@ -352,17 +390,22 @@ write_8021x_certs (NMSetting8021x *s_8021x, static gboolean write_8021x_setting (NMConnection *connection, shvarFile *ifcfg, + GHashTable *secrets, + GHashTable *blobs, gboolean wired, GError **error) { NMSetting8021x *s_8021x; NMSetting8021xAuthFlags auth_flags; const char *value, *match; + gconstpointer ptr; + GBytes* bytes; char *tmp = NULL; GString *phase2_auth; GString *str; guint32 i, num; - gint timeout; + gsize size; + int vint; s_8021x = nm_connection_get_setting_802_1x (connection); if (!s_8021x) { @@ -392,11 +435,26 @@ write_8021x_setting (NMConnection *connection, nm_setting_802_1x_get_anonymous_identity (s_8021x)); set_secret (ifcfg, + secrets, "IEEE_8021X_PASSWORD", nm_setting_802_1x_get_password (s_8021x), "IEEE_8021X_PASSWORD_FLAGS", nm_setting_802_1x_get_password_flags (s_8021x)); + tmp = NULL; + bytes = nm_setting_802_1x_get_password_raw (s_8021x); + if (bytes) { + ptr = g_bytes_get_data (bytes, &size); + tmp = nm_utils_bin2hexstr (ptr, size, -1); + } + set_secret (ifcfg, + secrets, + "IEEE_8021X_PASSWORD_RAW", + tmp, + "IEEE_8021X_PASSWORD_RAW_FLAGS", + nm_setting_802_1x_get_password_raw_flags (s_8021x)); + g_free (tmp); + /* PEAP version */ value = nm_setting_802_1x_get_phase1_peapver (s_8021x); svUnsetValue (ifcfg, "IEEE_8021X_PEAP_VERSION"); @@ -452,11 +510,9 @@ write_8021x_setting (NMConnection *connection, if (auth_flags == NM_SETTING_802_1X_AUTH_FLAGS_NONE) { svUnsetValue (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS"); } else { - gs_free char *flags_str = NULL; - - flags_str = _nm_utils_enum_to_str_full (nm_setting_802_1x_auth_flags_get_type (), - auth_flags, " "); - svSetValueStr (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS", flags_str); + svSetValueEnum (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS", + nm_setting_802_1x_auth_flags_get_type(), + auth_flags); } svSetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", @@ -501,17 +557,14 @@ write_8021x_setting (NMConnection *connection, svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH", nm_setting_802_1x_get_phase2_domain_suffix_match (s_8021x)); - timeout = nm_setting_802_1x_get_auth_timeout (s_8021x); - if (timeout > 0) - svSetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", timeout); - else - svUnsetValue (ifcfg, "IEEE_8021X_AUTH_TIMEOUT"); + vint = nm_setting_802_1x_get_auth_timeout (s_8021x); + svSetValueInt64_cond (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", vint > 0, vint); - if (!write_8021x_certs (s_8021x, FALSE, ifcfg, error)) + if (!write_8021x_certs (s_8021x, secrets, blobs, FALSE, ifcfg, error)) return FALSE; /* phase2/inner certs */ - if (!write_8021x_certs (s_8021x, TRUE, ifcfg, error)) + if (!write_8021x_certs (s_8021x, secrets, blobs, TRUE, ifcfg, error)) return FALSE; return TRUE; @@ -520,6 +573,7 @@ write_8021x_setting (NMConnection *connection, static gboolean write_wireless_security_setting (NMConnection *connection, shvarFile *ifcfg, + GHashTable *secrets, gboolean adhoc, gboolean *no_8021x, GError **error) @@ -528,6 +582,7 @@ write_wireless_security_setting (NMConnection *connection, const char *key_mgmt, *auth_alg, *key, *proto, *cipher; const char *psk = NULL; gboolean wep = FALSE, wpa = FALSE, dynamic_wep = FALSE; + NMSettingWirelessSecurityWpsMethod wps_method; char *tmp; guint32 i, num; GString *str; @@ -573,6 +628,7 @@ write_wireless_security_setting (NMConnection *connection, svSetValueStr (ifcfg, "IEEE_8021X_IDENTITY", nm_setting_wireless_security_get_leap_username (s_wsec)); set_secret (ifcfg, + secrets, "IEEE_8021X_PASSWORD", nm_setting_wireless_security_get_leap_password (s_wsec), "IEEE_8021X_PASSWORD_FLAGS", @@ -581,35 +637,42 @@ write_wireless_security_setting (NMConnection *connection, } } + /* WPS */ + wps_method = nm_setting_wireless_security_get_wps_method (s_wsec); + if (wps_method == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT) + svUnsetValue (ifcfg, "WPS_METHOD"); + else + svSetValueEnum (ifcfg, "WPS_METHOD", nm_setting_wireless_security_wps_method_get_type (), wps_method); + /* WEP keys */ /* Clear any default key */ - set_secret (ifcfg, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + set_secret (ifcfg, secrets, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); /* Clear existing keys */ for (i = 0; i < 4; i++) { - tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1); - set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - g_free (tmp); + char tag[64]; - tmp = g_strdup_printf ("KEY%d", i + 1); - set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - g_free (tmp); + numbered_tag (tag, "KEY_PASSPHRASE", i + 1); + set_secret (ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + + numbered_tag (tag, "KEY", i + 1); + set_secret (ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); } /* And write the new ones out */ if (wep) { /* Default WEP TX key index */ - tmp = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + 1); - svSetValueStr (ifcfg, "DEFAULTKEY", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "DEFAULTKEY", nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec) + 1); for (i = 0; i < 4; i++) { NMWepKeyType key_type; key = nm_setting_wireless_security_get_wep_key (s_wsec, i); if (key) { - char *ascii_key = NULL; + gs_free char *ascii_key = NULL; + char tag[64]; + gboolean key_valid = TRUE; /* Passphrase needs a different ifcfg key since with WEP, there * are some passphrases that are indistinguishable from WEP hex @@ -622,10 +685,11 @@ write_wireless_security_setting (NMConnection *connection, else if (nm_utils_wep_key_valid (key, NM_WEP_KEY_TYPE_PASSPHRASE)) key_type = NM_WEP_KEY_TYPE_PASSPHRASE; } + if (key_type == NM_WEP_KEY_TYPE_PASSPHRASE) - tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1); + numbered_tag (tag, "KEY_PASSPHRASE", i + 1); else if (key_type == NM_WEP_KEY_TYPE_KEY) { - tmp = g_strdup_printf ("KEY%d", i + 1); + numbered_tag (tag, "KEY", i + 1); /* Add 's:' prefix for ASCII keys */ if (strlen (key) == 5 || strlen (key) == 13) { @@ -633,19 +697,18 @@ write_wireless_security_setting (NMConnection *connection, key = ascii_key; } } else { - _LOGW ("invalid WEP key '%s'", key); - tmp = NULL; + g_warn_if_reached (); + key_valid = FALSE; } - if (tmp) { + if (key_valid) { set_secret (ifcfg, - tmp, + secrets, + tag, key, "WEP_KEY_FLAGS", nm_setting_wireless_security_get_wep_key_flags (s_wsec)); } - g_free (tmp); - g_free (ascii_key); } } } @@ -704,22 +767,31 @@ write_wireless_security_setting (NMConnection *connection, psk = nm_setting_wireless_security_get_psk (s_wsec); set_secret (ifcfg, + secrets, "WPA_PSK", psk, "WPA_PSK_FLAGS", wpa ? nm_setting_wireless_security_get_psk_flags (s_wsec) : NM_SETTING_SECRET_FLAG_NONE); + + if (nm_setting_wireless_security_get_pmf (s_wsec) == NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT) + svUnsetValue (ifcfg, "PMF"); + else { + svSetValueEnum (ifcfg, "PMF", nm_setting_wireless_security_pmf_get_type (), + nm_setting_wireless_security_get_pmf (s_wsec)); + } + return TRUE; } static gboolean write_wireless_setting (NMConnection *connection, shvarFile *ifcfg, + GHashTable *secrets, gboolean *no_8021x, GError **error) { NMSettingWireless *s_wireless; - char *tmp; GBytes *ssid; const guint8 *ssid_data; gsize ssid_len; @@ -755,13 +827,8 @@ write_wireless_setting (NMConnection *connection, g_free (blacklist_str); } - svUnsetValue (ifcfg, "MTU"); mtu = nm_setting_wireless_get_mtu (s_wireless); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - svSetValueStr (ifcfg, "MTU", tmp); - g_free (tmp); - } + svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu); ssid = nm_setting_wireless_get_ssid (s_wireless); if (!ssid) { @@ -837,9 +904,7 @@ write_wireless_setting (NMConnection *connection, svUnsetValue (ifcfg, "BAND"); chan = nm_setting_wireless_get_channel (s_wireless); if (chan) { - tmp = g_strdup_printf ("%u", chan); - svSetValueStr (ifcfg, "CHANNEL", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "CHANNEL", chan); } else { /* Band only set if channel is not, since channel implies band */ svSetValueStr (ifcfg, "BAND", nm_setting_wireless_get_band (s_wireless)); @@ -856,27 +921,25 @@ write_wireless_setting (NMConnection *connection, svUnsetValue (ifcfg, "SECURITYMODE"); if (nm_connection_get_setting_wireless_security (connection)) { - if (!write_wireless_security_setting (connection, ifcfg, adhoc, no_8021x, error)) + if (!write_wireless_security_setting (connection, ifcfg, secrets, adhoc, no_8021x, error)) return FALSE; } else { - char *keys_path; - /* Clear out wifi security keys */ svUnsetValue (ifcfg, "KEY_MGMT"); svUnsetValue (ifcfg, "IEEE_8021X_IDENTITY"); - set_secret (ifcfg, "IEEE_8021X_PASSWORD", NULL, "IEEE_8021X_PASSWORD_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + set_secret (ifcfg, secrets, "IEEE_8021X_PASSWORD", NULL, "IEEE_8021X_PASSWORD_FLAGS", NM_SETTING_SECRET_FLAG_NONE); svUnsetValue (ifcfg, "SECURITYMODE"); /* Clear existing keys */ - set_secret (ifcfg, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + set_secret (ifcfg, secrets, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); for (i = 0; i < 4; i++) { - tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1); - set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - g_free (tmp); + char tag[64]; - tmp = g_strdup_printf ("KEY%d", i + 1); - set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - g_free (tmp); + numbered_tag (tag, "KEY_PASSPHRASE", i + 1); + set_secret (ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + + numbered_tag (tag, "KEY", i + 1); + set_secret (ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); } svUnsetValue (ifcfg, "DEFAULTKEY"); @@ -884,12 +947,7 @@ write_wireless_setting (NMConnection *connection, svUnsetValue (ifcfg, "WPA_ALLOW_WPA2"); svUnsetValue (ifcfg, "CIPHER_PAIRWISE"); svUnsetValue (ifcfg, "CIPHER_GROUP"); - set_secret (ifcfg, "WPA_PSK", NULL, "WPA_PSK_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - - /* Kill any old keys file */ - keys_path = utils_get_keys_path (svFileGetName (ifcfg)); - (void) unlink (keys_path); - g_free (keys_path); + set_secret (ifcfg, secrets, "WPA_PSK", NULL, "WPA_PSK_FLAGS", NM_SETTING_SECRET_FLAG_NONE); } svSetValueStr (ifcfg, "SSID_HIDDEN", nm_setting_wireless_get_hidden (s_wireless) ? "yes" : NULL); @@ -932,7 +990,6 @@ static gboolean write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingInfiniband *s_infiniband; - char *tmp; const char *mac, *transport_mode, *parent; guint32 mtu; int p_key; @@ -947,13 +1004,8 @@ write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **e mac = nm_setting_infiniband_get_mac_address (s_infiniband); svSetValueStr (ifcfg, "HWADDR", mac); - svUnsetValue (ifcfg, "MTU"); mtu = nm_setting_infiniband_get_mtu (s_infiniband); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - svSetValueStr (ifcfg, "MTU", tmp); - g_free (tmp); - } + svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu); transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband); svSetValueBoolean (ifcfg, "CONNECTED_MODE", nm_streq (transport_mode, "connected")); @@ -961,9 +1013,7 @@ write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **e p_key = nm_setting_infiniband_get_p_key (s_infiniband); if (p_key != -1) { svSetValueStr (ifcfg, "PKEY", "yes"); - tmp = g_strdup_printf ("%u", p_key); - svSetValueStr (ifcfg, "PKEY_ID", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "PKEY_ID", p_key); parent = nm_setting_infiniband_get_parent (s_infiniband); if (parent) @@ -1016,13 +1066,8 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) g_free (blacklist_str); } - svUnsetValue (ifcfg, "MTU"); mtu = nm_setting_wired_get_mtu (s_wired); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - svSetValueStr (ifcfg, "MTU", tmp); - g_free (tmp); - } + svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu); svUnsetValue (ifcfg, "SUBCHANNELS"); s390_subchannels = nm_setting_wired_get_s390_subchannels (s_wired); @@ -1176,7 +1221,6 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) s_wired = nm_connection_get_setting_wired (connection); if (s_wired) { const char *device_mac, *cloned_mac; - char *tmp; guint32 mtu; has_wired = TRUE; @@ -1191,12 +1235,7 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) nm_setting_wired_get_generate_mac_address_mask (s_wired)); mtu = nm_setting_wired_get_mtu (s_wired); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - svSetValueStr (ifcfg, "MTU", tmp); - g_free (tmp); - } else - svUnsetValue (ifcfg, "MTU"); + svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu); } return has_wired; } @@ -1205,19 +1244,11 @@ static gboolean write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { NMSettingVlan *s_vlan; - NMSettingConnection *s_con; char *tmp; guint32 vlan_flags = 0; gsize s_buf_len; char s_buf[50], *s_buf_ptr; - s_con = nm_connection_get_setting_connection (connection); - if (!s_con) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Missing connection setting"); - return FALSE; - } - s_vlan = nm_connection_get_setting_vlan (connection); if (!s_vlan) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, @@ -1227,12 +1258,8 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, svSetValueStr (ifcfg, "VLAN", "yes"); svSetValueStr (ifcfg, "TYPE", TYPE_VLAN); - svSetValueStr (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con)); svSetValueStr (ifcfg, "PHYSDEV", nm_setting_vlan_get_parent (s_vlan)); - - tmp = g_strdup_printf ("%d", nm_setting_vlan_get_id (s_vlan)); - svSetValueStr (ifcfg, "VLAN_ID", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "VLAN_ID", nm_setting_vlan_get_id (s_vlan)); vlan_flags = nm_setting_vlan_get_flags (s_vlan); svSetValueBoolean (ifcfg, "REORDER_HDR", NM_FLAGS_HAS (vlan_flags, NM_VLAN_FLAG_REORDER_HEADERS)); @@ -1267,10 +1294,9 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, } static gboolean -write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) +write_bond_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { NMSettingBond *s_bond; - const char *iface; guint32 i, num_opts; s_bond = nm_connection_get_setting_bond (connection); @@ -1280,14 +1306,6 @@ write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wir return FALSE; } - iface = nm_connection_get_interface_name (connection); - if (!iface) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Missing interface name"); - return FALSE; - } - - svSetValueStr (ifcfg, "DEVICE", iface); svUnsetValue (ifcfg, "BONDING_OPTS"); num_opts = nm_setting_bond_get_num_options (s_bond); @@ -1324,7 +1342,6 @@ static gboolean write_team_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { NMSettingTeam *s_team; - const char *iface; const char *config; s_team = nm_connection_get_setting_team (connection); @@ -1334,14 +1351,6 @@ write_team_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, return FALSE; } - iface = nm_connection_get_interface_name (connection); - if (!iface) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Missing interface name"); - return FALSE; - } - - svSetValueStr (ifcfg, "DEVICE", iface); config = nm_setting_team_get_config (s_team); svSetValueStr (ifcfg, "TEAM_CONFIG", config); @@ -1385,15 +1394,13 @@ get_setting_default_boolean (NMSetting *setting, const char *prop) } static gboolean -write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { NMSettingBridge *s_bridge; - const char *iface; guint32 i; gboolean b; GString *opts; const char *mac; - char *s; s_bridge = nm_connection_get_setting_bridge (connection); if (!s_bridge) { @@ -1402,14 +1409,6 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error return FALSE; } - iface = nm_connection_get_interface_name (connection); - if (!iface) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Missing interface name"); - return FALSE; - } - - svSetValueStr (ifcfg, "DEVICE", iface); svUnsetValue (ifcfg, "BRIDGING_OPTS"); svSetValueBoolean (ifcfg, "STP", FALSE); svUnsetValue (ifcfg, "DELAY"); @@ -1424,11 +1423,8 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error svSetValueStr (ifcfg, "STP", "yes"); i = nm_setting_bridge_get_forward_delay (s_bridge); - if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_FORWARD_DELAY)) { - s = g_strdup_printf ("%u", i); - svSetValueStr (ifcfg, "DELAY", s); - g_free (s); - } + if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_FORWARD_DELAY)) + svSetValueInt64 (ifcfg, "DELAY", i); g_string_append_printf (opts, "priority=%u", nm_setting_bridge_get_priority (s_bridge)); @@ -1454,6 +1450,13 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error g_string_append_printf (opts, "ageing_time=%u", i); } + i = nm_setting_bridge_get_group_forward_mask (s_bridge); + if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_GROUP_FORWARD_MASK)) { + if (opts->len) + g_string_append_c (opts, ' '); + g_string_append_printf (opts, "group_fwd_mask=%u", i); + } + b = nm_setting_bridge_get_multicast_snooping (s_bridge); if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_MULTICAST_SNOOPING)) { if (opts->len) @@ -1467,6 +1470,8 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error svSetValueStr (ifcfg, "TYPE", TYPE_BRIDGE); + *wired = write_wired_for_virtual (connection, ifcfg); + return TRUE; } @@ -1715,8 +1720,9 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) guint32 n, i; GString *str; const char *master, *master_iface = NULL, *type; - char *tmp; - gint i_int; + gint vint; + guint32 vuint32; + const char *tmp; svSetValueStr (ifcfg, "NAME", nm_setting_connection_get_id (s_con)); svSetValueStr (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con)); @@ -1724,16 +1730,15 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) svSetValueStr (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con)); svSetValueBoolean (ifcfg, "ONBOOT", nm_setting_connection_get_autoconnect (s_con)); - i_int = nm_setting_connection_get_autoconnect_priority (s_con); - tmp = i_int != NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT - ? g_strdup_printf ("%d", i_int) : NULL; - svSetValueStr (ifcfg, "AUTOCONNECT_PRIORITY", tmp); - g_free (tmp); + vint = nm_setting_connection_get_autoconnect_priority (s_con); + svSetValueInt64_cond (ifcfg, "AUTOCONNECT_PRIORITY", + vint != NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT, + vint); - i_int = nm_setting_connection_get_autoconnect_retries (s_con); - tmp = i_int != -1 ? g_strdup_printf ("%d", i_int) : NULL; - svSetValueStr (ifcfg, "AUTOCONNECT_RETRIES", tmp); - g_free (tmp); + vint = nm_setting_connection_get_autoconnect_retries (s_con); + svSetValueInt64_cond (ifcfg, "AUTOCONNECT_RETRIES", + vint != -1, + vint); /* Only save the value for master connections */ type = nm_setting_connection_get_connection_type (s_con); @@ -1849,12 +1854,10 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) g_string_free (str, TRUE); } - svUnsetValue (ifcfg, "GATEWAY_PING_TIMEOUT"); - if (nm_setting_connection_get_gateway_ping_timeout (s_con)) { - tmp = g_strdup_printf ("%" G_GUINT32_FORMAT, nm_setting_connection_get_gateway_ping_timeout (s_con)); - svSetValueStr (ifcfg, "GATEWAY_PING_TIMEOUT", tmp); - g_free (tmp); - } + vuint32 = nm_setting_connection_get_gateway_ping_timeout (s_con); + svSetValueInt64_cond (ifcfg, "GATEWAY_PING_TIMEOUT", + vuint32 != 0, + vuint32); switch (nm_setting_connection_get_metered (s_con)) { case NM_METERED_YES: @@ -1866,23 +1869,26 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) default: svUnsetValue (ifcfg, "CONNECTION_METERED"); } + + vint = nm_setting_connection_get_auth_retries (s_con); + svSetValueInt64_cond (ifcfg, "AUTH_RETRIES", vint >= 0, vint); } static char * get_route_attributes_string (NMIPRoute *route, int family) { - gs_strfreev char **names = NULL; + gs_free const char **names = NULL; GVariant *attr, *lock; GString *str; - int i; + guint i, len; - names = nm_ip_route_get_attribute_names (route); - if (!names || !names[0]) + names = _nm_ip_route_get_attribute_names (route, TRUE, &len); + if (!len) return NULL; str = g_string_new (""); - for (i = 0; names[i]; i++) { + for (i = 0; i < len; i++) { attr = nm_ip_route_get_attribute (route, names[i]); if (!nm_ip_route_attribute_validate (names[i], attr, family, NULL, NULL)) @@ -1904,16 +1910,28 @@ get_route_attributes_string (NMIPRoute *route, int family) (lock && g_variant_get_boolean (lock)) ? "lock " : "", g_variant_get_uint32 (attr)); } else if (strstr (names[i], "lock-")) { - /* handled above */ + const char *n = &(names[i])[NM_STRLEN ("lock-")]; + + attr = nm_ip_route_get_attribute (route, n); + if (!attr) { + g_string_append_printf (str, + "%s lock 0", + n); + } else { + /* we also have a corresponding attribute with the numeric value. The + * lock setting is handled above. */ + } } else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TOS)) { - g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_byte (attr)); + g_string_append_printf (str, "%s 0x%02x", names[i], (unsigned) g_variant_get_byte (attr)); + } else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TABLE)) { + g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_uint32 (attr)); } else if ( nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC) || nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_FROM)) { char *arg = nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC) ? "src" : "from"; g_string_append_printf (str, "%s %s", arg, g_variant_get_string (attr, NULL)); } else { - _LOGW ("unknown route option '%s'", names[i]); + g_warn_if_reached (); continue; } if (names[i + 1]) @@ -1923,35 +1941,86 @@ get_route_attributes_string (NMIPRoute *route, int family) return g_string_free (str, FALSE); } -static gboolean -write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError **error) +static shvarFile * +write_route_file_svformat (const char *filename, NMSettingIPConfig *s_ip4) { - nm_auto_free_gstring GString *contents = NULL; - NMIPRoute *route; - guint32 i, num; + shvarFile *routefile; + guint i, num; - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (s_ip4 != NULL, FALSE); - g_return_val_if_fail (error != NULL, FALSE); - g_return_val_if_fail (*error == NULL, FALSE); + routefile = utils_get_route_ifcfg (filename, TRUE); + + svUnsetAll (routefile, SV_KEY_TYPE_ROUTE_SVFORMAT); num = nm_setting_ip_config_get_num_routes (s_ip4); - if (num == 0) { - unlink (filename); - return TRUE; + for (i = 0; i < num; i++) { + char buf[INET_ADDRSTRLEN]; + NMIPRoute *route; + guint32 netmask; + gint64 metric; + char addr_key[64]; + char gw_key[64]; + char netmask_key[64]; + char metric_key[64]; + char options_key[64]; + gs_free char *options = NULL; + + numbered_tag (addr_key, "ADDRESS", i); + numbered_tag (netmask_key, "NETMASK", i); + numbered_tag (gw_key, "GATEWAY", i); + + route = nm_setting_ip_config_get_route (s_ip4, i); + + svSetValueStr (routefile, addr_key, nm_ip_route_get_dest (route)); + + netmask = _nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route)); + svSetValueStr (routefile, netmask_key, + nm_utils_inet4_ntop (netmask, buf)); + + svSetValueStr (routefile, gw_key, nm_ip_route_get_next_hop (route)); + + metric = nm_ip_route_get_metric (route); + if (metric != -1) { + svSetValueInt64 (routefile, + numbered_tag (metric_key, "METRIC", i), + metric); + } + + options = get_route_attributes_string (route, AF_INET); + if (options) { + svSetValueStr (routefile, + numbered_tag (options_key, "OPTIONS", i), + options); + } } + return routefile; +} + +static GString * +write_route_file (NMSettingIPConfig *s_ip) +{ + GString *contents; + NMIPRoute *route; + guint32 i, num; + int addr_family; + + addr_family = nm_setting_ip_config_get_addr_family (s_ip); + + num = nm_setting_ip_config_get_num_routes (s_ip); + if (num == 0) + return NULL; + contents = g_string_new (""); for (i = 0; i < num; i++) { - const char *next_hop; gs_free char *options = NULL; + const char *next_hop; gint64 metric; - route = nm_setting_ip_config_get_route (s_ip4, i); + route = nm_setting_ip_config_get_route (s_ip, i); next_hop = nm_ip_route_get_next_hop (route); metric = nm_ip_route_get_metric (route); - options = get_route_attributes_string (route, AF_INET); + options = get_route_attributes_string (route, addr_family); g_string_append_printf (contents, "%s/%u", nm_ip_route_get_dest (route), @@ -1968,13 +2037,7 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError g_string_append_c (contents, '\n'); } - if (!g_file_set_contents (filename, contents->str, contents->len, NULL)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Writing route file '%s' failed", filename); - return FALSE; - } - - return TRUE; + return contents; } static gboolean @@ -2025,7 +2088,7 @@ write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) s_user = NM_SETTING_USER (nm_connection_get_setting (connection, NM_TYPE_SETTING_USER)); - svUnsetValuesWithPrefix (ifcfg, "NM_USER_"); + svUnsetAll (ifcfg, SV_KEY_TYPE_USER); if (!s_user) return TRUE; @@ -2050,25 +2113,29 @@ write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) } static gboolean -write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +write_ip4_setting (NMConnection *connection, + shvarFile *ifcfg, + shvarFile **out_route_content_svformat, + GString **out_route_content, + GError **error) { NMSettingIPConfig *s_ip4; const char *value; char *tmp; - char addr_key[64]; - char prefix_key[64]; - char netmask_key[64]; - char gw_key[64]; - char *route_path = NULL; + char tag[64]; gint j; guint i, num, n; gint64 route_metric; + NMIPRouteTableSyncMode route_table; gint priority; int timeout; GString *searches; const char *method = NULL; gboolean has_netmask; + NM_SET_OUT (out_route_content_svformat, NULL); + NM_SET_OUT (out_route_content, NULL); + s_ip4 = nm_connection_get_setting_ip4_config (connection); if (!s_ip4) { /* slave-type: clear IPv4 settings. @@ -2076,16 +2143,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) * Some IPv4 setting related options are not cleared, * for no strong reason. */ svUnsetValue (ifcfg, "BOOTPROTO"); - - svUnsetValue (ifcfg, "IPADDR"); - svUnsetValue (ifcfg, "PREFIX"); - svUnsetValue (ifcfg, "NETMASK"); - svUnsetValue (ifcfg, "GATEWAY"); - - svUnsetValue (ifcfg, "IPADDR0"); - svUnsetValue (ifcfg, "PREFIX0"); - svUnsetValue (ifcfg, "NETMASK0"); - svUnsetValue (ifcfg, "GATEWAY0"); + svUnsetAll (ifcfg, SV_KEY_TYPE_IP4_ADDRESS); return TRUE; } @@ -2096,32 +2154,14 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { - int result; - /* IPv4 disabled, clear IPv4 related parameters */ svUnsetValue (ifcfg, "BOOTPROTO"); for (j = -1; j < 256; j++) { - if (j == -1) { - nm_sprintf_buf (addr_key, "IPADDR"); - nm_sprintf_buf (prefix_key, "PREFIX"); - nm_sprintf_buf (netmask_key, "NETMASK"); - nm_sprintf_buf (gw_key, "GATEWAY"); - } else { - nm_sprintf_buf (addr_key, "IPADDR%d", (guint) j); - nm_sprintf_buf (prefix_key, "PREFIX%u", (guint) j); - nm_sprintf_buf (netmask_key, "NETMASK%u", (guint) j); - nm_sprintf_buf (gw_key, "GATEWAY%u", (guint) j); - } - - svUnsetValue (ifcfg, addr_key); - svUnsetValue (ifcfg, prefix_key); - svUnsetValue (ifcfg, netmask_key); - svUnsetValue (ifcfg, gw_key); + svUnsetValue (ifcfg, numbered_tag (tag, "IPADDR", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "PREFIX", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "NETMASK", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "GATEWAY", j)); } - - route_path = utils_get_route_path (svFileGetName (ifcfg)); - result = unlink (route_path); - g_free (route_path); return TRUE; } @@ -2165,72 +2205,60 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) * See https://bugzilla.redhat.com/show_bug.cgi?id=771673 * and https://bugzilla.redhat.com/show_bug.cgi?id=1105770 */ - nm_sprintf_buf (addr_key, "IPADDR"); - nm_sprintf_buf (prefix_key, "PREFIX"); - nm_sprintf_buf (netmask_key, "NETMASK"); - nm_sprintf_buf (gw_key, "GATEWAY"); - } else { - nm_sprintf_buf (addr_key, "IPADDR%u", n); - nm_sprintf_buf (prefix_key, "PREFIX%u", n); - nm_sprintf_buf (netmask_key, "NETMASK%u", n); - nm_sprintf_buf (gw_key, "GATEWAY%u", n); - } + j = -1; + } else + j = n; - svSetValueStr (ifcfg, addr_key, nm_ip_address_get_address (addr)); + svSetValueStr (ifcfg, + numbered_tag (tag, "IPADDR", j), + nm_ip_address_get_address (addr)); prefix = nm_ip_address_get_prefix (addr); - tmp = g_strdup_printf ("%u", prefix); - svSetValueStr (ifcfg, prefix_key, tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, numbered_tag (tag, "PREFIX", j), prefix); /* If the legacy "NETMASK" is present, keep it. */ + numbered_tag (tag, "NETMASK", j); if (has_netmask) { char buf[INET_ADDRSTRLEN]; - svSetValueStr (ifcfg, netmask_key, - nm_utils_inet4_ntop (nm_utils_ip4_prefix_to_netmask (prefix), buf)); + svSetValueStr (ifcfg, tag, + nm_utils_inet4_ntop (_nm_utils_ip4_prefix_to_netmask (prefix), buf)); } else - svUnsetValue (ifcfg, netmask_key); + svUnsetValue (ifcfg, tag); - svUnsetValue (ifcfg, gw_key); n++; } - svUnsetValue (ifcfg, "IPADDR0"); - svUnsetValue (ifcfg, "PREFIX0"); - svUnsetValue (ifcfg, "NETMASK0"); - svUnsetValue (ifcfg, "GATEWAY0"); + svUnsetValue (ifcfg, numbered_tag (tag, "IPADDR", 0)); + svUnsetValue (ifcfg, numbered_tag (tag, "PREFIX", 0)); + svUnsetValue (ifcfg, numbered_tag (tag, "NETMASK", 0)); if (n == 0) { svUnsetValue (ifcfg, "IPADDR"); svUnsetValue (ifcfg, "PREFIX"); svUnsetValue (ifcfg, "NETMASK"); - i = 1; - } else - i = n; - for (; i < 256; i++) { - nm_sprintf_buf (addr_key, "IPADDR%u", i); - nm_sprintf_buf (prefix_key, "PREFIX%u", i); - nm_sprintf_buf (netmask_key, "NETMASK%u", i); - nm_sprintf_buf (gw_key, "GATEWAY%u", i); - - svUnsetValue (ifcfg, addr_key); - svUnsetValue (ifcfg, prefix_key); - svUnsetValue (ifcfg, netmask_key); - svUnsetValue (ifcfg, gw_key); + } + for (j = n; j < 256; j++) { + svUnsetValue (ifcfg, numbered_tag (tag, "IPADDR", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "PREFIX", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "NETMASK", j)); } + for (j = -1; j < 256; j++) { + if (j != 0) + svUnsetValue (ifcfg, numbered_tag (tag, "GATEWAY", j)); + } svSetValueStr (ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway (s_ip4)); num = nm_setting_ip_config_get_num_dns (s_ip4); for (i = 0; i < 254; i++) { const char *dns; - nm_sprintf_buf (addr_key, "DNS%u", i + 1); + numbered_tag (tag, "DNS", i + 1); if (i >= num) - svUnsetValue (ifcfg, addr_key); + svUnsetValue (ifcfg, tag); else { dns = nm_setting_ip_config_get_dns (s_ip4, i); - svSetValueStr (ifcfg, addr_key, dns); + svSetValueStr (ifcfg, tag, dns); } } @@ -2273,98 +2301,27 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) svSetValueStr (ifcfg, "DHCP_CLIENT_ID", value); timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip4); - tmp = timeout ? g_strdup_printf ("%d", timeout) : NULL; - svSetValueStr (ifcfg, "IPV4_DHCP_TIMEOUT", tmp); - g_free (tmp); + svSetValueInt64_cond (ifcfg, + "IPV4_DHCP_TIMEOUT", + timeout != 0, + timeout); svSetValueBoolean (ifcfg, "IPV4_FAILURE_FATAL", !nm_setting_ip_config_get_may_fail (s_ip4)); route_metric = nm_setting_ip_config_get_route_metric (s_ip4); - tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL; - svSetValueStr (ifcfg, "IPV4_ROUTE_METRIC", tmp); - g_free (tmp); - - /* Static routes - route-<name> file */ - route_path = utils_get_route_path (svFileGetName (ifcfg)); - if (!route_path) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not get route file path for '%s'", svFileGetName (ifcfg)); - return FALSE; - } - - if (utils_has_route_file_new_syntax (route_path)) { - shvarFile *routefile; - - routefile = utils_get_route_ifcfg (svFileGetName (ifcfg), TRUE); - if (!routefile) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not create route file '%s'", route_path); - g_free (route_path); - return FALSE; - } - g_free (route_path); + svSetValueInt64_cond (ifcfg, + "IPV4_ROUTE_METRIC", + route_metric != -1, + route_metric); - num = nm_setting_ip_config_get_num_routes (s_ip4); - for (i = 0; i < 256; i++) { - char buf[INET_ADDRSTRLEN]; - NMIPRoute *route; - guint32 netmask; - gint64 metric; - char metric_key[64]; - char options_key[64]; - - nm_sprintf_buf (addr_key, "ADDRESS%u", i); - nm_sprintf_buf (netmask_key, "NETMASK%u", i); - nm_sprintf_buf (gw_key, "GATEWAY%u", i); - nm_sprintf_buf (metric_key, "METRIC%u", i); - nm_sprintf_buf (options_key, "OPTIONS%u", i); - - if (i >= num) { - svUnsetValue (routefile, addr_key); - svUnsetValue (routefile, netmask_key); - svUnsetValue (routefile, gw_key); - svUnsetValue (routefile, metric_key); - svUnsetValue (routefile, options_key); - } else { - gs_free char *options = NULL; - - route = nm_setting_ip_config_get_route (s_ip4, i); + route_table = nm_setting_ip_config_get_route_table (s_ip4); + svSetValueInt64_cond (ifcfg, + "IPV4_ROUTE_TABLE", + route_table != 0, + route_table); - svSetValueStr (routefile, addr_key, nm_ip_route_get_dest (route)); - - memset (buf, 0, sizeof (buf)); - netmask = nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route)); - inet_ntop (AF_INET, (const void *) &netmask, &buf[0], sizeof (buf)); - svSetValueStr (routefile, netmask_key, &buf[0]); - - svSetValueStr (routefile, gw_key, nm_ip_route_get_next_hop (route)); - - memset (buf, 0, sizeof (buf)); - metric = nm_ip_route_get_metric (route); - if (metric == -1) - svUnsetValue (routefile, metric_key); - else { - tmp = g_strdup_printf ("%u", (guint32) metric); - svSetValueStr (routefile, metric_key, tmp); - g_free (tmp); - } - - options = get_route_attributes_string (route, AF_INET); - if (options) - svSetValueStr (routefile, options_key, options); - } - } - if (!svWriteFile (routefile, 0644, error)) { - svCloseFile (routefile); - return FALSE; - } - svCloseFile (routefile); - } else { - write_route_file_legacy (route_path, s_ip4, error); - g_free (route_path); - if (error && *error) - return FALSE; - } + NM_SET_OUT (out_route_content_svformat, write_route_file_svformat (svFileGetName (ifcfg), s_ip4)); + NM_SET_OUT (out_route_content, write_route_file (s_ip4)); timeout = nm_setting_ip_config_get_dad_timeout (s_ip4); if (timeout < 0) @@ -2386,7 +2343,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) } static void -write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path) +write_ip4_aliases (NMConnection *connection, const char *base_ifcfg_path) { NMSettingIPConfig *s_ip4; gs_free char *base_ifcfg_dir = NULL, *base_ifcfg_name = NULL; @@ -2432,7 +2389,7 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path) for (i = 0; i < num; i++) { GVariant *label_var; const char *label, *p; - char *path, *tmp; + char *path; NMIPAddress *addr; shvarFile *ifcfg; @@ -2462,68 +2419,13 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path) addr = nm_setting_ip_config_get_address (s_ip4, i); svSetValueStr (ifcfg, "IPADDR", nm_ip_address_get_address (addr)); - tmp = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr)); - svSetValueStr (ifcfg, "PREFIX", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "PREFIX", nm_ip_address_get_prefix(addr)); svWriteFile (ifcfg, 0644, NULL); svCloseFile (ifcfg); } } -static gboolean -write_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error) -{ - nm_auto_free_gstring GString *contents = NULL; - NMIPRoute *route; - guint32 i, num; - - g_return_val_if_fail (filename, FALSE); - g_return_val_if_fail (s_ip6, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - - num = nm_setting_ip_config_get_num_routes (s_ip6); - if (num == 0) { - unlink (filename); - return TRUE; - } - - contents = g_string_new (""); - - for (i = 0; i < num; i++) { - gs_free char *options = NULL; - const char *next_hop; - gint64 metric; - - route = nm_setting_ip_config_get_route (s_ip6, i); - next_hop = nm_ip_route_get_next_hop (route); - metric = nm_ip_route_get_metric (route); - options = get_route_attributes_string (route, AF_INET6); - - g_string_append_printf (contents, "%s/%u", - nm_ip_route_get_dest (route), - nm_ip_route_get_prefix (route)); - if (next_hop) - g_string_append_printf (contents, " via %s", next_hop); - if (metric >= 0) - g_string_append_printf (contents, " metric %u", (guint) metric); - if (options) { - g_string_append_c (contents, ' '); - g_string_append (contents, options); - } - - g_string_append_c (contents, '\n'); - } - - if (!g_file_set_contents (filename, contents->str, contents->len, NULL)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Writing route6 file '%s' failed", filename); - return FALSE; - } - - return TRUE; -} - static void write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg) { @@ -2542,21 +2444,25 @@ write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg) } static gboolean -write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +write_ip6_setting (NMConnection *connection, + shvarFile *ifcfg, + GString **out_route6_content, + GError **error) { NMSettingIPConfig *s_ip6; NMSettingIPConfig *s_ip4; const char *value; - char *tmp; guint i, num, num4; gint priority; NMIPAddress *addr; const char *dns; gint64 route_metric; + NMIPRouteTableSyncMode route_table; GString *ip_str1, *ip_str2, *ip_ptr; - char *route6_path; NMSettingIP6ConfigAddrGenMode addr_gen_mode; + NM_SET_OUT (out_route6_content, NULL); + s_ip6 = nm_connection_get_setting_ip6_config (connection); if (!s_ip6) { /* slave-type: clear IPv6 settings @@ -2636,15 +2542,15 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) num4 = s_ip4 ? nm_setting_ip_config_get_num_dns (s_ip4) : 0; /* from where to start with IPv6 entries */ num = nm_setting_ip_config_get_num_dns (s_ip6); for (i = 0; i < 254; i++) { - char addr_key[64]; + char tag[64]; - nm_sprintf_buf (addr_key, "DNS%u", i + num4 + 1); + numbered_tag (tag, "DNS", i + num4 + 1); if (i >= num) - svUnsetValue (ifcfg, addr_key); + svUnsetValue (ifcfg, tag); else { dns = nm_setting_ip_config_get_dns (s_ip6, i); - svSetValueStr (ifcfg, addr_key, dns); + svSetValueStr (ifcfg, tag, dns); } } @@ -2681,9 +2587,16 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) nm_setting_ip_config_get_may_fail (s_ip6) ? "no" : "yes"); route_metric = nm_setting_ip_config_get_route_metric (s_ip6); - tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL; - svSetValueStr (ifcfg, "IPV6_ROUTE_METRIC", tmp); - g_free (tmp); + svSetValueInt64_cond (ifcfg, + "IPV6_ROUTE_METRIC", + route_metric != -1, + route_metric); + + route_table = nm_setting_ip_config_get_route_table (s_ip6); + svSetValueInt64_cond (ifcfg, + "IPV6_ROUTE_TABLE", + route_table != 0, + route_table); /* IPv6 Privacy Extensions */ svUnsetValue (ifcfg, "IPV6_PRIVACY"); @@ -2706,10 +2619,8 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) /* IPv6 Address generation mode */ addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode (NM_SETTING_IP6_CONFIG (s_ip6)); if (addr_gen_mode != NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) { - tmp = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (), - addr_gen_mode); - svSetValueStr (ifcfg, "IPV6_ADDR_GEN_MODE", tmp); - g_free (tmp); + svSetValueEnum (ifcfg, "IPV6_ADDR_GEN_MODE", nm_setting_ip6_config_addr_gen_mode_get_type (), + addr_gen_mode); } else { svUnsetValue (ifcfg, "IPV6_ADDR_GEN_MODE"); } @@ -2724,17 +2635,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) else svUnsetValue (ifcfg, "IPV6_DNS_PRIORITY"); - /* Static routes go to route6-<dev> file */ - route6_path = utils_get_route6_path (svFileGetName (ifcfg)); - if (!route6_path) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not get route6 file path for '%s'", svFileGetName (ifcfg)); - return FALSE; - } - write_route6_file (route6_path, s_ip6, error); - g_free (route6_path); - if (error && *error) - return FALSE; + NM_SET_OUT (out_route6_content, write_route_file (s_ip6)); return TRUE; } @@ -2817,30 +2718,43 @@ escape_id (const char *id) } static gboolean -write_connection (NMConnection *connection, - const char *ifcfg_dir, - const char *filename, - char **out_filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error) +do_write_construct (NMConnection *connection, + const char *ifcfg_dir, + const char *filename, + shvarFile **out_ifcfg, + GHashTable **out_blobs, + GHashTable **out_secrets, + gboolean *out_route_ignore, + shvarFile **out_route_content_svformat, + GString **out_route_content, + GString **out_route6_content, + GError **error) { NMSettingConnection *s_con; nm_auto_shvar_file_close shvarFile *ifcfg = NULL; gs_free char *ifcfg_name = NULL; + gs_free char *route_path = NULL; + gs_free char *route6_path = NULL; const char *type; - gboolean no_8021x = FALSE; - gboolean wired = FALSE; + gs_unref_hashtable GHashTable *blobs = NULL; + gs_unref_hashtable GHashTable *secrets = NULL; + gboolean wired; + gboolean no_8021x; + gboolean route_path_is_svformat; + gboolean has_complex_routes_v4; + gboolean has_complex_routes_v6; + gboolean route_ignore; + nm_auto_shvar_file_close shvarFile *route_content_svformat = NULL; + nm_auto_free_gstring GString *route_content = NULL; + nm_auto_free_gstring GString *route6_content = NULL; nm_assert (NM_IS_CONNECTION (connection)); nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); - nm_assert (!out_reread || !*out_reread); - if (!writer_can_write_connection (connection, error)) + if (!nms_ifcfg_rh_writer_can_write_connection (connection, error)) return FALSE; s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); if (filename) { /* For existing connections, 'filename' should be full path to ifcfg file */ @@ -2849,7 +2763,7 @@ write_connection (NMConnection *connection, return FALSE; ifcfg_name = g_strdup (filename); - } else { + } else if (ifcfg_dir) { char *escaped; escaped = escape_id (nm_setting_connection_get_id (s_con)); @@ -2880,6 +2794,21 @@ write_connection (NMConnection *connection, } ifcfg = svCreateFile (ifcfg_name); + } else + ifcfg = svCreateFile ("/tmp/ifcfg-dummy"); + + route_path = utils_get_route_path (svFileGetName (ifcfg)); + if (!route_path) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Could not get route file path for '%s'", svFileGetName (ifcfg)); + return FALSE; + } + + route6_path = utils_get_route6_path (svFileGetName (ifcfg)); + if (!route6_path) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Could not get route6 file path for '%s'", svFileGetName (ifcfg)); + return FALSE; } type = nm_setting_connection_get_connection_type (s_con); @@ -2889,6 +2818,10 @@ write_connection (NMConnection *connection, return FALSE; } + secrets = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); + + wired = FALSE; + no_8021x = FALSE; if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { // FIXME: can't write PPPoE at this time if (nm_connection_get_setting_pppoe (connection)) { @@ -2905,19 +2838,19 @@ write_connection (NMConnection *connection, if (!write_vlan_setting (connection, ifcfg, &wired, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { - if (!write_wireless_setting (connection, ifcfg, &no_8021x, error)) + if (!write_wireless_setting (connection, ifcfg, secrets, &no_8021x, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_INFINIBAND_SETTING_NAME)) { if (!write_infiniband_setting (connection, ifcfg, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME)) { - if (!write_bonding_setting (connection, ifcfg, &wired, error)) + if (!write_bond_setting (connection, ifcfg, &wired, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME)) { if (!write_team_setting (connection, ifcfg, &wired, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)) { - if (!write_bridge_setting (connection, ifcfg, error)) + if (!write_bridge_setting (connection, ifcfg, &wired, error)) return FALSE; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, @@ -2926,7 +2859,8 @@ write_connection (NMConnection *connection, } if (!no_8021x) { - if (!write_8021x_setting (connection, ifcfg, wired, error)) + blobs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_bytes_unref); + if (!write_8021x_setting (connection, ifcfg, secrets, blobs, wired, error)) return FALSE; } @@ -2948,11 +2882,49 @@ write_connection (NMConnection *connection, svUnsetValue (ifcfg, "DHCP_HOSTNAME"); svUnsetValue (ifcfg, "DHCP_FQDN"); - if (!write_ip4_setting (connection, ifcfg, error)) + route_path_is_svformat = utils_has_route_file_new_syntax (route_path); + + has_complex_routes_v4 = utils_has_complex_routes (ifcfg_name, AF_INET); + has_complex_routes_v6 = utils_has_complex_routes (ifcfg_name, AF_INET6); + + if (has_complex_routes_v4 || has_complex_routes_v6) { + NMSettingIPConfig *s_ip4, *s_ip6; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if ( ( s_ip4 + && nm_setting_ip_config_get_num_routes (s_ip4) > 0) + || ( s_ip6 + && nm_setting_ip_config_get_num_routes (s_ip6) > 0)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Cannot configure static routes on a connection that has an associated 'rule%s-' file", + has_complex_routes_v4 ? "" : "6"); + return FALSE; + } + if ( ( s_ip4 + && nm_setting_ip_config_get_route_table (s_ip4) != 0) + || ( s_ip6 + && nm_setting_ip_config_get_route_table (s_ip6) != 0)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Cannot configure a route table for policy routing on a connection that has an associated 'rule%s-' file", + has_complex_routes_v4 ? "" : "6"); + return FALSE; + } + route_ignore = TRUE; + } else + route_ignore = FALSE; + + if (!write_ip4_setting (connection, + ifcfg, + !route_ignore && route_path_is_svformat ? &route_content_svformat : NULL, + !route_ignore && route_path_is_svformat ? NULL :&route_content, + error)) return FALSE; - write_ip4_aliases (connection, ifcfg_name); - if (!write_ip6_setting (connection, ifcfg, error)) + if (!write_ip6_setting (connection, + ifcfg, + !route_ignore ? &route6_content : NULL, + error)) return FALSE; if (!write_res_options (connection, ifcfg, error)) @@ -2960,100 +2932,228 @@ write_connection (NMConnection *connection, write_connection_setting (s_con, ifcfg); + NM_SET_OUT (out_ifcfg, g_steal_pointer (&ifcfg)); + NM_SET_OUT (out_blobs, g_steal_pointer (&blobs)); + NM_SET_OUT (out_secrets, g_steal_pointer (&secrets)); + NM_SET_OUT (out_route_ignore, route_ignore); + NM_SET_OUT (out_route_content_svformat, g_steal_pointer (&route_content_svformat)); + NM_SET_OUT (out_route_content, g_steal_pointer (&route_content)); + NM_SET_OUT (out_route6_content, g_steal_pointer (&route6_content)); + return TRUE; +} + +static gboolean +do_write_to_disk (NMConnection *connection, + shvarFile *ifcfg, + GHashTable *blobs, + GHashTable *secrets, + gboolean route_ignore, + shvarFile *route_content_svformat, + GString *route_content, + GString *route6_content, + GError **error) +{ + /* From here on, we persist data to disk. Before, it was all in-memory + * only. But we loaded the ifcfg files from disk, and managled our + * new settings (in-momory). */ + if (!svWriteFile (ifcfg, 0644, error)) return FALSE; - if (out_reread || out_reread_same) { - gs_unref_object NMConnection *reread = NULL; - gs_free_error GError *local = NULL; - gs_free char *unhandled = NULL; - gboolean reread_same = FALSE; - - reread = connection_from_file (ifcfg_name, &unhandled, &local, NULL); - - if (unhandled) { - _LOGW ("failure to re-read the new connection from file \"%s\": %s", - ifcfg_name, "connection is unhandled"); - g_clear_object (&reread); - } else if (!reread) { - _LOGW ("failure to re-read the new connection from file \"%s\": %s", - ifcfg_name, local ? local->message : "<unknown>"); - } else { - if (out_reread_same) { - if (nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT)) - reread_same = TRUE; + write_ip4_aliases (connection, svFileGetName (ifcfg)); - nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); - nm_assert (reread_same == ({ - gs_unref_hashtable GHashTable *_settings = NULL; + if (!write_blobs (blobs, error)) + return FALSE; - ( nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings) - && !_settings); - })); + if (!write_secrets (ifcfg, secrets, error)) + return FALSE; + + if (!route_ignore) { + gs_free char *route_path = utils_get_route_path (svFileGetName (ifcfg)); + + if (!route_content && !route_content_svformat) + (void) unlink (route_path); + else { + nm_assert (route_content_svformat || route_content); + if (route_content_svformat) { + if (!svWriteFile (route_content_svformat, 0644, error)) + return FALSE; + } else { + if (!g_file_set_contents (route_path, route_content->str, route_content->len, NULL)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Writing route file '%s' failed", route_path); + return FALSE; + } } } - - 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 */ - if (out_filename && !filename) - *out_filename = g_steal_pointer (&ifcfg_name); + if (!route_ignore) { + gs_free char *route6_path = utils_get_route6_path (svFileGetName (ifcfg)); + + if (!route6_content) + (void) unlink (route6_path); + else { + if (!g_file_set_contents (route6_path, route6_content->str, route6_content->len, NULL)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Writing route6 file '%s' failed", route6_path); + return FALSE; + } + } + } return TRUE; } -gboolean -writer_can_write_connection (NMConnection *connection, GError **error) +static gboolean +do_write_reread (NMConnection *connection, + const char *ifcfg_name, + NMConnection **out_reread, + gboolean *out_reread_same, + GError **error) { - NMSettingConnection *s_con; + gs_unref_object NMConnection *reread = NULL; + gs_free_error GError *local = NULL; + gs_free char *unhandled = NULL; + gboolean reread_same = FALSE; - if ( ( nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME) - && !nm_connection_get_setting_pppoe (connection)) - || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) - return TRUE; + nm_assert (!out_reread || !*out_reread); - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "The ifcfg-rh plugin cannot write the connection '%s' (type '%s' pppoe %d)", - nm_connection_get_id (connection), - nm_setting_connection_get_connection_type (s_con), - !!nm_connection_get_setting_pppoe (connection)); - return FALSE; + 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 -writer_new_connection (NMConnection *connection, - const char *ifcfg_dir, - char **out_filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error) +nms_ifcfg_rh_writer_write_connection (NMConnection *connection, + const char *ifcfg_dir, + const char *filename, + char **out_filename, + NMConnection **out_reread, + gboolean *out_reread_same, + GError **error) { - return write_connection (connection, ifcfg_dir, NULL, out_filename, out_reread, out_reread_same, error); + nm_auto_shvar_file_close shvarFile *ifcfg = NULL; + nm_auto_free_gstring GString *route_content = NULL; + gboolean route_ignore = FALSE; + nm_auto_shvar_file_close shvarFile *route_content_svformat = NULL; + 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, + &ifcfg, + &blobs, + &secrets, + &route_ignore, + &route_content_svformat, + &route_content, + &route6_content, + error)) + return FALSE; + + _LOGT ("write: write connection %s (%s) to file \"%s\"", + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + svFileGetName (ifcfg)); + + if (!do_write_to_disk (connection, + ifcfg, + blobs, + secrets, + route_ignore, + route_content_svformat, + route_content, + route6_content, + error)) + return FALSE; + + /* Note that we just wrote the connection to disk, and re-read it from there. + * That is racy if somebody else modifies the connection. + * + * 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 + * 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)) { + _LOGW ("write: failure to re-read connection \"%s\": %s", + svFileGetName (ifcfg), local->message); + g_clear_error (&local); + } 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)); + } + } + } + + /* Only return the filename if this was a newly written ifcfg */ + if (out_filename && !filename) + *out_filename = g_strdup (svFileGetName (ifcfg)); + + return TRUE; } gboolean -writer_update_connection (NMConnection *connection, - const char *ifcfg_dir, - const char *filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error) +nms_ifcfg_rh_writer_can_write_connection (NMConnection *connection, GError **error) { - if (utils_has_complex_routes (filename)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Cannot modify a connection that has an associated 'rule-' or 'rule6-' file"); - return FALSE; - } + const char *type, *id; + + type = nm_connection_get_connection_type (connection); + if (NM_IN_STRSET (type, + NM_SETTING_VLAN_SETTING_NAME, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_INFINIBAND_SETTING_NAME, + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_BRIDGE_SETTING_NAME)) + return TRUE; + if ( nm_streq0 (type, NM_SETTING_WIRED_SETTING_NAME) + && !nm_connection_get_setting_pppoe (connection)) + return TRUE; - return write_connection (connection, ifcfg_dir, filename, NULL, out_reread, out_reread_same, error); + id = nm_connection_get_id (connection); + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "The ifcfg-rh plugin cannot write the connection %s%s%s (type %s%s%s)", + NM_PRINT_FMT_QUOTE_STRING (id), + NM_PRINT_FMT_QUOTE_STRING (type)); + return FALSE; } 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 9cd9513e..d7a255a9 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h @@ -18,26 +18,20 @@ * Copyright (C) 2009 Red Hat, Inc. */ -#ifndef _WRITER_H_ -#define _WRITER_H_ +#ifndef __NMS_IFCFG_RH_WRITER_H__ +#define __NMS_IFCFG_RH_WRITER_H__ #include "nm-connection.h" -gboolean writer_can_write_connection (NMConnection *connection, - GError **error); +gboolean nms_ifcfg_rh_writer_can_write_connection (NMConnection *connection, + GError **error); -gboolean writer_new_connection (NMConnection *connection, - const char *ifcfg_dir, - char **out_filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error); +gboolean nms_ifcfg_rh_writer_write_connection (NMConnection *connection, + const char *ifcfg_dir, + const char *filename, + char **out_filename, + NMConnection **out_reread, + gboolean *out_reread_same, + GError **error); -gboolean writer_update_connection (NMConnection *connection, - const char *ifcfg_dir, - const char *filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error); - -#endif /* _WRITER_H_ */ +#endif /* __NMS_IFCFG_RH_WRITER_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 47ad5a23..df03bf65 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -38,10 +38,15 @@ #include "nm-core-internal.h" #include "nm-core-utils.h" +#include "nm-utils/nm-enum-utils.h" +#include "nm-utils/c-list.h" /*****************************************************************************/ struct _shvarLine { + + CList lst; + /* There are three cases: * * 1) the line is not a valid variable assignment (that is, it doesn't @@ -68,7 +73,7 @@ typedef struct _shvarLine shvarLine; struct _shvarFile { char *fileName; int fd; - GList *lineList; + CList lst_head; gboolean modified; }; @@ -627,6 +632,7 @@ svFile_new (const char *name) s = g_slice_new0 (shvarFile); s->fd = -1; s->fileName = g_strdup (name); + c_list_init (&s->lst_head); return s; } @@ -639,7 +645,7 @@ svFileGetName (const shvarFile *s) } void -svFileSetName_test_only (shvarFile *s, const char *fileName) +_nmtst_svFileSetName (shvarFile *s, const char *fileName) { /* changing the file name is not supported for regular * operation. Only allowed to use in tests, othewise, @@ -649,7 +655,7 @@ svFileSetName_test_only (shvarFile *s, const char *fileName) } void -svFileSetModified_test_only (shvarFile *s) +_nmtst_svFileSetModified (shvarFile *s) { /* marking a file as modified is only for testing. */ s->modified = TRUE; @@ -687,6 +693,7 @@ line_new_parse (const char *value, gsize len) nm_assert (value); line = g_slice_new0 (shvarLine); + c_list_init (&line->lst); for (k = 0; k < len; k++) { if (g_ascii_isspace (value[k])) @@ -724,6 +731,7 @@ line_new_build (const char *key, const char *value) value = svEscape (value, &value_escaped); line = g_slice_new (shvarLine); + c_list_init (&line->lst); line->line = value_escaped ?: g_strdup (value); line->key_with_prefix = g_strdup (key); line->key = line->key_with_prefix; @@ -768,6 +776,7 @@ line_free (shvarLine *line) ASSERT_shvarLine (line); g_free (line->line); g_free (line->key_with_prefix); + c_list_unlink (&line->lst); g_slice_free (shvarLine, line); } @@ -785,9 +794,8 @@ svOpenFileInternal (const char *name, gboolean create, GError **error) int errsv = 0; char *arena; const char *p, *q; - GError *local = NULL; + gs_free_error GError *local = NULL; nm_auto_close int fd = -1; - GList *lineList = NULL; if (create) fd = open (name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */ @@ -810,34 +818,35 @@ svOpenFileInternal (const char *name, gboolean create, GError **error) return NULL; } - if (nm_utils_fd_get_contents (fd, + if (nm_utils_fd_get_contents (closefd ? nm_steal_fd (&fd) : fd, + closefd, 10 * 1024 * 1024, &arena, NULL, &local) < 0) { + if (create) + return svFile_new (name); + g_set_error (error, G_FILE_ERROR, local->domain == G_FILE_ERROR ? local->code : G_FILE_ERROR_FAILED, "Could not read file '%s': %s", name, local->message); - g_error_free (local); return NULL; } + s = svFile_new (name); + for (p = arena; (q = strchr (p, '\n')) != NULL; p = q + 1) - lineList = g_list_prepend (lineList, line_new_parse (p, q - p)); + c_list_link_tail (&s->lst_head, &line_new_parse (p, q - p)->lst); if (p[0]) - lineList = g_list_prepend (lineList, line_new_parse (p, strlen (p))); + c_list_link_tail (&s->lst_head, &line_new_parse (p, strlen (p))->lst); g_free (arena); - lineList = g_list_reverse (lineList); - - s = svFile_new (name); - s->lineList = lineList; /* closefd is set if we opened the file read-only, so go ahead and * close it, because we can't write to it anyway */ if (!closefd) { - s->fd = fd; - fd = -1; + nm_assert (fd > 0); + s->fd = nm_steal_fd (&fd); } return s; @@ -861,42 +870,22 @@ svCreateFile (const char *name) /*****************************************************************************/ -static const GList * -shlist_find (const GList *current, const char *key) -{ - nm_assert (_shell_is_name (key, -1)); - - if (current) { - do { - shvarLine *line = current->data; - - ASSERT_shvarLine (line); - if (line->key && nm_streq (line->key, key)) - return current; - current = current->next; - } while (current); - } - return NULL; -} - -/*****************************************************************************/ - GHashTable * svGetKeys (shvarFile *s) { GHashTable *keys = NULL; - const GList *current; + CList *current; const shvarLine *line; nm_assert (s); - for (current = s->lineList; current; current = current->next) { - line = current->data; + c_list_for_each (current, &s->lst_head) { + line = c_list_entry (current, shvarLine, lst); if (line->key && line->line) { /* we don't clone the keys. The keys are only valid * until @s gets modified. */ if (!keys) - keys = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + keys = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); g_hash_table_add (keys, (gpointer) line->key); } } @@ -908,14 +897,14 @@ svGetKeys (shvarFile *s) const char * svFindFirstKeyWithPrefix (shvarFile *s, const char *key_prefix) { - const GList *current; + CList *current; const shvarLine *l; g_return_val_if_fail (s, NULL); g_return_val_if_fail (key_prefix, NULL); - for (current = s->lineList; current; current = current->next) { - l = current->data; + c_list_for_each (current, &s->lst_head) { + l = c_list_entry (current, shvarLine, lst); if ( l->key && l->line && g_str_has_prefix (l->key, key_prefix)) @@ -930,34 +919,31 @@ svFindFirstKeyWithPrefix (shvarFile *s, const char *key_prefix) static const char * _svGetValue (shvarFile *s, const char *key, char **to_free) { - const GList *current, *last; - const shvarLine *line; + CList *current; + const shvarLine *line, *l; + const char *v; nm_assert (s); nm_assert (_shell_is_name (key, -1)); nm_assert (to_free); - last = NULL; - current = s->lineList; - while ((current = shlist_find (current, key))) { - last = current; - current = current->next; + line = NULL; + c_list_for_each (current, &s->lst_head) { + l = c_list_entry (current, shvarLine, lst); + if (l->key && nm_streq (l->key, key)) + line = l; } - if (last) { - line = last->data; - if (line->line) { - const char *v; - - v = svUnescape (line->line, to_free); - if (!v) { - /* a wrongly quoted value is treated like the empty string. - * See also svWriteFile(), which handles unparsable values - * that way. */ - nm_assert (!*to_free); - return ""; - } - return v; + + if (line && line->line) { + v = svUnescape (line->line, to_free); + if (!v) { + /* a wrongly quoted value is treated like the empty string. + * See also svWriteFile(), which handles unparsable values + * that way. */ + nm_assert (!*to_free); + return ""; } + return v; } *to_free = NULL; return NULL; @@ -1104,50 +1090,148 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m return result; } +gboolean +svGetValueEnum (shvarFile *s, const char *key, + GType gtype, int *out_value, + GError **error) +{ + gs_free char *to_free = NULL; + const char *svalue; + gs_free char *err_token = NULL; + int value; + + svalue = _svGetValue (s, key, &to_free); + if (!svalue) { + /* don't touch out_value. The caller is supposed + * to initialize it with the default value. */ + return TRUE; + } + + if (!nm_utils_enum_from_str (gtype, svalue, &value, &err_token)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "Invalid token \"%s\" in \"%s\" for %s", + err_token, svalue, key); + return FALSE; + } + + NM_SET_OUT (out_value, value); + return TRUE; +} + /*****************************************************************************/ +static gboolean +_is_all_digits (const char *str) +{ + return str[0] + && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch)); +} + +#define IS_NUMBERED_TAG(key, tab_name) \ + ({ \ + const char *_key = (key); \ + \ + ( (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \ + && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \ + }) + +gboolean +svUnsetAll (shvarFile *s, SvKeyType match_key_type) +{ + CList *current; + shvarLine *line; + gboolean changed = FALSE; + + g_return_val_if_fail (s, FALSE); + + c_list_for_each (current, &s->lst_head) { + line = c_list_entry (current, shvarLine, lst); + ASSERT_shvarLine (line); + if (!line->key) + continue; + + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY)) + goto do_clear; + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) { + if ( IS_NUMBERED_TAG (line->key, "ADDRESS") + || IS_NUMBERED_TAG (line->key, "NETMASK") + || IS_NUMBERED_TAG (line->key, "GATEWAY") + || IS_NUMBERED_TAG (line->key, "METRIC") + || IS_NUMBERED_TAG (line->key, "OPTIONS")) + goto do_clear; + } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) { + if ( IS_NUMBERED_TAG (line->key, "IPADDR") + || IS_NUMBERED_TAG (line->key, "PREFIX") + || IS_NUMBERED_TAG (line->key, "NETMASK") + || IS_NUMBERED_TAG (line->key, "GATEWAY")) + goto do_clear; + } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) { + if (g_str_has_prefix (line->key, "NM_USER_")) + goto do_clear; + } + + continue; +do_clear: + if (nm_clear_g_free (&line->line)) { + ASSERT_shvarLine (line); + changed = TRUE; + } + } + + if (changed) + s->modified = TRUE; + return changed; +} + /* Same as svSetValueStr() but it preserves empty @value -- contrary to * svSetValueStr() for which "" effectively means to remove the value. */ -void +gboolean svSetValue (shvarFile *s, const char *key, const char *value) { - GList *current, *last; + CList *current; + shvarLine *line, *l; + gboolean changed = FALSE; - g_return_if_fail (s != NULL); - g_return_if_fail (key != NULL); + g_return_val_if_fail (s, FALSE); + g_return_val_if_fail (key, FALSE); nm_assert (_shell_is_name (key, -1)); - last = NULL; - current = s->lineList; - while ((current = (GList *) shlist_find (current, key))) { - if (last) { - /* if we find multiple entries for the same key, we can - * delete all but the last. */ - line_free (last->data); - s->lineList = g_list_delete_link (s->lineList, last); - s->modified = TRUE; + line = NULL; + c_list_for_each (current, &s->lst_head) { + l = c_list_entry (current, shvarLine, lst); + if (l->key && nm_streq (l->key, key)) { + if (line) { + /* if we find multiple entries for the same key, we can + * delete all but the last. */ + line_free (line); + changed = TRUE; + } + line = l; } - last = current; - current = current->next; } if (!value) { - if (last) { - shvarLine *line = last->data; - - if (nm_clear_g_free (&line->line)) - s->modified = TRUE; + if (line) { + if (nm_clear_g_free (&line->line)) { + changed = TRUE; + } } } else { - if (!last) { - s->lineList = g_list_append (s->lineList, line_new_build (key, value)); - s->modified = TRUE; + if (!line) { + c_list_link_tail (&s->lst_head, &line_new_build (key, value)->lst); + changed = TRUE; } else { - if (line_set (last->data, value)) - s->modified = TRUE; + if (line_set (line, value)) + changed = TRUE; } } + + if (changed) + s->modified = TRUE; + return changed; } /* Set the variable <key> equal to the value <value>. @@ -1155,51 +1239,48 @@ svSetValue (shvarFile *s, const char *key, const char *value) * the key=value pair after that line. Otherwise, append the pair * to the bottom of the file. */ -void +gboolean svSetValueStr (shvarFile *s, const char *key, const char *value) { - svSetValue (s, key, value && value[0] ? value : NULL); + return svSetValue (s, key, value && value[0] ? value : NULL); } -void +gboolean svSetValueInt64 (shvarFile *s, const char *key, gint64 value) { char buf[NM_DECIMAL_STR_MAX (value)]; - svSetValue (s, key, nm_sprintf_buf (buf, "%"G_GINT64_FORMAT, value)); + return svSetValue (s, key, nm_sprintf_buf (buf, "%"G_GINT64_FORMAT, value)); } -void -svSetValueBoolean (shvarFile *s, const char *key, gboolean value) +gboolean +svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, gint64 value) { - svSetValue (s, key, value ? "yes" : "no"); + if (do_set) + return svSetValueInt64 (s, key, value); + else + return svUnsetValue (s, key); } -void -svUnsetValue (shvarFile *s, const char *key) +gboolean +svSetValueBoolean (shvarFile *s, const char *key, gboolean value) { - svSetValue (s, key, NULL); + return svSetValue (s, key, value ? "yes" : "no"); } -void -svUnsetValuesWithPrefix (shvarFile *s, const char *prefix) +gboolean +svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value) { - GList *current; - - g_return_if_fail (s); - g_return_if_fail (prefix); + gs_free char *v = NULL; - for (current = s->lineList; current; current = current->next) { - shvarLine *line = current->data; + v = _nm_utils_enum_to_str_full (gtype, value, " "); + return svSetValueStr (s, key, v); +} - ASSERT_shvarLine (line); - if ( line->key - && g_str_has_prefix (line->key, prefix)) { - if (nm_clear_g_free (&line->line)) - s->modified = TRUE; - } - ASSERT_shvarLine (line); - } +gboolean +svUnsetValue (shvarFile *s, const char *key) +{ + return svSetValue (s, key, NULL); } /*****************************************************************************/ @@ -1215,7 +1296,7 @@ svWriteFile (shvarFile *s, int mode, GError **error) { FILE *f; int tmpfd; - const GList *current; + CList *current; if (s->modified) { if (s->fd == -1) @@ -1248,8 +1329,8 @@ svWriteFile (shvarFile *s, int mode, GError **error) } f = fdopen (tmpfd, "w"); fseek (f, 0, SEEK_SET); - for (current = s->lineList; current; current = current->next) { - const shvarLine *line = current->data; + c_list_for_each (current, &s->lst_head) { + const shvarLine *line = c_list_entry (current, shvarLine, lst); const char *str; char *s_tmp; gboolean valid_value; @@ -1288,11 +1369,13 @@ svWriteFile (shvarFile *s, int mode, GError **error) void svCloseFile (shvarFile *s) { + CList *current, *safe; + g_return_if_fail (s != NULL); - if (s->fd != -1) - close (s->fd); + nm_close (s->fd); g_free (s->fileName); - g_list_free_full (s->lineList, (GDestroyNotify) line_free); + c_list_for_each_safe (current, safe, &s->lst_head) + line_free (c_list_entry (current, shvarLine, lst)); g_slice_free (shvarFile, s); } diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index a13920a1..c48bbfd3 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -35,8 +35,8 @@ typedef struct _shvarFile shvarFile; const char *svFileGetName (const shvarFile *s); -void svFileSetName_test_only (shvarFile *s, const char *fileName); -void svFileSetModified_test_only (shvarFile *s); +void _nmtst_svFileSetName (shvarFile *s, const char *fileName); +void _nmtst_svFileSetModified (shvarFile *s); /* Create the file <name>, return a shvarFile (never fails) */ shvarFile *svCreateFile (const char *name); @@ -68,19 +68,32 @@ gint svGetValueBoolean (shvarFile *s, const char *key, gint def); gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback); +gboolean svGetValueEnum (shvarFile *s, const char *key, + GType gtype, int *out_value, + GError **error); + /* Set the variable <key> equal to the value <value>. * If <key> does not exist, and the <current> pointer is set, append * the key=value pair after that line. Otherwise, prepend the pair * to the top of the file. */ -void svSetValue (shvarFile *s, const char *key, const char *value); -void svSetValueStr (shvarFile *s, const char *key, const char *value); -void svSetValueBoolean (shvarFile *s, const char *key, gboolean value); -void svSetValueInt64 (shvarFile *s, const char *key, gint64 value); - -void svUnsetValue (shvarFile *s, const char *key); - -void svUnsetValuesWithPrefix (shvarFile *s, const char *prefix); +gboolean svSetValue (shvarFile *s, const char *key, const char *value); +gboolean svSetValueStr (shvarFile *s, const char *key, const char *value); +gboolean svSetValueBoolean (shvarFile *s, const char *key, gboolean value); +gboolean svSetValueInt64 (shvarFile *s, const char *key, gint64 value); +gboolean svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, gint64 value); +gboolean svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value); + +gboolean svUnsetValue (shvarFile *s, const char *key); + +typedef enum { + SV_KEY_TYPE_ANY = (1LL << 0), + SV_KEY_TYPE_ROUTE_SVFORMAT = (1LL << 1), + SV_KEY_TYPE_IP4_ADDRESS = (1LL << 2), + SV_KEY_TYPE_USER = (1LL << 3), +} SvKeyType; + +gboolean svUnsetAll (shvarFile *s, SvKeyType match_key_type); /* Write the current contents iff modified. Returns FALSE on error * and TRUE on success. Do not write if no values have been modified. 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 3956003d..854d2490 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,4 +1,3 @@ -DEVICE=bond0 BONDING_OPTS=mode=balance-rr TYPE=Bond BONDING_MASTER=yes @@ -13,4 +12,5 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no NAME="Test Write Bond Main" UUID=${UUID} +DEVICE=bond0 ONBOOT=yes 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 60091b7b..793713ea 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 @@ -1,6 +1,5 @@ VLAN=yes TYPE=Vlan -DEVICE=vlan43 PHYSDEV=eth9 VLAN_ID=43 REORDER_HDR=yes @@ -19,4 +18,5 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no NAME="Vlan test-vlan-interface" UUID=${UUID} +DEVICE=vlan43 ONBOOT=yes 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 367d0ddb..87980dc4 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 @@ -1,5 +1,4 @@ VLAN=yes -DEVICE=enp31s0f1-142 PHYSDEV=enp31s0f1 VLAN_ID=142 REORDER_HDR=yes @@ -7,6 +6,7 @@ GVRP=no MVRP=no NAME=team-slave-enp31s0f1-142 UUID=74f435bb-ede4-415a-9d48-f580b60eba04 +DEVICE=enp31s0f1-142 ONBOOT=no TEAM_MASTER=team142 DEVICETYPE=TeamPort diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main index 1d31b5ba..2bc987c2 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main @@ -4,5 +4,5 @@ TYPE=Bridge BOOTPROTO=dhcp STP=on DELAY=2 -BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0" +BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0 group_fwd_mask=24" MACADDR=00:16:41:11:22:33 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-password-raw b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-password-raw new file mode 100644 index 00000000..181ffbef --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-password-raw @@ -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=TTLS +IEEE_8021X_IDENTITY="Bill Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_INNER_AUTH_METHODS=EAP-GTC +IEEE_8021X_PASSWORD_RAW=0408151623420001 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy index 3f02032a..faa247d8 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy @@ -5,3 +5,4 @@ via 8.8.8.8 to 32.42.52.62 43.53.0.0/16 metric 3 via 7.7.7.7 dev eth2 cwnd 14 mtu lock 9000 initrwnd 20 window lock 10000 initcwnd lock 42 src 1.2.3.4 +7.7.7.8/32 via (null) metric 18 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 496a164b..52dbd932 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -232,7 +232,7 @@ _assert_expected_content (NMConnection *connection, const char *filename, const g_assert (_ifcfg_dir && _ifcfg_dir[0]); \ g_assert (_filename && _filename[0]); \ \ - _success = writer_update_connection (_connection, _ifcfg_dir, _filename, _out_reread, _out_reread_same, &_error); \ + _success = nms_ifcfg_rh_writer_write_connection (_connection, _ifcfg_dir, _filename, NULL, _out_reread, _out_reread_same, &_error); \ nmtst_assert_success (_success, _error); \ _assert_expected_content (_connection, _filename, _expected); \ } G_STMT_END @@ -260,8 +260,8 @@ _connection_from_file (const char *filename, g_assert (!out_unhandled || !*out_unhandled); - connection = connection_from_file_test (filename, network_file, test_type, - out_unhandled ?: &unhandled_fallback, &error); + connection = nmtst_connection_from_file (filename, network_file, test_type, + out_unhandled ?: &unhandled_fallback, &error); g_assert_no_error (error); g_assert (!unhandled_fallback); @@ -282,7 +282,7 @@ _connection_from_file_fail (const char *filename, GError *local = NULL; char *unhandled = NULL; - connection = connection_from_file_test (filename, network_file, test_type, &unhandled, &local); + connection = nmtst_connection_from_file (filename, network_file, test_type, &unhandled, &local); g_assert (!connection); g_assert (local); @@ -310,12 +310,13 @@ _writer_new_connection_reread (NMConnection *connection, con_verified = nmtst_connection_duplicate_and_normalize (connection); - success = writer_new_connection (con_verified, - ifcfg_dir, - &filename, - reread, - out_reread_same, - &error); + success = nms_ifcfg_rh_writer_write_connection (con_verified, + ifcfg_dir, + NULL, + &filename, + reread, + out_reread_same, + &error); nmtst_assert_success (success, error); g_assert (filename && filename[0]); @@ -384,12 +385,13 @@ _writer_new_connection_fail (NMConnection *connection, connection_normalized = nmtst_connection_duplicate_and_normalize (connection); - success = writer_new_connection (connection_normalized, - ifcfg_dir, - &filename, - &reread, - NULL, - &local); + success = nms_ifcfg_rh_writer_write_connection (connection_normalized, + ifcfg_dir, + NULL, + &filename, + &reread, + NULL, + &local); nmtst_assert_no_success (success, local); g_assert (!filename); g_assert (!reread); @@ -1376,7 +1378,7 @@ test_read_wired_static_routes_legacy (void) g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); /* Routes */ - g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 3); + g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 4); /* Route #1 */ ip4_route = nm_setting_ip_config_get_route (s_ip4, 0); @@ -1410,6 +1412,13 @@ test_read_wired_static_routes_legacy (void) nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.2.3.4"); + ip4_route = nm_setting_ip_config_get_route (s_ip4, 3); + g_assert (ip4_route != NULL); + g_assert_cmpstr (nm_ip_route_get_dest (ip4_route), ==, "7.7.7.8"); + g_assert_cmpint (nm_ip_route_get_prefix (ip4_route), ==, 32); + g_assert_cmpstr (nm_ip_route_get_next_hop (ip4_route), ==, NULL); + g_assert_cmpint (nm_ip_route_get_metric (ip4_route), ==, 18); + g_object_unref (connection); } @@ -1954,6 +1963,46 @@ test_read_802_1x_ttls_eapgtc (void) } static void +test_read_write_802_1x_password_raw (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSetting8021x *s_8021x; + GBytes *bytes; + gconstpointer data; + gsize size; + + /* Test that the 802-1x.password-raw is correctly read and written. */ + + connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-802-1x-password-raw", + NULL, TYPE_ETHERNET, NULL); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x); + + bytes = nm_setting_802_1x_get_password_raw (s_8021x); + g_assert (bytes); + data = g_bytes_get_data (bytes, &size); + g_assert_cmpmem (data, size, "\x04\x08\x15\x16\x23\x42\x00\x01", 8); + + g_assert_cmpint (nm_setting_802_1x_get_password_raw_flags (s_8021x), + ==, + NM_SETTING_SECRET_FLAG_NONE); + + _writer_new_connection (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile); + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + keyfile = utils_get_keys_path (testfile); + g_assert (g_file_test (keyfile, G_FILE_TEST_EXISTS)); + + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + +static void test_read_wired_aliases_good (gconstpointer test_data) { const int N = GPOINTER_TO_INT (test_data); @@ -4108,7 +4157,6 @@ test_write_wired_static (void) route6 = nm_ip_route_new (AF_INET6, "::", 128, "2222:aaaa::9999", 1, &error); g_assert_no_error (error); - nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_TOS, g_variant_new_byte (0xb8)); nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (100)); nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1280)); nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, g_variant_new_boolean (TRUE)); @@ -4154,6 +4202,171 @@ test_write_wired_static (void) } static void +test_write_wired_static_with_generic (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *route6file = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4, *reread_s_ip4; + NMSettingIPConfig *s_ip6, *reread_s_ip6; + NMIPAddress *addr; + NMIPAddress *addr6; + NMIPRoute *route6; + GError *error = NULL; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Wired Static", + NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES, 1, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + g_object_set (s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, "31:33:33:37:be:cd", + NM_SETTING_WIRED_MTU, (guint32) 1492, + NULL); + + /* IP4 setting */ + 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_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, + NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1", + NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) 204, + NULL); + + addr = nm_ip_address_new (AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip4, addr); + nm_ip_address_unref (addr); + + addr = nm_ip_address_new (AF_INET, "1.1.1.5", 24, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip4, addr); + nm_ip_address_unref (addr); + + nm_setting_ip_config_add_dns (s_ip4, "4.2.2.1"); + nm_setting_ip_config_add_dns (s_ip4, "4.2.2.2"); + + nm_setting_ip_config_add_dns_search (s_ip4, "foobar.com"); + nm_setting_ip_config_add_dns_search (s_ip4, "lab.foobar.com"); + + /* IP6 setting */ + 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_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, + NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) 206, + NULL); + + /* Add addresses */ + addr6 = nm_ip_address_new (AF_INET6, "1003:1234:abcd::1", 11, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip6, addr6); + nm_ip_address_unref (addr6); + + addr6 = nm_ip_address_new (AF_INET6, "2003:1234:abcd::2", 22, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip6, addr6); + nm_ip_address_unref (addr6); + + addr6 = nm_ip_address_new (AF_INET6, "3003:1234:abcd::3", 33, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip6, addr6); + nm_ip_address_unref (addr6); + + /* Add routes */ + route6 = nm_ip_route_new (AF_INET6, + "2222:aaaa:bbbb:cccc::", 64, + "2222:aaaa:bbbb:cccc:dddd:eeee:5555:6666", 99, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_route (s_ip6, route6); + nm_ip_route_unref (route6); + + route6 = nm_ip_route_new (AF_INET6, "::", 128, "2222:aaaa::9999", 1, &error); + g_assert_no_error (error); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (100)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1280)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, g_variant_new_boolean (TRUE)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_FROM, g_variant_new_string ("2222::bbbb/32")); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("::42")); + nm_setting_ip_config_add_route (s_ip6, route6); + nm_ip_route_unref (route6); + + /* DNS servers */ + nm_setting_ip_config_add_dns (s_ip6, "fade:0102:0103::face"); + nm_setting_ip_config_add_dns (s_ip6, "cafe:ffff:eeee:dddd:cccc:bbbb:aaaa:feed"); + + /* DNS domains */ + nm_setting_ip_config_add_dns_search (s_ip6, "foobar6.com"); + nm_setting_ip_config_add_dns_search (s_ip6, "lab6.foobar.com"); + + nm_connection_add_setting (connection, nm_setting_generic_new ()); + + nmtst_assert_connection_verifies (connection); + + _writer_new_connection_FIXME (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile); + route6file = utils_get_route6_path (testfile); + + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + + /* FIXME: currently DNS domains from IPv6 setting are stored in 'DOMAIN' key in ifcfg-file + * However after re-reading they are dropped into IPv4 setting. + * So, in order to comparison succeeded, move DNS domains back to IPv6 setting. + */ + reread_s_ip4 = nm_connection_get_setting_ip4_config (reread); + reread_s_ip6 = nm_connection_get_setting_ip6_config (reread); + nm_setting_ip_config_add_dns_search (reread_s_ip6, nm_setting_ip_config_get_dns_search (reread_s_ip4, 2)); + nm_setting_ip_config_add_dns_search (reread_s_ip6, nm_setting_ip_config_get_dns_search (reread_s_ip4, 3)); + nm_setting_ip_config_remove_dns_search (reread_s_ip4, 3); + nm_setting_ip_config_remove_dns_search (reread_s_ip4, 2); + + g_assert_cmpint (nm_setting_ip_config_get_route_metric (reread_s_ip4), ==, 204); + g_assert_cmpint (nm_setting_ip_config_get_route_metric (reread_s_ip6), ==, 206); + + nm_connection_add_setting (connection, nm_setting_proxy_new ()); + + { + gs_unref_hashtable GHashTable *diffs = NULL; + + g_assert (!nm_connection_diff (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT, &diffs)); + g_assert (diffs); + g_assert (g_hash_table_size (diffs) == 1); + g_assert (g_hash_table_lookup (diffs, "generic")); + g_assert (!nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); + } + g_assert (!nm_connection_get_setting (reread, NM_TYPE_SETTING_GENERIC)); + nm_connection_add_setting (reread, nm_setting_generic_new ()); + { + gs_unref_hashtable GHashTable *diffs = NULL; + + g_assert (nm_connection_diff (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT, &diffs)); + g_assert (!diffs); + g_assert (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); + } +} + +static void test_write_wired_dhcp (void) { nmtst_auto_unlinkfile char *testfile = NULL; @@ -5867,6 +6080,7 @@ test_write_wifi_wpa_psk (gconstpointer test_data) g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", NM_SETTING_WIRELESS_SECURITY_PSK, args.psk, + NM_SETTING_WIRELESS_SECURITY_PMF, (int) NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED, NULL); if (GPOINTER_TO_INT (args.wep_group_p)) { @@ -7172,6 +7386,7 @@ test_read_bridge_main (void) g_assert_cmpuint (nm_setting_bridge_get_hello_time (s_bridge), ==, 7); g_assert_cmpuint (nm_setting_bridge_get_max_age (s_bridge), ==, 39); g_assert_cmpuint (nm_setting_bridge_get_ageing_time (s_bridge), ==, 235352); + g_assert_cmpuint (nm_setting_bridge_get_group_forward_mask (s_bridge), ==, 24); g_assert (!nm_setting_bridge_get_multicast_snooping (s_bridge)); /* MAC address */ @@ -7217,6 +7432,7 @@ test_write_bridge_main (void) g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, + NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, 19008, NULL); /* IP4 setting */ @@ -9147,8 +9363,8 @@ test_write_unknown (gconstpointer test_data) sv = _svOpenFile (testfile); - svFileSetName_test_only (sv, filename_tmp_1); - svFileSetModified_test_only (sv); + _nmtst_svFileSetName (sv, filename_tmp_1); + _nmtst_svFileSetModified (sv); if (g_str_has_suffix (testfile, "ifcfg-test-write-unknown-4")) { _svGetValue_check (sv, "NAME", "l4x"); @@ -9429,6 +9645,7 @@ 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_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); @@ -9492,6 +9709,7 @@ int main (int argc, char **argv) g_test_add_func (TPATH "wired/read/unkwnown-ethtool-opt", test_read_wired_unknown_ethtool_opt); g_test_add_func (TPATH "wired/write/static", test_write_wired_static); + g_test_add_func (TPATH "wired/write/static-with-generic", test_write_wired_static_with_generic); g_test_add_func (TPATH "wired/write/static-ip6-only", test_write_wired_static_ip6_only); g_test_add_func (TPATH "wired/write-static-routes", test_write_wired_static_routes); g_test_add_func (TPATH "wired/read-write-static-routes-legacy", test_read_write_static_routes_legacy); diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c index c5129fea..ed0a757f 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c +++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c @@ -31,7 +31,7 @@ #include "settings/nm-settings-plugin.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" -#include "nm-setting-metadata.h" +#include "nm-meta-setting.h" #include "nms-ifnet-net-utils.h" #include "nms-ifnet-wpa-parser.h" diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection.c b/src/settings/plugins/ifnet/nms-ifnet-connection.c index ba87d46c..5dbb124c 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-connection.c +++ b/src/settings/plugins/ifnet/nms-ifnet-connection.c @@ -74,89 +74,81 @@ nm_ifnet_connection_get_conn_name (NMIfnetConnection *connection) return NM_IFNET_CONNECTION_GET_PRIVATE (connection)->conn_name; } -static void +static gboolean commit_changes (NMSettingsConnection *connection, + NMConnection *new_connection, NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) + NMConnection **out_reread_connection, + char **out_logmsg_change, + GError **error) { - GError *error = NULL; NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection); - gchar *new_name = NULL; + char *new_name = NULL; gboolean success = FALSE; + gboolean added = FALSE; + + nm_assert (out_reread_connection && !*out_reread_connection); + nm_assert (!out_logmsg_change || !*out_logmsg_change); g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); if (priv->conn_name) { - /* Existing connection; update it */ success = ifnet_update_parsers_by_connection (NM_CONNECTION (connection), priv->conn_name, CONF_NET_FILE, WPA_SUPPLICANT_CONF, &new_name, NULL, - &error); + error); } else { - /* New connection, add it */ + added = TRUE; success = ifnet_add_new_connection (NM_CONNECTION (connection), CONF_NET_FILE, WPA_SUPPLICANT_CONF, &new_name, NULL, - &error); - if (success) - reload_parsers (); + error); } + g_assert (!!success == (new_name != NULL)); if (success) { - /* update connection name */ - g_assert (new_name); g_free (priv->conn_name); priv->conn_name = new_name; - - NM_SETTINGS_CONNECTION_CLASS (nm_ifnet_connection_parent_class)->commit_changes (connection, commit_reason, callback, user_data); - nm_log_info (LOGD_SETTINGS, "Successfully updated %s", priv->conn_name); - } else { - nm_log_warn (LOGD_SETTINGS, "Failed to update %s", - priv->conn_name ? priv->conn_name : - nm_connection_get_id (NM_CONNECTION (connection))); - reload_parsers (); - callback (connection, error, user_data); - g_error_free (error); } + reload_parsers (); + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + + if (success) { + NM_SET_OUT (out_logmsg_change, + g_strdup_printf ("ifcfg-rh: %s %s", + added ? "persist" : "updated", + new_name)); + } + return success; } -static void -do_delete (NMSettingsConnection *connection, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) +static gboolean +delete (NMSettingsConnection *connection, + GError **error) { - GError *error = NULL; NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection); - g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); - /* Only connections which exist in /etc/conf.d/net will have a conn_name */ if (priv->conn_name) { + g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); + if (!ifnet_delete_connection_in_parsers (priv->conn_name, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL)) { nm_log_warn (LOGD_SETTINGS, "Failed to delete %s", priv->conn_name); reload_parsers (); - callback (connection, error, user_data); - g_error_free (error); - g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); - return; + /* let's not return an error. */ } - } - - NM_SETTINGS_CONNECTION_CLASS (nm_ifnet_connection_parent_class)->delete (connection, callback, user_data); - g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + } - nm_log_info (LOGD_SETTINGS, "Successfully deleted %s", - priv->conn_name ? priv->conn_name : - nm_connection_get_id (NM_CONNECTION (connection))); + return TRUE; } /*****************************************************************************/ @@ -222,7 +214,7 @@ nm_ifnet_connection_class_init (NMIfnetConnectionClass * ifnet_connection_class) object_class->finalize = finalize; - settings_class->delete = do_delete; + settings_class->delete = delete; settings_class->commit_changes = commit_changes; signals[IFNET_SETUP_MONITORS] = diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-parser.c b/src/settings/plugins/ifnet/nms-ifnet-net-parser.c index 0007f9cd..d3e47219 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-net-parser.c +++ b/src/settings/plugins/ifnet/nms-ifnet-net-parser.c @@ -58,7 +58,7 @@ add_new_connection_config (const gchar * type, const gchar * name) /* Return existing connection */ if ((new_conn = g_hash_table_lookup (conn_table, name)) != NULL) return new_conn; - new_conn = g_hash_table_new (g_str_hash, g_str_equal); + new_conn = g_hash_table_new (nm_str_hash, g_str_equal); new_name = g_strdup (name); g_hash_table_insert (new_conn, g_strdup ("name"), new_name); g_hash_table_insert (new_conn, g_strdup ("type"), g_strdup (type)); @@ -302,8 +302,8 @@ ifnet_init (gchar * config_file) net_parser_data_changed = FALSE; - conn_table = g_hash_table_new (g_str_hash, g_str_equal); - global_settings_table = g_hash_table_new (g_str_hash, g_str_equal); + conn_table = g_hash_table_new (nm_str_hash, g_str_equal); + global_settings_table = g_hash_table_new (nm_str_hash, g_str_equal); functions_list = NULL; if (g_file_test (config_file, G_FILE_TEST_IS_REGULAR)) diff --git a/src/settings/plugins/ifnet/nms-ifnet-plugin.c b/src/settings/plugins/ifnet/nms-ifnet-plugin.c index 5a6a8ce8..998b04b4 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-plugin.c +++ b/src/settings/plugins/ifnet/nms-ifnet-plugin.c @@ -138,9 +138,9 @@ monitor_file_changes (const char *filename, info->callback = callback; info->user_data = user_data; g_object_weak_ref (G_OBJECT (monitor), (GWeakNotify) g_free, - info); + info); g_signal_connect (monitor, "changed", G_CALLBACK (file_changed), - info); + info); } else { nm_log_warn (LOGD_SETTINGS, "Monitoring %s failed, error: %s", filename, error == NULL ? "nothing" : (*error)->message); @@ -150,34 +150,38 @@ monitor_file_changes (const char *filename, } static void -setup_monitors (NMIfnetConnection * connection, gpointer user_data) +setup_monitors (NMIfnetConnection *connection, gpointer user_data) { SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data); SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); - if (nm_config_get_monitor_connection_files (nm_config_get ())) { - priv->net_monitor = - monitor_file_changes (CONF_NET_FILE, (FileChangedFn) reload_connections, - user_data); - priv->wpa_monitor = - monitor_file_changes (WPA_SUPPLICANT_CONF, (FileChangedFn) reload_connections, - user_data); - } + if (!nm_config_get_monitor_connection_files (nm_config_get ())) + return; + + if (priv->net_monitor || priv->wpa_monitor) + return; + + priv->net_monitor = monitor_file_changes (CONF_NET_FILE, + (FileChangedFn) reload_connections, + user_data); + priv->wpa_monitor = monitor_file_changes (WPA_SUPPLICANT_CONF, + (FileChangedFn) reload_connections, + user_data); } static void -cancel_monitors (NMIfnetConnection * connection, gpointer user_data) +cancel_monitors (NMIfnetConnection *connection, gpointer user_data) { SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data); SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); if (priv->net_monitor) { g_file_monitor_cancel (priv->net_monitor); - g_object_unref (priv->net_monitor); + g_clear_object (&priv->net_monitor); } if (priv->wpa_monitor) { g_file_monitor_cancel (priv->wpa_monitor); - g_object_unref (priv->wpa_monitor); + g_clear_object (&priv->wpa_monitor); } } @@ -226,7 +230,7 @@ reload_connections (NMSettingsPlugin *config) NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH, FALSE); - new_connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); + new_connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); /* Reread on-disk data and refresh in-memory connections from it */ conn_names = ifnet_get_connection_names (); @@ -321,11 +325,11 @@ add_connection (NMSettingsPlugin *config, * asked to write it to disk. */ if (!ifnet_can_write_connection (source, error)) - return NULL; + goto out; if (save_to_disk) { if (!ifnet_add_new_connection (source, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL, NULL, error)) - return NULL; + goto out; reload_connections (config); new = g_hash_table_lookup (priv->connections, nm_connection_get_uuid (source)); } else { @@ -337,6 +341,11 @@ add_connection (NMSettingsPlugin *config, } } +out: + if (!new && error && !*error) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "The ifnet plugin cannot add the connection (unknown error)."); + } return (NMSettingsConnection *) new; } @@ -439,7 +448,7 @@ init (NMSettingsPlugin *config) nm_log_info (LOGD_SETTINGS, "Initializing!"); - priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); priv->unmanaged_well_known = !is_managed_plugin (); nm_log_info (LOGD_SETTINGS, "management mode: %s", priv->unmanaged_well_known ? "unmanaged" : "managed"); diff --git a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c index 61e4da7c..2b62e886 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c +++ b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c @@ -26,6 +26,7 @@ #include <string.h> #include <stdlib.h> +#include "nm-utils/nm-hash-utils.h" #include "settings/nm-settings-plugin.h" #include "nms-ifnet-net-parser.h" @@ -270,8 +271,8 @@ wpa_parser_init (const char *wpa_supplicant_conf) gboolean complete = FALSE; wpa_parser_data_changed = FALSE; - wsec_table = g_hash_table_new (g_str_hash, g_str_equal); - wsec_global_table = g_hash_table_new (g_str_hash, g_str_equal); + wsec_table = g_hash_table_new (nm_str_hash, g_str_equal); + wsec_global_table = g_hash_table_new (nm_str_hash, g_str_equal); if (g_file_test (wpa_supplicant_conf, G_FILE_TEST_IS_REGULAR)) channel = @@ -292,7 +293,7 @@ wpa_parser_init (const char *wpa_supplicant_conf) continue; } else { GHashTable *network = - g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_new (nm_str_hash, g_str_equal); do { gchar *quote_start, *quote_end = NULL, *comment; @@ -512,7 +513,7 @@ wpa_add_security (const char *ssid) return TRUE; else { GHashTable *security = - g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_new (nm_str_hash, g_str_equal); gchar *ssid_i; nm_log_info (LOGD_SETTINGS, "Adding security for %s", ssid); diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c index 189b6e69..0928772a 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -139,7 +139,10 @@ bind_device_to_connection (SettingsPluginIfupdown *self, g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, address, NULL); } - nm_settings_connection_commit_changes (NM_SETTINGS_CONNECTION (exported), NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, NULL, NULL); + nm_settings_connection_commit_changes (NM_SETTINGS_CONNECTION (exported), + NULL, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + NULL); } static void @@ -338,16 +341,16 @@ init (NMSettingsPlugin *config) const char *block_name; NMIfupdownConnection *connection; - auto_ifaces = g_hash_table_new (g_str_hash, g_str_equal); + auto_ifaces = g_hash_table_new (nm_str_hash, g_str_equal); if(!priv->connections) - priv->connections = g_hash_table_new (g_str_hash, g_str_equal); + priv->connections = g_hash_table_new (nm_str_hash, g_str_equal); if(!priv->kernel_ifaces) - priv->kernel_ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _udev_device_unref); + priv->kernel_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _udev_device_unref); if(!priv->eni_ifaces) - priv->eni_ifaces = g_hash_table_new (g_str_hash, g_str_equal); + priv->eni_ifaces = g_hash_table_new (nm_str_hash, g_str_equal); nm_log_info (LOGD_SETTINGS, "init!"); @@ -409,7 +412,7 @@ init (NMSettingsPlugin *config) exported = g_hash_table_lookup (priv->connections, block->name); if (exported) { nm_log_info (LOGD_SETTINGS, "deleting %s from connections", block->name); - nm_settings_connection_delete (NM_SETTINGS_CONNECTION (exported), NULL, NULL); + nm_settings_connection_delete (NM_SETTINGS_CONNECTION (exported), NULL); g_hash_table_remove (priv->connections, block->name); } diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c index bd07d263..300aa9f7 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-connection.c +++ b/src/settings/plugins/keyfile/nms-keyfile-connection.c @@ -50,29 +50,30 @@ G_DEFINE_TYPE (NMSKeyfileConnection, nms_keyfile_connection, NM_TYPE_SETTINGS_CO /*****************************************************************************/ -static void +static gboolean commit_changes (NMSettingsConnection *connection, + NMConnection *new_connection, NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) + NMConnection **out_reread_connection, + char **out_logmsg_change, + GError **error) { - char *path = NULL; - GError *error = NULL; + gs_free char *path = NULL; gs_unref_object NMConnection *reread = NULL; gboolean reread_same = FALSE; - if (!nms_keyfile_writer_connection (NM_CONNECTION (connection), + nm_assert (out_reread_connection && !*out_reread_connection); + nm_assert (!out_logmsg_change || !*out_logmsg_change); + + if (!nms_keyfile_writer_connection (new_connection ?: NM_CONNECTION (connection), 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)) { - callback (connection, error, user_data); - g_clear_error (&error); - return; - } + error)) + return FALSE; /* Update the filename if it changed */ if ( path @@ -81,52 +82,37 @@ commit_changes (NMSettingsConnection *connection, nm_settings_connection_set_filename (connection, path); if (old_path) { - nm_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and rename from \"%s\"", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection), - 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_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and persist connection", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection)); + 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_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT, - NMS_KEYFILE_CONNECTION_LOG_ARG (connection)); - } - - if (reread && !reread_same) { - gs_free_error GError *local = NULL; - - if (!nm_settings_connection_replace_settings (connection, reread, FALSE, "update-during-write", &local)) { - nm_log_warn (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" after persisting connection failed: %s", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection), local->message); - } else { - nm_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" after persisting connection", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection)); - } + NM_SET_OUT (out_logmsg_change, + g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT, + NMS_KEYFILE_CONNECTION_LOG_ARG (connection))); } - g_free (path); + if (reread && !reread_same) + *out_reread_connection = g_steal_pointer (&reread); - NM_SETTINGS_CONNECTION_CLASS (nms_keyfile_connection_parent_class)->commit_changes (connection, - commit_reason, - callback, - user_data); + return TRUE; } -static void -do_delete (NMSettingsConnection *connection, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) +static gboolean +delete (NMSettingsConnection *connection, + GError **error) { const char *path; path = nm_settings_connection_get_filename (connection); if (path) g_unlink (path); - - NM_SETTINGS_CONNECTION_CLASS (nms_keyfile_connection_parent_class)->delete (connection, - callback, - user_data); + return TRUE; } /*****************************************************************************/ @@ -192,5 +178,5 @@ nms_keyfile_connection_class_init (NMSKeyfileConnectionClass *keyfile_connection NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (keyfile_connection_class); settings_class->commit_changes = commit_changes; - settings_class->delete = do_delete; + settings_class->delete = delete; } diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index 4af80142..ee4db320 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -378,7 +378,7 @@ _paths_from_connections (GHashTable *connections) { GHashTableIter iter; NMSKeyfileConnection *connection; - GHashTable *paths = g_hash_table_new (g_str_hash, g_str_equal); + GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); g_hash_table_iter_init (&iter, connections); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { @@ -588,7 +588,7 @@ 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 (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c index 92ed2849..270a217e 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.c +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c @@ -213,6 +213,9 @@ _internal_write_connection (NMConnection *connection, if (!data) return FALSE; + 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. */ diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index d9da5317..2584a722 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -312,11 +312,11 @@ test_read_valid_wired_connection (void) check_ip_route (s_ip4, 3, "1.1.1.3", 13, NULL, -1); check_ip_route (s_ip4, 4, "1.1.1.4", 14, "2.2.2.4", -1); check_ip_route (s_ip4, 5, "1.1.1.5", 15, "2.2.2.5", -1); - check_ip_route (s_ip4, 6, "1.1.1.6", 16, "2.2.2.6", -1); + check_ip_route (s_ip4, 6, "1.1.1.6", 16, "2.2.2.6", 0); check_ip_route (s_ip4, 7, "1.1.1.7", 17, NULL, -1); check_ip_route (s_ip4, 8, "1.1.1.8", 18, NULL, -1); - check_ip_route (s_ip4, 9, "1.1.1.9", 19, NULL, -1); - check_ip_route (s_ip4, 10, "1.1.1.10", 20, NULL, -1); + check_ip_route (s_ip4, 9, "1.1.1.9", 19, NULL, 0); + check_ip_route (s_ip4, 10, "1.1.1.10", 20, NULL, 0); check_ip_route (s_ip4, 11, "1.1.1.11", 21, NULL, 21); /* Route attributes */ |