Commit 8c78267b5f67f644d581783142ed4d5486361900

  • avatar
  • ssam
  • Fri Aug 14 19:05:57 GMT 2009
registry: initial notification code.
gio/gregistrystorage.c
(669 / 46)
  
1010 * Authors: Sam Thursfield <ssssam@gmail.com>
1111 */
1212
13/* FIXME:
14 * - use g_idle_add instead of all of the pipe nonsense.
15 * - remove g_thread dependency. */
16
1317/* GRegistryStorage implementation notes:
1418 *
1519 * - http://msdn.microsoft.com/en-us/library/ms724875%28VS.85%29.aspx
2626 * - Backslashes are used as path separators in the registry. Forward slashes are invalid in
2727 * gsettings key names, so we swap the two as a simple escape procedure.
2828 *
29 * - Notifications are handled; the change event is watched for in a separate thread (Windows
30 * seems not to want to give a callback), which then sends them down a pipe to the GLib main
31 * loop.
32 *
33 * - The threading is done using Windows API functions, so there is no GThread dependence.
34 *
35 * - The pipe io is done with MSVCRT functions and glib IOChannels. This means the code will not
36 * work with Visual C past version 6 (unless you use a glib built with Visual C past version 6).
37 * There is no alternative, because the glib main loop reads from the pipe during the poll and
38 * then doesn't pass the data to the callback - so the only way to integrate a pipe into the
39 * main loop on Windows is to use a GIOChannel.
40 *
41 * - Windows doesn't tell us what key has changed. This means we have to maintain a cache of every
42 * stored value so we can play spot the difference.
43 * FIXME: this would become very slow for a GSettings with lots and lots of keys! The solution
44 * here is (assuming it's a tree and not a flat list, in which case we are stuck) to break the
45 * tree up into multiple watches, so we have more granularity about the notifications. Use non
46 * recursive notify in the root of the subscription (there's a parameter for that). Set up
47 * separate watches for each of the subkeys.
48 *
49 * - RegNotifyChangeKeyValue is called on a key after each change notification on that key.
50 * MSDN says that the notification only occurs once:
51 * http://msdn.microsoft.com/en-us/library/ms724892%28VS.85%29.aspx
52 * but MS support says that it occurs as long as the handle stays open:
53 * http://support.microsoft.com/kb/236570
54 * the former seems to be true, at least on my system.
55 *
56 * - FIXME: there is an undocumented function in ntdll.dll which looks a lot better for this job:
57 * NtNotifyChangeKey:
58 * http://source.winehq.org/source/dlls/ntdll/reg.c#L618
59 * http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Key/NtNotifyChangeKey.html
60 * It looks like the optional parameters could be 1. an APC notification mechanism (better than
61 * the watch thread presumably) and 2. a buffer given the changes that have occured. Of course,
62 * it's hard to find that out without some serious research and disassembly. And it's inherently
63 * a bit scary using undocumented Windows API. Still, it would be worth testing because it could
64 * make the code a whole lot cleaner ..
65 *
2966 * - No attempt is made to solve clashes between keys differing only in case. You deserve to
3067 * suffer if you try to make keys that differ only by case.
3168 *
69 * - When a buffer is used for a key name it is sized 256. That means key names can't be longer
70 * than 255 characters. If that's too little, talk to someone who uses twitter
71 *
3272 * - FIXME: read failures should be handled better (not with g_warning) because in the registry,
3373 * we have to be open to the possibility of the user editing the registry by hand and being dumb
34 *
74 *
3575 * - No maximum size for binary data is enforced right now. See also:
3676 * http://msdn.microsoft.com/en-us/library/ms724872(VS.85).aspx
77 *
3778 */
3879
3980/* - RegCreateKeyA is used - Windows can also handle UTF16LE strings. GSettings doesn't pay
8383
8484#include <stdarg.h>
8585#include <stdio.h>
86#include <io.h>
87#include <fcntl.h>
88#include <unistd.h>
8689#include <windows.h>
8790#include "gregistrystorage.h"
91#include "gmemstorage.h"
8892
93typedef struct _WatchThreadState WatchThreadState;
94
95#define TRACE
96
8997struct _GRegistryStoragePrivate
9098{
9199 char *base;
100
101 WatchThreadState *watch;
92102};
93103
104static GSource *__g_fd_source_new (int fd, gushort events);
105
94106static void trace (const char *format, ...);
95107
96108G_DEFINE_TYPE (GRegistryStorage, g_registry_storage, G_TYPE_SETTINGS_STORAGE)
97109
110/* g_warning including a windows error message.
111 * FIXME: g_warning is only appropriate for programmer errors - which most of these ones are,
112 * because they can't really fail otherwise, but we need to be wary of not abusing g_warning. */
113static void
114g_warning_win32_error (DWORD result_code,
115 const gchar *format,
116 ...)
117{
118 va_list va;
119 gint pos;
120 gchar win32_message[1024];
121
122 if (result_code == 0)
123 result_code = GetLastError ();
124
125 va_start (va, format);
126 pos = g_vsnprintf (win32_message, 512, format, va);
127
128 win32_message[pos++] = ':'; win32_message[pos++] = ' ';
129
130 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, result_code, 0, (LPTSTR)(win32_message+pos),
131 1023 - pos, NULL);
132 g_warning (win32_message);
133};
134
98135/* Make gsettings key into a registry key & value pair. In registry terminology, the 'value' is the
99 * last component of the path, and the 'key' is the rest. The paths are \ separated. */
136 * last component of the path, and the 'key' is the rest. The paths are \ separated.
137 *
138 * Note that the return value *only* needs freeing - registry_value_name is a pointer to further
139 * inside the same block of memory.
140 */
100141static gchar *
101142parse_key (const gchar *gsettings_key,
102143 const gchar *prefix,
263263 result = RegCreateKeyExA (root_key, key_path, 0, NULL, 0, KEY_WRITE, NULL, &key, NULL);
264264 if (result != ERROR_SUCCESS)
265265 {
266 /* Error success is no error: no error success means error and no success */
267 char message[512];
268 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, result, 0, (LPTSTR)&message, 511, NULL);
269 g_warning ("gregistrystorage: opening key %s failed: %s\n", key_path+1, message);
270 HeapFree (GetProcessHeap (), 0, value_data); g_free (key_path);
266 g_warning_win32_error (result, "gregistrystorage: opening key %s failed", key_path+1);
267 g_free (value_data); g_free (key_path);
271268 return FALSE;
272269 }
273270
274271 result = RegSetValueExA (key, value_name, 0, value_type, value_data, value_data_size);
275272 if (result != ERROR_SUCCESS)
276 g_warning ("gregistrystorage: setting value %s\%s\\%s failed.\n", self->priv->base,
277 key_path, value_name);
273 g_warning_win32_error (result, "gregistrystorage: setting value %s\%s\\%s failed.\n",
274 self->priv->base, key_path, value_name);
278275
279276 RegCloseKey (key);
280277 g_free (value_data);
290290 LONG result;
291291 HKEY root_key;
292292
293 /* The dconf policy is to do the write while making out it succeeded, and then backtrack if it
294 * didn't. The registry functions are synchronous so I assume we don't need to bother with this.
295 */
296
293297 result = RegCreateKeyExA (HKEY_CURRENT_USER, self->priv->base, 0, NULL, 0,
294298 KEY_WRITE, NULL, &root_key, NULL);
295299 if (result != ERROR_SUCCESS) {
296300 trace ("Error creating key %s.\n", self->priv->base);
297 /* FIXME: what would dconf do? pretend that the value was set, but then notify that it was unset
298 * again after. */
299 return;
301 return;
300302 }
301303
302304 const gchar **changes;
327327 const gchar *key_path,
328328 const gchar *value_name)
329329{
330 /* file not found means key value not set. */
330 /* file not found means key value not set, this isn't an error for us. */
331331 if (result != ERROR_FILE_NOT_FOUND)
332 {
333 char message[512];
334 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, result, 0, (LPTSTR)&message, 511, NULL);
335 g_warning ("Unable to query value %s\\%s: %s.\n", key_path, value_name, message);
336 }
332 g_warning_win32_error (result, "Unable to query value %s\\%s: %s.\n", key_path, value_name);
337333}
338334
339335static GVariant *
339339 const gchar *expected_type)
340340{
341341 GVariant *variant = NULL;
342 DWORD value, value_type, value_data_size = 4;
342 DWORD value, value_data_size = 4;
343343 LONG result;
344344
345 result = RegQueryValueExA (key, value_name, 0, &value_type, (LPBYTE)&value, &value_data_size);
345 result = RegQueryValueExA (key, value_name, 0, NULL, (LPBYTE)&value, &value_data_size);
346346 if (result != ERROR_SUCCESS)
347347 {
348348 handle_read_error (result, key_path, value_name);
349349 return NULL;
350350 }
351351
352 /* This is allowed because the watch thread cache doesn't need to store variants as the correct
353 * type - just a consistent type so they can be compared. */
354 if (expected_type == NULL)
355 expected_type = g_variant_type_peek_string (G_VARIANT_TYPE_INT32);
356
352357 switch (expected_type[0])
353358 {
354359 case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
355 if (value_type == REG_DWORD)
356 variant = g_variant_new (expected_type, value);
360 variant = g_variant_new (expected_type, value);
357361 break;
358362
359363 default:
360 g_warn_if_reached ();
364 g_warning ("gregistrystorage: %s/%s: got dword, expected %s.\n", key_path, value_name,
365 expected_type);
361366 }
362367
363368 return variant;
375375 const gchar *expected_type)
376376{
377377 GVariant *variant = NULL;
378 guint64 value; DWORD value_type, value_data_size = 8;
378 guint64 value; DWORD value_data_size = 8;
379379 LONG result;
380380
381 result = RegQueryValueExA (key, value_name, 0, &value_type, (LPBYTE)&value, &value_data_size);
381 result = RegQueryValueExA (key, value_name, 0, NULL, (LPBYTE)&value, &value_data_size);
382382 if (result != ERROR_SUCCESS)
383383 {
384384 handle_read_error (result, key_path, value_name);
385385 return NULL;
386386 }
387387
388 if (expected_type == NULL)
389 expected_type = g_variant_type_peek_string (G_VARIANT_TYPE_INT64);
390
388391 switch (expected_type[0])
389392 {
390393 case 't': case 'x':
391 if (value_type == REG_QWORD)
392 variant = g_variant_new (expected_type, value);
394 variant = g_variant_new (expected_type, value);
393395 break;
394396
395397 default:
396 g_warn_if_reached ();
398 g_warning ("gregistrystorage: %s/%s: got qword, expected %s.\n", key_path, value_name,
399 expected_type);
397400 }
398401
399402 return variant;
409409 const gchar *expected_type)
410410{
411411 GVariant *variant = NULL;
412 gchar *value; DWORD value_type, value_data_size;
412 gchar *value; DWORD value_data_size;
413413 LONG result;
414414
415 if (expected_type != NULL && expected_type[0] != 's')
416 {
417 g_warning ("gregistrystorage: %s/%s: got string, expected %s.\n", key_path, value_name,
418 expected_type);
419 return NULL;
420 }
421
415422 /* First, find length of the string */
416 result = RegQueryValueExA (key, value_name, 0, &value_type, NULL, &value_data_size);
423 result = RegQueryValueExA (key, value_name, 0, NULL, NULL, &value_data_size);
417424 if (result != ERROR_SUCCESS)
418425 {
419426 handle_read_error (result, key_path, value_name);
439439 if (value[value_data_size] != 0)
440440 value[value_data_size + 1] = 0;
441441
442 if (value_type != REG_SZ)
443 return NULL;
442 variant = g_variant_new ("s", value);
444443
445 variant = g_variant_new (expected_type, value);
446
447444 return variant;
448445}
449446
452452 GRegistryStorage *self = G_REGISTRY_STORAGE (s_storage);
453453 gchar *key_path, *value_name;
454454 HKEY registry_key; LONG result;
455 const gchar *variant_type = g_variant_type_peek_string (expected_type);
456 GVariant *variant;
455 DWORD registry_type;
456 const gchar *variant_type = NULL;
457 GVariant *variant = NULL;
457458
459 if (expected_type != NULL)
460 variant_type = g_variant_type_peek_string (expected_type);
458461 key_path = parse_key (key, self->priv->base, &value_name);
459462 trace ("Reading key %s / %s\n", key_path, value_name);
460463
466466 {
467467 if (result != ERROR_FILE_NOT_FOUND)
468468 /* Warn if the error was something other than the key not existing yet. */
469 g_warning ("Unable to open key %s.", key_path);
470 g_free (key_path); return NULL;
469 g_warning_win32_error (result, "Unable to open key %s.", key_path);
470 goto fail_1;
471471 }
472472
473 /* Read the type first, so we use the correct buffer size. */
474 result = RegQueryValueExA (registry_key, value_name, 0, &registry_type, NULL, NULL);
475 if (result != ERROR_SUCCESS)
476 {
477 g_warning_win32_error (result, "Unable to read type of key %s\n", key_path);
478 goto fail_2;
479 }
480
473481 /* FIXME: the registry is user-editable, so we need to be very fault-tolerant here. */
474 switch (variant_type[0])
482 switch (registry_type)
475483 {
476 case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
484 case REG_DWORD:
477485 variant = g_registry_storage_read_dword (registry_key, key_path, value_name,
478486 variant_type);
479487 break;
480488
481 case 'x': case 't':
489 case REG_QWORD:
482490 variant = g_registry_storage_read_qword (registry_key, key_path, value_name,
483491 variant_type);
484492 break;
485493
486 case 's': case 'o': case 'g':
494 case REG_SZ:
487495 variant = g_registry_storage_read_string (registry_key, key_path, value_name,
488 variant_type);
496 variant_type);
489497 break;
490498
491499 default:
492 variant = NULL;
493500 g_warning ("gregistrystorage: Unable to read key of type %s.\n", variant_type);
494501 break;
495502 }
496503
504fail_2:
497505 RegCloseKey (registry_key);
506fail_1:
498507 g_free (key_path);
499508 return variant;
500509}
515515 return TRUE;
516516}
517517
518/*
519 * Thread to watch for registry change events. This is hopelessly complicated :(
520 */
521
522/* This struct represents a watch on one registry key and its subkeys */
523typedef struct
524{
525 HANDLE event;
526 HKEY key;
527 char *prefix;
528} RegistryWatch;
529
530/* One of these is sent down the pipe when something happens in the registry. */
531typedef struct
532{
533 /* prefix is a gsettings path, all items are subkeys of this. */
534 gchar *prefix;
535
536 /* each item is a subkey below prefix that has changed. */
537 GArray *items;
538} RegistryEvent;
539
540/* Simple message passing for the thread. I didn't bother with a queue. FIXME: there's no reason
541 * not to use GQueue.
542 */
543typedef enum
544{
545 WATCH_THREAD_ADD_WATCH,
546 WATCH_THREAD_REMOVE_WATCH,
547 WATCH_THREAD_STOP
548} WatchThreadMessageType;
549
550typedef struct
551{
552 WatchThreadMessageType type;
553 RegistryWatch watch;
554} WatchThreadMessage;
555
556struct _WatchThreadState
557{
558 HANDLE *thread;
559
560 /* Details of the things we are watching. */
561 GPtrArray *events, *keys, *prefixes;
562
563 /* A stored copy of the whole tree being watched. When we receive a change notification
564 * we have to check against this to see what has changed ... every time ...*/
565 GSettingsStorage *cache;
566
567 /* Output to the main thread and main loop - the write end of a pipe. */
568 int pipe_write;
569
570 /* Input from the main thread. Windows threading doesn't have conds, so to make sure the
571 * messages are acknowledged before being overwritten we create two events - one is signalled
572 * when a new message is set, and the other is signalled by the thread when it has processed the
573 * message.
574 */
575 WatchThreadMessage message;
576 CRITICAL_SECTION *message_lock, *cache_lock;
577 HANDLE message_sent_event, message_received_event;
578};
579
580
581static gboolean
582watch_handler (RegistryEvent *event)
583{
584 trace ("Watch handler: got event in %s.\n", event->prefix);
585 g_free (event->prefix);
586 g_slice_free (RegistryEvent, event);
587 return FALSE;
588};
589
590/* Called by watch thread. */
591static gboolean
592watch_key (HKEY key, HANDLE event, gchar *key_path)
593{
594 DWORD result;
595 result = RegNotifyChangeKeyValue (key, TRUE,
596 REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
597 event, TRUE);
598 if (result != ERROR_SUCCESS)
599 {
600 if (result == ERROR_KEY_DELETED)
601 {
602 /* Someone has called RegDeleteKey here, and Windows is waiting until all of the handles
603 * are closed to carry out the deletion.
604 */
605 }
606 else
607 g_warning_win32_error (result, "gregistrystorage: unable to watch prefix %s\n", key_path);
608 CloseHandle (event);
609 RegCloseKey (key);
610 g_free (key_path);
611 return FALSE;
612 }
613 return TRUE;
614}
615
616/* Thread which watches for win32 registry events */
617static DWORD WINAPI
618watch_thread_function (LPVOID parameter)
619{
620 WatchThreadState *self = (WatchThreadState *)parameter;
621 DWORD result;
622
623 self->events = g_ptr_array_new ();
624 self->keys = g_ptr_array_new ();
625 self->prefixes = g_ptr_array_new ();
626 g_ptr_array_add (self->events, self->message_sent_event);
627 g_ptr_array_add (self->keys, NULL);
628 g_ptr_array_add (self->prefixes, NULL);
629
630 while (1)
631 {
632 trace ("watch thread: going to sleep; %i events watched.\n", self->events->len);
633 result = WaitForMultipleObjects (self->events->len, self->events->pdata, FALSE, /*INFINITE*/1000);
634
635 if (result == WAIT_OBJECT_0)
636 {
637 /* A message to you. The sender (main thread) will block until we signal the received
638 * event, so there should be no danger of it sending another before we receive the
639 * first.
640 */
641 switch (self->message.type)
642 {
643 case WATCH_THREAD_ADD_WATCH:
644 {
645 RegistryWatch *watch = &self->message.watch;
646 if (watch_key (watch->key, watch->event, watch->prefix))
647 {
648 g_ptr_array_add (self->events, watch->event);
649 g_ptr_array_add (self->keys, watch->key);
650 g_ptr_array_add (self->prefixes, watch->prefix);
651 }
652 break;
653 }
654 case WATCH_THREAD_REMOVE_WATCH:
655 break;
656 case WATCH_THREAD_STOP:
657 SetEvent (self->message_received_event);
658 /* FIXME: send a null event down the pipe so the main loop can close its handle */
659 ExitThread (0);
660 }
661 SetEvent (self->message_received_event);
662 }
663 else if (result > WAIT_OBJECT_0 && result <= WAIT_OBJECT_0 + self->events->len)
664 {
665 RegistryEvent *event = g_slice_new (RegistryEvent);
666 HKEY key;
667 HANDLE cond;
668 gchar *prefix;
669 /*DWORD bytes_written = 0;*/
670 size_t bytes_written = 0;
671
672 /* One of our notifications has triggered. All we know is which one, and which key
673 * this is for. We do most of the processing here, because we may as well. If the
674 * registry changes further while we are processing it doesn't matter - we will then
675 * receive another change notification from the OS anyway.
676 */
677 gint notify_index = result - WAIT_OBJECT_0;
678 key = g_ptr_array_index (self->keys, notify_index);
679 cond = g_ptr_array_index (self->events, notify_index);
680 prefix = g_ptr_array_index (self->prefixes, notify_index);
681
682 trace ("Watch thread: notify received on prefix: %s.\n", prefix);
683
684 /* Firstly we need to reapply for the notification. FIXME: MSDN says you don't somewhere */
685 if (!watch_key (key, cond, prefix))
686 {
687 /* Error, or the key has been deleted. Stop watching it. The rest of the cleanup is
688 * done by watch_key(). */
689 /* FIXME: need to remove it from the cache .. */
690 g_ptr_array_remove_index (self->keys, notify_index);
691 g_ptr_array_remove_index (self->events, notify_index);
692 g_ptr_array_remove_index (self->prefixes, notify_index);
693 };
694
695 event->prefix = g_strdup (prefix);
696
697 /* FIXME: filter keys against stored copies (from when the notify was added) and only
698 * notify on keys that have changed. Update the stored copy as we go.
699 */
700
701 g_idle_add ((GSourceFunc) watch_handler, event);
702 /*bytes_written = write (self->pipe_write, &event, sizeof (RegistryEvent));
703 if (bytes_written != sizeof (RegistryEvent))
704 g_warning ("gregistrystorage: watch thread: error sending event: %i.\n", errno);*/
705 }
706 else
707 {
708 /* God knows what has happened */
709 trace ("watch thread: Unexpected result from WaitForMultipleObjects: %d\n", result);
710 }
711 }
712}
713
714/* Handler called for registry events once received inside the glib main loop */
715/*static gboolean
716watch_handler (GIOChannel *source,
717 GIOCondition condition,
718 gpointer data)
719{
720 GRegistryStorage *self = G_REGISTRY_STORAGE (data);
721 RegistryEvent event;
722 gsize bytes_read;
723 GIOStatus status;
724 GError *error = NULL;
725
726 trace ("watch handler: got condition %i. in %i, out %i, err %i, hup %i\n", condition, G_IO_IN,
727 G_IO_OUT, G_IO_ERR, G_IO_HUP);
728
729 trace ("watch handler: Reading from pipe...");
730 status = g_io_channel_read_chars (source, (gchar *)&event, sizeof (RegistryEvent), &bytes_read,
731 &error);
732 trace ("...done (%i bytes), status %i.\n", bytes_read, status);
733 if (bytes_read != sizeof (RegistryEvent) || status != G_IO_STATUS_NORMAL)
734 {
735 g_warning ("gregistrystorage: Could not read data from pipe: %s", error->message);
736 return TRUE;
737 }
738
739 if (event.prefix == NULL)
740 {
741 /* NULL event is HUP *//*
742 trace ("watch handler: received hangup.\n");
743 g_io_channel_unref (source);
744 return FALSE;
745 }
746
747 trace ("watch_handler: received a notify: %s\n", event.prefix);
748
749 // ULTIMATE GOAL: call this (from the main thread)
750 // g_settings_storage_changed (G_SETTINGS_STORAGE (self), key + strlen(self->priv->base),
751 // items, n_items, NULL);
752 g_free (event.prefix);
753
754 return TRUE;
755};*/
756
757static gboolean
758watch_start (GRegistryStorage *self)
759{
760 WatchThreadState *watch;
761 GIOChannel *pipe_read;
762 int tmp[2];
763
764 g_return_val_if_fail (self->priv->watch == NULL, FALSE);
765
766 watch = g_slice_new (WatchThreadState);
767
768 if (_pipe (tmp, 256, _O_BINARY) != 0)
769 {
770 g_warning ("gregistrystorage: Failed to create pipe for watch thread: %i", errno);
771 goto fail_1;
772 }
773 watch->pipe_write = tmp[1];
774 pipe_read = g_io_channel_win32_new_fd (tmp[0]);
775 /* FIXME: what buffer size do we get? */
776 g_io_channel_set_encoding (pipe_read, NULL, NULL);
777
778 watch->cache_lock = g_slice_new (CRITICAL_SECTION);
779 InitializeCriticalSection (watch->cache_lock);
780 watch->message_lock = g_slice_new (CRITICAL_SECTION);
781 InitializeCriticalSection (watch->message_lock);
782 watch->message_sent_event = CreateEvent (NULL, FALSE, FALSE, NULL);
783 watch->message_received_event = CreateEvent (NULL, FALSE, FALSE, NULL);
784 if (watch->message_sent_event == NULL || watch->message_received_event == NULL)
785 {
786 g_warning_win32_error (0, "gregistrystorage: Failed to create sync objects.");
787 goto fail_2;
788 }
789
790 /* Use a small stack to make the thread more lightweight. */
791 watch->thread = CreateThread (NULL, 1024, watch_thread_function, watch, 0, NULL);
792 if (watch->thread == NULL)
793 {
794 g_warning_win32_error (0, "gregistrystorage: Failed to create notify watch thread.");
795 goto fail_3;
796 }
797
798 watch->cache = g_mem_storage_new ();
799
800 self->priv->watch = watch;
801
802 /* Add a source to the glib main loop to read from the other end of the pipe and call
803 * g_settings_storage_changed for us.
804 */
805 g_io_add_watch (pipe_read, G_IO_ERR | G_IO_HUP | G_IO_IN, watch_handler, self);
806
807 return TRUE;
808
809fail_3:
810 DeleteCriticalSection (watch->message_lock);
811 CloseHandle (watch->message_sent_event); CloseHandle (watch->message_received_event);
812fail_2:
813 close (watch->pipe_write);
814 g_io_channel_unref (pipe_read);
815fail_1:
816 g_slice_free (WatchThreadState, watch);
817 return FALSE;
818}
819
518820static void
519g_registry_storage_subscribe (GSettingsStorage *base,
821watch_stop (GRegistryStorage *self)
822{
823 WatchThreadState *watch = self->priv->watch;
824 RegistryEvent null_event = { 0 };
825 DWORD result;
826 g_return_if_fail (watch != NULL);
827
828 EnterCriticalSection (watch->message_lock);
829
830 watch->message.type = WATCH_THREAD_STOP;
831 SetEvent (watch->message_sent_event);
832
833 /* Give a long delay - we need the thread to have stopped before the function returns. This
834 * function will probably only be called at the end of the program - while any GSettings objects
835 * exist, they will be subscribed to changes on their keys. If the stop fails, we don't free
836 * any data.
837 */
838 result = WaitForSingleObject (watch->message_received_event, 250);
839 if (result != WAIT_OBJECT_0)
840 {
841 g_warning ("gregistrystorage: unable to stop watch thread.");
842 return;
843 }
844 LeaveCriticalSection (watch->message_lock);
845 LeaveCriticalSection (watch->cache_lock);
846
847 /* Send null event down the pipe to close the other end.
848 * FIXME: could we sent HUP signal? there doesn't seem to be a way. */
849 write (watch->pipe_write, &null_event, sizeof(RegistryEvent));
850
851 /* Once we get a message received event, we know the thread will not access any more of its
852 * data.
853 */
854 CloseHandle (watch->thread);
855 g_object_unref (watch->cache);
856 DeleteCriticalSection (watch->message_lock);
857 CloseHandle (watch->message_sent_event); CloseHandle (watch->message_received_event);
858 close (watch->pipe_write);
859 g_slice_free (WatchThreadState, watch);
860};
861
862static void
863add_prefix_to_cache (GRegistryStorage *self,
864 GSettingsStorage *cache,
865 HKEY key,
866 const gchar *gsettings_prefix)
867{
868 /* FIXME: we copy stuff into the cache even if a prefix above it is already in there,
869 * which is pointless */
870 /* FIXME: also, a much better way to do this (with another use case I can think of) would be to
871 * implement a g_settings_copy function */
872 gchar buffer[256];
873 LONG result;
874 int i;
875
876 /* Recurse into each subkey */
877 i = 0;
878 trace ("Mirroring subkeys .. ");
879 while (1)
880 {
881 DWORD buffer_size = 255;
882 HKEY subkey;
883 result = RegEnumKeyEx (key, i++, buffer, &buffer_size, NULL, NULL, NULL, NULL);
884 if (result != ERROR_SUCCESS)
885 break;
886 result = RegOpenKeyEx (key, buffer, 0, KEY_READ, &subkey);
887 if (result == ERROR_SUCCESS)
888 {
889 char *gsettings_subprefix = g_build_path ("/", gsettings_prefix, buffer, NULL);
890 add_prefix_to_cache (self, cache, subkey, gsettings_subprefix);
891 g_free (gsettings_subprefix);
892 }
893 RegCloseKey (subkey);
894 }
895
896 if (result != ERROR_NO_MORE_ITEMS)
897 g_warning_win32_error (result, "gregistrystorage: error enumerating subkeys for cache.");
898
899 /* Add each value. We access 'self' here (outside the main thread), which is okay - we
900 * only read from self->priv, and it won't be freed until this thread quits. */
901 i = 0;
902 trace ("mirroring values .. \n");
903 while (1)
904 {
905 GVariant *value;
906 GTree *tree;
907 gchar *gsettings_key, *c;
908 DWORD buffer_size = 255;
909 result = RegEnumValue (key, i++, buffer, &buffer_size, NULL, NULL, NULL, NULL);
910 if (result != ERROR_SUCCESS)
911 break;
912 trace ("Got: %s\n", buffer);
913
914 /* \ in keys is escaped as / in the registry, / isn't allowed in gsettings key names */
915 for (c=buffer; *c!=0; c++)
916 if (*c=='/') *c = '\\';
917 gsettings_key = g_build_path ("/", gsettings_prefix, buffer, NULL);
918
919 /* Without an 'expected_type', the return value could be of the wrong type. This doesn't
920 * matter, because when we query the changes to check against the cache they will also be of
921 * the wrong type, so as long as we're consistent we're okay. We only need to pass the names
922 * of the changed keys to gsettings.
923 */
924 value = g_registry_storage_read (G_SETTINGS_STORAGE (self), gsettings_key, NULL);
925
926 tree = g_settings_storage_new_tree ();
927 g_tree_insert (tree, g_strdup (""), g_variant_ref_sink (value));
928 g_settings_storage_write (cache, gsettings_key, tree, NULL);
929 g_tree_unref (tree);
930
931 g_free (gsettings_key);
932 }
933
934 if (result != ERROR_NO_MORE_ITEMS)
935 g_warning_win32_error (result, "gregistrystorage: error enumerating values for cache.");
936 trace ("Mirrored current state.\n");
937}
938
939static gboolean
940watch_add_notify (GRegistryStorage *self,
941 HANDLE event,
942 HKEY registry_key,
943 gchar *gsettings_prefix,
944 gchar *registry_key_path,
945 gchar *registry_value_name)
946{
947 WatchThreadState *watch = self->priv->watch;
948 DWORD result;
949
950 g_return_val_if_fail (watch != NULL, FALSE);
951
952 /* Duplicate tree into the cache in the main thread, before we add the notify: if we do it in the
953 * thread we can miss changes while we are caching.
954 * FIXME: There is a danger of blocking the app ui when doing this for a big tree.
955 */
956 EnterCriticalSection (watch->cache_lock);
957 add_prefix_to_cache (self, watch->cache, registry_key, gsettings_prefix);
958 LeaveCriticalSection (watch->cache_lock);
959
960 EnterCriticalSection (watch->message_lock);
961
962 watch->message.type = WATCH_THREAD_ADD_WATCH;
963 watch->message.watch.event = event;
964 watch->message.watch.key = registry_key;
965 watch->message.watch.prefix = gsettings_prefix;
966 /* FIXME: not used */
967 g_free (registry_key_path);
968
969 SetEvent (watch->message_sent_event);
970
971 /* Wait for the received event in return, to avoid sending another message before the first
972 * one was received. Note this is only a small timeout, because there's no point freezing the
973 * app for ages when the only possible race error is that a notification doesn't get added, which
974 * is hardly the end of the world.
975 */
976 result = WaitForSingleObject (watch->message_received_event, 20);
977 if (result != WAIT_OBJECT_0)
978 g_warning ("gregistrystorage: watch thread is slow to respond - race conditions may occur.");
979 LeaveCriticalSection (watch->message_lock);
980 return TRUE;
981};
982
983
984/* dconf semantics are: if the key ends in /, watch the keys underneath it - if not, watch that
985 * key. Our job is easier because keys and values are separate.
986 */
987static void
988g_registry_storage_subscribe (GSettingsStorage *settings_storage,
520989 const char *name)
521990{
991 GRegistryStorage *self = G_REGISTRY_STORAGE (settings_storage);
992 gchar *key_path, *value_name;
993 HKEY registry_key;
994 HANDLE event;
995 LONG result;
996
997 if (self->priv->watch == NULL)
998 if (!watch_start (self))
999 return;
1000
1001 key_path = parse_key (name, self->priv->base, &value_name);
1002 trace ("Subscribing to key %s / %s\n", key_path, value_name);
1003
1004 /* Give the caller the benefit of the doubt if the key doesn't exist and create it. The caller
1005 * is almost certainly a new g_settings with this path as base path. */
1006 result = RegCreateKeyExA (HKEY_CURRENT_USER, key_path, 0, NULL, 0, KEY_READ, NULL, &registry_key,
1007 NULL);
1008 if (result != ERROR_SUCCESS)
1009 {
1010 g_warning_win32_error (result, "gregistrystorage: Unable to subscribe to key %s.", name);
1011 g_free (key_path);
1012 return;
1013 }
1014
1015 event = CreateEvent (NULL, FALSE, FALSE, NULL);
1016 if (event == NULL)
1017 {
1018 g_warning_win32_error (result, "gregistrystorage: CreateEvent failed.\n");
1019 RegCloseKey (registry_key);
1020 g_free (key_path);
1021 return;
1022 }
1023
1024 /* The actual watch is added by the thread, which has to re-subscribe each time it
1025 * receives a change. */
1026 watch_add_notify (self, event, registry_key, g_strdup (name), key_path, value_name);
5221027}
5231028
1029static void
1030g_registry_storage_unsubscribe (GSettingsStorage *base,
1031 const char *name)
1032{
1033 /* FIXME: if no more watches, stop the watch thread. */
1034}
1035
5241036GSettingsStorage* g_registry_storage_new (void) {
5251037 return g_object_new (G_TYPE_REGISTRY_STORAGE, NULL);
5261038}
10421042{
10431043 GRegistryStorage *self = G_REGISTRY_STORAGE (object);
10441044
1045 if (self->priv->watch != NULL)
1046 watch_stop (self);
1047
10451048 g_free (self->priv->base);
10461049}
10471050
10611061 storage_class->read = g_registry_storage_read;
10621062 storage_class->sensitive = g_registry_storage_sensitive;
10631063 storage_class->subscribe = g_registry_storage_subscribe;
1064 storage_class->unsubscribe = g_registry_storage_subscribe;
1064 storage_class->unsubscribe = g_registry_storage_unsubscribe;
10651065}
10661066
10671067static void
10721072 GRegistryStoragePrivate);
10731073
10741074 self->priv->base = g_strdup_printf ("Software\\%s", g_get_prgname ());
1075
1076 self->priv->watch = NULL;
10751077}
1078
10761079
10771080static void
10781081trace (const char *format, ...)
gsettings/Makefile.am
(3 / 2)
  
1CFLAGS = $(gio_CFLAGS) -I..
1AM_CFLAGS = $(gio_CFLAGS) -I..
22LDADD = $(top_builddir)/gio/libgsettings.la
33
44storage_test_SOURCES = storage-test.c
5notify_test_SOURCES = notify-test.c
56
6TESTS = storage-test
7TESTS = storage-test notify-test
78
89check_PROGRAMS = $(TESTS)
gsettings/notify-test.c
(112 / 0)
  
1/*
2 * Copyright © 2009 Sam Thursfield
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of version 3 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * See the included COPYING file for more information.
9 *
10 * Authors: Sam Thursfield <ssssam@gmail.com>
11 */
12
13#include <stdio.h>
14#include <windows.h>
15#include <glib.h>
16
17#include <gio/gsettings.h>
18#include <gio/gregistrystorage.h>
19
20typedef struct {
21 /* Need a main loop for notifications to make their way back to the main thread */
22 GMainLoop *main_loop;
23
24 GSettings *settings;
25 GSettingsStorage *storage;
26} RegistryFixture;
27
28void
29registry_fixture_setup (RegistryFixture *fixture,
30 gconstpointer test_data)
31{
32 fixture->main_loop = g_main_loop_new (NULL, FALSE);
33 fixture->storage = g_registry_storage_new ();
34 fixture->settings = g_object_new (G_TYPE_SETTINGS, "backend", fixture->storage,
35 "schema-name", "test.storage", NULL);
36}
37
38void
39registry_fixture_teardown (RegistryFixture *fixture,
40 gconstpointer test_data)
41{
42 g_object_unref (fixture->settings);
43 g_object_unref (fixture->storage);
44 g_main_loop_unref (fixture->main_loop);
45}
46
47static void
48test_registry_notify_1 (RegistryFixture *fixture,
49 gconstpointer test_data)
50{
51 char *string;
52 int i;
53 HKEY root_key;
54 LONG result;
55
56 /*TEST_TYPE (settings, gboolean, "bool", TRUE, FALSE);
57 TEST_TYPE (settings, gint32, "int32", 55, -999);
58 TEST_TYPE (settings, gint32, "\\", 666666, 66666666);
59 //TEST_TYPE (settings, guint64, "qword", 4398046511104, 31313131);*/
60
61 g_settings_set (fixture->settings, "string", "Notify me", NULL);
62
63 for (i=0; i<100; i++)
64 {
65 g_main_context_iteration (NULL, FALSE);
66 }
67};
68
69static void
70test_registry_notify_delete (RegistryFixture *fixture,
71 gconstpointer test_data)
72{
73 char *string;
74 HKEY root_key;
75 LONG result;
76
77/* g_settings_get (settings, "string", &string, NULL);
78 g_assert_cmpstr (string, ==, "Hello world");
79
80 g_usleep (400000);*/
81
82 printf ("Deleting the key.\n"); fflush (stdout);
83
84 /* Delete the entire key from under gsettings's nose. This causes problems further down the line.
85 * Windows queues the key for deletion and actually carries it out once all open handles on it are
86 * closed. The backend will get ERROR_KEY_DELETED when it tries to resubscribe and needs to handle
87 * this correctly. */
88 result = RegDeleteKey (HKEY_CURRENT_USER, "Software\\notify-test.exe\\test\\gsettings\\storage");
89 if (result != ERROR_SUCCESS)
90 {
91 g_warning ("Error deleting key.\n"); fflush (stdout);
92 }
93
94 g_usleep (400000);
95};
96
97int
98main (int argc, char **argv)
99{
100 g_type_init ();
101 g_test_init (&argc, &argv, NULL);
102
103 /* Notifications don't work unless we have a multithreaded app. */
104 //g_thread_init (NULL);
105
106 g_test_add ("/gsettings/notify/Windows Registry 1", RegistryFixture, 0,
107 registry_fixture_setup, test_registry_notify_1, registry_fixture_teardown);
108 g_test_add ("/gsettings/notify/Windows Registry - deletion", RegistryFixture, 0,
109 registry_fixture_setup, test_registry_notify_delete, registry_fixture_teardown);
110
111 return g_test_run();
112};
gsettings/storage-test.c
(12 / 0)
  
1/*
2 * Copyright © 2009 Sam Thursfield
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of version 3 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * See the included COPYING file for more information.
9 *
10 * Authors: Sam Thursfield <ssssam@gmail.com>
11 */
12
113#include <glib.h>
214
315#include <gio/gsettings.h>

Comments

Add a new comment:

Login or create an account to post a comment

Add your comment