core: merge branch 'th/cache-state-keyfiles'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/134
This commit is contained in:
Thomas Haller 2019-05-07 16:42:15 +02:00
commit 7a5bf59e5f
18 changed files with 794 additions and 231 deletions

View file

@ -347,6 +347,8 @@ shared_nm_glib_aux_libnm_glib_aux_la_SOURCES = \
shared/nm-glib-aux/nm-io-utils.c \
shared/nm-glib-aux/nm-io-utils.h \
shared/nm-glib-aux/nm-jansson.h \
shared/nm-glib-aux/nm-keyfile-aux.c \
shared/nm-glib-aux/nm-keyfile-aux.h \
shared/nm-glib-aux/nm-logging-fwd.h \
shared/nm-glib-aux/nm-macros-internal.h \
shared/nm-glib-aux/nm-obj.h \

View file

@ -415,7 +415,7 @@ _keyfile_key_encode (const char *name,
/* See g_key_file_is_key_name().
*
* GKeyfile allows all UTF-8 characters (even non-well formed sequences),
* GKeyFile allows all UTF-8 characters (even non-well formed sequences),
* except:
* - no empty keys
* - no leading/trailing ' '

View file

@ -144,6 +144,7 @@ shared_nm_glib_aux = static_library(
'nm-glib-aux/nm-errno.c',
'nm-glib-aux/nm-hash-utils.c',
'nm-glib-aux/nm-io-utils.c',
'nm-glib-aux/nm-keyfile-aux.c',
'nm-glib-aux/nm-random-utils.c',
'nm-glib-aux/nm-secret-utils.c',
'nm-glib-aux/nm-shared-utils.c',

View file

@ -0,0 +1,413 @@
/* NetworkManager -- Network link manager
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* (C) Copyright 2019 Red Hat, Inc.
*/
#include "nm-default.h"
#include "nm-keyfile-aux.h"
#include <syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "nm-io-utils.h"
/*****************************************************************************/
struct _NMKeyFileDB {
NMKeyFileDBLogFcn log_fcn;
NMKeyFileDBGotDirtyFcn got_dirty_fcn;
gpointer user_data;
const char *group_name;
GKeyFile *kf;
guint ref_count;
bool is_started:1;
bool dirty:1;
bool destroyed:1;
char filename[];
};
#define _NMLOG(self, \
syslog_level, \
fmt, \
...) \
G_STMT_START { \
NMKeyFileDB *_self = (self); \
\
nm_assert (_self); \
nm_assert (!_self->destroyed); \
\
if (_self->log_fcn) { \
_self->log_fcn (_self, \
(syslog_level), \
_self->user_data, \
""fmt"", \
##__VA_ARGS__); \
}; \
} G_STMT_END
#define _LOGD(...) _NMLOG (self, LOG_DEBUG, __VA_ARGS__)
static gboolean
_IS_KEY_FILE_DB (NMKeyFileDB *self, gboolean require_is_started, gboolean allow_destroyed)
{
if (self == NULL)
return FALSE;
if (self->ref_count <= 0) {
nm_assert_not_reached ();
return FALSE;
}
if ( require_is_started
&& !self->is_started)
return FALSE;
if ( !allow_destroyed
&& self->destroyed)
return FALSE;
return TRUE;
}
/*****************************************************************************/
NMKeyFileDB *
nm_key_file_db_new (const char *filename,
const char *group_name,
NMKeyFileDBLogFcn log_fcn,
NMKeyFileDBGotDirtyFcn got_dirty_fcn,
gpointer user_data)
{
NMKeyFileDB *self;
gsize l_filename;
gsize l_group;
g_return_val_if_fail (filename && filename[0], NULL);
g_return_val_if_fail (group_name && group_name[0], NULL);
l_filename = strlen (filename);
l_group = strlen (group_name);
self = g_malloc0 (sizeof (NMKeyFileDB) + l_filename + 1 + l_group + 1);
self->ref_count = 1;
self->log_fcn = log_fcn;
self->got_dirty_fcn = got_dirty_fcn;
self->user_data = user_data;
self->kf = g_key_file_new ();
g_key_file_set_list_separator (self->kf, ',');
memcpy (self->filename, filename, l_filename + 1);
self->group_name = &self->filename[l_filename + 1];
memcpy ((char *) self->group_name, group_name, l_group + 1);
return self;
}
NMKeyFileDB *
nm_key_file_db_ref (NMKeyFileDB *self)
{
if (!self)
return NULL;
g_return_val_if_fail (_IS_KEY_FILE_DB (self, FALSE, TRUE), NULL);
nm_assert (self->ref_count <= G_MAXUINT);
self->ref_count++;
return self;
}
void
nm_key_file_db_unref (NMKeyFileDB *self)
{
if (!self)
return;
g_return_if_fail (_IS_KEY_FILE_DB (self, FALSE, TRUE));
if (--self->ref_count > 0)
return;
g_key_file_unref (self->kf);
g_free (self);
}
/* destroy() is like unref, but it also makes the instance unusable.
* All changes afterwards fail with an assertion.
*
* The point is that NMKeyFileDB is ref-counted in principle. But there
* is a primary owner who also provides the log_fcn().
*
* When the primary owner goes out of scope and gives up the reference, it does
* not want to receive any log notifications anymore.
*
* The way NMKeyFileDB is intended to be used is in a very strict context:
* NMSettings owns the NMKeyFileDB instance and receives logging notifications.
* It's also the last one to persist the data to disk. Afterwards, no other user
* is supposed to be around and do anything with NMKeyFileDB. But since NMKeyFileDB
* is ref-counted it's hard to ensure that this is truly honored. So we start
* asserting at that point.
*/
void
nm_key_file_db_destroy (NMKeyFileDB *self)
{
if (!self)
return;
g_return_if_fail (_IS_KEY_FILE_DB (self, FALSE, FALSE));
g_return_if_fail (!self->destroyed);
self->destroyed = TRUE;
nm_key_file_db_unref (self);
}
/*****************************************************************************/
/* nm_key_file_db_start() is supposed to be called right away, after creating the
* instance.
*
* It's not done as separate step after nm_key_file_db_new(), because we want to log,
* and the log_fcn returns the self pointer (which we should not expose before
* nm_key_file_db_new() returns. */
void
nm_key_file_db_start (NMKeyFileDB *self)
{
int r;
gs_free char *contents = NULL;
gsize contents_len;
gs_free_error GError *error = NULL;
g_return_if_fail (_IS_KEY_FILE_DB (self, FALSE, FALSE));
g_return_if_fail (!self->is_started);
self->is_started = TRUE;
r = nm_utils_file_get_contents (-1,
self->filename,
20*1024*1024,
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
&contents,
&contents_len,
&error);
if (r < 0) {
_LOGD ("failed to read \"%s\": %s", self->filename, error->message);
return;
}
if (!g_key_file_load_from_data (self->kf,
contents,
contents_len,
G_KEY_FILE_KEEP_COMMENTS,
&error)) {
_LOGD ("failed to load keyfile \"%s\": %s", self->filename, error->message);
return;
}
_LOGD ("loaded keyfile-db for \"%s\"", self->filename);
}
/*****************************************************************************/
const char *
nm_key_file_db_get_filename (NMKeyFileDB *self)
{
g_return_val_if_fail (_IS_KEY_FILE_DB (self, FALSE, TRUE), NULL);
return self->filename;
}
gboolean
nm_key_file_db_is_dirty (NMKeyFileDB *self)
{
g_return_val_if_fail (_IS_KEY_FILE_DB (self, FALSE, TRUE), FALSE);
return self->dirty;
}
/*****************************************************************************/
char *
nm_key_file_db_get_value (NMKeyFileDB *self,
const char *key)
{
g_return_val_if_fail (_IS_KEY_FILE_DB (self, TRUE, TRUE), NULL);
return g_key_file_get_value (self->kf, self->group_name, key, NULL);
}
char **
nm_key_file_db_get_string_list (NMKeyFileDB *self,
const char *key,
gsize *out_len)
{
g_return_val_if_fail (_IS_KEY_FILE_DB (self, TRUE, TRUE), NULL);
return g_key_file_get_string_list (self->kf, self->group_name, key, out_len, NULL);
}
/*****************************************************************************/
static void
_got_dirty (NMKeyFileDB *self,
const char *key)
{
nm_assert (_IS_KEY_FILE_DB (self, TRUE, FALSE));
nm_assert (!self->dirty);
_LOGD ("updated entry for %s.%s", self->group_name, key);
self->dirty = TRUE;
if (self->got_dirty_fcn)
self->got_dirty_fcn (self, self->user_data);
}
/*****************************************************************************/
void
nm_key_file_db_remove_key (NMKeyFileDB *self,
const char *key)
{
gboolean got_dirty = FALSE;
g_return_if_fail (_IS_KEY_FILE_DB (self, TRUE, FALSE));
if (!key)
return;
if (!self->dirty) {
gs_free_error GError *error = NULL;
g_key_file_has_key (self->kf, self->group_name, key, &error);
got_dirty = (error != NULL);
}
g_key_file_remove_key (self->kf, self->group_name, key, NULL);
if (got_dirty)
_got_dirty (self, key);
}
void
nm_key_file_db_set_value (NMKeyFileDB *self,
const char *key,
const char *value)
{
gs_free char *old_value = NULL;
gboolean got_dirty = FALSE;
g_return_if_fail (_IS_KEY_FILE_DB (self, TRUE, FALSE));
g_return_if_fail (key);
if (!value) {
nm_key_file_db_remove_key (self, key);
return;
}
if (!self->dirty) {
gs_free_error GError *error = NULL;
old_value = g_key_file_get_value (self->kf, self->group_name, key, &error);
if (error)
got_dirty = TRUE;
}
g_key_file_set_value (self->kf, self->group_name, key, value);
if ( !self->dirty
&& !got_dirty) {
gs_free_error GError *error = NULL;
gs_free char *new_value = NULL;
new_value = g_key_file_get_value (self->kf, self->group_name, key, &error);
if ( error
|| !new_value
|| !nm_streq0 (old_value, new_value))
got_dirty = TRUE;
}
if (got_dirty)
_got_dirty (self, key);
}
void
nm_key_file_db_set_string_list (NMKeyFileDB *self,
const char *key,
const char *const*value,
gssize len)
{
gs_free char *old_value = NULL;
gboolean got_dirty = FALSE;;
g_return_if_fail (_IS_KEY_FILE_DB (self, TRUE, FALSE));
g_return_if_fail (key);
if (!value) {
nm_key_file_db_remove_key (self, key);
return;
}
if (!self->dirty) {
gs_free_error GError *error = NULL;
old_value = g_key_file_get_value (self->kf, self->group_name, key, &error);
if (error)
got_dirty = TRUE;
}
if (len < 0)
len = NM_PTRARRAY_LEN (value);
g_key_file_set_string_list (self->kf, self->group_name, key, value, len);
if ( !self->dirty
&& !got_dirty) {
gs_free_error GError *error = NULL;
gs_free char *new_value = NULL;
new_value = g_key_file_get_value (self->kf, self->group_name, key, &error);
if ( error
|| !new_value
|| !nm_streq0 (old_value, new_value))
got_dirty = TRUE;
}
if (got_dirty)
_got_dirty (self, key);
}
/*****************************************************************************/
void
nm_key_file_db_to_file (NMKeyFileDB *self,
gboolean force)
{
gs_free_error GError *error = NULL;
g_return_if_fail (_IS_KEY_FILE_DB (self, TRUE, FALSE));
if ( !force
&& !self->dirty)
return;
self->dirty = FALSE;
if (!g_key_file_save_to_file (self->kf,
self->filename,
&error)) {
_LOGD ("failure to write keyfile \"%s\": %s", self->filename, error->message);
} else
_LOGD ("write keyfile: \"%s\"", self->filename);
}

View file

@ -0,0 +1,78 @@
/* NetworkManager -- Network link manager
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* (C) Copyright 2019 Red Hat, Inc.
*/
#ifndef __NM_KEYFILE_AUX_H__
#define __NM_KEYFILE_AUX_H__
/*****************************************************************************/
typedef struct _NMKeyFileDB NMKeyFileDB;
typedef void (*NMKeyFileDBLogFcn) (NMKeyFileDB *self,
int syslog_level,
gpointer user_data,
const char *fmt,
...) G_GNUC_PRINTF (4, 5);
typedef void (*NMKeyFileDBGotDirtyFcn) (NMKeyFileDB *self,
gpointer user_data);
NMKeyFileDB *nm_key_file_db_new (const char *filename,
const char *group,
NMKeyFileDBLogFcn log_fcn,
NMKeyFileDBGotDirtyFcn got_dirty_fcn,
gpointer user_data);
void nm_key_file_db_start (NMKeyFileDB *self);
NMKeyFileDB *nm_key_file_db_ref (NMKeyFileDB *self);
void nm_key_file_db_unref (NMKeyFileDB *self);
void nm_key_file_db_destroy (NMKeyFileDB *self);
const char *nm_key_file_db_get_filename (NMKeyFileDB *self);
gboolean nm_key_file_db_is_dirty (NMKeyFileDB *self);
char *nm_key_file_db_get_value (NMKeyFileDB *self,
const char *key);
char **nm_key_file_db_get_string_list (NMKeyFileDB *self,
const char *key,
gsize *out_len);
void nm_key_file_db_remove_key (NMKeyFileDB *self,
const char *key);
void nm_key_file_db_set_value (NMKeyFileDB *self,
const char *key,
const char *value);
void nm_key_file_db_set_string_list (NMKeyFileDB *self,
const char *key,
const char *const*value,
gssize len);
void nm_key_file_db_to_file (NMKeyFileDB *self,
gboolean force);
/*****************************************************************************/
#endif /* __NM_KEYFILE_AUX_H__ */

View file

@ -110,4 +110,21 @@ void _nm_log_impl (const char *file,
const char *fmt,
...) _nm_printf (10, 11);
static inline NMLogLevel
nm_log_level_from_syslog (int syslog_level)
{
switch (syslog_level) {
case 0 /* LOG_EMERG */ : return LOGL_ERR;
case 1 /* LOG_ALERT */ : return LOGL_ERR;
case 2 /* LOG_CRIT */ : return LOGL_ERR;
case 3 /* LOG_ERR */ : return LOGL_ERR;
case 4 /* LOG_WARNING */ : return LOGL_WARN;
case 5 /* LOG_NOTICE */ : return LOGL_INFO;
case 6 /* LOG_INFO */ : return LOGL_DEBUG;
case 7 /* LOG_DEBUG */ : return LOGL_TRACE;
default:
return syslog_level >= 0 ? LOGL_TRACE : LOGL_ERR;
}
}
#endif /* __NM_LOGGING_DEFINES_H__ */

View file

@ -21,8 +21,6 @@
#include "nm-default.h"
#include <syslog.h>
#include "nm-glib-aux/nm-logging-fwd.h"
/*****************************************************************************/
@ -32,32 +30,18 @@
/*****************************************************************************/
static inline NMLogLevel
_slog_level_to_nm (int slevel)
{
switch (LOG_PRI (slevel)) {
case LOG_DEBUG: return LOGL_DEBUG;
case LOG_WARNING: return LOGL_WARN;
case LOG_CRIT:
case LOG_ERR: return LOGL_ERR;
case LOG_INFO:
case LOG_NOTICE:
default: return LOGL_INFO;
}
}
static inline int
_nm_log_get_max_level_realm (void)
{
/* inline function, to avoid coverity warning about constant expression. */
return LOG_DEBUG;
return 7 /* LOG_DEBUG */;
}
#define log_get_max_level_realm(realm) _nm_log_get_max_level_realm ()
#define log_internal_realm(level, error, file, line, func, format, ...) \
({ \
const int _nm_e = (error); \
const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \
const NMLogLevel _nm_l = nm_log_level_from_syslog (LOG_PRI (level)); \
\
if (_nm_log_enabled_impl (!(NM_THREAD_SAFE_ON_MAIN_THREAD), _nm_l, LOGD_SYSTEMD)) { \
const char *_nm_location = strrchr ((""file), '/'); \

View file

@ -15121,7 +15121,7 @@ _set_state_full (NMDevice *self,
* and those we haven't tried yet (no timestamp).
*/
if (sett_conn && !nm_settings_connection_get_timestamp (sett_conn, NULL))
nm_settings_connection_update_timestamp (sett_conn, (guint64) 0, TRUE);
nm_settings_connection_update_timestamp (sett_conn, (guint64) 0);
/* Schedule the transition to DISCONNECTED. The device can't transition
* immediately because we can't change states again from the state

View file

@ -900,7 +900,7 @@ nm_iwd_manager_init (NMIwdManager *self)
g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED,
G_CALLBACK (device_added), self);
priv->settings = g_object_ref (nm_settings_get ());
priv->settings = g_object_ref (NM_SETTINGS_GET);
g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
G_CALLBACK (connection_removed), self);

View file

@ -458,6 +458,8 @@ done:
nm_dns_manager_stop (nm_dns_manager_get ());
nm_settings_kf_db_write (NM_SETTINGS_GET);
done_no_manager:
if (global_opt.pidfile && wrote_pidfile)
unlink (global_opt.pidfile);

View file

@ -285,7 +285,7 @@ nm_active_connection_set_state (NMActiveConnection *self,
if ( new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
|| old_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
nm_settings_connection_update_timestamp (priv->settings_connection.obj,
(guint64) time (NULL), TRUE);
(guint64) time (NULL));
}
if (priv->device) {

View file

@ -171,7 +171,7 @@ find_settings_connection (NMCheckpoint *self,
*need_update = FALSE;
uuid = nm_connection_get_uuid (dev_checkpoint->settings_connection);
sett_conn = nm_settings_get_connection_by_uuid (nm_settings_get (), uuid);
sett_conn = nm_settings_get_connection_by_uuid (NM_SETTINGS_GET, uuid);
if (!sett_conn)
return NULL;
@ -239,7 +239,7 @@ restore_and_activate_connection (NMCheckpoint *self,
_LOGD ("rollback: adding connection %s again",
nm_connection_get_uuid (dev_checkpoint->settings_connection));
connection = nm_settings_add_connection (nm_settings_get (),
connection = nm_settings_add_connection (NM_SETTINGS_GET,
dev_checkpoint->settings_connection,
TRUE,
&local_error);
@ -419,7 +419,7 @@ next_dev:
gs_free NMSettingsConnection **list = NULL;
g_return_val_if_fail (priv->connection_uuids, NULL);
list = nm_settings_get_connections_clone (nm_settings_get (), NULL,
list = nm_settings_get_connections_clone (NM_SETTINGS_GET, NULL,
NULL, NULL,
nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
@ -687,7 +687,7 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
priv->connection_uuids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) {
for (con = nm_settings_get_connections (NM_SETTINGS_GET, NULL); *con; con++) {
g_hash_table_add (priv->connection_uuids,
g_strdup (nm_settings_connection_get_uuid (*con)));
}

View file

@ -68,6 +68,15 @@
/*****************************************************************************/
G_STATIC_ASSERT (LOG_EMERG == 0);
G_STATIC_ASSERT (LOG_ALERT == 1);
G_STATIC_ASSERT (LOG_CRIT == 2);
G_STATIC_ASSERT (LOG_ERR == 3);
G_STATIC_ASSERT (LOG_WARNING == 4);
G_STATIC_ASSERT (LOG_NOTICE == 5);
G_STATIC_ASSERT (LOG_INFO == 6);
G_STATIC_ASSERT (LOG_DEBUG == 7);
/* We have more then 32 logging domains. Assert that it compiles to a 64 bit sized enum */
G_STATIC_ASSERT (sizeof (NMLogDomain) >= sizeof (guint64));

View file

@ -7313,7 +7313,7 @@ periodic_update_active_connection_timestamps (gpointer user_data)
c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
nm_settings_connection_update_timestamp (nm_active_connection_get_settings_connection (ac),
(guint64) time (NULL), FALSE);
(guint64) time (NULL));
}
}
return G_SOURCE_CONTINUE;

View file

@ -25,6 +25,7 @@
#include "c-list/src/c-list.h"
#include "nm-glib-aux/nm-keyfile-aux.h"
#include "nm-libnm-core-intern/nm-common-macros.h"
#include "nm-config.h"
#include "nm-config-data.h"
@ -38,9 +39,6 @@
#include "nm-core-internal.h"
#include "nm-audit-manager.h"
#define SETTINGS_TIMESTAMPS_FILE NMSTATEDIR "/timestamps"
#define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids"
#define AUTOCONNECT_RETRIES_UNSET -2
#define AUTOCONNECT_RETRIES_FOREVER -1
#define AUTOCONNECT_RESET_RETRIES_TIMER 300
@ -86,6 +84,9 @@ static guint signals[LAST_SIGNAL] = { 0 };
typedef struct _NMSettingsConnectionPrivate {
NMKeyFileDB *kf_db_timestamps;
NMKeyFileDB *kf_db_seen_bssids;
NMAgentManager *agent_mgr;
NMSessionMonitor *session_monitor;
gulong session_changed_id;
@ -658,42 +659,6 @@ out:
return TRUE;
}
static void
remove_entry_from_db (NMSettingsConnection *self, const char* db_name)
{
GKeyFile *key_file;
const char *db_file;
if (strcmp (db_name, "timestamps") == 0)
db_file = SETTINGS_TIMESTAMPS_FILE;
else if (strcmp (db_name, "seen-bssids") == 0)
db_file = SETTINGS_SEEN_BSSIDS_FILE;
else
return;
key_file = g_key_file_new ();
if (g_key_file_load_from_file (key_file, db_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
const char *connection_uuid;
char *data;
gsize len;
GError *error = NULL;
connection_uuid = nm_settings_connection_get_uuid (self);
g_key_file_remove_key (key_file, db_name, connection_uuid, NULL);
data = g_key_file_to_data (key_file, &len, &error);
if (data) {
g_file_set_contents (db_file, data, len, &error);
g_free (data);
}
if (error) {
_LOGW ("error writing %s file '%s': %s", db_name, db_file, error->message);
g_error_free (error);
}
}
g_key_file_free (key_file);
}
gboolean
nm_settings_connection_delete (NMSettingsConnection *self,
GError **error)
@ -701,6 +666,7 @@ nm_settings_connection_delete (NMSettingsConnection *self,
gs_unref_object NMSettingsConnection *self_keep_alive = NULL;
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
NMConnection *for_agents;
const char *connection_uuid;
g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
@ -719,11 +685,13 @@ nm_settings_connection_delete (NMSettingsConnection *self,
for_agents);
g_object_unref (for_agents);
/* Remove timestamp from timestamps database file */
remove_entry_from_db (self, "timestamps");
connection_uuid = nm_settings_connection_get_uuid (self);
/* Remove connection from seen-bssids database file */
remove_entry_from_db (self, "seen-bssids");
if (priv->kf_db_timestamps)
nm_key_file_db_remove_key (priv->kf_db_timestamps, connection_uuid);
if (priv->kf_db_seen_bssids)
nm_key_file_db_remove_key (priv->kf_db_seen_bssids, connection_uuid);
nm_settings_connection_signal_remove (self);
return TRUE;
@ -2338,11 +2306,13 @@ gboolean
nm_settings_connection_get_timestamp (NMSettingsConnection *self,
guint64 *out_timestamp)
{
NMSettingsConnectionPrivate *priv;
g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
if (out_timestamp)
*out_timestamp = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp;
return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp_set;
priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
NM_SET_OUT (out_timestamp, priv->timestamp);
return priv->timestamp_set;
}
/**
@ -2350,56 +2320,31 @@ nm_settings_connection_get_timestamp (NMSettingsConnection *self,
* @self: the #NMSettingsConnection
* @timestamp: timestamp to set into the connection and to store into
* the timestamps database
* @flush_to_disk: if %TRUE, commit timestamp update to persistent storage
*
* Updates the connection and timestamps database with the provided timestamp.
**/
void
nm_settings_connection_update_timestamp (NMSettingsConnection *self,
guint64 timestamp,
gboolean flush_to_disk)
guint64 timestamp)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
const char *connection_uuid;
GKeyFile *timestamps_file;
char *data, *tmp;
gsize len;
GError *error = NULL;
char sbuf[60];
g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
/* Update timestamp in private storage */
priv->timestamp = timestamp;
priv->timestamp_set = TRUE;
if (flush_to_disk == FALSE)
if (!priv->kf_db_timestamps)
return;
if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD)
return;
/* Save timestamp to timestamps database file */
timestamps_file = g_key_file_new ();
if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
_LOGW ("error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
g_clear_error (&error);
}
connection_uuid = nm_settings_connection_get_uuid (self);
tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp);
g_free (tmp);
data = g_key_file_to_data (timestamps_file, &len, &error);
if (data) {
g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error);
g_free (data);
if (connection_uuid) {
nm_key_file_db_set_value (priv->kf_db_timestamps,
connection_uuid,
nm_sprintf_buf (sbuf, "%" G_GUINT64_FORMAT, timestamp));
}
if (error) {
_LOGW ("error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
g_error_free (error);
}
g_key_file_free (timestamps_file);
}
/**
@ -2410,38 +2355,79 @@ nm_settings_connection_update_timestamp (NMSettingsConnection *self,
* stores it into the connection private data.
**/
void
nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self)
nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
NMKeyFileDB *kf_db_timestamps,
NMKeyFileDB *kf_db_seen_bssids)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
gs_unref_keyfile GKeyFile *timestamps_file = NULL;
gs_free_error GError *error = NULL;
gs_free char *tmp_str = NULL;
NMSettingsConnectionPrivate *priv;
const char *connection_uuid;
gint64 timestamp;
g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
g_return_if_fail (kf_db_timestamps);
g_return_if_fail (kf_db_seen_bssids);
timestamps_file = g_key_file_new ();
if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
_LOGD ("failed to read connection timestamp: %s", error->message);
return;
}
priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
connection_uuid = nm_settings_connection_get_uuid (self);
tmp_str = g_key_file_get_value (timestamps_file, "timestamps", connection_uuid, &error);
if (!tmp_str) {
_LOGD ("failed to read connection timestamp: %s", error->message);
return;
if (priv->kf_db_timestamps != kf_db_timestamps) {
gs_free char *tmp_str = NULL;
guint64 timestamp;
nm_key_file_db_unref (priv->kf_db_timestamps);
priv->kf_db_timestamps = nm_key_file_db_ref (kf_db_timestamps);
tmp_str = nm_key_file_db_get_value (priv->kf_db_timestamps, connection_uuid);
timestamp = _nm_utils_ascii_str_to_uint64 (tmp_str, 10, 0, G_MAXUINT64, G_MAXUINT64);
if (timestamp != G_MAXUINT64) {
priv->timestamp = timestamp;
priv->timestamp_set = TRUE;
_LOGT ("read timestamp %"G_GUINT64_FORMAT" from keyfile database \"%s\"",
timestamp, nm_key_file_db_get_filename (priv->kf_db_timestamps));
} else
_LOGT ("no timestamp from keyfile database \"%s\"",
nm_key_file_db_get_filename (priv->kf_db_timestamps));
}
timestamp = _nm_utils_ascii_str_to_int64 (tmp_str, 10, 0, G_MAXINT64, -1);
if (timestamp < 0) {
_LOGD ("failed to read connection timestamp: %s", "invalid number");
return;
}
if (priv->kf_db_seen_bssids != kf_db_seen_bssids) {
gs_strfreev char **tmp_strv = NULL;
gsize i, len;
priv->timestamp = timestamp;
priv->timestamp_set = TRUE;
nm_key_file_db_unref (priv->kf_db_seen_bssids);
priv->kf_db_seen_bssids = nm_key_file_db_ref (kf_db_seen_bssids);
tmp_strv = nm_key_file_db_get_string_list (priv->kf_db_seen_bssids, connection_uuid, &len);
if (tmp_strv) {
_LOGT ("read %zu seen-bssids from keyfile database \"%s\"",
NM_PTRARRAY_LEN (tmp_strv),
nm_key_file_db_get_filename (priv->kf_db_seen_bssids));
g_hash_table_remove_all (priv->seen_bssids);
for (i = len; i > 0; )
g_hash_table_add (priv->seen_bssids, g_steal_pointer (&tmp_strv[--i]));
} else {
NMSettingWireless *s_wifi;
_LOGT ("no seen-bssids from keyfile database \"%s\"",
nm_key_file_db_get_filename (priv->kf_db_seen_bssids));
/* If this connection didn't have an entry in the seen-bssids database,
* maybe this is the first time we've read it in, so populate the
* seen-bssids list from the deprecated seen-bssids property of the
* wifi setting.
*/
s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (self));
if (s_wifi) {
len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
for (i = 0; i < len; i++) {
const char *bssid = nm_setting_wireless_get_seen_bssid (s_wifi, i);
g_hash_table_add (priv->seen_bssids, g_strdup (bssid));
}
}
}
}
}
/**
@ -2504,108 +2490,26 @@ nm_settings_connection_add_seen_bssid (NMSettingsConnection *self,
const char *seen_bssid)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
gs_free const char **strv = NULL;
const char *connection_uuid;
GKeyFile *seen_bssids_file;
char *data, *bssid_str;
const char **list;
gsize len;
GError *error = NULL;
GHashTableIter iter;
guint n;
g_return_if_fail (seen_bssid != NULL);
if (g_hash_table_lookup (priv->seen_bssids, seen_bssid))
return; /* Already in the list */
g_hash_table_add (priv->seen_bssids, g_strdup (seen_bssid));
/* Add the new BSSID; let the hash take ownership of the allocated BSSID string */
bssid_str = g_strdup (seen_bssid);
g_hash_table_insert (priv->seen_bssids, bssid_str, bssid_str);
/* Build up a list of all the BSSIDs in string form */
n = 0;
list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *));
g_hash_table_iter_init (&iter, priv->seen_bssids);
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str))
list[n++] = bssid_str;
/* Save BSSID to seen-bssids file */
seen_bssids_file = g_key_file_new ();
g_key_file_set_list_separator (seen_bssids_file, ',');
if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
_LOGW ("error parsing seen-bssids file '%s': %s",
SETTINGS_SEEN_BSSIDS_FILE, error->message);
}
g_clear_error (&error);
}
if (!priv->kf_db_seen_bssids)
return;
connection_uuid = nm_settings_connection_get_uuid (self);
g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n);
g_free (list);
if (!connection_uuid)
return;
data = g_key_file_to_data (seen_bssids_file, &len, &error);
if (data) {
g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error);
g_free (data);
}
g_key_file_free (seen_bssids_file);
strv = nm_utils_strdict_get_keys (priv->seen_bssids, TRUE, NULL);
if (error) {
_LOGW ("error saving seen-bssids to file '%s': %s",
SETTINGS_SEEN_BSSIDS_FILE, error->message);
g_error_free (error);
}
}
/**
* nm_settings_connection_read_and_fill_seen_bssids:
* @self: the #NMSettingsConnection
*
* Retrieves seen BSSIDs of the connection from database file and stores then into the
* connection private data.
**/
void
nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
const char *connection_uuid;
GKeyFile *seen_bssids_file;
char **tmp_strv = NULL;
gsize i, len = 0;
NMSettingWireless *s_wifi;
/* Get seen BSSIDs from database file */
seen_bssids_file = g_key_file_new ();
g_key_file_set_list_separator (seen_bssids_file, ',');
if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
connection_uuid = nm_settings_connection_get_uuid (self);
tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL);
}
g_key_file_free (seen_bssids_file);
/* Update connection's seen-bssids */
if (tmp_strv) {
g_hash_table_remove_all (priv->seen_bssids);
for (i = 0; i < len; i++)
g_hash_table_insert (priv->seen_bssids, tmp_strv[i], tmp_strv[i]);
g_free (tmp_strv);
} else {
/* If this connection didn't have an entry in the seen-bssids database,
* maybe this is the first time we've read it in, so populate the
* seen-bssids list from the deprecated seen-bssids property of the
* wifi setting.
*/
s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (self));
if (s_wifi) {
len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
for (i = 0; i < len; i++) {
char *bssid_dup = g_strdup (nm_setting_wireless_get_seen_bssid (s_wifi, i));
g_hash_table_insert (priv->seen_bssids, bssid_dup, bssid_dup);
}
}
}
nm_key_file_db_set_string_list (priv->kf_db_seen_bssids,
connection_uuid,
strv ?: NM_PTRARRAY_EMPTY (const char *),
-1);
}
/*****************************************************************************/
@ -2932,6 +2836,9 @@ dispose (GObject *object)
g_clear_pointer (&priv->filename, g_free);
g_clear_pointer (&priv->kf_db_timestamps, nm_key_file_db_unref);
g_clear_pointer (&priv->kf_db_seen_bssids, nm_key_file_db_unref);
G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);
}

View file

@ -220,14 +220,17 @@ int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpo
int nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b);
int nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data);
struct _NMKeyFileDB;
void nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
struct _NMKeyFileDB *kf_db_timestamps,
struct _NMKeyFileDB *kf_db_seen_bssids);
gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self,
guint64 *out_timestamp);
void nm_settings_connection_update_timestamp (NMSettingsConnection *self,
guint64 timestamp,
gboolean flush_to_disk);
void nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self);
guint64 timestamp);
char **nm_settings_connection_get_seen_bssids (NMSettingsConnection *self);
@ -237,8 +240,6 @@ gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *self,
void nm_settings_connection_add_seen_bssid (NMSettingsConnection *self,
const char *seen_bssid);
void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self);
int nm_settings_connection_autoconnect_retries_get (NMSettingsConnection *self);
void nm_settings_connection_autoconnect_retries_set (NMSettingsConnection *self,
int retries);

View file

@ -37,6 +37,7 @@
#endif
#include "nm-libnm-core-intern/nm-common-macros.h"
#include "nm-glib-aux/nm-keyfile-aux.h"
#include "nm-dbus-interface.h"
#include "nm-connection.h"
#include "nm-setting-8021x.h"
@ -119,6 +120,9 @@ typedef struct {
GSList *plugins;
NMKeyFileDB *kf_db_timestamps;
NMKeyFileDB *kf_db_seen_bssids;
CList connections_lst_head;
NMSettingsConnection **connections_cached_list;
@ -131,6 +135,9 @@ typedef struct {
guint connections_len;
guint kf_db_flush_idle_id_timestamps;
guint kf_db_flush_idle_id_seen_bssids;
bool started:1;
bool startup_complete:1;
bool connections_loaded:1;
@ -935,11 +942,9 @@ claim_connection (NMSettings *self, NMSettingsConnection *sett_conn)
return;
}
/* Read timestamp from look-aside file and put it into the connection's data */
nm_settings_connection_read_and_fill_timestamp (sett_conn);
/* Read seen-bssids from look-aside file and put it into the connection's data */
nm_settings_connection_read_and_fill_seen_bssids (sett_conn);
nm_settings_connection_register_kf_dbs (sett_conn,
priv->kf_db_timestamps,
priv->kf_db_seen_bssids);
/* Ensure its initial visibility is up-to-date */
nm_settings_connection_recheck_visibility (sett_conn);
@ -1764,6 +1769,128 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin
/*****************************************************************************/
G_GNUC_PRINTF (4, 5)
static void
_kf_db_log_fcn (NMKeyFileDB *kf_db,
int syslog_level,
gpointer user_data,
const char *fmt,
...)
{
NMSettings *self = user_data;
NMLogLevel level = nm_log_level_from_syslog (syslog_level);
if (_NMLOG_ENABLED (level)) {
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
gs_free char *msg = NULL;
va_list ap;
const char *prefix;
va_start (ap, fmt);
msg = g_strdup_vprintf (fmt, ap);
va_end (ap);
if (priv->kf_db_timestamps == kf_db)
prefix = "timestamps";
else if (priv->kf_db_seen_bssids == kf_db)
prefix = "seen-bssids";
else {
nm_assert_not_reached ();
prefix = "???";
}
_NMLOG (level, "[%s-keyfile]: %s", prefix, msg);
}
}
static gboolean
_kf_db_got_dirty_flush (NMSettings *self,
gboolean is_timestamps)
{
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
const char *prefix;
NMKeyFileDB *kf_db;
if (is_timestamps) {
prefix = "timestamps";
kf_db = priv->kf_db_timestamps;
priv->kf_db_flush_idle_id_timestamps = 0;
} else {
prefix = "seen-bssids";
kf_db = priv->kf_db_seen_bssids;
priv->kf_db_flush_idle_id_seen_bssids = 0;
}
if (nm_key_file_db_is_dirty (kf_db))
nm_key_file_db_to_file (kf_db, FALSE);
else {
_LOGT ("[%s-keyfile]: skip saving changes to \"%s\"",
prefix,
nm_key_file_db_get_filename (kf_db));
}
return G_SOURCE_REMOVE;
}
static gboolean
_kf_db_got_dirty_flush_timestamps_cb (gpointer user_data)
{
return _kf_db_got_dirty_flush (user_data,
TRUE);
}
static gboolean
_kf_db_got_dirty_flush_seen_bssids_cb (gpointer user_data)
{
return _kf_db_got_dirty_flush (user_data,
FALSE);
}
static void
_kf_db_got_dirty_fcn (NMKeyFileDB *kf_db,
gpointer user_data)
{
NMSettings *self = user_data;
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
GSourceFunc idle_func;
guint *p_id;
const char *prefix;
if (priv->kf_db_timestamps == kf_db) {
prefix = "timestamps";
p_id = &priv->kf_db_flush_idle_id_timestamps;
idle_func = _kf_db_got_dirty_flush_timestamps_cb;
} else if (priv->kf_db_seen_bssids == kf_db) {
prefix = "seen-bssids";
p_id = &priv->kf_db_flush_idle_id_seen_bssids;
idle_func = _kf_db_got_dirty_flush_seen_bssids_cb;
} else {
nm_assert_not_reached ();
return;
}
if (*p_id != 0)
return;
_LOGT ("[%s-keyfile]: schedule flushing changes to disk", prefix);
*p_id = g_idle_add_full (G_PRIORITY_LOW, idle_func, self, NULL);
}
void
nm_settings_kf_db_write (NMSettings *self)
{
NMSettingsPrivate *priv;
g_return_if_fail (NM_IS_SETTINGS (self));
priv = NM_SETTINGS_GET_PRIVATE (self);
if (priv->kf_db_timestamps)
nm_key_file_db_to_file (priv->kf_db_timestamps, TRUE);
if (priv->kf_db_seen_bssids)
nm_key_file_db_to_file (priv->kf_db_seen_bssids, TRUE);
}
/*****************************************************************************/
const char *
nm_settings_get_startup_complete_blocked_reason (NMSettings *self)
{
@ -1797,6 +1924,19 @@ nm_settings_start (NMSettings *self, GError **error)
priv = NM_SETTINGS_GET_PRIVATE (self);
priv->kf_db_timestamps = nm_key_file_db_new (NMSTATEDIR "/timestamps",
"timestamps",
_kf_db_log_fcn,
_kf_db_got_dirty_fcn,
self);
priv->kf_db_seen_bssids = nm_key_file_db_new (NMSTATEDIR "/seen-bssids",
"seen-bssids",
_kf_db_log_fcn,
_kf_db_got_dirty_fcn,
self);
nm_key_file_db_start (priv->kf_db_timestamps);
nm_key_file_db_start (priv->kf_db_seen_bssids);
/* Load the plugins; fail if a plugin is not found. */
plugins = nm_config_data_get_plugins (nm_config_get_data_orig (priv->config), TRUE);
@ -1933,6 +2073,13 @@ finalize (GObject *object)
g_clear_object (&priv->config);
nm_clear_g_source (&priv->kf_db_flush_idle_id_timestamps);
nm_clear_g_source (&priv->kf_db_flush_idle_id_seen_bssids);
nm_key_file_db_to_file (priv->kf_db_timestamps, FALSE);
nm_key_file_db_to_file (priv->kf_db_seen_bssids, FALSE);
nm_key_file_db_destroy (priv->kf_db_timestamps);
nm_key_file_db_destroy (priv->kf_db_seen_bssids);
G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object);
}

View file

@ -114,4 +114,6 @@ void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean qu
const char *nm_settings_get_startup_complete_blocked_reason (NMSettings *self);
void nm_settings_kf_db_write (NMSettings *settings);
#endif /* __NM_SETTINGS_H__ */