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
;
27 for (int i
= 1; name
[i
] != '\0'; i
++) {
28 if (name
[i
- 1] == '/') {
35 var parent_name
= name
.substring (0, end
);
36 parent
= table
.lookup (parent_name
);
39 parent
= table
.insert (parent_name
);
40 parent
.set_parent (get_parent (table
, parent_name
));
46 SList
<string>?
list_directory (string dirname
, Posix
.mode_t mode
) {
48 var list
= new SList
<string> ();
49 var dir
= Dir
.open (dirname
);
52 while ((name
= dir
.read_name ()) != null) {
53 if (name
.has_prefix (".")) {
57 var filename
= Path
.build_filename (dirname
, name
);
60 // only files of the requested type
61 if (Posix
.stat (filename
, out buf
) < 0 || (buf
.st_mode
& Posix
.S_IFMT
) != mode
) {
65 list
.prepend (filename
);
69 } catch (FileError
.NOENT e
) {
70 /* This is expected if the directory does not exist.
71 * Just return the empty list in that case...
75 /* Unexpected error. Report it and return null. */
76 printerr ("warning: %s\n", e
.message
);
81 Gvdb
.HashTable?
read_locks_directory (string dirname
) {
82 var files
= list_directory (dirname
, Posix
.S_IFREG
);
85 /* No locks directory or directory is empty? */
89 var table
= new Gvdb
.HashTable ();
91 foreach (var filename
in files
) {
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
, "");
102 printerr ("warning: %s\n", e
.message
);
109 Gvdb
.HashTable
read_directory (string dirname
) {
110 var table
= new Gvdb
.HashTable ();
113 var files
= list_directory (dirname
, Posix
.S_IFREG
);
117 foreach (var filename
in files
) {
118 var kf
= new
KeyFile ();
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
);
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
);
134 foreach (var key
in kf
.get_keys (group
)) {
136 stderr
.printf ("%s: [%s]: ignoring invalid key name: %s\n", filename
, group
, key
);
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
150 var text
= kf
.get_value (group
, key
);
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");
172 unowned Gvdb
.Item item
= table
.insert (".locks");
173 item
.set_hash_table (locks
);
179 void maybe_update_from_directory (string dirname
) {
182 if (Posix
.stat (dirname
, out dir_buf
) == 0 && Posix
.S_ISDIR (dir_buf
.st_mode
)) {
183 Posix
.Stat lockdir_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
) {
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
));
207 // We expect that fd < 0 here if ENOENT (ie: the db merely didn't exist yet)
210 table
.write_contents (filename
);
213 Posix
.write (fd
, "\0\0\0\0\0\0\0\0", 8);
216 printerr ("warning: %s\n", e
.message
);
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 ();
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