summary refs log tree commit diff
path: root/src/core/settings/nm-settings.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/settings/nm-settings.c')
-rw-r--r--src/core/settings/nm-settings.c250
1 files changed, 191 insertions, 59 deletions
diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c
index 3d31fb8b..f9f98de7 100644
--- a/src/core/settings/nm-settings.c
+++ b/src/core/settings/nm-settings.c
@@ -20,9 +20,10 @@
     #include <selinux/selinux.h>
 #endif
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
-#include "nm-glib-aux/nm-keyfile-aux.h"
-#include "nm-keyfile-internal.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-keyfile-aux.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 #include "nm-dbus-interface.h"
 #include "nm-connection.h"
 #include "nm-setting-8021x.h"
@@ -44,17 +45,17 @@
 #include "nm-setting-proxy.h"
 #include "nm-setting-bond.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
-#include "nm-std-aux/c-list-util.h"
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-std-aux/c-list-util.h"
+#include "libnm-glib-aux/nm-c-list.h"
 #include "nm-dbus-object.h"
 #include "devices/nm-device-ethernet.h"
 #include "nm-settings-connection.h"
 #include "nm-settings-plugin.h"
 #include "nm-dbus-manager.h"
 #include "nm-auth-utils.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-session-monitor.h"
 #include "plugins/keyfile/nms-keyfile-plugin.h"
 #include "plugins/keyfile/nms-keyfile-storage.h"
@@ -139,7 +140,7 @@ nm_assert_storage_data_lst(CList *head)
         u = nm_settings_storage_get_uuid(sd->storage);
         if (!uuid) {
             uuid = u;
-            nm_assert(nm_utils_is_uuid(uuid));
+            nm_assert(nm_uuid_is_normalized(uuid));
         } else
             nm_assert(nm_streq0(uuid, u));
     }
@@ -182,7 +183,7 @@ _sett_conn_entry_new(const char *uuid)
     SettConnEntry *sett_conn_entry;
     gsize          l_p_1;
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     l_p_1 = strlen(uuid) + 1;
 
@@ -370,6 +371,7 @@ typedef struct {
     CList connections_lst_head;
 
     NMSettingsConnection **connections_cached_list;
+    NMSettingsConnection **connections_cached_list_sorted_by_autoconnect_priority;
 
     GSList *unmanaged_specs;
     GSList *unrecognized_specs;
@@ -386,8 +388,17 @@ typedef struct {
     guint kf_db_flush_idle_id_timestamps;
     guint kf_db_flush_idle_id_seen_bssids;
 
+    bool kf_db_pruned_timestamps;
+    bool kf_db_pruned_seen_bssid;
+
     bool started : 1;
 
+    /* Whether NMSettingsConnections changed in a way that affects the comparison
+     * with nm_settings_connection_cmp_autoconnect_priority_with_data(). In that case,
+     * we may need to re-sort the connections_cached_list_sorted_by_autoconnect_priority
+     * list. */
+    bool sorted_by_autoconnect_priority_maybe_changed : 1;
+
 } NMSettingsPrivate;
 
 struct _NMSettings {
@@ -487,6 +498,8 @@ _startup_complete_check_is_ready(NMSettings *          self,
     conn = nm_settings_connection_get_connection(sett_conn);
 
     nm_manager_for_each_device (priv->manager, device, tmp_lst) {
+        gs_free_error GError *error = NULL;
+
         if (!nm_device_is_real(device))
             continue;
 
@@ -497,7 +510,13 @@ _startup_complete_check_is_ready(NMSettings *          self,
             continue;
         }
 
-        if (!nm_device_check_connection_compatible(device, conn, NULL))
+        /* Check that device is compatible with the device. We are also happy
+         * with a device compatible but for which the connection is disallowed
+         * by NM configuration. */
+        if (!nm_device_check_connection_compatible(device, conn, &error)
+            && !g_error_matches(error,
+                                NM_UTILS_ERROR,
+                                NM_UTILS_ERROR_CONNECTION_AVAILABLE_DISALLOWED))
             continue;
 
         return TRUE;
@@ -1433,7 +1452,8 @@ _plugin_connections_reload(NMSettings *self)
         NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
         TRUE,
         NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
-            | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET);
 
     for (iter = priv->plugins; iter; iter = iter->next)
         nm_settings_plugin_load_connections_done(iter->data);
@@ -1460,7 +1480,7 @@ _add_connection_to_first_plugin(NMSettings *                 self,
 
     uuid = nm_connection_get_uuid(new_connection);
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     for (iter = priv->plugins; iter; iter = iter->next) {
         NMSettingsPlugin *plugin                               = NM_SETTINGS_PLUGIN(iter->data);
@@ -1541,9 +1561,7 @@ _add_connection_to_first_plugin(NMSettings *                 self,
         }
 
         agent_owned_secrets =
-            nm_connection_to_dbus(new_connection,
-                                  NM_CONNECTION_SERIALIZE_ONLY_SECRETS
-                                      | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+            nm_connection_to_dbus(new_connection, NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
         connection_to_add_real =
             _connection_changed_normalize_connection(storage,
                                                      connection_to_add,
@@ -1912,7 +1930,8 @@ again_delete_tombstone:
         _NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK,
         FALSE,
         NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
-            | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET
             | (NM_FLAGS_HAS(add_reason, NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT)
                    ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT
                    : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE));
@@ -2218,9 +2237,7 @@ nm_settings_update_connection(NMSettings *                     self,
             nm_assert(nm_streq(uuid, nm_settings_storage_get_uuid(new_storage)));
 
             agent_owned_secrets =
-                nm_connection_to_dbus(connection,
-                                      NM_CONNECTION_SERIALIZE_ONLY_SECRETS
-                                          | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+                nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
             new_connection_real = _connection_changed_normalize_connection(new_storage,
                                                                            new_connection,
                                                                            agent_owned_secrets,
@@ -2297,7 +2314,7 @@ nm_settings_delete_connection(NMSettings *          self,
     nm_assert(NM_IS_SETTINGS_STORAGE(cur_storage));
 
     uuid = nm_settings_storage_get_uuid(cur_storage);
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     sett_conn_entry = _sett_conn_entries_get(self, uuid);
 
@@ -2691,9 +2708,9 @@ impl_settings_add_connection2(NMDBusObject *                     obj,
     g_variant_get(parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
 
     if (NM_FLAGS_ANY(flags_u,
-                     ~((guint32)(NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK
-                                 | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY
-                                 | NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)))) {
+                     ~((guint32) (NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK
+                                  | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY
+                                  | NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)))) {
         g_dbus_method_invocation_take_error(invocation,
                                             g_error_new_literal(NM_SETTINGS_ERROR,
                                                                 NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
@@ -2814,7 +2831,8 @@ impl_settings_load_connections(NMDBusObject *                     obj,
             NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
             TRUE,
             NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
-                | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET);
 
         for (iter = priv->plugins; iter; iter = iter->next)
             nm_settings_plugin_load_connections_done(iter->data);
@@ -2832,7 +2850,7 @@ impl_settings_load_connections(NMDBusObject *                     obj,
 
     g_dbus_method_invocation_return_value(invocation,
                                           g_variant_new("(b^as)",
-                                                        (gboolean)(!failures),
+                                                        (gboolean) (!failures),
                                                         failures
                                                             ? (const char **) failures->pdata
                                                             : NM_PTRARRAY_EMPTY(const char *)));
@@ -2870,25 +2888,48 @@ impl_settings_reload_connections(NMDBusObject *                     obj,
 
 /*****************************************************************************/
 
+void
+_nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(NMSettings *self)
+{
+    NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self);
+
+    priv->sorted_by_autoconnect_priority_maybe_changed = TRUE;
+}
+
 static void
 _clear_connections_cached_list(NMSettingsPrivate *priv)
 {
-    if (!priv->connections_cached_list)
-        return;
+    if (priv->connections_cached_list) {
+        nm_assert(priv->connections_len == NM_PTRARRAY_LEN(priv->connections_cached_list));
+
+#if NM_MORE_ASSERTS
+        /* set the pointer to a bogus value. This makes it more apparent
+         * if somebody has a reference to the cached list and still uses
+         * it. That is a bug, this code just tries to make it blow up
+         * more eagerly. */
+        memset(priv->connections_cached_list,
+               0x43,
+               sizeof(NMSettingsConnection *) * (priv->connections_len + 1));
+#endif
 
-    nm_assert(priv->connections_len == NM_PTRARRAY_LEN(priv->connections_cached_list));
+        nm_clear_g_free(&priv->connections_cached_list);
+    }
+    if (priv->connections_cached_list_sorted_by_autoconnect_priority) {
+        nm_assert(priv->connections_len
+                  == NM_PTRARRAY_LEN(priv->connections_cached_list_sorted_by_autoconnect_priority));
 
 #if NM_MORE_ASSERTS
-    /* set the pointer to a bogus value. This makes it more apparent
-     * if somebody has a reference to the cached list and still uses
-     * it. That is a bug, this code just tries to make it blow up
-     * more eagerly. */
-    memset(priv->connections_cached_list,
-           0x43,
-           sizeof(NMSettingsConnection *) * (priv->connections_len + 1));
+        /* set the pointer to a bogus value. This makes it more apparent
+         * if somebody has a reference to the cached list and still uses
+         * it. That is a bug, this code just tries to make it blow up
+         * more eagerly. */
+        memset(priv->connections_cached_list_sorted_by_autoconnect_priority,
+               0x42,
+               sizeof(NMSettingsConnection *) * (priv->connections_len + 1));
 #endif
 
-    nm_clear_g_free(&priv->connections_cached_list);
+        nm_clear_g_free(&priv->connections_cached_list_sorted_by_autoconnect_priority);
+    }
 }
 
 static void
@@ -3027,6 +3068,65 @@ nm_settings_get_connections(NMSettings *self, guint *out_len)
     return priv->connections_cached_list;
 }
 
+NMSettingsConnection *const *
+nm_settings_get_connections_sorted_by_autoconnect_priority(NMSettings *self, guint *out_len)
+{
+    NMSettingsPrivate *priv;
+    gboolean           needs_sort = FALSE;
+
+    g_return_val_if_fail(NM_IS_SETTINGS(self), NULL);
+
+    priv = NM_SETTINGS_GET_PRIVATE(self);
+
+    nm_assert(priv->connections_len == c_list_length(&priv->connections_lst_head));
+    nm_assert(
+        !priv->connections_cached_list_sorted_by_autoconnect_priority
+        || (priv->connections_len
+            == NM_PTRARRAY_LEN(priv->connections_cached_list_sorted_by_autoconnect_priority)));
+
+    if (!priv->connections_cached_list_sorted_by_autoconnect_priority) {
+        NMSettingsConnection *const *list_cached;
+        guint                        len;
+
+        list_cached = nm_settings_get_connections(self, &len);
+        priv->connections_cached_list_sorted_by_autoconnect_priority =
+            nm_memdup(list_cached, sizeof(NMSettingsConnection *) * (len + 1));
+        needs_sort = (len > 1);
+    } else if (priv->sorted_by_autoconnect_priority_maybe_changed) {
+        if (!nm_utils_ptrarray_is_sorted(
+                (gconstpointer *) priv->connections_cached_list_sorted_by_autoconnect_priority,
+                priv->connections_len,
+                FALSE,
+                nm_settings_connection_cmp_autoconnect_priority_with_data,
+                NULL)) {
+            /* We cache the sorted list, but we don't monitor all entries whether they
+             * get modified to invalidate the sort order. So every time we have to check
+             * whether the sort order is still correct. The vast majority of the time it
+             * is, and this check is faster than sorting anew. */
+            needs_sort = TRUE;
+        }
+    } else {
+        nm_assert(nm_utils_ptrarray_is_sorted(
+            (gconstpointer *) priv->connections_cached_list_sorted_by_autoconnect_priority,
+            priv->connections_len,
+            TRUE,
+            nm_settings_connection_cmp_autoconnect_priority_with_data,
+            NULL));
+    }
+
+    priv->sorted_by_autoconnect_priority_maybe_changed = FALSE;
+    if (needs_sort) {
+        g_qsort_with_data(priv->connections_cached_list_sorted_by_autoconnect_priority,
+                          priv->connections_len,
+                          sizeof(NMSettingsConnection *),
+                          nm_settings_connection_cmp_autoconnect_priority_p_with_data,
+                          NULL);
+    }
+
+    NM_SET_OUT(out_len, priv->connections_len);
+    return priv->connections_cached_list_sorted_by_autoconnect_priority;
+}
+
 /**
  * nm_settings_get_connections_clone:
  * @self: the #NMSetting
@@ -3058,9 +3158,13 @@ nm_settings_get_connections_clone(NMSettings *                   self,
 
     g_return_val_if_fail(NM_IS_SETTINGS(self), NULL);
 
-    list_cached = nm_settings_get_connections(self, &len);
+    if (sort_compare_func == nm_settings_connection_cmp_autoconnect_priority_p_with_data) {
+        list_cached       = nm_settings_get_connections_sorted_by_autoconnect_priority(self, &len);
+        sort_compare_func = NULL;
+    } else
+        list_cached = nm_settings_get_connections(self, &len);
 
-#if NM_MORE_ASSERTS
+#if NM_MORE_ASSERTS > 10
     nm_assert(list_cached);
     for (i = 0; i < len; i++)
         nm_assert(NM_IS_SETTINGS_CONNECTION(list_cached[i]));
@@ -3204,7 +3308,7 @@ add_plugin_load_file(NMSettings *self, const char *pname, GError **error)
 
     /* errors after this point are fatal, because we loaded the shared library already. */
 
-    if (!g_module_symbol(module, "nm_settings_plugin_factory", (gpointer)(&factory_func))) {
+    if (!g_module_symbol(module, "nm_settings_plugin_factory", (gpointer) (&factory_func))) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
                     NM_SETTINGS_ERROR_FAILED,
@@ -3591,6 +3695,41 @@ again:
 
 /*****************************************************************************/
 
+static gboolean
+_kf_db_prune_predicate(const char *uuid, gpointer user_data)
+{
+    return !!nm_settings_get_connection_by_uuid(user_data, uuid);
+}
+
+static void
+_kf_db_to_file(NMSettings *self, gboolean is_timestamps, gboolean force_write)
+{
+    NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self);
+    NMKeyFileDB *      kf_db;
+    bool *             p_kf_db_pruned;
+
+    if (is_timestamps) {
+        kf_db          = priv->kf_db_timestamps;
+        p_kf_db_pruned = &priv->kf_db_pruned_timestamps;
+    } else {
+        kf_db          = priv->kf_db_seen_bssids;
+        p_kf_db_pruned = &priv->kf_db_pruned_seen_bssid;
+    }
+
+    if (!*p_kf_db_pruned) {
+        /* we only prune the DB once, because afterwards every
+         * add/remove of an connection will lead to a direct update. */
+        *p_kf_db_pruned = TRUE;
+        nm_key_file_db_prune(kf_db, _kf_db_prune_predicate, self);
+
+        /* once we also go over the directory, and see whether we
+         * have any left over temporary files to delete. */
+        nm_key_file_db_prune_tmp_files(kf_db);
+    }
+
+    nm_key_file_db_to_file(kf_db, force_write);
+}
+
 G_GNUC_PRINTF(4, 5)
 static void
 _kf_db_log_fcn(NMKeyFileDB *kf_db, int syslog_level, gpointer user_data, const char *fmt, ...)
@@ -3639,7 +3778,7 @@ _kf_db_got_dirty_flush(NMSettings *self, gboolean is_timestamps)
     }
 
     if (nm_key_file_db_is_dirty(kf_db))
-        nm_key_file_db_to_file(kf_db, FALSE);
+        _kf_db_to_file(self, is_timestamps, FALSE);
     else {
         _LOGT("[%s-keyfile]: skip saving changes to \"%s\"",
               prefix,
@@ -3692,15 +3831,10 @@ _kf_db_got_dirty_fcn(NMKeyFileDB *kf_db, gpointer user_data)
 void
 nm_settings_kf_db_write(NMSettings *self)
 {
-    NMSettingsPrivate *priv;
-
     g_return_if_fail(NM_IS_SETTINGS(self));
 
-    priv = NM_SETTINGS_GET_PRIVATE(self);
-    if (priv->kf_db_timestamps)
-        nm_key_file_db_to_file(priv->kf_db_timestamps, TRUE);
-    if (priv->kf_db_seen_bssids)
-        nm_key_file_db_to_file(priv->kf_db_seen_bssids, TRUE);
+    _kf_db_to_file(self, TRUE, TRUE);
+    _kf_db_to_file(self, FALSE, TRUE);
 }
 
 /*****************************************************************************/
@@ -3938,8 +4072,8 @@ finalize(GObject *object)
 
     nm_clear_g_source(&priv->kf_db_flush_idle_id_timestamps);
     nm_clear_g_source(&priv->kf_db_flush_idle_id_seen_bssids);
-    nm_key_file_db_to_file(priv->kf_db_timestamps, FALSE);
-    nm_key_file_db_to_file(priv->kf_db_seen_bssids, FALSE);
+    _kf_db_to_file(self, TRUE, FALSE);
+    _kf_db_to_file(self, FALSE, FALSE);
     nm_key_file_db_destroy(priv->kf_db_timestamps);
     nm_key_file_db_destroy(priv->kf_db_seen_bssids);
 
@@ -4027,18 +4161,16 @@ static const NMDBusInterfaceInfoExtended interface_info_settings = {
                     .in_args =
                         NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("hostname", "s"), ), ),
                 .handle = impl_settings_save_hostname, ), ),
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy,
-                                                &signal_info_new_connection,
+        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&signal_info_new_connection,
                                                 &signal_info_connection_removed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Connections",
-                                                             "ao",
-                                                             NM_SETTINGS_CONNECTIONS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Hostname", "s", NM_SETTINGS_HOSTNAME),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("CanModify",
-                                                             "b",
-                                                             NM_SETTINGS_CAN_MODIFY), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Connections",
+                                                           "ao",
+                                                           NM_SETTINGS_CONNECTIONS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Hostname", "s", NM_SETTINGS_HOSTNAME),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("CanModify",
+                                                           "b",
+                                                           NM_SETTINGS_CAN_MODIFY), ), ),
 };
 
 static void