Implemented registry change notifications.
diff --git a/server/handle.c b/server/handle.c
index 00dbc54..9df4293 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -332,6 +332,8 @@
table = handle_is_global(handle) ? global_table : process->handles;
if (entry < table->entries + table->free) table->free = entry - table->entries;
if (entry == table->entries + table->last) shrink_handle_table( table );
+ /* hack: windows seems to treat registry handles differently */
+ registry_close_handle( obj, handle );
release_object( obj );
return 1;
}
diff --git a/server/object.h b/server/object.h
index d2f57bc..711f584 100644
--- a/server/object.h
+++ b/server/object.h
@@ -195,6 +195,7 @@
extern void init_registry(void);
extern void flush_registry(void);
extern void close_registry(void);
+extern void registry_close_handle( struct object *obj, obj_handle_t hkey );
/* atom functions */
diff --git a/server/protocol.def b/server/protocol.def
index 1b15d85..056902c 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1358,6 +1358,14 @@
@END
+@REQ(set_registry_notification)
+ obj_handle_t hkey; /* key to watch for changes */
+ obj_handle_t event; /* event to set */
+ int subtree; /* should we watch the whole subtree? */
+ unsigned int filter; /* things to watch */
+@END
+
+
/* Create a waitable timer */
@REQ(create_timer)
int inherit; /* inherit flag */
diff --git a/server/registry.c b/server/registry.c
index 792750c..77ddf9f 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -48,6 +48,16 @@
#include "winternl.h"
#include "wine/library.h"
+struct notify
+{
+ struct event *event; /* event to set when changing this key */
+ int subtree; /* true if subtree notification */
+ unsigned int filter; /* which events to notify on */
+ obj_handle_t hkey; /* hkey associated with this notification */
+ struct notify *next; /* list of notifications */
+ struct notify *prev; /* list of notifications */
+};
+
/* a registry key */
struct key
{
@@ -64,6 +74,8 @@
short flags; /* flags */
short level; /* saving level */
time_t modif; /* last modification time */
+ struct notify *first_notify; /* list of notifications */
+ struct notify *last_notify; /* list of notifications */
};
/* key flags */
@@ -284,6 +296,53 @@
fprintf( stderr, "\n" );
}
+/* notify waiter and maybe delete the notification */
+static void do_notification( struct key *key, struct notify *notify, int del )
+{
+ if( notify->event )
+ {
+ set_event( notify->event );
+ release_object( notify->event );
+ notify->event = NULL;
+ }
+
+ if ( !del )
+ return;
+ if( notify->next )
+ notify->next->prev = notify->prev;
+ else
+ key->last_notify = notify->prev;
+ if( notify->prev )
+ notify->prev->next = notify->next;
+ else
+ key->first_notify = notify->next;
+ free( notify );
+}
+
+static struct notify *find_notify( struct key *key, obj_handle_t hkey)
+{
+ struct notify *n;
+
+ for( n=key->first_notify; n; n = n->next)
+ if( n->hkey == hkey )
+ break;
+ return n;
+}
+
+/* close the notification associated with a handle */
+void registry_close_handle( struct object *obj, obj_handle_t hkey )
+{
+ struct key * key = (struct key *) obj;
+ struct notify *notify;
+
+ if( obj->ops != &key_ops )
+ return;
+ notify = find_notify( key, hkey );
+ if( !notify )
+ return;
+ do_notification( key, notify, 1 );
+}
+
static void key_destroy( struct object *obj )
{
int i;
@@ -302,6 +361,9 @@
key->subkeys[i]->parent = NULL;
release_object( key->subkeys[i] );
}
+ /* unconditionally notify everything waiting on this key */
+ while ( key->first_notify )
+ do_notification( key, key->first_notify, 1 );
}
/* duplicate a key path */
@@ -389,6 +451,8 @@
key->level = current_level;
key->modif = modif;
key->parent = NULL;
+ key->first_notify = NULL;
+ key->last_notify = NULL;
if (!(key->name = strdupW( name )))
{
release_object( key );
@@ -420,12 +484,32 @@
for (i = 0; i <= key->last_subkey; i++) make_clean( key->subkeys[i] );
}
-/* update key modification time */
-static void touch_key( struct key *key )
+/* go through all the notifications and send them if necessary */
+void check_notify( struct key *key, unsigned int change, int not_subtree )
{
+ struct notify *n = key->first_notify;
+ while (n)
+ {
+ struct notify *next = n->next;
+ if ( ( not_subtree || n->subtree ) && ( change & n->filter ) )
+ do_notification( key, n, 0 );
+ n = next;
+ }
+}
+
+/* update key modification time */
+static void touch_key( struct key *key, unsigned int change )
+{
+ struct key *k;
+
key->modif = time(NULL);
key->level = max( key->level, current_level );
make_dirty( key );
+
+ /* do notifications */
+ check_notify( key, change, 1 );
+ for ( k = key->parent; k; k = k->parent )
+ check_notify( k, change & ~REG_NOTIFY_CHANGE_LAST_SET, 0 );
}
/* try to grow the array of subkeys; return 1 if OK, 0 on error */
@@ -583,6 +667,7 @@
if (!*path) goto done;
*created = 1;
+ touch_key( key, REG_NOTIFY_CHANGE_NAME ); /* FIXME: is this right? */
if (flags & KEY_DIRTY) make_dirty( key );
base = key;
base_idx = index;
@@ -718,7 +803,7 @@
}
if (debug_level > 1) dump_operation( key, NULL, "Delete" );
free_subkey( parent, index );
- touch_key( parent );
+ touch_key( parent, REG_NOTIFY_CHANGE_NAME );
}
/* try to grow the array of values; return 1 if OK, 0 on error */
@@ -821,7 +906,7 @@
value->type = type;
value->len = len;
value->data = ptr;
- touch_key( key );
+ touch_key( key, REG_NOTIFY_CHANGE_LAST_SET );
if (debug_level > 1) dump_operation( key, value, "Set" );
}
@@ -912,7 +997,7 @@
if (value->data) free( value->data );
for (i = index; i < key->last_value; i++) key->values[i] = key->values[i + 1];
key->last_value--;
- touch_key( key );
+ touch_key( key, REG_NOTIFY_CHANGE_LAST_SET );
/* try to shrink the array */
nb_values = key->nb_values;
@@ -1817,3 +1902,52 @@
release_object( key );
}
}
+
+/* add a registry key change notification */
+DECL_HANDLER(set_registry_notification)
+{
+ struct key *key;
+ struct event *event;
+ struct notify *notify;
+
+ key = get_hkey_obj( req->hkey, KEY_NOTIFY );
+ if( key )
+ {
+ event = get_event_obj( current->process, req->event, SYNCHRONIZE );
+ if( event )
+ {
+ notify = find_notify( key, req->hkey );
+ if( notify )
+ {
+ release_object( notify->event );
+ grab_object( event );
+ notify->event = event;
+ }
+ else
+ {
+ notify = (struct notify *) malloc (sizeof *notify);
+ if( notify )
+ {
+ grab_object( event );
+ notify->event = event;
+ notify->subtree = req->subtree;
+ notify->filter = req->filter;
+ notify->hkey = req->hkey;
+
+ /* add to linked list */
+ notify->prev = NULL;
+ notify->next = key->first_notify;
+ if ( notify->next )
+ notify->next->prev = notify;
+ else
+ key->last_notify = notify;
+ key->first_notify = notify;
+ }
+ else
+ set_error( STATUS_NO_MEMORY );
+ }
+ release_object( event );
+ }
+ release_object( key );
+ }
+}
diff --git a/server/request.h b/server/request.h
index f5fd97d..213da98 100644
--- a/server/request.h
+++ b/server/request.h
@@ -206,6 +206,7 @@
DECL_HANDLER(save_registry);
DECL_HANDLER(save_registry_atexit);
DECL_HANDLER(set_registry_levels);
+DECL_HANDLER(set_registry_notification);
DECL_HANDLER(create_timer);
DECL_HANDLER(open_timer);
DECL_HANDLER(set_timer);
@@ -381,6 +382,7 @@
(req_handler)req_save_registry,
(req_handler)req_save_registry_atexit,
(req_handler)req_set_registry_levels,
+ (req_handler)req_set_registry_notification,
(req_handler)req_create_timer,
(req_handler)req_open_timer,
(req_handler)req_set_timer,
diff --git a/server/trace.c b/server/trace.c
index 368cca2..6412212 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1569,6 +1569,14 @@
fprintf( stderr, " period=%d", req->period );
}
+static void dump_set_registry_notification_request( const struct set_registry_notification_request *req )
+{
+ fprintf( stderr, " hkey=%p,", req->hkey );
+ fprintf( stderr, " event=%p,", req->event );
+ fprintf( stderr, " subtree=%d,", req->subtree );
+ fprintf( stderr, " filter=%08x", req->filter );
+}
+
static void dump_create_timer_request( const struct create_timer_request *req )
{
fprintf( stderr, " inherit=%d,", req->inherit );
@@ -2445,6 +2453,7 @@
(dump_func)dump_save_registry_request,
(dump_func)dump_save_registry_atexit_request,
(dump_func)dump_set_registry_levels_request,
+ (dump_func)dump_set_registry_notification_request,
(dump_func)dump_create_timer_request,
(dump_func)dump_open_timer_request,
(dump_func)dump_set_timer_request,
@@ -2617,6 +2626,7 @@
(dump_func)0,
(dump_func)0,
(dump_func)0,
+ (dump_func)0,
(dump_func)dump_create_timer_reply,
(dump_func)dump_open_timer_reply,
(dump_func)0,
@@ -2789,6 +2799,7 @@
"save_registry",
"save_registry_atexit",
"set_registry_levels",
+ "set_registry_notification",
"create_timer",
"open_timer",
"set_timer",