summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-05-03 12:07:37 +0200
committerThomas Haller <thaller@redhat.com>2019-05-07 16:41:21 +0200
commitb0693863c1f6c6bf31033499e222a6f6135411eb (patch)
tree97ad07032afa61b7c44bf9aded2f7ad6eafcf16e
parent8c2fda7ca09e0fb5119e5ba15b242aa6a93fde99 (diff)
downloadNetworkManager-b0693863c1f6c6bf31033499e222a6f6135411eb.tar.gz
shared: add NMKeyFileDB API
It will be used for "/var/lib/NetworkManager/seen-bssids" and "/var/lib/NetworkManager/timestamps" which currently is implemented in NMSettingConnection.
-rw-r--r--shared/nm-glib-aux/nm-keyfile-aux.c389
-rw-r--r--shared/nm-glib-aux/nm-keyfile-aux.h54
2 files changed, 443 insertions, 0 deletions
diff --git a/shared/nm-glib-aux/nm-keyfile-aux.c b/shared/nm-glib-aux/nm-keyfile-aux.c
index a0d8186d8d..0257bcca7f 100644
--- a/shared/nm-glib-aux/nm-keyfile-aux.c
+++ b/shared/nm-glib-aux/nm-keyfile-aux.c
@@ -22,3 +22,392 @@
#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);
+}
diff --git a/shared/nm-glib-aux/nm-keyfile-aux.h b/shared/nm-glib-aux/nm-keyfile-aux.h
index f7ef866c92..8563f4d186 100644
--- a/shared/nm-glib-aux/nm-keyfile-aux.h
+++ b/shared/nm-glib-aux/nm-keyfile-aux.h
@@ -21,4 +21,58 @@
#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__ */