nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Copyright © 2009-10 Sam Thursfield |
||
3 | * |
||
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. |
||
8 | * |
||
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. |
||
13 | * |
||
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/>. |
||
16 | * |
||
17 | * Author: Sam Thursfield <ssssam@gmail.com> |
||
18 | */ |
||
19 | |||
20 | /* GRegistryBackend implementation notes: |
||
21 | * |
||
22 | * - All settings are stored under the path: |
||
23 | * HKEY_CURRENT_USER\Software\GSettings\ |
||
24 | * This means all settings are per-user. Permissions and system-wide |
||
25 | * defaults are not implemented and will probably always be out of scope of |
||
26 | * the Windows port of GLib. |
||
27 | * |
||
28 | * - The registry type system is limited. Most GVariant types are stored as |
||
29 | * literals via g_variant_print/parse(). Strings are stored without the |
||
30 | * quotes that GVariant requires. Integer types are stored as native |
||
31 | * REG_DWORD or REG_QWORD. The REG_MULTI_SZ (string array) type could be |
||
32 | * used to avoid flattening container types. |
||
33 | * |
||
34 | * - Notifications are handled; the change event is watched for in a separate |
||
35 | * thread (Windows does not provide a callback API) which sends them with |
||
36 | * g_idle_add to the GLib main loop. The threading is done using Windows |
||
37 | * API functions, so there is no dependence on GThread. |
||
38 | * |
||
39 | * - Windows doesn't tell us which value has changed. This means we have to |
||
40 | * maintain a cache of every stored value so we can play spot the |
||
41 | * difference. This should not be a performance issue because if you are |
||
42 | * storing thousands of values in GSettings, you are probably using it |
||
43 | * wrong. |
||
44 | * |
||
45 | * - The cache stores the value as a registry type. Because many variants are |
||
46 | * stored as string representations, values which have changed equality but |
||
47 | * not equivalence may trigger spurious change notifications. GSettings |
||
48 | * users must already deal with this possibility and converting all data to |
||
49 | * GVariant values would be more effort. |
||
50 | * |
||
51 | * - Because we have to cache every registry value locally, reads are done |
||
52 | * from the cache rather than directly from the registry. Writes update |
||
53 | * both. This means that the backend will not work if the watch thread is |
||
54 | * not running. A GSettings object always subscribes to changes so we can |
||
55 | * be sure that the watch thread will be running, but if for some reason |
||
56 | * the backend is being used directly you should bear that in mind. |
||
57 | * |
||
58 | * - The registry is totally user-editable, so we are very forgiving about |
||
59 | * errors in the data we get. |
||
60 | * |
||
61 | * - The registry uses backslashes as path separators. GSettings keys only |
||
62 | * allow [A-Za-z\-] so no escaping is needed. No attempt is made to solve |
||
63 | * clashes between keys differing only in case. |
||
64 | * |
||
65 | * - RegCreateKeyW is used - We should always make the UTF-8 -> UTF-16 |
||
66 | * conversion ourselves to avoid problems when the system language changes. |
||
67 | * |
||
68 | * - The Windows registry has the following limitations: a key may not exceed |
||
69 | * 255 characters, an entry's value may not exceed 16,383 characters, and |
||
70 | * all the values of a key may not exceed 65,535 characters. |
||
71 | * |
||
72 | * - Terminology: |
||
73 | * * in GSettings, a 'key' is eg. /desktop/gnome/background/primary-color |
||
74 | * * in the registry, the 'key' is path, which contains some 'values'. |
||
75 | * * in this file, any GSettings key is a 'key', while a registry key is |
||
76 | * termed a 'path', which contains 'values'. |
||
77 | * |
||
78 | * - My set of tests for this backend are currently at: |
||
79 | * http://gitorious.org/gsettings-gtk/gsettings-test.git |
||
80 | * |
||
81 | * - There is an undocumented function in ntdll.dll which might be more |
||
82 | * than RegNotifyChangeKeyValue(), NtNotifyChangeKey: |
||
83 | * http://source.winehq.org/source/dlls/ntdll/reg.c#L618 |
||
84 | * http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Key/NtNotifyChangeKey.html |
||
85 | * |
||
86 | * - If updating the cache ever becomes a performance issue it may make sense |
||
87 | * to use a red-black tree, but I don't currently think it's worth the time |
||
88 | */ |
||
89 | |||
90 | #include "config.h" |
||
91 | |||
92 | #include "gregistrysettingsbackend.h" |
||
93 | #include "gsettingsbackend.h" |
||
94 | #include "giomodule.h" |
||
95 | |||
96 | #include <windows.h> |
||
97 | |||
98 | //#define TRACE |
||
99 | |||
100 | /* GSettings' limit */ |
||
101 | #define MAX_KEY_NAME_LENGTH 32 |
||
102 | |||
103 | /* Testing (on Windows XP SP3) shows that WaitForMultipleObjects fails with |
||
104 | * "The parameter is incorrect" after 64 watches. We need one for the |
||
105 | * message_sent cond, which is allowed for in the way the watches_remaining |
||
106 | * variable is used. |
||
107 | */ |
||
108 | #define MAX_WATCHES 64 |
||
109 | |||
110 | /* A watch on one registry path and its subkeys */ |
||
111 | typedef struct |
||
112 | { |
||
113 | HANDLE event; |
||
114 | HKEY hpath; |
||
115 | char *prefix; |
||
116 | GNode *cache_node; |
||
117 | } RegistryWatch; |
||
118 | |||
119 | /* Simple message passing for the watch thread. Not enough traffic to |
||
120 | * justify a queue. |
||
121 | */ |
||
122 | typedef enum |
||
123 | { |
||
124 | WATCH_THREAD_NONE, |
||
125 | WATCH_THREAD_ADD_WATCH, |
||
126 | WATCH_THREAD_REMOVE_WATCH, |
||
127 | WATCH_THREAD_STOP |
||
128 | } WatchThreadMessageType; |
||
129 | |||
130 | typedef struct |
||
131 | { |
||
132 | WatchThreadMessageType type; |
||
133 | RegistryWatch watch; |
||
134 | } WatchThreadMessage; |
||
135 | |||
136 | typedef struct |
||
137 | { |
||
138 | GSettingsBackend *owner; |
||
139 | HANDLE *thread; |
||
140 | |||
141 | /* Details of the things we are watching. */ |
||
142 | int watches_remaining; |
||
143 | GPtrArray *events, *handles, *prefixes, *cache_nodes; |
||
144 | |||
145 | /* Communication with the main thread. Only one message is stored at a time, |
||
146 | * to make sure that messages are acknowledged before being overwritten we |
||
147 | * create two events - one is signalled when a new message is set, the |
||
148 | * other is signalled by the thread when it has processed the message. |
||
149 | */ |
||
150 | WatchThreadMessage message; |
||
151 | CRITICAL_SECTION *message_lock; |
||
152 | HANDLE message_sent_event, message_received_event; |
||
153 | } WatchThreadState; |
||
154 | |||
155 | #define G_TYPE_REGISTRY_BACKEND (g_registry_backend_get_type ()) |
||
156 | #define G_REGISTRY_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ |
||
157 | G_TYPE_REGISTRY_BACKEND, GRegistryBackend)) |
||
158 | #define G_IS_REGISTRY_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ |
||
159 | G_TYPE_REGISTRY_BACKEND)) |
||
160 | |||
161 | typedef GSettingsBackendClass GRegistryBackendClass; |
||
162 | |||
163 | typedef struct { |
||
164 | GSettingsBackend parent_instance; |
||
165 | |||
166 | gchar *base_path; |
||
167 | gunichar2 *base_pathw; |
||
168 | |||
169 | /* A stored copy of the whole tree being watched. When we receive a change notification |
||
170 | * we have to check against this to see what has changed ... every time ...*/ |
||
171 | CRITICAL_SECTION *cache_lock; |
||
172 | GNode *cache_root; |
||
173 | |||
174 | WatchThreadState *watch; |
||
175 | } GRegistryBackend; |
||
176 | |||
177 | G_DEFINE_TYPE_WITH_CODE (GRegistryBackend, |
||
178 | g_registry_backend, |
||
179 | G_TYPE_SETTINGS_BACKEND, |
||
180 | g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME, |
||
181 | g_define_type_id, "registry", 90)) |
||
182 | |||
183 | /********************************************************************************** |
||
184 | * Utility functions |
||
185 | **********************************************************************************/ |
||
186 | |||
187 | #include <stdio.h> |
||
188 | static void |
||
189 | trace (const char *format, |
||
190 | ...) |
||
191 | { |
||
192 | #ifdef TRACE |
||
193 | va_list va; va_start (va, format); |
||
194 | vprintf (format, va); |
||
195 | fflush (stdout); |
||
196 | va_end (va); |
||
197 | #endif |
||
198 | } |
||
199 | |||
200 | /* g_message including a windows error message. It is not useful to have an |
||
201 | * equivalent function for g_warning because none of the registry errors can |
||
202 | * result from programmer error (Microsoft programmers don't count), instead |
||
203 | * they will mostly occur from people messing with the registry by hand. */ |
||
204 | static void |
||
205 | g_message_win32_error (DWORD result_code, |
||
206 | const gchar *format, |
||
207 | ...) |
||
208 | { |
||
209 | va_list va; |
||
210 | gchar *message; |
||
211 | gchar *win32_error; |
||
212 | gchar *win32_message; |
||
213 | |||
214 | g_return_if_fail (result_code != 0); |
||
215 | |||
216 | va_start (va, format); |
||
217 | message = g_strdup_vprintf (format, va); |
||
218 | win32_error = g_win32_error_message (result_code); |
||
219 | win32_message = g_strdup_printf ("%s: %s", message, win32_error); |
||
220 | g_free (message); |
||
221 | g_free (win32_error); |
||
222 | |||
223 | if (result_code == ERROR_KEY_DELETED) |
||
224 | trace ("(%s)", win32_message); |
||
225 | else |
||
226 | g_message ("%s", win32_message); |
||
227 | |||
228 | g_free (win32_message); |
||
229 | } |
||
230 | |||
231 | /* Make gsettings key into a registry path & value pair. |
||
232 | * |
||
233 | * Note that the return value *only* needs freeing - registry_value_name |
||
234 | * is a pointer to further inside the same block of memory. |
||
235 | */ |
||
236 | static gchar * |
||
237 | parse_key (const gchar *key_name, |
||
238 | const gchar *registry_prefix, |
||
239 | gchar **value_name) |
||
240 | { |
||
241 | gchar *path_name, *c; |
||
242 | |||
243 | /* All key paths are treated as absolute; gsettings doesn't seem to enforce a |
||
244 | * preceding /. |
||
245 | */ |
||
246 | if (key_name[0] == '/') |
||
247 | key_name++; |
||
248 | |||
249 | if (registry_prefix == NULL) |
||
250 | path_name = g_strdup (key_name); |
||
251 | else |
||
252 | path_name = g_strjoin ("/", registry_prefix, key_name, NULL); |
||
253 | |||
254 | /* Prefix is expected to be in registry format (\ separators) so don't escape that. */ |
||
255 | for (c = path_name + (registry_prefix ? strlen (registry_prefix) : 0); *c != 0; c++) |
||
256 | { |
||
257 | if (*c == '/') |
||
258 | { |
||
259 | *c = '\\'; |
||
260 | *value_name = c; |
||
261 | } |
||
262 | } |
||
263 | |||
264 | **value_name = 0; |
||
265 | (*value_name)++; |
||
266 | |||
267 | return path_name; |
||
268 | } |
||
269 | |||
270 | static DWORD |
||
271 | g_variant_get_as_dword (GVariant *variant) |
||
272 | { |
||
273 | switch (g_variant_get_type_string (variant)[0]) |
||
274 | { |
||
275 | case 'b': |
||
276 | return g_variant_get_boolean (variant); |
||
277 | case 'y': |
||
278 | return g_variant_get_byte (variant); |
||
279 | case 'n': |
||
280 | return g_variant_get_int16 (variant); |
||
281 | case 'q': |
||
282 | return g_variant_get_uint16 (variant); |
||
283 | case 'i': |
||
284 | return g_variant_get_int32 (variant); |
||
285 | case 'u': |
||
286 | return g_variant_get_uint32 (variant); |
||
287 | default: |
||
288 | g_warn_if_reached (); |
||
289 | } |
||
290 | return 0; |
||
291 | } |
||
292 | |||
293 | static DWORDLONG |
||
294 | g_variant_get_as_qword (GVariant *variant) |
||
295 | { |
||
296 | switch (g_variant_get_type_string (variant)[0]) |
||
297 | { |
||
298 | case 'x': |
||
299 | return g_variant_get_int64 (variant); |
||
300 | case 't': |
||
301 | return g_variant_get_uint64 (variant); |
||
302 | default: |
||
303 | g_warn_if_reached (); |
||
304 | } |
||
305 | return 0; |
||
306 | } |
||
307 | |||
308 | static void |
||
309 | handle_read_error (LONG result, |
||
310 | const gchar *path_name, |
||
311 | const gchar *value_name) |
||
312 | { |
||
313 | /* file not found means key value not set, this isn't an error for us. */ |
||
314 | if (result != ERROR_FILE_NOT_FOUND) |
||
315 | g_message_win32_error (result, "Unable to query value %s/%s: %s.\n", |
||
316 | path_name, value_name); |
||
317 | } |
||
318 | |||
319 | /*************************************************************************** |
||
320 | * Cache of registry values |
||
321 | ***************************************************************************/ |
||
322 | |||
323 | /* Generic container for registry values */ |
||
324 | typedef struct { |
||
325 | DWORD type; |
||
326 | |||
327 | union { |
||
328 | gint dword; /* FIXME: could inline QWORD on 64-bit systems too */ |
||
329 | void *ptr; |
||
330 | }; |
||
331 | } RegistryValue; |
||
332 | |||
333 | static char * |
||
334 | registry_value_dump (RegistryValue value) |
||
335 | { |
||
336 | if (value.type == REG_DWORD) |
||
337 | return g_strdup_printf ("%d", value.dword); |
||
338 | else if (value.type == REG_QWORD) |
||
339 | return g_strdup_printf ("%"G_GINT64_FORMAT, value.ptr == NULL ? 0: *(DWORDLONG *)value.ptr); |
||
340 | else if (value.type == REG_SZ) |
||
341 | return g_strdup_printf ("%s", (char *)value.ptr); |
||
342 | else if (value.type == REG_NONE) |
||
343 | return g_strdup_printf ("<empty>"); |
||
344 | else |
||
345 | return g_strdup_printf ("<invalid>"); |
||
346 | } |
||
347 | |||
348 | static void |
||
349 | registry_value_free (RegistryValue value) |
||
350 | { |
||
351 | if (value.type == REG_SZ || value.type == REG_QWORD) |
||
352 | g_free (value.ptr); |
||
353 | |||
354 | value.type = REG_NONE; |
||
355 | value.ptr = NULL; |
||
356 | } |
||
357 | |||
358 | /* The registry cache is stored as a tree, for easy traversal. Right now we |
||
359 | * don't sort it in a clever way. Each node corresponds to a path element |
||
360 | * ('key' in registry terms) or a value. |
||
361 | * |
||
362 | * Each subscription uses the same cache. Because GSettings can subscribe to |
||
363 | * the tree at any node any number of times, we need to reference count the |
||
364 | * nodes. |
||
365 | */ |
||
366 | typedef struct |
||
367 | { |
||
368 | /* Component of path that this node represents */ |
||
369 | gchar *name; |
||
370 | |||
371 | /* If a watch is subscribed at this point (subscription_count > 0) we can |
||
372 | * block its next notification. This is useful because if two watches cover |
||
373 | * the same path, both will trigger when it changes. It also allows changes |
||
374 | * done by the application to be ignored by the watch thread. |
||
375 | */ |
||
376 | gint32 block_count : 8; |
||
377 | |||
378 | /* Number of times g_settings_subscribe has been called for this location |
||
379 | * (I guess you can't subscribe more than 16383 times) */ |
||
380 | gint32 subscription_count : 14; |
||
381 | |||
382 | gint32 ref_count : 9; |
||
383 | |||
384 | gint32 readable : 1; |
||
385 | RegistryValue value; |
||
386 | } RegistryCacheItem; |
||
387 | |||
388 | static GNode * |
||
389 | registry_cache_add_item (GNode *parent, |
||
390 | gchar *name, |
||
391 | RegistryValue value, |
||
392 | gint ref_count) |
||
393 | { |
||
394 | RegistryCacheItem *item; |
||
395 | GNode *cache_node; |
||
396 | |||
397 | g_return_val_if_fail (name != NULL, NULL); |
||
398 | g_return_val_if_fail (parent != NULL, NULL); |
||
399 | |||
400 | item = g_slice_new (RegistryCacheItem); |
||
401 | |||
402 | /* Ref count should be the number of watch points above this node */ |
||
403 | item->ref_count = ref_count; |
||
404 | |||
405 | item->name = g_strdup (name); |
||
406 | item->value = value; |
||
407 | item->subscription_count = 0; |
||
408 | item->block_count = 0; |
||
409 | item->readable = FALSE; |
||
410 | |||
411 | trace ("\treg cache: adding %s to %s\n", |
||
412 | name, ((RegistryCacheItem *)parent->data)->name); |
||
413 | |||
414 | cache_node = g_node_new (item); |
||
415 | g_node_append (parent, cache_node); |
||
416 | |||
417 | return cache_node; |
||
418 | } |
||
419 | |||
420 | /* The reference counting of cache tree nodes works like this: when a node is |
||
421 | * subscribed to (GSettings wants us to watch that path and everything below |
||
422 | * it) the reference count of that node and everything below is increased, as |
||
423 | * well as each parent up to the root. |
||
424 | */ |
||
425 | |||
426 | static void |
||
427 | _ref_down (GNode *node) |
||
428 | { |
||
429 | RegistryCacheItem *item = node->data; |
||
430 | |||
431 | g_node_children_foreach (node, G_TRAVERSE_ALL, |
||
432 | (GNodeForeachFunc)_ref_down, NULL); |
||
433 | item->ref_count++; |
||
434 | } |
||
435 | |||
436 | static void |
||
437 | registry_cache_ref_tree (GNode *tree) |
||
438 | { |
||
439 | RegistryCacheItem *item = tree->data; |
||
440 | GNode *node = tree->parent; |
||
441 | |||
442 | g_return_if_fail (tree != NULL); |
||
443 | |||
444 | item->ref_count++; |
||
445 | |||
446 | g_node_children_foreach (tree, G_TRAVERSE_ALL, |
||
447 | (GNodeForeachFunc)_ref_down, NULL); |
||
448 | |||
449 | for (node = tree->parent; node; node = node->parent) |
||
450 | { |
||
451 | item = node->data; |
||
452 | item->ref_count++; |
||
453 | } |
||
454 | } |
||
455 | |||
456 | static void |
||
457 | registry_cache_item_free (RegistryCacheItem *item) |
||
458 | { |
||
459 | trace ("\t -- Free node %s\n", item->name); |
||
460 | |||
461 | g_free (item->name); |
||
462 | registry_value_free (item->value); |
||
463 | g_slice_free (RegistryCacheItem, item); |
||
464 | } |
||
465 | |||
466 | /* Unreferencing has to be done bottom-up */ |
||
467 | static void |
||
468 | _unref_node (GNode *node) |
||
469 | { |
||
470 | RegistryCacheItem *item = node->data; |
||
471 | |||
472 | item->ref_count--; |
||
473 | |||
474 | g_warn_if_fail (item->ref_count >= 0); |
||
475 | |||
476 | if (item->ref_count == 0) |
||
477 | { |
||
478 | registry_cache_item_free (item); |
||
479 | g_node_destroy (node); |
||
480 | } |
||
481 | } |
||
482 | |||
483 | static void |
||
484 | _unref_down (GNode *node) |
||
485 | { |
||
486 | g_node_children_foreach (node, G_TRAVERSE_ALL, |
||
487 | (GNodeForeachFunc)_unref_down, NULL); |
||
488 | _unref_node (node); |
||
489 | } |
||
490 | |||
491 | static void |
||
492 | registry_cache_unref_tree (GNode *tree) |
||
493 | { |
||
494 | GNode *parent = tree->parent, *next_parent; |
||
495 | |||
496 | _unref_down (tree); |
||
497 | |||
498 | while (parent) |
||
499 | { |
||
500 | next_parent = parent->parent; |
||
501 | _unref_node (parent); |
||
502 | parent = next_parent; |
||
503 | } |
||
504 | } |
||
505 | |||
506 | #if 0 |
||
507 | static void |
||
508 | registry_cache_dump (GNode *cache_node, |
||
509 | gpointer data) |
||
510 | { |
||
511 | RegistryCacheItem *item = cache_node->data; |
||
512 | |||
513 | int depth = GPOINTER_TO_INT(data), |
||
514 | new_depth = depth+1, |
||
515 | i; |
||
516 | |||
517 | g_return_if_fail (cache_node != NULL); |
||
518 | |||
519 | for (i=0; i<depth; i++) |
||
520 | g_print (" "); |
||
521 | if (item == NULL) |
||
522 | g_print ("*root*\n"); |
||
523 | else |
||
524 | g_print ("'%s' [%i] @ %x = %s\n", item->name, item->ref_count, (guint)cache_node, |
||
525 | registry_value_dump (item->value)); |
||
526 | g_node_children_foreach (cache_node, G_TRAVERSE_ALL, registry_cache_dump, |
||
527 | GINT_TO_POINTER (new_depth)); |
||
528 | } |
||
529 | #endif |
||
530 | |||
531 | typedef struct |
||
532 | { |
||
533 | gchar *name; |
||
534 | GNode *result; |
||
535 | } RegistryCacheSearch; |
||
536 | |||
537 | static gboolean |
||
538 | registry_cache_find_compare (GNode *node, |
||
539 | gpointer data) |
||
540 | { |
||
541 | RegistryCacheSearch *search = data; |
||
542 | RegistryCacheItem *item = node->data; |
||
543 | |||
544 | if (item == NULL) /* root node */ |
||
545 | return FALSE; |
||
546 | |||
547 | g_return_val_if_fail (search->name != NULL, FALSE); |
||
548 | g_return_val_if_fail (item->name != NULL, FALSE); |
||
549 | |||
550 | if (strcmp (search->name, item->name) == 0) |
||
551 | { |
||
552 | search->result = node; |
||
553 | return TRUE; |
||
554 | } |
||
555 | |||
556 | return FALSE; |
||
557 | } |
||
558 | |||
559 | static GNode * |
||
560 | registry_cache_find_immediate_child (GNode *node, |
||
561 | gchar *name) |
||
562 | { |
||
563 | RegistryCacheSearch search; |
||
564 | |||
565 | search.result = NULL; |
||
566 | search.name = name; |
||
567 | |||
568 | g_node_traverse (node, G_POST_ORDER, G_TRAVERSE_ALL, 2, |
||
569 | registry_cache_find_compare, &search); |
||
570 | |||
571 | return search.result; |
||
572 | } |
||
573 | |||
574 | static GNode * |
||
575 | registry_cache_get_node_for_key_recursive (GNode *node, |
||
576 | gchar *key_name, |
||
577 | gboolean create_if_not_found, |
||
578 | gint n_parent_watches) |
||
579 | { |
||
580 | RegistryCacheItem *item; |
||
581 | gchar *component = key_name; |
||
582 | gchar *c = strchr (component, '/'); |
||
583 | GNode *child; |
||
584 | |||
585 | if (c != NULL) |
||
586 | *c = 0; |
||
587 | |||
588 | /* We count up how many watch points we travel through finding this node, |
||
589 | * because a new node should have as many references as there are watches at |
||
590 | * points above it in the tree. |
||
591 | */ |
||
592 | item = node->data; |
||
593 | if (item->subscription_count > 0) |
||
594 | n_parent_watches++; |
||
595 | |||
596 | child = registry_cache_find_immediate_child (node, component); |
||
597 | if (child == NULL && create_if_not_found) |
||
598 | { |
||
599 | RegistryValue null_value = { REG_NONE, {0} }; |
||
600 | |||
601 | child = registry_cache_add_item (node, component, |
||
602 | null_value, n_parent_watches); |
||
603 | |||
604 | trace ("\tget node for key recursive: new %x = %s.\n", node, component); |
||
605 | } |
||
606 | |||
607 | /* We are done if there are no more path components. Allow for a trailing /. */ |
||
608 | if (child == NULL || c == NULL || *(c + 1) == 0) |
||
609 | return child; |
||
610 | |||
611 | trace ("get node for key recursive: next: %s.\n", c + 1); |
||
612 | |||
613 | return registry_cache_get_node_for_key_recursive (child, c + 1, |
||
614 | create_if_not_found, |
||
615 | n_parent_watches); |
||
616 | } |
||
617 | |||
618 | /* Look up a GSettings key in the cache. */ |
||
619 | static GNode * |
||
620 | registry_cache_get_node_for_key (GNode *root, |
||
621 | const gchar *key_name, |
||
622 | gboolean create_if_not_found) |
||
623 | { |
||
624 | GNode *child = NULL; |
||
625 | GNode *result = NULL; |
||
626 | gchar *component, *c; |
||
627 | |||
628 | g_return_val_if_fail (key_name != NULL, NULL); |
||
629 | |||
630 | if (key_name[0] == '/') |
||
631 | key_name++; |
||
632 | |||
633 | /* Ignore preceding / */ |
||
634 | component = g_strdup (key_name); |
||
635 | c = strchr (component, '/'); |
||
636 | |||
637 | if (c == NULL) |
||
638 | { |
||
639 | g_free (component); |
||
640 | return root; |
||
641 | } |
||
642 | |||
643 | if (c != NULL) |
||
644 | *c = 0; |
||
645 | |||
646 | child = registry_cache_find_immediate_child (root, component); |
||
647 | if (child == NULL && create_if_not_found) |
||
648 | { |
||
649 | RegistryValue null_value = { REG_NONE, {0} }; |
||
650 | |||
651 | /* Reference count is set to 0, tree should be referenced by the caller */ |
||
652 | child = registry_cache_add_item (root, component, |
||
653 | null_value, 0); |
||
654 | |||
655 | trace ("get_node_for_key: New node for component '%s'\n", component); |
||
656 | } |
||
657 | |||
658 | if (*(c + 1) == 0) |
||
659 | result = child; |
||
660 | else if (child != NULL) |
||
661 | result = registry_cache_get_node_for_key_recursive (child, c + 1, |
||
662 | create_if_not_found, 0); |
||
663 | |||
664 | g_free (component); |
||
665 | |||
666 | return result; |
||
667 | } |
||
668 | |||
669 | /* Check the cache node against the registry key it represents. Return TRUE if |
||
670 | * they differ, and update the cache with the new value. |
||
671 | */ |
||
672 | static gboolean |
||
673 | registry_cache_update_node (GNode *cache_node, |
||
674 | RegistryValue registry_value) |
||
675 | { |
||
676 | RegistryCacheItem *cache_item; |
||
677 | |||
678 | g_return_val_if_fail (cache_node != NULL, FALSE); |
||
679 | g_return_val_if_fail (cache_node->data != NULL, FALSE); |
||
680 | |||
681 | cache_item = cache_node->data; |
||
682 | |||
683 | if (registry_value.type != cache_item->value.type) |
||
684 | { |
||
685 | /* The type has changed. Update cache item and register it as changed. |
||
686 | * Either the schema has changed and this is entirely legitimate, or |
||
687 | * whenever the app reads the key it will get the default value due to |
||
688 | * the type mismatch. |
||
689 | */ |
||
690 | cache_item->value = registry_value; |
||
691 | return TRUE; |
||
692 | } |
||
693 | |||
694 | switch (registry_value.type) |
||
695 | { |
||
696 | case REG_DWORD: |
||
697 | { |
||
698 | if (cache_item->value.dword == registry_value.dword) |
||
699 | return FALSE; |
||
700 | else |
||
701 | { |
||
702 | cache_item->value.dword = registry_value.dword; |
||
703 | return TRUE; |
||
704 | } |
||
705 | } |
||
706 | case REG_QWORD: |
||
707 | { |
||
708 | g_return_val_if_fail (registry_value.ptr != NULL && |
||
709 | cache_item->value.ptr != NULL, FALSE); |
||
710 | |||
711 | if (memcmp (registry_value.ptr, cache_item->value.ptr, 8)==0) |
||
712 | { |
||
713 | g_free (registry_value.ptr); |
||
714 | return FALSE; |
||
715 | } |
||
716 | else |
||
717 | { |
||
718 | g_free (cache_item->value.ptr); |
||
719 | cache_item->value.ptr = registry_value.ptr; |
||
720 | return TRUE; |
||
721 | } |
||
722 | } |
||
723 | case REG_SZ: |
||
724 | { |
||
725 | /* Value should not exist if it is NULL, an empty string is "" */ |
||
726 | g_return_val_if_fail (cache_item->value.ptr != NULL, FALSE); |
||
727 | g_return_val_if_fail (registry_value.ptr != NULL, FALSE); |
||
728 | |||
729 | if (strcmp (registry_value.ptr, cache_item->value.ptr) == 0) |
||
730 | { |
||
731 | g_free (registry_value.ptr); |
||
732 | return FALSE; |
||
733 | } |
||
734 | else |
||
735 | { |
||
736 | g_free (cache_item->value.ptr); |
||
737 | cache_item->value.ptr = registry_value.ptr; |
||
738 | return TRUE; |
||
739 | } |
||
740 | } |
||
741 | default: |
||
742 | g_warning ("gregistrybackend: registry_cache_update_node: Unhandled value type"); |
||
743 | return FALSE; |
||
744 | } |
||
745 | } |
||
746 | |||
747 | /* Blocking notifications is a useful optimisation. When a change is made |
||
748 | * through GSettings we update the cache manually, but a notifcation is |
||
749 | * triggered as well. This function is also used for nested notifications, |
||
750 | * eg. if /test and /test/foo are watched, and /test/foo/value is changed then |
||
751 | * we will get notified both for /test/foo and /test and it is helpful to block |
||
752 | * the second. |
||
753 | */ |
||
754 | static void |
||
755 | registry_cache_block_notification (GNode *node) |
||
756 | { |
||
757 | RegistryCacheItem *item = node->data; |
||
758 | |||
759 | g_return_if_fail (node != NULL); |
||
760 | |||
761 | if (item->subscription_count > 0) |
||
762 | item->block_count++; |
||
763 | |||
764 | if (node->parent != NULL) |
||
765 | registry_cache_block_notification (node->parent); |
||
766 | } |
||
767 | |||
768 | static void registry_cache_destroy_tree (GNode *node, |
||
769 | WatchThreadState *self); |
||
770 | |||
771 | /*************************************************************************** |
||
772 | * Reading and writing |
||
773 | ***************************************************************************/ |
||
774 | |||
775 | static gboolean |
||
776 | registry_read (HKEY hpath, |
||
777 | const gchar *path_name, |
||
778 | const gchar *value_name, |
||
779 | RegistryValue *p_value) |
||
780 | { |
||
781 | LONG result; |
||
782 | DWORD value_data_size; |
||
783 | gpointer *buffer; |
||
784 | gunichar2 *value_namew; |
||
785 | |||
786 | g_return_val_if_fail (p_value != NULL, FALSE); |
||
787 | |||
788 | p_value->type = REG_NONE; |
||
789 | p_value->ptr = NULL; |
||
790 | |||
791 | value_namew = g_utf8_to_utf16 (value_name, -1, NULL, NULL, NULL); |
||
792 | |||
793 | result = RegQueryValueExW (hpath, value_namew, 0, &p_value->type, NULL, &value_data_size); |
||
794 | if (result != ERROR_SUCCESS) |
||
795 | { |
||
796 | handle_read_error (result, path_name, value_name); |
||
797 | g_free (value_namew); |
||
798 | return FALSE; |
||
799 | } |
||
800 | |||
801 | if (p_value->type == REG_SZ && value_data_size == 0) |
||
802 | { |
||
803 | p_value->ptr = g_strdup (""); |
||
804 | g_free (value_namew); |
||
805 | return TRUE; |
||
806 | } |
||
807 | |||
808 | if (p_value->type == REG_DWORD) |
||
809 | /* REG_DWORD is inlined */ |
||
810 | buffer = (void *)&p_value->dword; |
||
811 | else |
||
812 | buffer = p_value->ptr = g_malloc (value_data_size); |
||
813 | |||
814 | result = RegQueryValueExW (hpath, value_namew, 0, NULL, (LPBYTE)buffer, &value_data_size); |
||
815 | g_free (value_namew); |
||
816 | |||
817 | if (result != ERROR_SUCCESS) |
||
818 | { |
||
819 | handle_read_error (result, path_name, value_name); |
||
820 | |||
821 | if (p_value->type != REG_DWORD) |
||
822 | g_free (buffer); |
||
823 | |||
824 | return FALSE; |
||
825 | } |
||
826 | |||
827 | if (p_value->type == REG_SZ) |
||
828 | { |
||
829 | gchar *valueu8 = g_utf16_to_utf8 (p_value->ptr, -1, NULL, NULL, NULL); |
||
830 | g_free (p_value->ptr); |
||
831 | p_value->ptr = valueu8; |
||
832 | } |
||
833 | |||
834 | return TRUE; |
||
835 | } |
||
836 | |||
837 | static GVariant * |
||
838 | g_registry_backend_read (GSettingsBackend *backend, |
||
839 | const gchar *key_name, |
||
840 | const GVariantType *expected_type, |
||
841 | gboolean default_value) |
||
842 | { |
||
843 | GRegistryBackend *self = G_REGISTRY_BACKEND (backend); |
||
844 | GNode *cache_node; |
||
845 | RegistryValue registry_value; |
||
846 | GVariant *gsettings_value = NULL; |
||
847 | gchar *gsettings_type; |
||
848 | |||
849 | g_return_val_if_fail (expected_type != NULL, NULL); |
||
850 | |||
851 | if (default_value) |
||
852 | return NULL; |
||
853 | |||
854 | /* Simply read from the cache, which is updated from the registry by the |
||
855 | * watch thread as soon as changes can propagate. Any changes not yet in the |
||
856 | * cache will have the 'changed' signal emitted after this function returns. |
||
857 | */ |
||
858 | EnterCriticalSection (self->cache_lock); |
||
859 | cache_node = registry_cache_get_node_for_key (self->cache_root, key_name, FALSE); |
||
860 | LeaveCriticalSection (self->cache_lock); |
||
861 | |||
862 | trace ("Reading key %s, cache node %x\n", key_name, cache_node); |
||
863 | |||
864 | /* Maybe it's not set, we can return to default */ |
||
865 | if (cache_node == NULL) |
||
866 | return NULL; |
||
867 | |||
868 | trace ("\t- cached value %s\n", registry_value_dump (((RegistryCacheItem *)cache_node->data)->value)); |
||
869 | |||
870 | registry_value = ((RegistryCacheItem *)cache_node->data)->value; |
||
871 | |||
872 | gsettings_type = g_variant_type_dup_string (expected_type); |
||
873 | |||
874 | /* The registry is user-editable, so we need to be fault-tolerant here. */ |
||
875 | switch (gsettings_type[0]) |
||
876 | { |
||
877 | case 'b': |
||
878 | case 'y': |
||
879 | case 'n': |
||
880 | case 'q': |
||
881 | case 'i': |
||
882 | case 'u': |
||
883 | if (registry_value.type == REG_DWORD) |
||
884 | gsettings_value = g_variant_new (gsettings_type, registry_value.dword); |
||
885 | break; |
||
886 | |||
887 | case 't': |
||
888 | case 'x': |
||
889 | if (registry_value.type == REG_QWORD) |
||
890 | { |
||
891 | DWORDLONG qword_value = *(DWORDLONG *)registry_value.ptr; |
||
892 | gsettings_value = g_variant_new (gsettings_type, qword_value); |
||
893 | } |
||
894 | break; |
||
895 | |||
896 | default: |
||
897 | if (registry_value.type == REG_SZ) |
||
898 | { |
||
899 | if (gsettings_type[0] == 's') |
||
900 | gsettings_value = g_variant_new_string ((char *)registry_value.ptr); |
||
901 | else |
||
902 | { |
||
903 | GError *error = NULL; |
||
904 | |||
905 | gsettings_value = g_variant_parse (expected_type, registry_value.ptr, |
||
906 | NULL, NULL, &error); |
||
907 | |||
908 | if (error != NULL) |
||
909 | g_message ("gregistrysettingsbackend: error parsing key %s: %s", |
||
910 | key_name, error->message); |
||
911 | } |
||
912 | } |
||
913 | break; |
||
914 | } |
||
915 | |||
916 | g_free (gsettings_type); |
||
917 | |||
918 | return gsettings_value; |
||
919 | } |
||
920 | |||
921 | |||
922 | typedef struct |
||
923 | { |
||
924 | GRegistryBackend *self; |
||
925 | HKEY hroot; |
||
926 | } RegistryWrite; |
||
927 | |||
928 | static gboolean |
||
929 | g_registry_backend_write_one (const char *key_name, |
||
930 | GVariant *variant, |
||
931 | gpointer user_data) |
||
932 | { |
||
933 | GRegistryBackend *self; |
||
934 | RegistryWrite *action; |
||
935 | RegistryValue value; |
||
936 | HKEY hroot; |
||
937 | HKEY hpath; |
||
938 | gchar *path_name; |
||
939 | gunichar2 *path_namew; |
||
940 | gchar *value_name = NULL; |
||
941 | gunichar2 *value_namew; |
||
942 | DWORD value_data_size; |
||
943 | LPVOID value_data; |
||
944 | gunichar2 *value_dataw; |
||
945 | LONG result; |
||
946 | GNode *node; |
||
947 | gboolean changed; |
||
948 | const gchar *type_string; |
||
949 | |||
950 | type_string = g_variant_get_type_string (variant); |
||
951 | action = user_data; |
||
952 | self = G_REGISTRY_BACKEND (action->self); |
||
953 | hroot = action->hroot; |
||
954 | |||
955 | value.type = REG_NONE; |
||
956 | value.ptr = NULL; |
||
957 | |||
958 | switch (type_string[0]) |
||
959 | { |
||
960 | case 'b': |
||
961 | case 'y': |
||
962 | case 'n': |
||
963 | case 'q': |
||
964 | case 'i': |
||
965 | case 'u': |
||
966 | value.type = REG_DWORD; |
||
967 | value.dword = g_variant_get_as_dword (variant); |
||
968 | value_data_size = 4; |
||
969 | value_data = &value.dword; |
||
970 | break; |
||
971 | |||
972 | case 'x': |
||
973 | case 't': |
||
974 | value.type = REG_QWORD; |
||
975 | value.ptr = g_malloc (8); |
||
976 | *(DWORDLONG *)value.ptr = g_variant_get_as_qword (variant); |
||
977 | value_data_size = 8; |
||
978 | value_data = value.ptr; |
||
979 | break; |
||
980 | |||
981 | default: |
||
982 | value.type = REG_SZ; |
||
983 | if (type_string[0] == 's') |
||
984 | { |
||
985 | gsize length; |
||
986 | value.ptr = g_strdup (g_variant_get_string (variant, &length)); |
||
987 | value_data_size = length + 1; |
||
988 | value_data = value.ptr; |
||
989 | } |
||
990 | else |
||
991 | { |
||
992 | GString *value_string; |
||
993 | value_string = g_variant_print_string (variant, NULL, FALSE); |
||
994 | value_data_size = value_string->len + 1; |
||
995 | value.ptr = value_data = g_string_free (value_string, FALSE); |
||
996 | } |
||
997 | break; |
||
998 | } |
||
999 | |||
1000 | /* First update the cache, because the value may not have changed and we can |
||
1001 | * save a write. |
||
1002 | * |
||
1003 | * If 'value' has changed then its memory will not be freed by update_node(), |
||
1004 | * because it will be stored in the node. |
||
1005 | */ |
||
1006 | EnterCriticalSection (self->cache_lock); |
||
1007 | node = registry_cache_get_node_for_key (self->cache_root, key_name, TRUE); |
||
1008 | changed = registry_cache_update_node (node, value); |
||
1009 | LeaveCriticalSection (self->cache_lock); |
||
1010 | |||
1011 | if (!changed) |
||
1012 | return FALSE; |
||
1013 | |||
1014 | /* Block the next notification to any watch points above this location, |
||
1015 | * because they will each get triggered on a change that is already updated |
||
1016 | * in the cache. |
||
1017 | */ |
||
1018 | registry_cache_block_notification (node); |
||
1019 | |||
1020 | path_name = parse_key (key_name, NULL, &value_name); |
||
1021 | |||
1022 | trace ("Set key: %s / %s\n", path_name, value_name); |
||
1023 | |||
1024 | path_namew = g_utf8_to_utf16 (path_name, -1, NULL, NULL, NULL); |
||
1025 | |||
1026 | /* Store the value in the registry */ |
||
1027 | result = RegCreateKeyExW (hroot, path_namew, 0, NULL, 0, KEY_WRITE, NULL, &hpath, NULL); |
||
1028 | if (result != ERROR_SUCCESS) |
||
1029 | { |
||
1030 | g_message_win32_error (result, "gregistrybackend: opening key %s failed", |
||
1031 | path_name + 1); |
||
1032 | registry_value_free (value); |
||
1033 | g_free (path_namew); |
||
1034 | g_free (path_name); |
||
1035 | return FALSE; |
||
1036 | } |
||
1037 | |||
1038 | g_free (path_namew); |
||
1039 | |||
1040 | value_namew = g_utf8_to_utf16 (value_name, -1, NULL, NULL, NULL); |
||
1041 | |||
1042 | value_dataw = NULL; |
||
1043 | |||
1044 | switch (type_string[0]) |
||
1045 | { |
||
1046 | case 'b': |
||
1047 | case 'y': |
||
1048 | case 'n': |
||
1049 | case 'q': |
||
1050 | case 'i': |
||
1051 | case 'u': |
||
1052 | case 'x': |
||
1053 | case 't': |
||
1054 | break; |
||
1055 | default: |
||
1056 | value_dataw = g_utf8_to_utf16 (value_data, -1, NULL, NULL, NULL); |
||
1057 | value_data = value_dataw; |
||
1058 | value_data_size = (DWORD)((wcslen (value_data) + 1) * sizeof (gunichar2)); |
||
1059 | break; |
||
1060 | } |
||
1061 | |||
1062 | result = RegSetValueExW (hpath, value_namew, 0, value.type, value_data, value_data_size); |
||
1063 | |||
1064 | if (result != ERROR_SUCCESS) |
||
1065 | g_message_win32_error (result, "gregistrybackend: setting value %s\\%s\\%s failed.\n", |
||
1066 | self->base_path, path_name, value_name); |
||
1067 | |||
1068 | /* If the write fails then it will seem like the value has changed until the |
||
1069 | * next execution (because we wrote to the cache first). There's no reason |
||
1070 | * for it to fail unless something is weirdly broken, however. |
||
1071 | */ |
||
1072 | |||
1073 | RegCloseKey (hpath); |
||
1074 | g_free (path_name); |
||
1075 | g_free (value_namew); |
||
1076 | g_free (value_dataw); |
||
1077 | |||
1078 | return FALSE; |
||
1079 | } |
||
1080 | |||
1081 | /* The dconf write policy is to do the write while making out it succeeded, |
||
1082 | * and then backtrack if it didn't. The registry functions are synchronous so |
||
1083 | * we can't do that. */ |
||
1084 | |||
1085 | static gboolean |
||
1086 | g_registry_backend_write (GSettingsBackend *backend, |
||
1087 | const gchar *key_name, |
||
1088 | GVariant *value, |
||
1089 | gpointer origin_tag) |
||
1090 | { |
||
1091 | GRegistryBackend *self = G_REGISTRY_BACKEND (backend); |
||
1092 | LONG result; |
||
1093 | HKEY hroot; |
||
1094 | RegistryWrite action; |
||
1095 | |||
1096 | result = RegCreateKeyExW (HKEY_CURRENT_USER, self->base_pathw, 0, NULL, 0, |
||
1097 | KEY_WRITE, NULL, &hroot, NULL); |
||
1098 | if (result != ERROR_SUCCESS) |
||
1099 | { |
||
1100 | trace ("Error opening/creating key %s.\n", self->base_path); |
||
1101 | return FALSE; |
||
1102 | } |
||
1103 | |||
1104 | action.self = self; |
||
1105 | action.hroot = hroot; |
||
1106 | g_registry_backend_write_one (key_name, value, &action); |
||
1107 | g_settings_backend_changed (backend, key_name, origin_tag); |
||
1108 | |||
1109 | RegCloseKey (hroot); |
||
1110 | |||
1111 | return TRUE; |
||
1112 | } |
||
1113 | |||
1114 | static gboolean |
||
1115 | g_registry_backend_write_tree (GSettingsBackend *backend, |
||
1116 | GTree *values, |
||
1117 | gpointer origin_tag) |
||
1118 | { |
||
1119 | GRegistryBackend *self = G_REGISTRY_BACKEND (backend); |
||
1120 | LONG result; |
||
1121 | HKEY hroot; |
||
1122 | RegistryWrite action; |
||
1123 | |||
1124 | result = RegCreateKeyExW (HKEY_CURRENT_USER, self->base_pathw, 0, NULL, 0, |
||
1125 | KEY_WRITE, NULL, &hroot, NULL); |
||
1126 | if (result != ERROR_SUCCESS) |
||
1127 | { |
||
1128 | trace ("Error opening/creating key %s.\n", self->base_path); |
||
1129 | return FALSE; |
||
1130 | } |
||
1131 | |||
1132 | action.self = self; |
||
1133 | action.hroot = hroot; |
||
1134 | g_tree_foreach (values, (GTraverseFunc)g_registry_backend_write_one, |
||
1135 | &action); |
||
1136 | |||
1137 | g_settings_backend_changed_tree (backend, values, origin_tag); |
||
1138 | RegCloseKey (hroot); |
||
1139 | |||
1140 | return TRUE; |
||
1141 | } |
||
1142 | |||
1143 | static void |
||
1144 | g_registry_backend_reset (GSettingsBackend *backend, |
||
1145 | const gchar *key_name, |
||
1146 | gpointer origin_tag) |
||
1147 | { |
||
1148 | GRegistryBackend *self = G_REGISTRY_BACKEND (backend); |
||
1149 | gchar *path_name; |
||
1150 | gunichar2 *path_namew; |
||
1151 | gchar *value_name = NULL; |
||
1152 | gunichar2 *value_namew; |
||
1153 | GNode *cache_node; |
||
1154 | LONG result; |
||
1155 | HKEY hpath; |
||
1156 | |||
1157 | /* Remove from cache */ |
||
1158 | EnterCriticalSection (self->cache_lock); |
||
1159 | cache_node = registry_cache_get_node_for_key (self->cache_root, key_name, FALSE); |
||
1160 | if (cache_node) |
||
1161 | registry_cache_destroy_tree (cache_node, self->watch); |
||
1162 | LeaveCriticalSection (self->cache_lock); |
||
1163 | |||
1164 | /* Remove from the registry */ |
||
1165 | path_name = parse_key (key_name, self->base_path, &value_name); |
||
1166 | path_namew = g_utf8_to_utf16 (path_name, -1, NULL, NULL, NULL); |
||
1167 | |||
1168 | result = RegOpenKeyExW (HKEY_CURRENT_USER, path_namew, 0, KEY_SET_VALUE, &hpath); |
||
1169 | g_free (path_namew); |
||
1170 | |||
1171 | if (result != ERROR_SUCCESS) |
||
1172 | { |
||
1173 | g_message_win32_error (result, "Registry: resetting key '%s'", path_name); |
||
1174 | g_free (path_name); |
||
1175 | return; |
||
1176 | } |
||
1177 | |||
1178 | value_namew = g_utf8_to_utf16 (value_name, -1, NULL, NULL, NULL); |
||
1179 | |||
1180 | result = RegDeleteValueW (hpath, value_namew); |
||
1181 | g_free (value_namew); |
||
1182 | RegCloseKey (hpath); |
||
1183 | |||
1184 | if (result != ERROR_SUCCESS) |
||
1185 | { |
||
1186 | g_message_win32_error (result, "Registry: resetting key '%s'", path_name); |
||
1187 | g_free (path_name); |
||
1188 | return; |
||
1189 | } |
||
1190 | |||
1191 | g_free (path_name); |
||
1192 | |||
1193 | g_settings_backend_changed (backend, key_name, origin_tag); |
||
1194 | } |
||
1195 | |||
1196 | static gboolean |
||
1197 | g_registry_backend_get_writable (GSettingsBackend *backend, |
||
1198 | const gchar *key_name) |
||
1199 | { |
||
1200 | GRegistryBackend *self = G_REGISTRY_BACKEND (backend); |
||
1201 | gchar *path_name; |
||
1202 | gunichar2 *path_namew; |
||
1203 | gchar *value_name; |
||
1204 | HKEY hpath; |
||
1205 | LONG result; |
||
1206 | |||
1207 | path_name = parse_key (key_name, self->base_path, &value_name); |
||
1208 | path_namew = g_utf8_to_utf16 (path_name, -1, NULL, NULL, NULL); |
||
1209 | |||
1210 | /* Note: we create the key if it wasn't created yet, but it is not much |
||
1211 | * of a problem since at the end of the day we have to create it anyway |
||
1212 | * to read or to write from it |
||
1213 | */ |
||
1214 | result = RegCreateKeyExW (HKEY_CURRENT_USER, path_namew, 0, NULL, 0, |
||
1215 | KEY_WRITE, NULL, &hpath, NULL); |
||
1216 | g_free (path_namew); |
||
1217 | |||
1218 | if (result != ERROR_SUCCESS) |
||
1219 | { |
||
1220 | trace ("Error opening/creating key to check writability: %s.\n", |
||
1221 | path_name); |
||
1222 | g_free (path_name); |
||
1223 | |||
1224 | return FALSE; |
||
1225 | } |
||
1226 | |||
1227 | g_free (path_name); |
||
1228 | RegCloseKey (hpath); |
||
1229 | |||
1230 | return TRUE; |
||
1231 | } |
||
1232 | |||
1233 | /******************************************************************************** |
||
1234 | * Spot-the-difference engine |
||
1235 | ********************************************************************************/ |
||
1236 | |||
1237 | static void |
||
1238 | _free_watch (WatchThreadState *self, |
||
1239 | guint index, |
||
1240 | GNode *cache_node); |
||
1241 | |||
1242 | static void |
||
1243 | registry_cache_item_reset_readable (GNode *node, |
||
1244 | gpointer data) |
||
1245 | { |
||
1246 | RegistryCacheItem *item = node->data; |
||
1247 | item->readable = FALSE; |
||
1248 | } |
||
1249 | |||
1250 | /* Delete a node and any children, for when it has been deleted from the registry */ |
||
1251 | static void |
||
1252 | registry_cache_destroy_tree (GNode *node, |
||
1253 | WatchThreadState *self) |
||
1254 | { |
||
1255 | RegistryCacheItem *item = node->data; |
||
1256 | |||
1257 | g_node_children_foreach (node, G_TRAVERSE_ALL, |
||
1258 | (GNodeForeachFunc)registry_cache_destroy_tree, self); |
||
1259 | |||
1260 | if (item->subscription_count > 0) |
||
1261 | { |
||
1262 | guint i; |
||
1263 | |||
1264 | /* There must be some watches active if this node is a watch point */ |
||
1265 | g_warn_if_fail (self->cache_nodes->len > 1); |
||
1266 | |||
1267 | /* This is a watch point that has been deleted. Let's free the watch! */ |
||
1268 | for (i = 1; i < self->cache_nodes->len; i++) |
||
1269 | { |
||
1270 | if (g_ptr_array_index (self->cache_nodes, i) == node) |
||
1271 | break; |
||
1272 | } |
||
1273 | |||
1274 | if (i >= self->cache_nodes->len) |
||
1275 | g_warning ("watch thread: a watch point was deleted, but unable to " |
||
1276 | "find '%s' in the list of %i watch nodes\n", item->name, |
||
1277 | self->cache_nodes->len - 1); |
||
1278 | else |
||
1279 | { |
||
1280 | _free_watch (self, i, node); |
||
1281 | g_atomic_int_inc (&self->watches_remaining); |
||
1282 | } |
||
1283 | } |
||
1284 | registry_cache_item_free (node->data); |
||
1285 | g_node_destroy (node); |
||
1286 | } |
||
1287 | |||
1288 | /* One of these is sent down the pipe when something happens in the registry. */ |
||
1289 | typedef struct |
||
1290 | { |
||
1291 | GRegistryBackend *self; |
||
1292 | gchar *prefix; /* prefix is a gsettings path, all items are subkeys of this. */ |
||
1293 | GPtrArray *items; /* each item is a subkey below prefix that has changed. */ |
||
1294 | } RegistryEvent; |
||
1295 | |||
1296 | typedef struct |
||
1297 | { |
||
1298 | RegistryEvent *event; |
||
1299 | gchar *current_key_name; |
||
1300 | } DeletedItemData; |
||
1301 | |||
1302 | static void |
||
1303 | mark_all_subkeys_as_changed (GNode *node, |
||
1304 | gpointer data) |
||
1305 | { |
||
1306 | RegistryCacheItem *item = node->data; |
||
1307 | DeletedItemData *item_data = data; |
||
1308 | |||
1309 | if (item_data->current_key_name == NULL) |
||
1310 | item_data->current_key_name = g_strdup (item->name); |
||
1311 | else |
||
1312 | { |
||
1313 | gchar *name; |
||
1314 | |||
1315 | name = g_build_path ("/", item_data->current_key_name, item->name, NULL); |
||
1316 | g_free (item_data->current_key_name); |
||
1317 | item_data->current_key_name = name; |
||
1318 | } |
||
1319 | |||
1320 | /* Iterate until we find an item that is a value */ |
||
1321 | if (item->value.type == REG_NONE) |
||
1322 | g_node_children_foreach (node, G_TRAVERSE_ALL, |
||
1323 | mark_all_subkeys_as_changed, data); |
||
1324 | else |
||
1325 | g_ptr_array_add (item_data->event->items, item_data->current_key_name); |
||
1326 | } |
||
1327 | |||
1328 | static void |
||
1329 | registry_cache_remove_deleted (GNode *node, |
||
1330 | gpointer data) |
||
1331 | { |
||
1332 | RegistryCacheItem *item = node->data; |
||
1333 | RegistryEvent *event = data; |
||
1334 | |||
1335 | if (!item->readable) |
||
1336 | { |
||
1337 | DeletedItemData item_data; |
||
1338 | |||
1339 | item_data.event = event; |
||
1340 | item_data.current_key_name = NULL; |
||
1341 | |||
1342 | mark_all_subkeys_as_changed (node, &item_data); |
||
1343 | registry_cache_destroy_tree (node, event->self->watch); |
||
1344 | } |
||
1345 | } |
||
1346 | |||
1347 | /* Update cache from registry, and optionally report on the changes. |
||
1348 | * |
||
1349 | * This function is sometimes called from the watch thread, with no locking. It |
||
1350 | * does call g_registry_backend functions, but this is okay because they only |
||
1351 | * access self->base which is constant. |
||
1352 | * |
||
1353 | * When looking at this code bear in mind the terminology: in the registry, keys |
||
1354 | * are containers that contain values, and other keys. Keys have a 'default' |
||
1355 | * value which we always ignore. |
||
1356 | * |
||
1357 | * n_parent_watches: a counter used to set the reference count of any new nodes |
||
1358 | * that are created - they should have as many references as |
||
1359 | * there are notifications that are watching them. |
||
1360 | */ |
||
1361 | static void |
||
1362 | registry_cache_update (GRegistryBackend *self, |
||
1363 | HKEY hpath, |
||
1364 | const gchar *prefix, |
||
1365 | const gchar *partial_key_name, |
||
1366 | GNode *cache_node, |
||
1367 | int n_watches, |
||
1368 | RegistryEvent *event) |
||
1369 | { |
||
1370 | gunichar2 bufferw[MAX_KEY_NAME_LENGTH + 1]; |
||
1371 | gchar *buffer; |
||
1372 | gchar *key_name; |
||
1373 | gint i; |
||
1374 | LONG result; |
||
1375 | RegistryCacheItem *item; |
||
1376 | |||
1377 | item = cache_node->data; |
||
1378 | |||
1379 | if (item->subscription_count > 0) |
||
1380 | n_watches++; |
||
1381 | |||
1382 | /* prefix is the level that all changes occur below; partial_key_name should |
||
1383 | * be NULL on the first call to this function */ |
||
1384 | key_name = g_build_path ("/", prefix, partial_key_name, NULL); |
||
1385 | |||
1386 | trace ("registry cache update: %s. Node %x has %i children\n", key_name, |
||
1387 | cache_node, g_node_n_children (cache_node)); |
||
1388 | |||
1389 | /* Start by zeroing 'readable' flag. When the registry traversal is done, any unreadable nodes |
||
1390 | * must have been deleted from the registry. |
||
1391 | */ |
||
1392 | g_node_children_foreach (cache_node, G_TRAVERSE_ALL, |
||
1393 | registry_cache_item_reset_readable, NULL); |
||
1394 | |||
1395 | /* Recurse into each subpath at the current level, if any */ |
||
1396 | i = 0; |
||
1397 | while (1) |
||
1398 | { |
||
1399 | DWORD bufferw_size = MAX_KEY_NAME_LENGTH + 1; |
||
1400 | HKEY hsubpath; |
||
1401 | |||
1402 | result = RegEnumKeyExW (hpath, i++, bufferw, &bufferw_size, NULL, NULL, NULL, NULL); |
||
1403 | if (result != ERROR_SUCCESS) |
||
1404 | break; |
||
1405 | |||
1406 | result = RegOpenKeyExW (hpath, bufferw, 0, KEY_READ, &hsubpath); |
||
1407 | if (result == ERROR_SUCCESS) |
||
1408 | { |
||
1409 | GNode *subkey_node; |
||
1410 | RegistryCacheItem *child_item; |
||
1411 | gchar *new_partial_key_name; |
||
1412 | |||
1413 | buffer = g_utf16_to_utf8 (bufferw, -1, NULL, NULL, NULL); |
||
1414 | if (buffer == NULL) |
||
1415 | continue; |
||
1416 | |||
1417 | subkey_node = registry_cache_find_immediate_child (cache_node, buffer); |
||
1418 | if (subkey_node == NULL) |
||
1419 | { |
||
1420 | RegistryValue null_value = {REG_NONE, {0}}; |
||
1421 | subkey_node = registry_cache_add_item (cache_node, buffer, |
||
1422 | null_value, n_watches); |
||
1423 | } |
||
1424 | |||
1425 | new_partial_key_name = g_build_path ("/", partial_key_name, buffer, NULL); |
||
1426 | registry_cache_update (self, hsubpath, prefix, new_partial_key_name, |
||
1427 | subkey_node, n_watches, event); |
||
1428 | g_free (new_partial_key_name); |
||
1429 | |||
1430 | child_item = subkey_node->data; |
||
1431 | child_item->readable = TRUE; |
||
1432 | |||
1433 | g_free (buffer); |
||
1434 | RegCloseKey (hsubpath); |
||
1435 | } |
||
1436 | } |
||
1437 | |||
1438 | if (result != ERROR_NO_MORE_ITEMS) |
||
1439 | g_message_win32_error (result, "gregistrybackend: error enumerating subkeys for cache."); |
||
1440 | |||
1441 | /* Enumerate each value at 'path' and check if it has changed */ |
||
1442 | i = 0; |
||
1443 | while (1) |
||
1444 | { |
||
1445 | DWORD bufferw_size = MAX_KEY_NAME_LENGTH + 1; |
||
1446 | GNode *cache_child_node; |
||
1447 | RegistryCacheItem *child_item; |
||
1448 | RegistryValue value; |
||
1449 | gboolean changed = FALSE; |
||
1450 | |||
1451 | result = RegEnumValueW (hpath, i++, bufferw, &bufferw_size, NULL, NULL, NULL, NULL); |
||
1452 | if (result != ERROR_SUCCESS) |
||
1453 | break; |
||
1454 | |||
1455 | buffer = g_utf16_to_utf8 (bufferw, -1, NULL, NULL, NULL); |
||
1456 | |||
1457 | if (buffer == NULL || buffer[0] == 0) |
||
1458 | { |
||
1459 | /* This is the key's 'default' value, for which we have no use. */ |
||
1460 | g_free (buffer); |
||
1461 | continue; |
||
1462 | } |
||
1463 | |||
1464 | cache_child_node = registry_cache_find_immediate_child (cache_node, buffer); |
||
1465 | |||
1466 | if (!registry_read (hpath, key_name, buffer, &value)) |
||
1467 | { |
||
1468 | g_free (buffer); |
||
1469 | continue; |
||
1470 | } |
||
1471 | |||
1472 | trace ("\tgot value %s for %s, node %x\n", |
||
1473 | registry_value_dump (value), buffer, cache_child_node); |
||
1474 | |||
1475 | if (cache_child_node == NULL) |
||
1476 | { |
||
1477 | /* This is a new value */ |
||
1478 | cache_child_node = registry_cache_add_item (cache_node, buffer, value, |
||
1479 | n_watches); |
||
1480 | changed = TRUE; |
||
1481 | } |
||
1482 | else |
||
1483 | { |
||
1484 | /* For efficiency, instead of converting every value back to a GVariant to |
||
1485 | * compare it, we compare them as registry values (integers, or string |
||
1486 | * representations of the variant). The spurious change notifications that may |
||
1487 | * result should not be a big issue. |
||
1488 | * |
||
1489 | * Note that 'value' is swallowed or freed. |
||
1490 | */ |
||
1491 | changed = registry_cache_update_node (cache_child_node, value); |
||
1492 | } |
||
1493 | |||
1494 | child_item = cache_child_node->data; |
||
1495 | child_item->readable = TRUE; |
||
1496 | if (changed && event != NULL) |
||
1497 | { |
||
1498 | gchar *item; |
||
1499 | |||
1500 | if (partial_key_name == NULL) |
||
1501 | item = g_strdup (buffer); |
||
1502 | else |
||
1503 | item = g_build_path ("/", partial_key_name, buffer, NULL); |
||
1504 | |||
1505 | g_ptr_array_add (event->items, item); |
||
1506 | } |
||
1507 | |||
1508 | g_free (buffer); |
||
1509 | } |
||
1510 | |||
1511 | if (result != ERROR_NO_MORE_ITEMS) |
||
1512 | g_message_win32_error (result, "gregistrybackend: error enumerating values for cache"); |
||
1513 | |||
1514 | /* Any nodes now left unreadable must have been deleted, remove them from cache */ |
||
1515 | g_node_children_foreach (cache_node, G_TRAVERSE_ALL, |
||
1516 | registry_cache_remove_deleted, event); |
||
1517 | |||
1518 | trace ("registry cache update complete.\n"); |
||
1519 | |||
1520 | g_free (key_name); |
||
1521 | } |
||
1522 | |||
1523 | /*********************************************************************************** |
||
1524 | * Thread to watch for registry change events |
||
1525 | ***********************************************************************************/ |
||
1526 | |||
1527 | /* Called by watch thread. Apply for notifications on a registry key and its subkeys. */ |
||
1528 | static DWORD |
||
1529 | registry_watch_key (HKEY hpath, |
||
1530 | HANDLE event) |
||
1531 | { |
||
1532 | return RegNotifyChangeKeyValue (hpath, TRUE, |
||
1533 | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, |
||
1534 | event, TRUE); |
||
1535 | } |
||
1536 | |||
1537 | /* This handler runs in the main thread to emit the changed signals */ |
||
1538 | static gboolean |
||
1539 | watch_handler (RegistryEvent *event) |
||
1540 | { |
||
1541 | trace ("Watch handler: got event in %s, items %i.\n", event->prefix, event->items->len); |
||
1542 | |||
1543 | /* GSettings requires us to NULL-terminate the array. */ |
||
1544 | g_ptr_array_add (event->items, NULL); |
||
1545 | g_settings_backend_keys_changed (G_SETTINGS_BACKEND (event->self), event->prefix, |
||
1546 | (gchar const **)event->items->pdata, NULL); |
||
1547 | |||
1548 | g_ptr_array_free (event->items, TRUE); |
||
1549 | g_free (event->prefix); |
||
1550 | g_object_unref (event->self); |
||
1551 | g_slice_free (RegistryEvent, event); |
||
1552 | |||
1553 | return G_SOURCE_REMOVE; |
||
1554 | } |
||
1555 | |||
1556 | static void |
||
1557 | _free_watch (WatchThreadState *self, |
||
1558 | guint index, |
||
1559 | GNode *cache_node) |
||
1560 | { |
||
1561 | HKEY hpath; |
||
1562 | HANDLE cond; |
||
1563 | gchar *prefix; |
||
1564 | |||
1565 | g_return_if_fail (index > 0 && index < self->events->len); |
||
1566 | |||
1567 | cond = g_ptr_array_index (self->events, index); |
||
1568 | hpath = g_ptr_array_index (self->handles, index); |
||
1569 | prefix = g_ptr_array_index (self->prefixes, index); |
||
1570 | |||
1571 | trace ("Freeing watch %i [%s]\n", index, prefix); |
||
1572 | |||
1573 | /* These can be NULL if the watch was already dead, this can happen when eg. |
||
1574 | * a key is deleted but GSettings is still subscribed to it - the watch is |
||
1575 | * kept alive so that the unsubscribe function works properly, but does not |
||
1576 | * do anything. |
||
1577 | */ |
||
1578 | if (hpath != NULL) |
||
1579 | RegCloseKey (hpath); |
||
1580 | |||
1581 | if (cache_node != NULL) |
||
1582 | { |
||
1583 | //registry_cache_dump (G_REGISTRY_BACKEND (self->owner)->cache_root, NULL); |
||
1584 | registry_cache_unref_tree (cache_node); |
||
1585 | } |
||
1586 | |||
1587 | CloseHandle (cond); |
||
1588 | g_free (prefix); |
||
1589 | |||
1590 | /* As long as we remove from each array at the same time, it doesn't matter that |
||
1591 | * their orders get messed up - they all get messed up the same. |
||
1592 | */ |
||
1593 | g_ptr_array_remove_index_fast (self->handles, index); |
||
1594 | g_ptr_array_remove_index_fast (self->events, index); |
||
1595 | g_ptr_array_remove_index_fast (self->prefixes, index); |
||
1596 | g_ptr_array_remove_index_fast (self->cache_nodes, index); |
||
1597 | } |
||
1598 | |||
1599 | static void |
||
1600 | watch_thread_handle_message (WatchThreadState *self) |
||
1601 | { |
||
1602 | switch (self->message.type) |
||
1603 | { |
||
1604 | case WATCH_THREAD_NONE: |
||
1605 | trace ("watch thread: you woke me up for nothin', man!"); |
||
1606 | break; |
||
1607 | |||
1608 | case WATCH_THREAD_ADD_WATCH: |
||
1609 | { |
||
1610 | RegistryWatch *watch = &self->message.watch; |
||
1611 | LONG result; |
||
1612 | |||
1613 | result = registry_watch_key (watch->hpath, watch->event); |
||
1614 | |||
1615 | if (result == ERROR_SUCCESS) |
||
1616 | { |
||
1617 | g_ptr_array_add (self->events, watch->event); |
||
1618 | g_ptr_array_add (self->handles, watch->hpath); |
||
1619 | g_ptr_array_add (self->prefixes, watch->prefix); |
||
1620 | g_ptr_array_add (self->cache_nodes, watch->cache_node); |
||
1621 | |||
1622 | trace ("watch thread: new watch on %s, %i total\n", watch->prefix, |
||
1623 | self->events->len); |
||
1624 | } |
||
1625 | else |
||
1626 | { |
||
1627 | g_message_win32_error (result, "watch thread: could not watch %s", watch->prefix); |
||
1628 | |||
1629 | CloseHandle (watch->event); |
||
1630 | RegCloseKey (watch->hpath); |
||
1631 | g_free (watch->prefix); |
||
1632 | registry_cache_unref_tree (watch->cache_node); |
||
1633 | } |
||
1634 | break; |
||
1635 | } |
||
1636 | |||
1637 | case WATCH_THREAD_REMOVE_WATCH: |
||
1638 | { |
||
1639 | GNode *cache_node; |
||
1640 | RegistryCacheItem *cache_item; |
||
1641 | guint i; |
||
1642 | |||
1643 | for (i = 1; i < self->prefixes->len; i++) |
||
1644 | { |
||
1645 | if (strcmp (g_ptr_array_index (self->prefixes, i), |
||
1646 | self->message.watch.prefix) == 0) |
||
1647 | break; |
||
1648 | } |
||
1649 | |||
1650 | if (i >= self->prefixes->len) |
||
1651 | { |
||
1652 | /* Don't make a fuss if the prefix is not being watched because |
||
1653 | * maybe the path was deleted so we removed the watch. |
||
1654 | */ |
||
1655 | trace ("unsubscribe: prefix %s is not being watched [%i things are]!\n", |
||
1656 | self->message.watch.prefix, self->prefixes->len); |
||
1657 | g_free (self->message.watch.prefix); |
||
1658 | break; |
||
1659 | } |
||
1660 | |||
1661 | cache_node = g_ptr_array_index (self->cache_nodes, i); |
||
1662 | |||
1663 | trace ("watch thread: unsubscribe: freeing node %p, prefix %s, index %i\n", |
||
1664 | cache_node, self->message.watch.prefix, i); |
||
1665 | |||
1666 | if (cache_node != NULL) |
||
1667 | { |
||
1668 | cache_item = cache_node->data; |
||
1669 | |||
1670 | /* There may be more than one GSettings object subscribed to this |
||
1671 | * path, only free the watch when the last one unsubscribes. |
||
1672 | */ |
||
1673 | cache_item->subscription_count--; |
||
1674 | if (cache_item->subscription_count > 0) |
||
1675 | break; |
||
1676 | } |
||
1677 | |||
1678 | _free_watch (self, i, cache_node); |
||
1679 | g_free (self->message.watch.prefix); |
||
1680 | |||
1681 | g_atomic_int_inc (&self->watches_remaining); |
||
1682 | break; |
||
1683 | } |
||
1684 | |||
1685 | case WATCH_THREAD_STOP: |
||
1686 | { |
||
1687 | guint i; |
||
1688 | |||
1689 | /* Free any remaining cache and watch handles */ |
||
1690 | for (i = 1; i < self->events->len; i++) |
||
1691 | _free_watch (self, i, g_ptr_array_index (self->cache_nodes, i)); |
||
1692 | |||
1693 | SetEvent (self->message_received_event); |
||
1694 | ExitThread (0); |
||
1695 | } |
||
1696 | } |
||
1697 | |||
1698 | self->message.type = WATCH_THREAD_NONE; |
||
1699 | SetEvent (self->message_received_event); |
||
1700 | } |
||
1701 | |||
1702 | /* Thread which watches for win32 registry events */ |
||
1703 | static DWORD WINAPI |
||
1704 | watch_thread_function (LPVOID parameter) |
||
1705 | { |
||
1706 | WatchThreadState *self = (WatchThreadState *)parameter; |
||
1707 | DWORD result; |
||
1708 | |||
1709 | self->events = g_ptr_array_new (); |
||
1710 | self->handles = g_ptr_array_new (); |
||
1711 | self->prefixes = g_ptr_array_new (); |
||
1712 | self->cache_nodes = g_ptr_array_new (); |
||
1713 | g_ptr_array_add (self->events, self->message_sent_event); |
||
1714 | g_ptr_array_add (self->handles, NULL); |
||
1715 | g_ptr_array_add (self->prefixes, NULL); |
||
1716 | g_ptr_array_add (self->cache_nodes, NULL); |
||
1717 | |||
1718 | while (1) |
||
1719 | { |
||
1720 | trace ("watch thread: going to sleep; %i events watched.\n", self->events->len); |
||
1721 | result = WaitForMultipleObjects (self->events->len, self->events->pdata, FALSE, INFINITE); |
||
1722 | |||
1723 | if (result == WAIT_OBJECT_0) |
||
1724 | { |
||
1725 | /* A message to you. The sender (main thread) will block until we signal the received |
||
1726 | * event, so there should be no danger of it sending another before we receive the |
||
1727 | * first. |
||
1728 | */ |
||
1729 | watch_thread_handle_message (self); |
||
1730 | } |
||
1731 | else if (result > WAIT_OBJECT_0 && result <= WAIT_OBJECT_0 + self->events->len) |
||
1732 | { |
||
1733 | HKEY hpath; |
||
1734 | HANDLE cond; |
||
1735 | gchar *prefix; |
||
1736 | GNode *cache_node; |
||
1737 | RegistryCacheItem *cache_item; |
||
1738 | RegistryEvent *event; |
||
1739 | gint notify_index; |
||
1740 | |||
1741 | /* One of our notifications has triggered. All we know is which one, and which key |
||
1742 | * this is for. We do most of the processing here, because we may as well. If the |
||
1743 | * registry changes further while we are processing it doesn't matter - we will then |
||
1744 | * receive another change notification from the OS anyway. |
||
1745 | */ |
||
1746 | notify_index = result - WAIT_OBJECT_0; |
||
1747 | hpath = g_ptr_array_index (self->handles, notify_index); |
||
1748 | cond = g_ptr_array_index (self->events, notify_index); |
||
1749 | prefix = g_ptr_array_index (self->prefixes, notify_index); |
||
1750 | cache_node = g_ptr_array_index (self->cache_nodes, notify_index); |
||
1751 | |||
1752 | trace ("Watch thread: notify received on prefix %i: %s.\n", notify_index, prefix); |
||
1753 | |||
1754 | if (cache_node == NULL) |
||
1755 | { |
||
1756 | /* This path has been deleted */ |
||
1757 | trace ("Notify received on a path that was deleted\n"); |
||
1758 | continue; |
||
1759 | } |
||
1760 | |||
1761 | /* Firstly we need to reapply for the notification, because (what a |
||
1762 | * sensible API) we won't receive any more. MSDN is pretty |
||
1763 | * inconsistent on this matter: |
||
1764 | * http://msdn.microsoft.com/en-us/library/ms724892%28VS.85%29.aspx |
||
1765 | * http://support.microsoft.com/kb/236570 |
||
1766 | * But my tests (on Windows XP SP3) show that we need to reapply |
||
1767 | * each time. |
||
1768 | */ |
||
1769 | result = registry_watch_key (hpath, cond); |
||
1770 | |||
1771 | if (result != ERROR_SUCCESS) |
||
1772 | { |
||
1773 | /* Watch failed, most likely because the key has just been |
||
1774 | * deleted. Free the watch and unref the cache nodes. |
||
1775 | */ |
||
1776 | if (result != ERROR_KEY_DELETED) |
||
1777 | g_message_win32_error (result, "watch thread: failed to watch %s", prefix); |
||
1778 | |||
1779 | _free_watch (self, notify_index, cache_node); |
||
1780 | g_atomic_int_inc (&self->watches_remaining); |
||
1781 | continue; |
||
1782 | } |
||
1783 | |||
1784 | /* The notification may have been blocked because we just changed |
||
1785 | * some data ourselves. |
||
1786 | */ |
||
1787 | cache_item = cache_node->data; |
||
1788 | if (cache_item->block_count) |
||
1789 | { |
||
1790 | cache_item->block_count--; |
||
1791 | trace ("Watch thread: notify blocked at %s\n", prefix); |
||
1792 | continue; |
||
1793 | } |
||
1794 | |||
1795 | /* Now we update our stored cache from registry data, and find which keys have |
||
1796 | * actually changed. If more changes happen while we are processing, we will get |
||
1797 | * another event because we have reapplied for change notifications already. |
||
1798 | * |
||
1799 | * Working here rather than in the main thread is preferable because the UI is less |
||
1800 | * likely to block (only when changing notification subscriptions). |
||
1801 | */ |
||
1802 | event = g_slice_new (RegistryEvent); |
||
1803 | event->self = g_object_ref (self->owner); |
||
1804 | event->prefix = g_strdup (prefix); |
||
1805 | event->items = g_ptr_array_new_with_free_func (g_free); |
||
1806 | |||
1807 | EnterCriticalSection (G_REGISTRY_BACKEND (self->owner)->cache_lock); |
||
1808 | registry_cache_update (G_REGISTRY_BACKEND (self->owner), hpath, |
||
1809 | prefix, NULL, cache_node, 0, event); |
||
1810 | LeaveCriticalSection (G_REGISTRY_BACKEND (self->owner)->cache_lock); |
||
1811 | |||
1812 | if (event->items->len > 0) |
||
1813 | g_idle_add ((GSourceFunc) watch_handler, event); |
||
1814 | else |
||
1815 | { |
||
1816 | g_object_unref (event->self); |
||
1817 | g_free (event->prefix); |
||
1818 | g_ptr_array_free (event->items, TRUE); |
||
1819 | g_slice_free (RegistryEvent, event); |
||
1820 | } |
||
1821 | } |
||
1822 | else |
||
1823 | { |
||
1824 | /* God knows what has happened */ |
||
1825 | g_message_win32_error (GetLastError(), "watch thread: WaitForMultipleObjects error"); |
||
1826 | } |
||
1827 | } |
||
1828 | |||
1829 | return -1; |
||
1830 | } |
||
1831 | |||
1832 | static gboolean |
||
1833 | watch_start (GRegistryBackend *self) |
||
1834 | { |
||
1835 | WatchThreadState *watch; |
||
1836 | |||
1837 | g_return_val_if_fail (self->watch == NULL, FALSE); |
||
1838 | |||
1839 | watch = g_slice_new (WatchThreadState); |
||
1840 | watch->owner = G_SETTINGS_BACKEND (self); |
||
1841 | |||
1842 | watch->watches_remaining = MAX_WATCHES; |
||
1843 | |||
1844 | watch->message_lock = g_slice_new (CRITICAL_SECTION); |
||
1845 | InitializeCriticalSection (watch->message_lock); |
||
1846 | watch->message_sent_event = CreateEvent (NULL, FALSE, FALSE, NULL); |
||
1847 | watch->message_received_event = CreateEvent (NULL, FALSE, FALSE, NULL); |
||
1848 | if (watch->message_sent_event == NULL || watch->message_received_event == NULL) |
||
1849 | { |
||
1850 | g_message_win32_error (GetLastError (), "gregistrybackend: Failed to create sync objects."); |
||
1851 | goto fail; |
||
1852 | } |
||
1853 | |||
1854 | /* Use a small stack to make the thread more lightweight. */ |
||
1855 | watch->thread = CreateThread (NULL, 1024, watch_thread_function, watch, 0, NULL); |
||
1856 | if (watch->thread == NULL) |
||
1857 | { |
||
1858 | g_message_win32_error (GetLastError (), "gregistrybackend: Failed to create notify watch thread."); |
||
1859 | goto fail; |
||
1860 | } |
||
1861 | |||
1862 | self->watch = watch; |
||
1863 | |||
1864 | return TRUE; |
||
1865 | |||
1866 | fail: |
||
1867 | DeleteCriticalSection (watch->message_lock); |
||
1868 | g_slice_free (CRITICAL_SECTION, watch->message_lock); |
||
1869 | if (watch->message_sent_event != NULL) |
||
1870 | CloseHandle (watch->message_sent_event); |
||
1871 | if (watch->message_received_event != NULL) |
||
1872 | CloseHandle (watch->message_received_event); |
||
1873 | g_slice_free (WatchThreadState, watch); |
||
1874 | |||
1875 | return FALSE; |
||
1876 | } |
||
1877 | |||
1878 | /* This function assumes you hold the message lock! */ |
||
1879 | static void |
||
1880 | watch_stop_unlocked (GRegistryBackend *self) |
||
1881 | { |
||
1882 | WatchThreadState *watch = self->watch; |
||
1883 | DWORD result; |
||
1884 | |||
1885 | g_return_if_fail (watch != NULL); |
||
1886 | |||
1887 | watch->message.type = WATCH_THREAD_STOP; |
||
1888 | SetEvent (watch->message_sent_event); |
||
1889 | |||
1890 | /* This is signalled as soon as the message is received. We must not return |
||
1891 | * while the watch thread is still firing off callbacks. Freeing all of the |
||
1892 | * memory is done in the watch thread after this is signalled. |
||
1893 | */ |
||
1894 | result = WaitForSingleObject (watch->message_received_event, INFINITE); |
||
1895 | if (result != WAIT_OBJECT_0) |
||
1896 | { |
||
1897 | g_warning ("gregistrybackend: unable to stop watch thread."); |
||
1898 | return; |
||
1899 | } |
||
1900 | |||
1901 | LeaveCriticalSection (watch->message_lock); |
||
1902 | DeleteCriticalSection (watch->message_lock); |
||
1903 | g_slice_free (CRITICAL_SECTION, watch->message_lock); |
||
1904 | CloseHandle (watch->message_sent_event); |
||
1905 | CloseHandle (watch->message_received_event); |
||
1906 | CloseHandle (watch->thread); |
||
1907 | g_slice_free (WatchThreadState, watch); |
||
1908 | |||
1909 | trace ("\nwatch thread: %x: all data freed.\n", self); |
||
1910 | self->watch = NULL; |
||
1911 | } |
||
1912 | |||
1913 | static gboolean |
||
1914 | watch_add_notify (GRegistryBackend *self, |
||
1915 | HANDLE event, |
||
1916 | HKEY hpath, |
||
1917 | gchar *gsettings_prefix) |
||
1918 | { |
||
1919 | WatchThreadState *watch = self->watch; |
||
1920 | GNode *cache_node; |
||
1921 | RegistryCacheItem *cache_item; |
||
1922 | #ifdef TRACE |
||
1923 | DWORD result; |
||
1924 | #endif |
||
1925 | |||
1926 | g_return_val_if_fail (watch != NULL, FALSE); |
||
1927 | |||
1928 | trace ("watch_add_notify: prefix %s.\n", gsettings_prefix); |
||
1929 | |||
1930 | /* Duplicate tree into the cache in the main thread, before we add the notify: if we do it in the |
||
1931 | * thread we can miss changes while we are caching. |
||
1932 | */ |
||
1933 | EnterCriticalSection (self->cache_lock); |
||
1934 | cache_node = registry_cache_get_node_for_key (self->cache_root, gsettings_prefix, TRUE); |
||
1935 | |||
1936 | if (cache_node == NULL || cache_node->data == NULL) |
||
1937 | { |
||
1938 | LeaveCriticalSection (self->cache_lock); |
||
1939 | g_warn_if_reached (); |
||
1940 | return FALSE; |
||
1941 | } |
||
1942 | |||
1943 | cache_item = cache_node->data; |
||
1944 | |||
1945 | cache_item->subscription_count++; |
||
1946 | if (cache_item->subscription_count > 1) |
||
1947 | { |
||
1948 | trace ("watch_add_notify: prefix %s already watched, %i subscribers.\n", |
||
1949 | gsettings_prefix, cache_item->subscription_count); |
||
1950 | LeaveCriticalSection (self->cache_lock); |
||
1951 | return FALSE; |
||
1952 | } |
||
1953 | |||
1954 | registry_cache_ref_tree (cache_node); |
||
1955 | registry_cache_update (self, hpath, gsettings_prefix, NULL, cache_node, 0, NULL); |
||
1956 | //registry_cache_dump (self->cache_root, NULL); |
||
1957 | LeaveCriticalSection (self->cache_lock); |
||
1958 | |||
1959 | EnterCriticalSection (watch->message_lock); |
||
1960 | watch->message.type = WATCH_THREAD_ADD_WATCH; |
||
1961 | watch->message.watch.event = event; |
||
1962 | watch->message.watch.hpath = hpath; |
||
1963 | watch->message.watch.prefix = gsettings_prefix; |
||
1964 | watch->message.watch.cache_node = cache_node; |
||
1965 | |||
1966 | SetEvent (watch->message_sent_event); |
||
1967 | |||
1968 | /* Wait for the received event in return, to avoid sending another message before the first |
||
1969 | * one was received. If it takes > 200ms there is a possible race but the worst outcome is |
||
1970 | * a notification is ignored. |
||
1971 | */ |
||
1972 | #ifdef TRACE |
||
1973 | result = |
||
1974 | #endif |
||
1975 | WaitForSingleObject (watch->message_received_event, 200); |
||
1976 | #ifdef TRACE |
||
1977 | if (result != WAIT_OBJECT_0) |
||
1978 | trace ("watch thread is slow to respond - notification may not be added."); |
||
1979 | #endif |
||
1980 | |||
1981 | LeaveCriticalSection (watch->message_lock); |
||
1982 | |||
1983 | return TRUE; |
||
1984 | } |
||
1985 | |||
1986 | static void |
||
1987 | watch_remove_notify (GRegistryBackend *self, |
||
1988 | const gchar *key_name) |
||
1989 | { |
||
1990 | WatchThreadState *watch = self->watch; |
||
1991 | LONG result; |
||
1992 | |||
1993 | if (self->watch == NULL) |
||
1994 | /* Here we assume that the unsubscribe message is for somewhere that was |
||
1995 | * deleted, and so it has already been removed and the watch thread has |
||
1996 | * stopped. |
||
1997 | */ |
||
1998 | return; |
||
1999 | |||
2000 | EnterCriticalSection (watch->message_lock); |
||
2001 | watch->message.type = WATCH_THREAD_REMOVE_WATCH; |
||
2002 | watch->message.watch.prefix = g_strdup (key_name); |
||
2003 | |||
2004 | SetEvent (watch->message_sent_event); |
||
2005 | |||
2006 | /* Wait for the received event in return, to avoid sending another message before the first |
||
2007 | * one was received. |
||
2008 | */ |
||
2009 | result = WaitForSingleObject (watch->message_received_event, INFINITE); |
||
2010 | |||
2011 | if (result != ERROR_SUCCESS) |
||
2012 | g_warning ("unsubscribe from %s: message not acknowledged", key_name); |
||
2013 | |||
2014 | if (g_atomic_int_get (&watch->watches_remaining) >= MAX_WATCHES) |
||
2015 | /* Stop it before any new ones can get added and confuse things */ |
||
2016 | watch_stop_unlocked (self); |
||
2017 | else |
||
2018 | LeaveCriticalSection (watch->message_lock); |
||
2019 | } |
||
2020 | |||
2021 | /* dconf semantics are: if the key ends in /, watch the keys underneath it - if not, watch that |
||
2022 | * key. Our job is easier because keys and values are separate. |
||
2023 | */ |
||
2024 | static void |
||
2025 | g_registry_backend_subscribe (GSettingsBackend *backend, |
||
2026 | const char *key_name) |
||
2027 | { |
||
2028 | GRegistryBackend *self = G_REGISTRY_BACKEND (backend); |
||
2029 | gchar *path_name; |
||
2030 | gunichar2 *path_namew; |
||
2031 | gchar *value_name = NULL; |
||
2032 | HKEY hpath; |
||
2033 | HANDLE event; |
||
2034 | LONG result; |
||
2035 | |||
2036 | if (self->watch == NULL && !watch_start (self)) |
||
2037 | return; |
||
2038 | |||
2039 | if (g_atomic_int_dec_and_test (&self->watch->watches_remaining)) |
||
2040 | { |
||
2041 | g_atomic_int_inc (&self->watch->watches_remaining); |
||
2042 | g_warning ("subscribe() failed: only %i different paths may be watched.", MAX_WATCHES); |
||
2043 | return; |
||
2044 | } |
||
2045 | |||
2046 | path_name = parse_key (key_name, self->base_path, &value_name); |
||
2047 | |||
2048 | /* Must check for this, otherwise strange crashes occur because the cache |
||
2049 | * node that is being watched gets freed. All path names to subscribe must |
||
2050 | * end in a slash! |
||
2051 | */ |
||
2052 | if (value_name != NULL && *value_name != 0) |
||
2053 | g_warning ("subscribe() failed: path must end in a /, got %s", key_name); |
||
2054 | |||
2055 | trace ("Subscribing to %s [registry %s / %s] - watch %x\n", key_name, path_name, value_name, self->watch); |
||
2056 | |||
2057 | path_namew = g_utf8_to_utf16 (path_name, -1, NULL, NULL, NULL); |
||
2058 | g_free (path_name); |
||
2059 | |||
2060 | /* Give the caller the benefit of the doubt if the key doesn't exist and create it. The caller |
||
2061 | * is almost certainly a new g_settings with this path as base path. */ |
||
2062 | result = RegCreateKeyExW (HKEY_CURRENT_USER, path_namew, 0, NULL, 0, KEY_READ, NULL, &hpath, |
||
2063 | NULL); |
||
2064 | g_free (path_namew); |
||
2065 | |||
2066 | if (result != ERROR_SUCCESS) |
||
2067 | { |
||
2068 | g_message_win32_error (result, "gregistrybackend: Unable to subscribe to key %s.", key_name); |
||
2069 | g_atomic_int_inc (&self->watch->watches_remaining); |
||
2070 | return; |
||
2071 | } |
||
2072 | |||
2073 | event = CreateEvent (NULL, FALSE, FALSE, NULL); |
||
2074 | if (event == NULL) |
||
2075 | { |
||
2076 | g_message_win32_error (result, "gregistrybackend: CreateEvent failed."); |
||
2077 | g_atomic_int_inc (&self->watch->watches_remaining); |
||
2078 | RegCloseKey (hpath); |
||
2079 | return; |
||
2080 | } |
||
2081 | |||
2082 | /* The actual watch is added by the thread, which has to re-subscribe each time it |
||
2083 | * receives a change. */ |
||
2084 | if (!watch_add_notify (self, event, hpath, g_strdup (key_name))) |
||
2085 | { |
||
2086 | g_atomic_int_inc (&self->watch->watches_remaining); |
||
2087 | RegCloseKey (hpath); |
||
2088 | CloseHandle (event); |
||
2089 | } |
||
2090 | } |
||
2091 | |||
2092 | static void |
||
2093 | g_registry_backend_unsubscribe (GSettingsBackend *backend, |
||
2094 | const char *key_name) |
||
2095 | { |
||
2096 | trace ("unsubscribe: %s.\n", key_name); |
||
2097 | |||
2098 | watch_remove_notify (G_REGISTRY_BACKEND (backend), key_name); |
||
2099 | } |
||
2100 | |||
2101 | /******************************************************************************** |
||
2102 | * Object management junk |
||
2103 | ********************************************************************************/ |
||
2104 | |||
2105 | static void |
||
2106 | g_registry_backend_finalize (GObject *object) |
||
2107 | { |
||
2108 | GRegistryBackend *self = G_REGISTRY_BACKEND (object); |
||
2109 | RegistryCacheItem *item; |
||
2110 | |||
2111 | item = self->cache_root->data; |
||
2112 | g_warn_if_fail (item->ref_count == 1); |
||
2113 | |||
2114 | registry_cache_item_free (item); |
||
2115 | g_node_destroy (self->cache_root); |
||
2116 | |||
2117 | if (self->watch != NULL) |
||
2118 | { |
||
2119 | EnterCriticalSection (self->watch->message_lock); |
||
2120 | watch_stop_unlocked (self); |
||
2121 | } |
||
2122 | |||
2123 | DeleteCriticalSection (self->cache_lock); |
||
2124 | g_slice_free (CRITICAL_SECTION, self->cache_lock); |
||
2125 | |||
2126 | g_free (self->base_path); |
||
2127 | g_free (self->base_pathw); |
||
2128 | } |
||
2129 | |||
2130 | static void |
||
2131 | g_registry_backend_class_init (GRegistryBackendClass *class) |
||
2132 | { |
||
2133 | GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class); |
||
2134 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
||
2135 | |||
2136 | object_class->finalize = g_registry_backend_finalize; |
||
2137 | |||
2138 | backend_class->read = g_registry_backend_read; |
||
2139 | backend_class->write = g_registry_backend_write; |
||
2140 | backend_class->write_tree = g_registry_backend_write_tree; |
||
2141 | backend_class->reset = g_registry_backend_reset; |
||
2142 | backend_class->get_writable = g_registry_backend_get_writable; |
||
2143 | backend_class->subscribe = g_registry_backend_subscribe; |
||
2144 | backend_class->unsubscribe = g_registry_backend_unsubscribe; |
||
2145 | } |
||
2146 | |||
2147 | static void |
||
2148 | g_registry_backend_init (GRegistryBackend *self) |
||
2149 | { |
||
2150 | RegistryCacheItem *item; |
||
2151 | |||
2152 | self->base_path = g_strdup_printf ("Software\\GSettings"); |
||
2153 | self->base_pathw = g_utf8_to_utf16 (self->base_path, -1, NULL, NULL, NULL); |
||
2154 | |||
2155 | item = g_slice_new (RegistryCacheItem); |
||
2156 | item->value.type = REG_NONE; |
||
2157 | item->value.ptr = NULL; |
||
2158 | item->name = g_strdup ("<root>"); |
||
2159 | item->ref_count = 1; |
||
2160 | self->cache_root = g_node_new (item); |
||
2161 | |||
2162 | self->cache_lock = g_slice_new (CRITICAL_SECTION); |
||
2163 | InitializeCriticalSection (self->cache_lock); |
||
2164 | |||
2165 | self->watch = NULL; |
||
2166 | } |