Avoid use of link_whole in the gsettings backend
[dconf.git] / bin / dconf-update.vala
blobb7a41dceb8e4fc86dbfffabc152ce1fd562d7296
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, see <http://www.gnu.org/licenses/>.
17 * Author: Ryan Lortie <desrt@desrt.ca>
20 unowned Gvdb.Item get_parent (Gvdb.HashTable table, string name) {
21 unowned Gvdb.Item parent;
23 int end = 0;
25 for (int i = 1; name[i] != '\0'; i++) {
26 if (name[i - 1] == '/') {
27 end = i;
31 assert (end != 0);
33 var parent_name = name.substring (0, end);
34 parent = table.lookup (parent_name);
36 if (parent == null) {
37 parent = table.insert (parent_name);
38 parent.set_parent (get_parent (table, parent_name));
41 return parent;
44 SList<string>? list_directory (string dirname, Posix.mode_t mode) throws GLib.Error {
45 var list = new SList<string> ();
46 var dir = Dir.open (dirname);
47 unowned string? name;
49 while ((name = dir.read_name ()) != null) {
50 if (name.has_prefix (".")) {
51 continue;
54 var filename = Path.build_filename (dirname, name);
55 Posix.Stat buf;
57 // only files of the requested type
58 if (Posix.stat (filename, out buf) < 0 || (buf.st_mode & Posix.S_IFMT) != mode) {
59 continue;
62 list.prepend (filename);
65 return list;
68 Gvdb.HashTable? read_locks_directory (string dirname) throws GLib.Error {
69 SList<string>? files;
71 try {
72 files = list_directory (dirname, Posix.S_IFREG);
73 } catch (FileError.NOENT e) {
74 /* If locks directory is missing, there are just no locks... */
75 return null;
78 var table = new Gvdb.HashTable ();
80 foreach (var filename in files) {
81 string contents;
82 FileUtils.get_contents (filename, out contents, null);
84 foreach (var line in contents.split ("\n")) {
85 if (line.has_prefix ("/")) {
86 table.insert_string (line, "");
91 return table;
94 Gvdb.HashTable read_directory (string dirname) throws GLib.Error {
95 var table = new Gvdb.HashTable ();
96 table.insert ("/");
98 var files = list_directory (dirname, Posix.S_IFREG);
99 files.sort (strcmp);
100 files.reverse ();
102 foreach (var filename in files) {
103 var kf = new KeyFile ();
105 try {
106 kf.load_from_file (filename, KeyFileFlags.NONE);
107 } catch (GLib.Error e) {
108 e.message = "warning: Failed to read keyfile '%s': %s".printf (filename, e.message);
109 throw e;
112 try {
113 foreach (var group in kf.get_groups ()) {
114 if (group.has_prefix ("/") || group.has_suffix ("/") || "//" in group) {
115 stderr.printf ("%s: ignoring invalid group name: %s\n", filename, group);
116 continue;
119 foreach (var key in kf.get_keys (group)) {
120 if ("/" in key) {
121 throw new KeyFileError.INVALID_VALUE ("%s: [%s]: invalid key name: %s",
122 filename, group, key);
125 var path = "/" + group + "/" + key;
127 if (table.lookup (path) != null) {
128 /* We process the files in reverse alphabetical order. If the key is already set then
129 * it must have been set from a file with higher precedence so we should ignore this
130 * one.
132 continue;
135 var text = kf.get_value (group, key);
137 try {
138 var value = Variant.parse (null, text);
139 unowned Gvdb.Item item = table.insert (path);
140 item.set_parent (get_parent (table, path));
141 item.set_value (value);
142 } catch (VariantParseError e) {
143 e.message = "%s: [%s]: %s: invalid value: %s (%s)".printf (filename, group, key, text, e.message);
144 throw e;
148 } catch (KeyFileError e) {
149 /* This should never happen... */
150 warning ("unexpected keyfile error: %s. Please file a bug.", e.message);
151 assert_not_reached ();
155 var locks = read_locks_directory (dirname + "/locks");
157 if (locks != null) {
158 unowned Gvdb.Item item = table.insert (".locks");
159 item.set_hash_table (locks);
162 return table;
165 void maybe_update_from_directory (string dirname) throws GLib.Error {
166 Posix.Stat dir_buf;
168 if (Posix.stat (dirname, out dir_buf) == 0 && Posix.S_ISDIR (dir_buf.st_mode)) {
169 Posix.Stat lockdir_buf;
170 Posix.Stat file_buf;
172 var filename = dirname.substring (0, dirname.length - 2);
174 if (Posix.stat (dirname + "/locks", out lockdir_buf) == 0 && lockdir_buf.st_mtime > dir_buf.st_mtime) {
175 // if the lock directory has been updated more recently then consider its timestamp instead
176 dir_buf.st_mtime = lockdir_buf.st_mtime;
179 if (Posix.stat (filename, out file_buf) == 0 && file_buf.st_mtime > dir_buf.st_mtime) {
180 return;
183 var table = read_directory (dirname);
185 var fd = Posix.open (filename, Posix.O_WRONLY);
187 if (fd < 0 && errno != Posix.ENOENT) {
188 var saved_error = errno;
189 printerr ("warning: Failed to open '%s' for replacement: %s\n", filename, strerror (saved_error));
190 return;
193 // We expect that fd < 0 here if ENOENT (ie: the db merely didn't exist yet)
195 try {
196 table.write_contents (filename);
198 if (fd >= 0) {
199 Posix.write (fd, "\0\0\0\0\0\0\0\0", 8);
201 } catch (Error e) {
202 printerr ("warning: %s\n", e.message);
203 return;
204 } finally {
205 if (fd >= 0) {
206 Posix.close (fd);
210 try {
211 var system_bus = Bus.get_sync (BusType.SYSTEM);
212 system_bus.emit_signal (null, "/ca/desrt/dconf/Writer/" + Path.get_basename (filename),
213 "ca.desrt.dconf.Writer", "WritabilityNotify", new Variant ("(s)", "/"));
214 system_bus.flush_sync ();
215 } catch {
216 /* if we can't, ... don't. */
221 void update_all (string dirname) throws GLib.Error {
222 foreach (var name in list_directory (dirname, Posix.S_IFDIR)) {
223 if (name.has_suffix (".d")) {
224 try {
225 maybe_update_from_directory (name);
226 } catch (Error e) {
227 printerr ("unable to compile %s: %s\n", name, e.message);
233 void dconf_compile (string[] args) throws GLib.Error {
234 if (args[2] == null || args[3] == null || args[4] != null) {
235 throw new OptionError.FAILED ("must give output file and .d dir");
238 try {
239 // We always write the result of "dconf compile" as little endian
240 // so that it can be installed in /usr/share
241 var table = read_directory (args[3]);
242 var should_byteswap = (BYTE_ORDER == ByteOrder.BIG_ENDIAN);
243 table.write_contents (args[2], should_byteswap);
244 } catch (Error e) {
245 printerr ("%s\n", e.message);
246 Process.exit (1);
250 void dconf_update (string[] args) throws GLib.Error {
251 update_all ("/etc/dconf/db");
254 // vim:noet ts=4 sw=4