diff options
author | Ryan Lortie <desrt@desrt.ca> | 2013-01-11 19:23:18 -0500 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2013-01-11 19:23:18 -0500 |
commit | bdd4dc68f54f9700a9b7c13cf035b0deba03612c (patch) | |
tree | aa17702bd17bfebc850f0b5245683f207e5f5099 /service | |
parent | 1f97cd016106267c722be637310b3a9c774996ba (diff) | |
download | dconf-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.c | 64 |
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 |