engine: issue warnings once per source
[dconf.git] / bin / dconf-update.vala
blob9fcc4ec33fbb7dbaedec24afb45b4770cf2bc58c
1 /*
2 * Copyright © 2010 Codethink Limited
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * Author: Ryan Lortie <desrt@desrt.ca>
22 unowned Gvdb.Item get_parent (Gvdb.HashTable table, string name) {
23 unowned Gvdb.Item parent;
25 int end = 0;
27 for (int i = 1; name[i] != '\0'; i++) {
28 if (name[i - 1] == '/') {
29 end = i;
33 assert (end != 0);
35 var parent_name = name.substring (0, end);
36 parent = table.lookup (parent_name);
38 if (parent == null) {
39 parent = table.insert (parent_name);
40 parent.set_parent (get_parent (table, parent_name));
43 return parent;
46 SList<string>? list_directory (string dirname, Posix.mode_t mode) {
47 try {
48 var list = new SList<string> ();
49 var dir = Dir.open (dirname);
50 unowned string? name;
52 while ((name = dir.read_name ()) != null) {
53 if (name.has_prefix (".")) {
54 continue;
57 var filename = Path.build_filename (dirname, name);
58 Posix.Stat buf;
60 // only files of the requested type
61 if (Posix.stat (filename, out buf) < 0 || (buf.st_mode & Posix.S_IFMT) != mode) {
62 continue;
65 list.prepend (filename);
68 return list;
69 } catch (FileError.NOENT e) {
70 /* This is expected if the directory does not exist.
71 * Just return the empty list in that case...
73 return null;
74 } catch (Error e) {
75 /* Unexpected error. Report it and return null. */
76 printerr ("warning: %s\n", e.message);
77 return null;
81 Gvdb.HashTable? read_locks_directory (string dirname) {
82 var files = list_directory (dirname, Posix.S_IFREG);
84 if (files == null) {
85 /* No locks directory or directory is empty? */
86 return null;
89 var table = new Gvdb.HashTable ();
91 foreach (var filename in files) {
92 try {
93 string contents;
94 FileUtils.get_contents (filename, out contents, null);
96 foreach (var line in contents.split ("\n")) {
97 if (line.has_prefix ("/")) {
98 table.insert_string (line, "");
101 } catch (Error e) {
102 printerr ("warning: %s\n", e.message);
106 return table;
109 Gvdb.HashTable read_directory (string dirname) {
110 var table = new Gvdb.HashTable ();
111 table.insert ("/");
113 var files = list_directory (dirname, Posix.S_IFREG);
114 files.sort (strcmp);
115 files.reverse ();
117 foreach (var filename in files) {
118 var kf = new KeyFile ();
120 try {
121 kf.load_from_file (filename, KeyFileFlags.NONE);
122 } catch (GLib.Error e) {
123 stderr.printf ("warning: Failed to read keyfile '%s': %s\n", filename, e.message);
124 continue;
127 try {
128 foreach (var group in kf.get_groups ()) {
129 if (group.has_prefix ("/") || group.has_suffix ("/") || "//" in group) {
130 stderr.printf ("%s: ignoring invalid group name: %s\n", filename, group);
131 continue;
134 foreach (var key in kf.get_keys (group)) {
135 if ("/" in key) {
136 stderr.printf ("%s: [%s]: ignoring invalid key name: %s\n", filename, group, key);
137 continue;
140 var path = "/" + group + "/" + key;
142 if (table.lookup (path) != null) {
143 /* We process the files in reverse alphabetical order. If the key is already set then
144 * it must have been set from a file with higher precedence so we should ignore this
145 * one.
147 continue;
150 var text = kf.get_value (group, key);
152 try {
153 var value = Variant.parse (null, text);
154 unowned Gvdb.Item item = table.insert (path);
155 item.set_parent (get_parent (table, path));
156 item.set_value (value);
157 } catch (VariantParseError e) {
158 stderr.printf ("%s: [%s]: %s: skipping invalid value: %s (%s)\n",
159 filename, group, key, text, e.message);
163 } catch (KeyFileError e) {
164 /* This should never happen... */
165 warning ("unexpected keyfile error: %s. Please file a bug.", e.message);
169 var locks = read_locks_directory (dirname + "/locks");
171 if (locks != null) {
172 unowned Gvdb.Item item = table.insert (".locks");
173 item.set_hash_table (locks);
176 return table;
179 void maybe_update_from_directory (string dirname) {
180 Posix.Stat dir_buf;
182 if (Posix.stat (dirname, out dir_buf) == 0 && Posix.S_ISDIR (dir_buf.st_mode)) {
183 Posix.Stat lockdir_buf;
184 Posix.Stat file_buf;
186 var filename = dirname.substring (0, dirname.length - 2);
188 if (Posix.stat (dirname + "/locks", out lockdir_buf) == 0 && lockdir_buf.st_mtime > dir_buf.st_mtime) {
189 // if the lock directory has been updated more recently then consider its timestamp instead
190 dir_buf.st_mtime = lockdir_buf.st_mtime;
193 if (Posix.stat (filename, out file_buf) == 0 && file_buf.st_mtime > dir_buf.st_mtime) {
194 return;
197 var table = read_directory (dirname);
199 var fd = Posix.open (filename, Posix.O_WRONLY);
201 if (fd < 0 && errno != Posix.ENOENT) {
202 var saved_error = errno;
203 printerr ("warning: Failed to open '%s' for replacement: %s\n", filename, strerror (saved_error));
204 return;
207 // We expect that fd < 0 here if ENOENT (ie: the db merely didn't exist yet)
209 try {
210 table.write_contents (filename);
212 if (fd >= 0) {
213 Posix.write (fd, "\0\0\0\0\0\0\0\0", 8);
215 } catch (Error e) {
216 printerr ("warning: %s\n", e.message);
217 return;
218 } finally {
219 if (fd >= 0) {
220 Posix.close (fd);
224 try {
225 var system_bus = Bus.get_sync (BusType.SYSTEM);
226 system_bus.emit_signal (null, "/ca/desrt/dconf/Writer/" + Path.get_basename (filename), "ca.desrt.dconf.Writer",
227 "WritabilityNotify", new Variant ("(s)", "/"));
228 system_bus.flush_sync ();
229 } catch {
230 /* if we can't, ... don't. */
235 void update_all (string dirname) {
236 foreach (var name in list_directory (dirname, Posix.S_IFDIR)) {
237 if (name.has_suffix (".d")) {
238 maybe_update_from_directory (name);
243 void dconf_update (string[] args) throws GLib.Error {
244 update_all ("/etc/dconf/db");
247 // vim:noet ts=4 sw=4