summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2013-01-11 19:23:18 -0500
committerRyan Lortie <desrt@desrt.ca>2013-01-11 19:23:18 -0500
commitbdd4dc68f54f9700a9b7c13cf035b0deba03612c (patch)
treeaa17702bd17bfebc850f0b5245683f207e5f5099 /service
parent1f97cd016106267c722be637310b3a9c774996ba (diff)
downloaddconf-bdd4dc68f54f9700a9b7c13cf035b0deba03612c.tar.gz
keyfile: add advisory locking
Use fcntl() on a lockfile when accessing a keyfile. Now reading the keyfile, notifying local processes of changes in it, applying changes from local request and rewriting it is all done under a single acquire of the lock. This effectively means that concurrent changes made to the database across several machines sharing a home directory over NFS will be seen by all machines as having occurred in the same order (decided by who won the race to the lock).
Diffstat (limited to 'service')
-rw-r--r--service/dconf-keyfile-writer.c64
1 files changed, 62 insertions, 2 deletions
diff --git a/service/dconf-keyfile-writer.c b/service/dconf-keyfile-writer.c
index 2845c65..e41d0fe 100644
--- a/service/dconf-keyfile-writer.c
+++ b/service/dconf-keyfile-writer.c
@@ -23,6 +23,9 @@
#include "dconf-writer.h"
#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
typedef DConfWriterClass DConfKeyfileWriterClass;
@@ -30,6 +33,8 @@ typedef struct
{
DConfWriter parent_instance;
gchar *filename;
+ gchar *lock_filename;
+ gint lock_fd;
GFileMonitor *monitor;
guint scheduled_update;
gchar *contents;
@@ -159,6 +164,7 @@ dconf_keyfile_writer_begin (DConfWriter *writer,
filename_base = g_build_filename (g_get_user_config_dir (), "dconf", dconf_writer_get_name (writer), NULL);
kfw->filename = g_strconcat (filename_base, ".txt", NULL);
+ kfw->lock_filename = g_strconcat (kfw->filename, "-lock", NULL);
g_free (filename_base);
file = g_file_new_for_path (kfw->filename);
@@ -170,6 +176,55 @@ dconf_keyfile_writer_begin (DConfWriter *writer,
g_clear_pointer (&kfw->contents, g_free);
+ kfw->lock_fd = open (kfw->lock_filename, O_RDWR | O_CREAT, 0666);
+ if (kfw->lock_fd == -1)
+ {
+ gchar *dirname;
+
+ /* Maybe it failed because the directory doesn't exist. Try
+ * again, after mkdir().
+ */
+ dirname = g_path_get_dirname (kfw->lock_filename);
+ g_mkdir_with_parents (dirname, 0777);
+ g_free (dirname);
+
+ kfw->lock_fd = open (kfw->lock_filename, O_RDWR | O_CREAT, 0666);
+ if (kfw->lock_fd == -1)
+ {
+ gint saved_errno = errno;
+
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
+ "%s: %s", kfw->lock_filename, g_strerror (saved_errno));
+ return FALSE;
+ }
+ }
+
+ while (TRUE)
+ {
+ struct flock lock;
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = 0;
+ lock.l_start = 0;
+ lock.l_len = 0; /* lock all bytes */
+
+ if (fcntl (kfw->lock_fd, F_SETLKW, &lock) == 0)
+ break;
+
+ if (errno != EINTR)
+ {
+ gint saved_errno = errno;
+
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
+ "%s: unable to fcntl(F_SETLKW): %s", kfw->lock_filename, g_strerror (saved_errno));
+ close (kfw->lock_fd);
+ kfw->lock_fd = -1;
+ return FALSE;
+ }
+
+ /* it was EINTR. loop again. */
+ }
+
if (!g_file_get_contents (kfw->filename, &kfw->contents, NULL, &local_error))
{
if (!g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
@@ -394,6 +449,8 @@ dconf_keyfile_writer_end (DConfWriter *writer)
g_clear_pointer (&kfw->keyfile, g_key_file_free);
g_clear_pointer (&kfw->contents, g_free);
+ close (kfw->lock_fd);
+ kfw->lock_fd = -1;
}
static gboolean
@@ -421,15 +478,18 @@ dconf_keyfile_writer_finalize (GObject *object)
g_source_remove (kfw->scheduled_update);
g_clear_object (&kfw->monitor);
+ g_free (kfw->lock_filename);
g_free (kfw->filename);
G_OBJECT_CLASS (dconf_keyfile_writer_parent_class)->finalize (object);
}
static void
-dconf_keyfile_writer_init (DConfKeyfileWriter *writer)
+dconf_keyfile_writer_init (DConfKeyfileWriter *kfw)
{
- dconf_writer_set_basepath (DCONF_WRITER (writer), "keyfile");
+ dconf_writer_set_basepath (DCONF_WRITER (kfw), "keyfile");
+
+ kfw->lock_fd = -1;
}
static void