[dconf] add 'dconf-update' utility



commit 46bed217bae3ffb796d9acde4c830ee3bc587852
Author: Ryan Lortie <desrt desrt ca>
Date:   Sun Jul 18 19:37:48 2010 -0400

    add 'dconf-update' utility
    
    This utility scans /etc/dconf/db/ looking for directories named *.d.
    
    For each directory name.d that it finds, it reads each file contained
    within as a keyfile containing a list of keys to populate a dconf
    database with.
    
    That database is then written to 'name' (ie: the directory name with the
    '.d' removed), the old file is invalidated, and a signal is sent over
    the system DBus to indicate the change.
    
    This tool facilitates updating of dconf databases by sysadmins who
    prefer to follow the 'drop a text file here' approach to system
    management.

 bin/.gitignore        |    3 +
 bin/Makefile.am       |    8 ++-
 bin/dconf-update.vala |  143 +++++++++++++++++++++++++++++++++++++++++++++++++
 bin/fixes.vapi        |    4 ++
 bin/gvdb.vapi         |   21 +++++++
 5 files changed, 177 insertions(+), 2 deletions(-)
---
diff --git a/bin/.gitignore b/bin/.gitignore
index 042fec5..08c4537 100644
--- a/bin/.gitignore
+++ b/bin/.gitignore
@@ -1 +1,4 @@
 dconf
+dconf-update
+dconf-update.c
+*.stamp
diff --git a/bin/Makefile.am b/bin/Makefile.am
index d771ce4..5544f82 100644
--- a/bin/Makefile.am
+++ b/bin/Makefile.am
@@ -1,6 +1,10 @@
 AM_CFLAGS = -std=c89 -Wall -Wmissing-prototypes -Wwrite-strings
-INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/engine -I$(top_srcdir)/client $(gio_CFLAGS)
+INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/engine -I$(top_srcdir)/client -I$(top_srcdir)/gvdb $(gio_CFLAGS)
 
-bin_PROGRAMS = dconf
+bin_PROGRAMS = dconf dconf-update
 
 dconf_LDADD = ../client/libdconf.la $(gio_LIBS)
+
+dconf_update_VALAFLAGS = --pkg=posix --pkg=gio-2.0
+dconf_update_LDADD = $(gio_LIBS)
+dconf_update_SOURCES = dconf-update.vala ../gvdb/gvdb-builder.c gvdb.vapi fixes.vapi
diff --git a/bin/dconf-update.vala b/bin/dconf-update.vala
new file mode 100644
index 0000000..9a81140
--- /dev/null
+++ b/bin/dconf-update.vala
@@ -0,0 +1,143 @@
+unowned Gvdb.Item get_parent (Gvdb.HashTable table, string name) {
+	unowned Gvdb.Item parent;
+
+	int end = 0;
+
+	for (int i = 0; name[i] != '\0'; i++) {
+		if (name[i - 1] == '/') {
+			end = i;
+		}
+	}
+
+	var parent_name = name.ndup (end);
+	parent = table.lookup (parent_name);
+
+	if (parent == null) {
+		parent = table.insert (parent_name);
+		parent.set_parent (get_parent (table, parent_name));
+	}
+
+	return parent;
+}
+
+Gvdb.HashTable read_directory (string dirname) throws GLib.Error {
+	var table = new Gvdb.HashTable ();
+	unowned string? name;
+
+	table.insert ("/");
+
+	var dir = Dir.open (dirname);
+	while ((name = dir.read_name ()) != null) {
+		var filename = Path.build_filename (dirname, name);
+
+		var kf = new KeyFile ();
+
+		try {
+			kf.load_from_file (filename, KeyFileFlags.NONE);
+		} catch (GLib.Error e) {
+			stderr.printf ("%s: %s\n", filename, e.message);
+			continue;
+		}
+
+		foreach (var group in kf.get_groups ()) {
+			if (group.has_prefix ("/") || group.has_suffix ("/") || group.str ("//") != null) {
+				stderr.printf ("%s: ignoring invalid group name: %s\n", filename, group);
+				continue;
+			}
+
+			foreach (var key in kf.get_keys (group)) {
+				if (key.str ("/") != null) {
+					stderr.printf ("%s: [%s]: ignoring invalid key name: %s\n", filename, group, key);
+					continue;
+				}
+
+				var path = "/" + group + "/" + key;
+
+				if (table.lookup (path) != null) {
+					stderr.printf ("%s: [%s]: %s: ignoring duplicate definition of key %s\n",
+					               filename, group, key, path);
+					continue;
+				}
+
+				var text = kf.get_value (group, key);
+
+				try {
+					var value = Variant.parse (null, text);
+					unowned Gvdb.Item item = table.insert (path);
+					item.set_parent (get_parent (table, path));
+					item.set_value (value);
+				} catch (VariantParseError e) {
+					stderr.printf ("%s: [%s]: %s: skipping invalid value: %s (%s)\n",
+					               filename, group, key, text, e.message);
+				}
+			}
+		}
+	}
+
+	return table;
+}
+
+void maybe_update_from_directory (string dirname) throws GLib.Error {
+	Posix.Stat dir_buf;
+
+	if (Posix.stat (dirname, out dir_buf) == 0 && Posix.S_ISDIR (dir_buf.st_mode)) {
+		Posix.Stat file_buf;
+
+		var filename = dirname.ndup (dirname.length - 2);
+
+		if (Posix.stat (filename, out file_buf) == 0 && file_buf.st_mtime > dir_buf.st_mtime) {
+			return;
+		}
+
+		var table = read_directory (dirname);
+
+		var fd = Posix.open (filename, Posix.O_WRONLY);
+
+		if (fd < 0 && errno != Posix.EEXIST) {
+			var saved_error = errno;
+			throw new FileError.FAILED ("Can not open '%s' for replacement: %s", filename, strerror (saved_error));
+		}
+
+		table.write_contents (filename);
+
+		if (fd >= 0) {
+			Posix.write (fd, "\0\0\0\0\0\0\0\0", 8);
+			Posix.close (fd);
+		}
+
+		try {
+			var system_bus = Bus.get_sync (BusType.SYSTEM);
+			system_bus.emit_signal (null, "/" + Path.get_basename (filename), "ca.desrt.dconf.Writer", "Notify",
+			                        new Variant ("(tsas)", (uint64) 0, "/", new VariantBuilder (STRING_ARRAY)));
+			flush_the_bus (system_bus);
+		} catch {
+			/* if we can't, ... don't. */
+		}
+	}
+}
+
+void update_all (string dirname) throws GLib.Error {
+	unowned string? name;
+
+	var dir = Dir.open (dirname);
+
+	while ((name = dir.read_name ()) != null) {
+		if (name.has_suffix (".d")) {
+			try {
+				maybe_update_from_directory (Path.build_filename (dirname, name));
+			} catch (GLib.Error e) {
+				stderr.printf ("%s\n", e.message);
+			}
+		}
+	}
+}
+
+void main () {
+	try {
+		update_all ("/etc/dconf/db");
+	} catch (GLib.Error e) {
+		stderr.printf ("fatal: %s\n", e.message);
+	}
+}
+
+// vim:noet ts=4 sw=4
diff --git a/bin/fixes.vapi b/bin/fixes.vapi
new file mode 100644
index 0000000..ada6ab6
--- /dev/null
+++ b/bin/fixes.vapi
@@ -0,0 +1,4 @@
+[CCode (cname = "g_dbus_connection_flush_sync")]
+void flush_the_bus (GLib.DBusConnection connection, GLib.Cancellable? cancellable = null) throws GLib.Error;
+[CCode (cname = "G_VARIANT_TYPE_STRING_ARRAY")]
+public static GLib.VariantType STRING_ARRAY;
diff --git a/bin/gvdb.vapi b/bin/gvdb.vapi
new file mode 100644
index 0000000..1848c1d
--- /dev/null
+++ b/bin/gvdb.vapi
@@ -0,0 +1,21 @@
+[CCode (cheader_filename = "gvdb-builder.h")]
+namespace Gvdb {
+	[Compact]
+	[CCode (cname = "GHashTable")]
+	class HashTable : GLib.HashTable<string, Item> {
+		public HashTable (HashTable? parent = null, string? key = null);
+		public unowned Item insert (string key);
+		public void insert_string (string key, string value);
+		[CCode (cname = "gvdb_table_write_contents")]
+		public void write_contents (string filename, bool byteswap = false) throws GLib.Error;
+	}
+
+	[Compact]
+	class Item {
+		public void set_value (GLib.Variant value);
+		public void set_hash_table (HashTable table);
+		public void set_parent (Item parent);
+	}
+}
+
+// vim:noet ts=4 sw=4



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]