windows-nt/Source/XPSP1/NT/base/cluster/service/dm/dmnotify.c

628 lines
14 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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);
}