628 lines
14 KiB
C
628 lines
14 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
dmnotify.c
|
||
|
||
Abstract:
|
||
|
||
Contains notification support for the Configuration Database Manager
|
||
|
||
Each call to DmNotifyChangeKey adds a leaf to the notification tree. This
|
||
tree is expected to be sparse, so each node is implemented as a linked list of
|
||
subnodes and a linked list of leaves.
|
||
|
||
When a registry modification occurs, the tree is traversed from the root
|
||
to the leaf representing the key. Any leaves along the path are candidates
|
||
for reporting a notification event.
|
||
|
||
Author:
|
||
|
||
John Vert (jvert) 9/18/1996
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "dmp.h"
|
||
|
||
typedef struct _DM_NOTIFY_BRANCH {
|
||
LIST_ENTRY SiblingList; // Links onto parent's ChildList.
|
||
LIST_ENTRY ChildList; // Links onto child's SiblingList.
|
||
LIST_ENTRY LeafList; // Links
|
||
struct _DM_NOTIFY_BRANCH *Parent; // Parent
|
||
USHORT NameLength;
|
||
WCHAR KeyName[0]; // Name component (a single keyname, not a path)
|
||
} DM_NOTIFY_BRANCH, *PDM_NOTIFY_BRANCH;
|
||
|
||
typedef struct _DM_NOTIFY_LEAF {
|
||
LIST_ENTRY SiblingList; // Links onto parent branch's ChildList
|
||
LIST_ENTRY KeyList; // Links onto DMKEY.NotifyList
|
||
LIST_ENTRY RundownList; // Passed into DmNotifyChangeKey, used for rundown
|
||
HDMKEY hKey;
|
||
DWORD CompletionFilter;
|
||
DM_NOTIFY_CALLBACK NotifyCallback;
|
||
DWORD_PTR Context1;
|
||
DWORD_PTR Context2;
|
||
PDM_NOTIFY_BRANCH Parent;
|
||
BOOL WatchTree;
|
||
} DM_NOTIFY_LEAF, *PDM_NOTIFY_LEAF;
|
||
|
||
CRITICAL_SECTION NotifyLock;
|
||
PDM_NOTIFY_BRANCH NotifyRoot=NULL;
|
||
|
||
//
|
||
// Local function prototypes
|
||
//
|
||
VOID
|
||
DmpPruneBranch(
|
||
IN PDM_NOTIFY_BRANCH Branch
|
||
);
|
||
|
||
PDM_NOTIFY_BRANCH
|
||
DmpFindKeyInBranch(
|
||
IN PDM_NOTIFY_BRANCH RootBranch,
|
||
IN OUT LPCWSTR *RelativeName,
|
||
OUT WORD *pNameLength
|
||
);
|
||
|
||
DWORD
|
||
DmpAddNotifyLeaf(
|
||
IN PDM_NOTIFY_BRANCH RootBranch,
|
||
IN PDM_NOTIFY_LEAF NewLeaf,
|
||
IN LPCWSTR RelativeName
|
||
);
|
||
|
||
VOID
|
||
DmpReportNotifyWorker(
|
||
IN PDM_NOTIFY_BRANCH RootBranch,
|
||
IN LPCWSTR RelativeName,
|
||
IN LPCWSTR FullName,
|
||
IN DWORD Filter
|
||
);
|
||
|
||
|
||
BOOL
|
||
DmpInitNotify(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initializes the notification package for the DM.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE if successful
|
||
|
||
FALSE otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
InitializeCriticalSection(&NotifyLock);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
DWORD
|
||
DmNotifyChangeKey(
|
||
IN HDMKEY hKey,
|
||
IN DWORD CompletionFilter,
|
||
IN BOOL WatchTree,
|
||
IN OPTIONAL PLIST_ENTRY ListHead,
|
||
IN DM_NOTIFY_CALLBACK NotifyCallback,
|
||
IN DWORD_PTR Context1,
|
||
IN DWORD_PTR Context2
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Registers a notification for a specific registry key. When the
|
||
notification event occurs, ApiReportRegistryNotify will be called.
|
||
|
||
Arguments:
|
||
|
||
hKey - Supplies the registry key handle on which the notification
|
||
should be posted.
|
||
|
||
CompletionFilter - Supplies the registry events which should trigger
|
||
the notification.
|
||
|
||
WatchTree - Supplies whether or not changes to the children of the specified
|
||
key should trigger the notification.
|
||
|
||
ListHead - If present, supplies the listhead that the new notification should be
|
||
queued to. This listhead should be passed to DmRundownList.
|
||
|
||
NotifyCallback - Supplies the notification routine that should be called
|
||
when the notification occurs.
|
||
|
||
Context1 - Supplies the first DWORD of context to be passed to ApiReportRegistryNotify
|
||
|
||
Context2 - Supplies the second DWORD of context to be passed to ApiReportRegistryNotify
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDMKEY Key;
|
||
PDM_NOTIFY_LEAF Leaf;
|
||
DWORD Status;
|
||
|
||
Key = (PDMKEY)hKey;
|
||
|
||
Leaf = LocalAlloc(LMEM_FIXED, sizeof(DM_NOTIFY_LEAF));
|
||
if (Leaf == NULL) {
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
Leaf->hKey = hKey;
|
||
Leaf->CompletionFilter = CompletionFilter;
|
||
Leaf->WatchTree = WatchTree;
|
||
Leaf->NotifyCallback = NotifyCallback;
|
||
Leaf->Context1 = Context1;
|
||
Leaf->Context2 = Context2;
|
||
|
||
EnterCriticalSection(&NotifyLock);
|
||
|
||
if (NotifyRoot == NULL) {
|
||
//
|
||
// Create notify root here.
|
||
//
|
||
NotifyRoot = LocalAlloc(LMEM_FIXED, sizeof(DM_NOTIFY_BRANCH));
|
||
if (NotifyRoot == NULL) {
|
||
LeaveCriticalSection(&NotifyLock);
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
InitializeListHead(&NotifyRoot->SiblingList);
|
||
InitializeListHead(&NotifyRoot->ChildList);
|
||
InitializeListHead(&NotifyRoot->LeafList);
|
||
NotifyRoot->Parent = NULL;
|
||
}
|
||
|
||
Status = DmpAddNotifyLeaf(NotifyRoot, Leaf, Key->Name);
|
||
if (Status == ERROR_SUCCESS) {
|
||
InsertHeadList(&Key->NotifyList, &Leaf->KeyList);
|
||
if (ARGUMENT_PRESENT(ListHead)) {
|
||
InsertHeadList(ListHead, &Leaf->RundownList);
|
||
} else {
|
||
Leaf->RundownList.Flink = NULL;
|
||
Leaf->RundownList.Blink = NULL;
|
||
}
|
||
} else {
|
||
LocalFree(Leaf);
|
||
}
|
||
|
||
LeaveCriticalSection(&NotifyLock);
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
VOID
|
||
DmRundownList(
|
||
IN PLIST_ENTRY ListHead
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Runs down a list of leaves. Used by the API when the notify port
|
||
is closed.
|
||
|
||
Arguments:
|
||
|
||
ListHead - Supplies the head of the rundown list. This is the
|
||
same listhead passed to DmNotifyChangeKey
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PDM_NOTIFY_LEAF Leaf;
|
||
|
||
//
|
||
// Remove all outstanding DM_NOTIFY_LEAF structures from this list.
|
||
//
|
||
EnterCriticalSection(&NotifyLock);
|
||
while (!IsListEmpty(ListHead)) {
|
||
ListEntry = RemoveHeadList(ListHead);
|
||
Leaf = CONTAINING_RECORD(ListEntry,
|
||
DM_NOTIFY_LEAF,
|
||
RundownList);
|
||
RemoveEntryList(&Leaf->SiblingList);
|
||
RemoveEntryList(&Leaf->KeyList);
|
||
|
||
//
|
||
// Attempt to prune this branch.
|
||
//
|
||
DmpPruneBranch(Leaf->Parent);
|
||
|
||
LocalFree(Leaf);
|
||
}
|
||
|
||
LeaveCriticalSection(&NotifyLock);
|
||
}
|
||
|
||
|
||
VOID
|
||
DmpRundownNotify(
|
||
IN PDMKEY Key
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Cleans up any outstanding notifications for a key when the
|
||
key is being closed.
|
||
|
||
Arguments:
|
||
|
||
Key - Supplies the key
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PDM_NOTIFY_LEAF Leaf;
|
||
|
||
//
|
||
// Remove all outstanding DM_NOTIFY_LEAF structures from this key.
|
||
//
|
||
EnterCriticalSection(&NotifyLock);
|
||
while (!IsListEmpty(&Key->NotifyList)) {
|
||
ListEntry = RemoveHeadList(&Key->NotifyList);
|
||
Leaf = CONTAINING_RECORD(ListEntry,
|
||
DM_NOTIFY_LEAF,
|
||
KeyList);
|
||
RemoveEntryList(&Leaf->SiblingList);
|
||
if (Leaf->RundownList.Flink != NULL) {
|
||
RemoveEntryList(&Leaf->RundownList);
|
||
}
|
||
|
||
//
|
||
// Attempt to prune this branch.
|
||
//
|
||
DmpPruneBranch(Leaf->Parent);
|
||
|
||
LocalFree(Leaf);
|
||
}
|
||
|
||
LeaveCriticalSection(&NotifyLock);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
DmpPruneBranch(
|
||
IN PDM_NOTIFY_BRANCH Branch
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Checks to see if a branch is empty and should be pruned (freed).
|
||
If the branch is empty, this routine will recursively call itself
|
||
on the parent until a non-empty branch is found.
|
||
|
||
Arguments:
|
||
|
||
Branch - Supplies the branch to be pruned.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
if ((IsListEmpty(&Branch->ChildList)) &&
|
||
(IsListEmpty(&Branch->LeafList))) {
|
||
|
||
//
|
||
// No need to keep this branch around any more. Remove
|
||
// it from its parent, then check to see if the parent
|
||
// should be pruned.
|
||
//
|
||
if (Branch->Parent == NULL) {
|
||
//
|
||
// This is the root, go ahead and free it up too.
|
||
//
|
||
CL_ASSERT(NotifyRoot == Branch);
|
||
NotifyRoot = NULL;
|
||
|
||
} else {
|
||
RemoveEntryList(&Branch->SiblingList);
|
||
DmpPruneBranch(Branch->Parent);
|
||
}
|
||
LocalFree(Branch);
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
DmpAddNotifyLeaf(
|
||
IN PDM_NOTIFY_BRANCH RootBranch,
|
||
IN PDM_NOTIFY_LEAF NewLeaf,
|
||
IN LPCWSTR RelativeName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds a leaf to the notification key.
|
||
|
||
If the RelativeName is empty, a leaf is created in RootBranch.
|
||
|
||
If the RelativeName is not empty, look up its first component
|
||
in RootBranch. If it's not there, create it. Then call ourselves
|
||
recursively after stripping off the first component of RelativeName
|
||
|
||
Arguments:
|
||
|
||
RootBranch - Supplies the root where the leaf is to be added
|
||
|
||
NewLeaf - Supplies the new leaf structure
|
||
|
||
RelativeName - Supplies the relative name.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PDM_NOTIFY_BRANCH Branch;
|
||
USHORT NameLength;
|
||
LPCWSTR NextName;
|
||
|
||
if (RelativeName[0] == '\0') {
|
||
InsertHeadList(&RootBranch->LeafList, &NewLeaf->SiblingList);
|
||
NewLeaf->Parent = RootBranch;
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
NextName = RelativeName;
|
||
Branch = DmpFindKeyInBranch(RootBranch, &NextName, &NameLength);
|
||
if (Branch == NULL) {
|
||
//
|
||
// No branch existed with this name. Create a new branch.
|
||
//
|
||
Branch = LocalAlloc(LMEM_FIXED, sizeof(DM_NOTIFY_BRANCH) + NameLength*sizeof(WCHAR));
|
||
if (Branch == NULL) {
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
InitializeListHead(&Branch->ChildList);
|
||
InitializeListHead(&Branch->LeafList);
|
||
Branch->Parent = RootBranch;
|
||
Branch->NameLength = NameLength;
|
||
CopyMemory(Branch->KeyName, RelativeName, NameLength*sizeof(WCHAR));
|
||
InsertHeadList(&RootBranch->ChildList, &Branch->SiblingList);
|
||
}
|
||
|
||
//
|
||
// Call ourselves recursively on the new branch.
|
||
//
|
||
return(DmpAddNotifyLeaf(Branch, NewLeaf, NextName));
|
||
}
|
||
|
||
|
||
VOID
|
||
DmpReportNotify(
|
||
IN LPCWSTR KeyName,
|
||
IN DWORD Filter
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Interface to the rest of DM to report a notification event on
|
||
a particular key.
|
||
|
||
Arguments:
|
||
|
||
Key - Supplies the key that was modified.
|
||
|
||
Filter - Supplies the modification type.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
if (NotifyRoot == NULL) {
|
||
return;
|
||
}
|
||
|
||
EnterCriticalSection(&NotifyLock);
|
||
|
||
if (NotifyRoot != NULL) {
|
||
DmpReportNotifyWorker(NotifyRoot,
|
||
KeyName,
|
||
KeyName,
|
||
Filter);
|
||
}
|
||
|
||
LeaveCriticalSection(&NotifyLock);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
DmpReportNotifyWorker(
|
||
IN PDM_NOTIFY_BRANCH RootBranch,
|
||
IN LPCWSTR RelativeName,
|
||
IN LPCWSTR FullName,
|
||
IN DWORD Filter
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Recursive worker routine that drills down through the notification
|
||
tree until it reaches the supplied name. Notifications are issued
|
||
for any leaves along the path that match the event.
|
||
|
||
Arguments:
|
||
|
||
RootBranch - Supplies the branch of the tree to start with.
|
||
|
||
RelativeName - Supplies the name of the changed key, relative to Branch.
|
||
|
||
FullName - Supplies the full name of the changed key.
|
||
|
||
Filter - Supplies the type of event.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PDM_NOTIFY_LEAF Leaf;
|
||
PDM_NOTIFY_BRANCH Branch;
|
||
LPCWSTR NextName;
|
||
WORD Dummy;
|
||
|
||
//
|
||
// First, issue notifies for any leaves at this node
|
||
//
|
||
ListEntry = RootBranch->LeafList.Flink;
|
||
while (ListEntry != &RootBranch->LeafList) {
|
||
Leaf = CONTAINING_RECORD(ListEntry,
|
||
DM_NOTIFY_LEAF,
|
||
SiblingList);
|
||
if (Leaf->CompletionFilter & Filter) {
|
||
if ( Leaf->WatchTree ||
|
||
(RelativeName[0] == '\0')) {
|
||
|
||
(Leaf->NotifyCallback)(Leaf->Context1,
|
||
Leaf->Context2,
|
||
Filter,
|
||
RelativeName);
|
||
|
||
}
|
||
}
|
||
ListEntry = ListEntry->Flink;
|
||
}
|
||
|
||
//
|
||
// Now search the child list for a subkey that matches the next component
|
||
// of the key name. If there isn't one, we are done. If there is one,
|
||
// call ourselves recursively on it.
|
||
//
|
||
if (RelativeName[0] == '\0') {
|
||
return;
|
||
}
|
||
NextName = RelativeName;
|
||
Branch = DmpFindKeyInBranch(RootBranch, &NextName, &Dummy);
|
||
if (Branch != NULL) {
|
||
DmpReportNotifyWorker(Branch, NextName, FullName, Filter);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
PDM_NOTIFY_BRANCH
|
||
DmpFindKeyInBranch(
|
||
IN PDM_NOTIFY_BRANCH RootBranch,
|
||
IN OUT LPCWSTR *RelativeName,
|
||
OUT WORD *pNameLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Finds the next component of a key name in a branch.
|
||
|
||
Arguments:
|
||
|
||
RootBranch - Supplies the branch to search.
|
||
|
||
RelativeName - Supplies the relative name of the key.
|
||
Returns the remaining name
|
||
|
||
NameLength - Returns the length of the next component.
|
||
|
||
Return Value:
|
||
|
||
Pointer to the found branch if successful.
|
||
|
||
NULL otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDM_NOTIFY_BRANCH Branch;
|
||
USHORT NameLength;
|
||
LPCWSTR NextName;
|
||
PLIST_ENTRY ListEntry;
|
||
|
||
//
|
||
// Find the first component of the relative name.
|
||
//
|
||
NextName = wcschr(*RelativeName, '\\');
|
||
if (NextName==NULL) {
|
||
NameLength = (USHORT)lstrlenW(*RelativeName);
|
||
NextName = *RelativeName + NameLength;
|
||
} else {
|
||
NameLength = (USHORT)(NextName - *RelativeName);
|
||
++NextName;
|
||
}
|
||
*pNameLength = NameLength;
|
||
|
||
//
|
||
// Search through the root's children to try and find a match on the
|
||
// first component.
|
||
//
|
||
ListEntry = RootBranch->ChildList.Flink;
|
||
while (ListEntry != &RootBranch->ChildList) {
|
||
Branch = CONTAINING_RECORD(ListEntry,
|
||
DM_NOTIFY_BRANCH,
|
||
SiblingList);
|
||
if ((NameLength == Branch->NameLength) &&
|
||
(wcsncmp(*RelativeName, Branch->KeyName, NameLength)==0)) {
|
||
|
||
//
|
||
// We have matched an existing branch. Return success.
|
||
//
|
||
*RelativeName = NextName;
|
||
return(Branch);
|
||
}
|
||
ListEntry = ListEntry->Flink;
|
||
}
|
||
*RelativeName = NextName;
|
||
|
||
return(NULL);
|
||
|
||
}
|