summaryrefslogtreecommitdiff
path: root/shm/dconf-shm.c
diff options
context:
space:
mode:
Diffstat (limited to 'shm/dconf-shm.c')
-rw-r--r--shm/dconf-shm.c151
1 files changed, 151 insertions, 0 deletions
diff --git a/shm/dconf-shm.c b/shm/dconf-shm.c
new file mode 100644
index 0000000..10bfec9
--- /dev/null
+++ b/shm/dconf-shm.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ * Copyright © 2012 Canonical Limited
+ *
+ * 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 licence, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "dconf-shm.h"
+
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static gchar *
+dconf_shm_get_shmdir (void)
+{
+ static gchar *shmdir;
+
+ if (g_once_init_enter (&shmdir))
+ g_once_init_leave (&shmdir, g_build_filename (g_get_user_runtime_dir (), "dconf", NULL));
+
+ return shmdir;
+}
+
+void
+dconf_shm_close (guint8 *shm)
+{
+ if (shm)
+ munmap (shm, 1);
+}
+
+guint8 *
+dconf_shm_open (const gchar *name)
+{
+ const gchar *shmdir;
+ gchar *filename;
+ void *memory;
+ gint fd;
+
+ shmdir = dconf_shm_get_shmdir ();
+ filename = g_build_filename (shmdir, name, NULL);
+ memory = NULL;
+ fd = -1;
+
+ if (g_mkdir_with_parents (shmdir, 0700) != 0)
+ {
+ g_critical ("unable to create directory '%s': %s. dconf will not work properly.", shmdir, g_strerror (errno));
+ goto out;
+ }
+
+ fd = open (filename, O_RDWR | O_CREAT, 0600);
+ if (fd == -1)
+ {
+ g_critical ("unable to create file '%s': %s. dconf will not work properly.", filename, g_strerror (errno));
+ goto out;
+ }
+
+ /* fruncate(fd, 1) is not sufficient because it does not actually
+ * ensure that the space is available (which could give a SIGBUS
+ * later).
+ *
+ * posix_fallocate() is also problematic because it is implemented in
+ * a racy way in the libc if unavailable for a particular filesystem
+ * (as is the case for tmpfs, which is where we probably are).
+ *
+ * By writing to the second byte in the file we ensure we don't
+ * overwrite the first byte (which is the one we care about).
+ */
+ if (pwrite (fd, "", 1, 1) != 1)
+ {
+ g_critical ("failed to allocate file '%s': %s. dconf will not work properly.", filename, g_strerror (errno));
+ goto out;
+ }
+
+ memory = mmap (NULL, 1, PROT_READ, MAP_SHARED, fd, 0);
+ g_assert (memory != MAP_FAILED);
+ g_assert (memory != NULL);
+
+ out:
+ g_free (filename);
+ close (fd);
+
+ return memory;
+}
+
+void
+dconf_shm_flag (const gchar *name)
+{
+ const gchar *shmdir;
+ gchar *filename;
+ gint fd;
+
+ shmdir = dconf_shm_get_shmdir ();
+ filename = g_build_filename (shmdir, name, NULL);
+
+ /* We need O_RDWR for PROT_WRITE.
+ *
+ * This is probably due to the fact that some architectures can't make
+ * write-only mappings (so they end up being readable as well).
+ */
+ fd = open (filename, O_RDWR);
+ if (fd >= 0)
+ {
+ /* In theory we could have opened the file after a client created
+ * it but before they called pwrite(). Do the pwrite() ourselves
+ * to make sure (so we don't get SIGBUS in a moment).
+ *
+ * If this fails then it will probably fail for the client too.
+ * If it doesn't then there's not really much we can do...
+ */
+ if (pwrite (fd, "", 1, 1) == 1)
+ {
+ guint8 *shm;
+
+ /* It would ahve been easier for us to do write(fd, "\1", 1);
+ * but this causes problems on kernels (ie: OpenBSD) that
+ * don't sync up their filesystem cache with mmap()ed regions.
+ *
+ * Using mmap() works everywhere.
+ */
+ shm = mmap (NULL, 1, PROT_WRITE, MAP_SHARED, fd, 0);
+ g_assert (shm != MAP_FAILED);
+
+ *shm = 1;
+
+ munmap (shm, 1);
+ }
+
+ close (fd);
+
+ unlink (filename);
+ }
+
+ g_free (filename);
+}