1553 lines
48 KiB
C
1553 lines
48 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
notify.c
|
||
|
||
Abstract:
|
||
|
||
Public interface for cluster notification API
|
||
|
||
Author:
|
||
|
||
John Vert (jvert) 19-Mar-1996
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "clusapip.h"
|
||
|
||
//
|
||
// Define some handy constants
|
||
//
|
||
#define FILTER_NODE (CLUSTER_CHANGE_NODE_STATE | \
|
||
CLUSTER_CHANGE_NODE_DELETED | \
|
||
CLUSTER_CHANGE_NODE_ADDED | \
|
||
CLUSTER_CHANGE_NODE_PROPERTY)
|
||
#define NOT_FILTER_NODE (~(FILTER_NODE |CLUSTER_CHANGE_HANDLE_CLOSE))
|
||
|
||
#define FILTER_REGISTRY (CLUSTER_CHANGE_REGISTRY_NAME | \
|
||
CLUSTER_CHANGE_REGISTRY_ATTRIBUTES | \
|
||
CLUSTER_CHANGE_REGISTRY_VALUE | \
|
||
CLUSTER_CHANGE_REGISTRY_SUBTREE)
|
||
#define NOT_FILTER_REGISTRY (~(FILTER_REGISTRY |CLUSTER_CHANGE_HANDLE_CLOSE))
|
||
|
||
#define FILTER_RESOURCE (CLUSTER_CHANGE_RESOURCE_STATE | \
|
||
CLUSTER_CHANGE_RESOURCE_DELETED | \
|
||
CLUSTER_CHANGE_RESOURCE_ADDED | \
|
||
CLUSTER_CHANGE_RESOURCE_PROPERTY)
|
||
#define NOT_FILTER_RESOURCE (~(FILTER_RESOURCE | CLUSTER_CHANGE_HANDLE_CLOSE))
|
||
|
||
#define FILTER_GROUP (CLUSTER_CHANGE_GROUP_STATE | \
|
||
CLUSTER_CHANGE_GROUP_DELETED | \
|
||
CLUSTER_CHANGE_GROUP_ADDED | \
|
||
CLUSTER_CHANGE_GROUP_PROPERTY)
|
||
#define NOT_FILTER_GROUP (~(FILTER_GROUP | CLUSTER_CHANGE_HANDLE_CLOSE))
|
||
|
||
#define FILTER_NETWORK (CLUSTER_CHANGE_NETWORK_STATE | \
|
||
CLUSTER_CHANGE_NETWORK_DELETED | \
|
||
CLUSTER_CHANGE_NETWORK_ADDED | \
|
||
CLUSTER_CHANGE_NETWORK_PROPERTY)
|
||
#define NOT_FILTER_NETWORK (~(FILTER_NETWORK | CLUSTER_CHANGE_HANDLE_CLOSE))
|
||
|
||
#define FILTER_NETINTERFACE (CLUSTER_CHANGE_NETINTERFACE_STATE | \
|
||
CLUSTER_CHANGE_NETINTERFACE_DELETED | \
|
||
CLUSTER_CHANGE_NETINTERFACE_ADDED | \
|
||
CLUSTER_CHANGE_NETINTERFACE_PROPERTY)
|
||
#define NOT_FILTER_NETINTERFACE (~(FILTER_NETINTERFACE | CLUSTER_CHANGE_HANDLE_CLOSE))
|
||
|
||
#define FILTER_CLUSTER (CLUSTER_CHANGE_CLUSTER_STATE | \
|
||
CLUSTER_CHANGE_CLUSTER_RECONNECT)
|
||
|
||
#define NOT_FILTER_CLUSTER (~(FILTER_CLUSTER | CLUSTER_CHANGE_HANDLE_CLOSE))
|
||
//
|
||
// Define prototypes for functions local to this module
|
||
//
|
||
|
||
VOID
|
||
DestroyNotify(
|
||
IN PCNOTIFY Notify
|
||
);
|
||
|
||
VOID
|
||
DestroySession(
|
||
IN PCNOTIFY_SESSION Session
|
||
);
|
||
|
||
PCNOTIFY_SESSION
|
||
CreateNotifySession(
|
||
IN PCNOTIFY Notify,
|
||
IN PCLUSTER Cluster
|
||
);
|
||
|
||
DWORD
|
||
AddEventToSession(
|
||
IN PCNOTIFY_SESSION Session,
|
||
IN PVOID Object,
|
||
IN DWORD dwFilter,
|
||
IN DWORD_PTR dwNotifyKey
|
||
);
|
||
|
||
DWORD
|
||
NotifyThread(
|
||
IN LPVOID lpThreadParameter
|
||
);
|
||
|
||
DWORD
|
||
GetClusterNotifyCallback(
|
||
IN PLIST_ENTRY ListEntry,
|
||
IN OUT PVOID Context
|
||
);
|
||
|
||
HCHANGE
|
||
WINAPI
|
||
CreateClusterNotifyPort(
|
||
IN OPTIONAL HCHANGE hChange,
|
||
IN OPTIONAL HCLUSTER hCluster,
|
||
IN DWORD dwFilter,
|
||
IN DWORD_PTR dwNotifyKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates a cluster notification port to be used for notification of
|
||
cluster state changes.
|
||
|
||
Arguments:
|
||
|
||
hChange - Optionally supplies a handle to an existing cluster notification
|
||
port. If present, the specified notification events will be added
|
||
to the existing port.
|
||
|
||
hCluster - Optionally supplies a handle to the cluster. If not present, an
|
||
empty notification port will be created. CreateClusterNotifyPort
|
||
and RegisterClusterNotify may be used later to add notification
|
||
events to the notification port.
|
||
|
||
dwFilter - Supplies the events that will be delivered to the
|
||
notification port. Any events of the specified type will be
|
||
delivered to the notification port. Currently defined event
|
||
types are:
|
||
|
||
CLUSTER_CHANGE_NODE_STATE
|
||
CLUSTER_CHANGE_NODE_DELETED
|
||
CLUSTER_CHANGE_NODE_ADDED
|
||
CLUSTER_CHANGE_RESOURCE_STATE
|
||
CLUSTER_CHANGE_RESOURCE_DELETED
|
||
CLUSTER_CHANGE_RESOURCE_ADDED
|
||
CLUSTER_CHANGE_GROUP_STATE
|
||
CLUSTER_CHANGE_GROUP_DELETED
|
||
CLUSTER_CHANGE_GROUP_ADDED
|
||
CLUSTER_CHANGE_RESOURCE_TYPE_DELETED
|
||
CLUSTER_CHANGE_RESOURCE_TYPE_ADDED
|
||
CLUSTER_CHANGE_QUORUM_STATE
|
||
|
||
dwNotifyKey - Supplies the notification key to be returned as
|
||
part of the notification event.
|
||
|
||
Return Value:
|
||
|
||
If the function is successful, the return value is a handle of the
|
||
change notification object.
|
||
|
||
If the function fails, the return value is NULL. To get extended error
|
||
information, call GetLastError.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCNOTIFY Notify;
|
||
PCLUSTER Cluster;
|
||
DWORD Status;
|
||
PCNOTIFY_SESSION Session;
|
||
|
||
if (hChange == INVALID_HANDLE_VALUE) {
|
||
|
||
//
|
||
// This is a newly created notification session
|
||
//
|
||
|
||
Notify = LocalAlloc(LMEM_FIXED, sizeof(CNOTIFY));
|
||
if (Notify == NULL) {
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return(NULL);
|
||
}
|
||
InitializeListHead(&Notify->SessionList);
|
||
InitializeListHead(&Notify->OrphanedEventList);
|
||
InitializeCriticalSection(&Notify->Lock);
|
||
ClRtlInitializeQueue(&Notify->Queue);
|
||
|
||
#ifdef _WIN64
|
||
ClRtlInitializeHash(&Notify->NotifyKeyHash);
|
||
#else
|
||
ZeroMemory(&Notify->NotifyKeyHash,sizeof(CL_HASH));
|
||
#endif
|
||
|
||
|
||
if (hCluster == INVALID_HANDLE_VALUE) {
|
||
|
||
//
|
||
// Caller has asked for an empty notification port.
|
||
//
|
||
return((HCHANGE)Notify);
|
||
}
|
||
} else {
|
||
//
|
||
// This is an existing notification port that the specified
|
||
// cluster should be added to.
|
||
//
|
||
Notify = (PCNOTIFY)hChange;
|
||
if ((hCluster == INVALID_HANDLE_VALUE) ||
|
||
(hCluster == NULL)) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return(NULL);
|
||
}
|
||
}
|
||
|
||
Cluster = (PCLUSTER)hCluster;
|
||
|
||
//
|
||
// Chittur Subbaraman (chitturs) - 4/11/2000
|
||
//
|
||
// Make sure the cluster lock is acquired before the notify lock.
|
||
// If this order is violated, it could be a potential source of
|
||
// hard-to-track deadlocks.
|
||
//
|
||
EnterCriticalSection(&Cluster->Lock);
|
||
EnterCriticalSection(&Notify->Lock);
|
||
Session = CreateNotifySession(Notify, Cluster);
|
||
if (Session == NULL) {
|
||
Status = GetLastError();
|
||
LeaveCriticalSection(&Notify->Lock);
|
||
LeaveCriticalSection(&Cluster->Lock);
|
||
if (hChange == INVALID_HANDLE_VALUE) {
|
||
DestroyNotify(Notify);
|
||
}
|
||
SetLastError(Status);
|
||
return(NULL);
|
||
}
|
||
Status = AddEventToSession(Session,
|
||
NULL,
|
||
dwFilter,
|
||
dwNotifyKey);
|
||
LeaveCriticalSection(&Notify->Lock);
|
||
LeaveCriticalSection(&Cluster->Lock);
|
||
|
||
if (Status != ERROR_SUCCESS) {
|
||
if (hChange == INVALID_HANDLE_VALUE) {
|
||
DestroyNotify(Notify);
|
||
}
|
||
SetLastError(Status);
|
||
return(NULL);
|
||
}
|
||
TIME_PRINT(("CreateClusterNotifyPort: Returning Notify=0x%08lx\n",
|
||
Notify));
|
||
|
||
return((HCHANGE)Notify);
|
||
}
|
||
|
||
|
||
PCNOTIFY_SESSION
|
||
CreateNotifySession(
|
||
IN PCNOTIFY Notify,
|
||
IN PCLUSTER Cluster
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finds a notification session to the specified cluster.
|
||
If a session already exists, it is found and used. If a session does
|
||
not exist, a new one is created.
|
||
|
||
The Notify lock must be held.
|
||
|
||
Arguments:
|
||
|
||
Notify - Supplies the notification port.
|
||
|
||
Cluster - Supplies the cluster that the session should be opened to.
|
||
|
||
Return Value:
|
||
|
||
A pointer to the notification session.
|
||
|
||
NULL on error, GetLastError() will return the specific error code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PCNOTIFY_SESSION Session;
|
||
error_status_t Status = ERROR_SUCCESS;
|
||
|
||
//
|
||
// First, try to find an existing session.
|
||
//
|
||
ListEntry = Notify->SessionList.Flink;
|
||
while (ListEntry != &Notify->SessionList) {
|
||
Session = CONTAINING_RECORD(ListEntry,
|
||
CNOTIFY_SESSION,
|
||
ListEntry);
|
||
if (Session->Cluster == Cluster) {
|
||
TIME_PRINT(("CreateNotifySession: found a matching session\n"));
|
||
|
||
//
|
||
// Found a match, return it directly.
|
||
//
|
||
return(Session);
|
||
}
|
||
ListEntry = ListEntry->Flink;
|
||
}
|
||
|
||
//
|
||
// There is no existing session. Go ahead and create a new one.
|
||
//
|
||
Session = LocalAlloc(LMEM_FIXED, sizeof(CNOTIFY_SESSION));
|
||
if (Session == NULL) {
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
||
return(NULL);
|
||
}
|
||
TIME_PRINT(("CreateNotifySession: Calling ApiCreateNotify\n"));
|
||
WRAP_NULL(Session->hNotify,
|
||
(ApiCreateNotify(Cluster->RpcBinding, &Status)),
|
||
&Status,
|
||
Cluster);
|
||
if (Session->hNotify == NULL) {
|
||
LocalFree(Session);
|
||
SetLastError(Status);
|
||
return(NULL);
|
||
}
|
||
InitializeListHead(&Session->EventList);
|
||
Session->Cluster = Cluster;
|
||
Session->ParentNotify = Notify;
|
||
Session->Destroyed = FALSE;
|
||
|
||
//
|
||
// Spin up the notification thread for this session.
|
||
//
|
||
Session->NotifyThread = CreateThread(NULL,
|
||
0,
|
||
NotifyThread,
|
||
Session,
|
||
0,
|
||
NULL);
|
||
if (Session->NotifyThread == NULL) {
|
||
Status = GetLastError();
|
||
ApiCloseNotify(&Session->hNotify);
|
||
LocalFree(Session);
|
||
SetLastError(Status);
|
||
return(NULL);
|
||
}
|
||
InsertHeadList(&Notify->SessionList, &Session->ListEntry);
|
||
EnterCriticalSection(&Cluster->Lock);
|
||
InsertHeadList(&Cluster->SessionList, &Session->ClusterList);
|
||
LeaveCriticalSection(&Cluster->Lock);
|
||
TIME_PRINT(("CreateNotifySession: Session=0x%08lx hNotifyRpc=0x%08lx Thread=0x%08lx\n",
|
||
Session, Session->hNotify, NotifyThread));
|
||
|
||
return(Session);
|
||
|
||
}
|
||
|
||
|
||
DWORD
|
||
NotifyThread(
|
||
IN LPVOID lpThreadParameter
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Notification thread that gets notification messages from the cluster
|
||
and reposts them to the client-side notify queue.
|
||
|
||
Arguments:
|
||
|
||
lpThreadParameter - Supplies the CNOTIFY_SESSION structure to be monitored
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCNOTIFY_SESSION Session = (PCNOTIFY_SESSION)lpThreadParameter;
|
||
PCLUSTER Cluster = Session->Cluster;
|
||
PLIST_ENTRY ListEntry;
|
||
PCNOTIFY_EVENT Event;
|
||
DWORD Status = ERROR_INVALID_HANDLE_STATE;
|
||
error_status_t rpc_error;
|
||
PCNOTIFY_PACKET Packet;
|
||
LPWSTR Name;
|
||
|
||
do {
|
||
if (Session->Destroyed)
|
||
{
|
||
TIME_PRINT(("NotifyThread: Session 0x%08lx destroyed\n",
|
||
Session));
|
||
break;
|
||
}
|
||
Packet = LocalAlloc(LMEM_FIXED, sizeof(CNOTIFY_PACKET));
|
||
if (Packet == NULL) {
|
||
Status = ERROR_NOT_ENOUGH_MEMORY;
|
||
break;
|
||
}
|
||
Packet->Status = ERROR_SUCCESS;
|
||
Packet->Name = NULL;
|
||
TIME_PRINT(("NotifyThread: Calling ApiGetNotify, hNotify=0x%08lx\n",
|
||
Session->hNotify));
|
||
WRAP_CHECK(Status,
|
||
(ApiGetNotify(Session->hNotify,
|
||
INFINITE,
|
||
&Packet->KeyId,
|
||
&Packet->Filter,
|
||
&Packet->StateSequence,
|
||
&Packet->Name)),
|
||
Session->Cluster,
|
||
!Session->Destroyed);
|
||
if (Status != ERROR_SUCCESS)
|
||
{
|
||
TIME_PRINT(("NotifyThread : ApiGetNotify on hNotify=0x%08lx returns %u\n",
|
||
Session->hNotify, Status));
|
||
//if the error is due to a reconnect, hide it and map it to success
|
||
if ((Status == ERROR_NO_MORE_ITEMS) && (Session->hNotify != NULL))
|
||
{
|
||
//set the status to sucess again - this might happen on a
|
||
//reconnect and then we do want to continue
|
||
//so we retry apigetnotify again
|
||
Status = ERROR_SUCCESS;
|
||
LocalFree(Packet);
|
||
TIME_PRINT(("NotifyThread : Reconnect map error to success\n"));
|
||
}
|
||
else
|
||
{
|
||
//when can we be sure that the cluster is dead?
|
||
//If session is null(reconnect failed) OR
|
||
//If the cluster is marked dead(reconnect failed after session was established) OR
|
||
//If the cluster is dead, and wrap returns RPC_S_SERVER_UNAVAILABLE
|
||
|
||
//if so, we can terminate this thread because the thread
|
||
//maps to a cluster
|
||
//what do we document, if this returns error, call closeclusternotifyport
|
||
if ((Session->hNotify == NULL) ||
|
||
(Session->Cluster->Flags & CLUS_DEAD) ||
|
||
(Status == RPC_S_SERVER_UNAVAILABLE))
|
||
{
|
||
//SS: it is not clear why we post this event
|
||
//multiple times??? Chittur, any ideas????
|
||
//Does this mean that if you register for the
|
||
//same filter twice, you get the event twice?
|
||
// We should probably hold the cluster lock here
|
||
EnterCriticalSection(&Cluster->Lock);
|
||
//That seems bizarre.
|
||
//
|
||
// Something horrible has happened, probably the cluster has crashed.
|
||
//
|
||
// Run down the notify list for this cluster and post a packet for
|
||
// each registered notify event for CLUSTER_CHANGE_CLUSTER_STATE
|
||
//
|
||
Name = Cluster->ClusterName;
|
||
ListEntry = Cluster->NotifyList.Flink;
|
||
while (ListEntry != &Cluster->NotifyList) {
|
||
Event = CONTAINING_RECORD(ListEntry,
|
||
CNOTIFY_EVENT,
|
||
ObjectList);
|
||
if (Event->dwFilter & CLUSTER_CHANGE_CLUSTER_STATE) {
|
||
if (Packet == NULL) {
|
||
Packet = LocalAlloc(LMEM_FIXED, sizeof(CNOTIFY_PACKET));
|
||
if (Packet == NULL) {
|
||
LeaveCriticalSection(&Cluster->Lock);
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
}
|
||
//SS: Dont know what the Status was meant for
|
||
//It looks like it is not being used
|
||
Packet->Status = ERROR_SUCCESS;
|
||
Packet->Filter = CLUSTER_CHANGE_CLUSTER_STATE;
|
||
Packet->KeyId = Event->EventId;
|
||
Packet->Name = MIDL_user_allocate((lstrlenW(Name)+1)*sizeof(WCHAR));
|
||
if (Packet->Name != NULL) {
|
||
lstrcpyW(Packet->Name, Name);
|
||
}
|
||
TIME_PRINT(("NotifyThread - posting CLUSTER_CHANGE_CLUSTER_STATE to notify queue\n"));
|
||
ClRtlInsertTailQueue(&Session->ParentNotify->Queue,
|
||
&Packet->ListEntry);
|
||
Packet = NULL;
|
||
}
|
||
ListEntry = ListEntry->Flink;
|
||
}
|
||
LeaveCriticalSection(&Cluster->Lock);
|
||
//cluster is dead, map the error to success
|
||
Status = ERROR_SUCCESS;
|
||
//break out of the loop to terminate this thread
|
||
TIME_PRINT(("NotifyThread : Cluster is dead, break to exit notify thread\n"));
|
||
LocalFree(Packet);
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
//it is some other error, the user must
|
||
//call closeclusternotify port to clean up
|
||
//this thread
|
||
//free the packet
|
||
LocalFree(Packet);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Post this onto the notification queue
|
||
//
|
||
ClRtlInsertTailQueue(&Session->ParentNotify->Queue,
|
||
&Packet->ListEntry);
|
||
}
|
||
|
||
} while ( Status == ERROR_SUCCESS );
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
DWORD
|
||
AddEventToSession(
|
||
IN PCNOTIFY_SESSION Session,
|
||
IN PVOID Object,
|
||
IN DWORD dwFilter,
|
||
IN DWORD_PTR dwNotifyKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds a specific event to a cluster notification session
|
||
|
||
Arguments:
|
||
|
||
Notify - Supplies the notify object
|
||
|
||
Object - Supplies the specific object, NULL if it is the cluster.
|
||
|
||
dwFilter - Supplies the type of events
|
||
|
||
dwNotifyKey - Supplies the notification key to be returned.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCNOTIFY_EVENT NotifyEvent;
|
||
PCLUSTER Cluster;
|
||
PLIST_ENTRY NotifyList;
|
||
DWORD Status;
|
||
|
||
Cluster = Session->Cluster;
|
||
NotifyEvent = LocalAlloc(LMEM_FIXED, sizeof(CNOTIFY_EVENT));
|
||
if (NotifyEvent == NULL) {
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
NotifyEvent->Session = Session;
|
||
NotifyEvent->dwFilter = dwFilter;
|
||
NotifyEvent->dwNotifyKey = dwNotifyKey;
|
||
NotifyEvent->Object = Object;
|
||
|
||
#ifdef _WIN64
|
||
NotifyEvent->EventId = 0;
|
||
|
||
Status = ClRtlInsertTailHash(&Session->ParentNotify->NotifyKeyHash,
|
||
NotifyEvent, &NotifyEvent->EventId);
|
||
|
||
if (ERROR_SUCCESS != Status) {
|
||
LocalFree(NotifyEvent);
|
||
return(Status);
|
||
}
|
||
#else
|
||
NotifyEvent->EventId=(DWORD)NotifyEvent;
|
||
#endif
|
||
|
||
WRAP(Status,
|
||
(RegisterNotifyEvent(Session,
|
||
NotifyEvent,
|
||
&NotifyList)),
|
||
Cluster);
|
||
|
||
if (Status != ERROR_SUCCESS) {
|
||
|
||
#ifdef _WIN64
|
||
ClRtlRemoveEntryHash(&Session->ParentNotify->NotifyKeyHash,
|
||
NotifyEvent->EventId);
|
||
#endif
|
||
|
||
LocalFree(NotifyEvent);
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Add this notification event to the appropriate lists so it can be
|
||
// recreated when the cluster node fails.
|
||
//
|
||
EnterCriticalSection(&Cluster->Lock);
|
||
EnterCriticalSection(&Session->ParentNotify->Lock);
|
||
|
||
InsertHeadList(&Session->EventList, &NotifyEvent->ListEntry);
|
||
InsertHeadList(NotifyList, &NotifyEvent->ObjectList);
|
||
|
||
LeaveCriticalSection(&Session->ParentNotify->Lock);
|
||
LeaveCriticalSection(&Cluster->Lock);
|
||
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
DWORD
|
||
RegisterNotifyEvent(
|
||
IN PCNOTIFY_SESSION Session,
|
||
IN PCNOTIFY_EVENT Event,
|
||
OUT OPTIONAL PLIST_ENTRY *pNotifyList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Common routine for registering a notification event on
|
||
a cluster session
|
||
|
||
Arguments:
|
||
|
||
Session - Supplies the notification session the event
|
||
should be added to.
|
||
|
||
Event - Supplies the event to be added to the session.
|
||
|
||
NotifyList - if present, returns the list that the notification
|
||
event should be added to.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD Status;
|
||
|
||
if (Event->Object == NULL) {
|
||
TIME_PRINT(("RegisterNotifyEvent : Calling ApiAddNotifyCluster\n"));
|
||
Status = ApiAddNotifyCluster(Session->hNotify,
|
||
Session->Cluster->hCluster,
|
||
Event->dwFilter,
|
||
Event->EventId);
|
||
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &Session->Cluster->NotifyList;
|
||
}
|
||
} else if (Event->dwFilter & FILTER_NODE) {
|
||
Status = ApiAddNotifyNode(Session->hNotify,
|
||
((PCNODE)(Event->Object))->hNode,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
&Event->StateSequence);
|
||
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCNODE)(Event->Object))->NotifyList;
|
||
}
|
||
} else if (Event->dwFilter & FILTER_RESOURCE) {
|
||
Status = ApiAddNotifyResource(Session->hNotify,
|
||
((PCRESOURCE)(Event->Object))->hResource,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
&Event->StateSequence);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCRESOURCE)(Event->Object))->NotifyList;
|
||
}
|
||
|
||
} else if (Event->dwFilter & FILTER_GROUP) {
|
||
Status = ApiAddNotifyGroup(Session->hNotify,
|
||
((PCGROUP)(Event->Object))->hGroup,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
&Event->StateSequence);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCGROUP)(Event->Object))->NotifyList;
|
||
}
|
||
|
||
} else if (Event->dwFilter & FILTER_NETWORK) {
|
||
Status = ApiAddNotifyNetwork(Session->hNotify,
|
||
((PCNETWORK)(Event->Object))->hNetwork,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
&Event->StateSequence);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCNETWORK)(Event->Object))->NotifyList;
|
||
}
|
||
|
||
} else if (Event->dwFilter & FILTER_NETINTERFACE) {
|
||
Status = ApiAddNotifyNetInterface(Session->hNotify,
|
||
((PCNETINTERFACE)(Event->Object))->hNetInterface,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
&Event->StateSequence);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCNETINTERFACE)(Event->Object))->NotifyList;
|
||
}
|
||
|
||
} else if (Event->dwFilter & FILTER_REGISTRY) {
|
||
Status = ApiAddNotifyKey(Session->hNotify,
|
||
((PCKEY)(Event->Object))->RemoteKey,
|
||
Event->EventId,
|
||
Event->dwFilter & ~CLUSTER_CHANGE_REGISTRY_SUBTREE,
|
||
(Event->dwFilter & CLUSTER_CHANGE_REGISTRY_SUBTREE) ? TRUE : FALSE);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCKEY)(Event->Object))->NotifyList;
|
||
}
|
||
} else if (Event->dwFilter & FILTER_CLUSTER) {
|
||
Status = ERROR_SUCCESS;
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &Session->Cluster->NotifyList;
|
||
}
|
||
}
|
||
else {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
|
||
TIME_PRINT(("RegisterNotifyEvent :returned 0x%08lx\n",
|
||
Status));
|
||
return(Status);
|
||
|
||
}
|
||
|
||
|
||
DWORD
|
||
ReRegisterNotifyEvent(
|
||
IN PCNOTIFY_SESSION Session,
|
||
IN PCNOTIFY_EVENT Event,
|
||
OUT OPTIONAL PLIST_ENTRY *pNotifyList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Common routine for re-registering a notification event on
|
||
a cluster session. The only difference between this and
|
||
RegisterNotifyEvent is that this passes the SessionState
|
||
DWORD to the server, which will cause an immediate notification
|
||
trigger if it does not match.
|
||
|
||
Arguments:
|
||
|
||
Session - Supplies the notification session the event
|
||
should be added to.
|
||
|
||
Event - Supplies the event to be added to the session.
|
||
|
||
NotifyList - if present, returns the list that the notification
|
||
event should be added to.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD Status;
|
||
|
||
if (Event->Object == NULL) {
|
||
Status = ApiAddNotifyCluster(Session->hNotify,
|
||
Session->Cluster->hCluster,
|
||
Event->dwFilter,
|
||
Event->EventId);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &Session->Cluster->NotifyList;
|
||
}
|
||
} else if (Event->dwFilter & FILTER_NODE) {
|
||
Status = ApiReAddNotifyNode(Session->hNotify,
|
||
((PCNODE)(Event->Object))->hNode,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
Event->StateSequence);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCNODE)(Event->Object))->NotifyList;
|
||
}
|
||
} else if (Event->dwFilter & FILTER_RESOURCE) {
|
||
Status = ApiReAddNotifyResource(Session->hNotify,
|
||
((PCRESOURCE)(Event->Object))->hResource,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
Event->StateSequence);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCRESOURCE)(Event->Object))->NotifyList;
|
||
}
|
||
|
||
} else if (Event->dwFilter & FILTER_GROUP) {
|
||
Status = ApiReAddNotifyGroup(Session->hNotify,
|
||
((PCGROUP)(Event->Object))->hGroup,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
Event->StateSequence);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCGROUP)(Event->Object))->NotifyList;
|
||
}
|
||
|
||
} else if (Event->dwFilter & FILTER_NETWORK) {
|
||
Status = ApiReAddNotifyNetwork(Session->hNotify,
|
||
((PCNETWORK)(Event->Object))->hNetwork,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
Event->StateSequence);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCNETWORK)(Event->Object))->NotifyList;
|
||
}
|
||
|
||
} else if (Event->dwFilter & FILTER_NETINTERFACE) {
|
||
Status = ApiReAddNotifyNetInterface(Session->hNotify,
|
||
((PCNETINTERFACE)(Event->Object))->hNetInterface,
|
||
Event->dwFilter,
|
||
Event->EventId,
|
||
Event->StateSequence);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCNETINTERFACE)(Event->Object))->NotifyList;
|
||
}
|
||
|
||
} else if (Event->dwFilter & FILTER_REGISTRY) {
|
||
Status = ApiAddNotifyKey(Session->hNotify,
|
||
((PCKEY)(Event->Object))->RemoteKey,
|
||
Event->EventId,
|
||
Event->dwFilter & ~CLUSTER_CHANGE_REGISTRY_SUBTREE,
|
||
(Event->dwFilter & CLUSTER_CHANGE_REGISTRY_SUBTREE) ? TRUE : FALSE);
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &((PCKEY)(Event->Object))->NotifyList;
|
||
}
|
||
} else if (Event->dwFilter & FILTER_CLUSTER) {
|
||
Status = ERROR_SUCCESS;
|
||
if (ARGUMENT_PRESENT(pNotifyList)) {
|
||
*pNotifyList = &Session->Cluster->NotifyList;
|
||
}
|
||
}
|
||
else {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
DestroyNotify(
|
||
IN PCNOTIFY Notify
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Cleans up and frees all allocations and references associated with
|
||
a notification session.
|
||
|
||
Arguments:
|
||
|
||
Notify - supplies the CNOTIFY structure to be destroyed
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCNOTIFY_SESSION Session;
|
||
PLIST_ENTRY ListEntry;
|
||
PLIST_ENTRY EventList;
|
||
PCRESOURCE Resource;
|
||
PCGROUP Group;
|
||
PCNODE Node;
|
||
PCLUSTER Cluster;
|
||
PCNOTIFY_EVENT Event;
|
||
LIST_ENTRY QueueEntries;
|
||
PCNOTIFY_PACKET Packet;
|
||
|
||
//
|
||
// Rundown each session associated with this notification session
|
||
//
|
||
while (!IsListEmpty(&Notify->SessionList)) {
|
||
ListEntry = RemoveHeadList(&Notify->SessionList);
|
||
Session = CONTAINING_RECORD(ListEntry,
|
||
CNOTIFY_SESSION,
|
||
ListEntry);
|
||
Cluster = Session->Cluster;
|
||
|
||
EnterCriticalSection(&Cluster->Lock);
|
||
|
||
//
|
||
// Rundown each event registered on this session.
|
||
//
|
||
while (!IsListEmpty(&Session->EventList)) {
|
||
EventList = RemoveHeadList(&Session->EventList);
|
||
Event = CONTAINING_RECORD(EventList,
|
||
CNOTIFY_EVENT,
|
||
ListEntry);
|
||
RemoveEntryList(&Event->ObjectList);
|
||
LocalFree(Event);
|
||
}
|
||
|
||
DestroySession(Session);
|
||
|
||
LeaveCriticalSection(&Cluster->Lock);
|
||
|
||
}
|
||
|
||
//
|
||
// Rundown any outstanding notifications remaining on the queue
|
||
//
|
||
ClRtlRundownQueue(&Notify->Queue, &QueueEntries);
|
||
while (!IsListEmpty(&QueueEntries)) {
|
||
ListEntry = RemoveHeadList(&QueueEntries);
|
||
Packet = CONTAINING_RECORD(ListEntry,
|
||
CNOTIFY_PACKET,
|
||
ListEntry);
|
||
MIDL_user_free(Packet->Name);
|
||
LocalFree(Packet);
|
||
}
|
||
|
||
//
|
||
// Now that we know there are no outstanding references to the orphaned
|
||
// events, free up anything on that list.
|
||
//
|
||
while (!IsListEmpty(&Notify->OrphanedEventList)) {
|
||
ListEntry = RemoveHeadList(&Notify->OrphanedEventList);
|
||
Event = CONTAINING_RECORD(ListEntry,
|
||
CNOTIFY_EVENT,
|
||
ListEntry);
|
||
LocalFree(Event);
|
||
}
|
||
|
||
DeleteCriticalSection(&Notify->Lock);
|
||
ClRtlDeleteQueue(&Notify->Queue);
|
||
|
||
#ifdef _WIN64
|
||
ClRtlDeleteHash(&Notify->NotifyKeyHash);
|
||
#endif
|
||
|
||
LocalFree(Notify);
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
RegisterClusterNotify(
|
||
IN HCHANGE hChange,
|
||
IN DWORD dwFilterType,
|
||
IN HANDLE hObject,
|
||
IN DWORD_PTR dwNotifyKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds a specific notification type to a cluster notification port. This allows
|
||
an application to register for notification events that affect only a particular
|
||
cluster object. The currently supported specific cluster objects are nodes,
|
||
resources, and groups.
|
||
|
||
Arguments:
|
||
|
||
hChange - Supplies the change notification object.
|
||
|
||
dwFilterType - Supplies the type of object that the specific notification
|
||
events should be delivered for. hObject is a handle to an object
|
||
of this type. Currently supported specific filters include:
|
||
|
||
CLUSTER_CHANGE_NODE_STATE - hObject is an HNODE
|
||
CLUSTER_CHANGE_RESOURCE_STATE - hObject is an HRESOURCE
|
||
CLUSTER_CHANGE_GROUP_STATE - hObject is an HGROUP
|
||
CLUSTER_CHANGE_REGISTRY_NAME \
|
||
CLUSTER_CHANGE_REGISTRY_ATTRIBUTES \ - hObject is an HKEY
|
||
CLUSTER_CHANGE_REGISTRY_VALUE /
|
||
CLUSTER_CHANGE_REGISTRY_SUBTREE /
|
||
|
||
hObject - Supplies the handle to the specific object of the type specified
|
||
by dwFilterType.
|
||
|
||
dwNotifyKey - Supplies the notification key to be returned as
|
||
part of the notification event.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCNOTIFY Notify;
|
||
PCLUSTER Cluster;
|
||
PCNOTIFY_SESSION Session;
|
||
DWORD dwStatus;
|
||
|
||
if (dwFilterType & FILTER_NODE) {
|
||
if (dwFilterType & NOT_FILTER_NODE) {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
Cluster = ((PCNODE)hObject)->Cluster;
|
||
} else if (dwFilterType & FILTER_RESOURCE) {
|
||
if (dwFilterType & NOT_FILTER_RESOURCE) {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
Cluster = ((PCRESOURCE)hObject)->Cluster;
|
||
} else if (dwFilterType & FILTER_GROUP) {
|
||
if (dwFilterType & NOT_FILTER_GROUP) {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
Cluster = ((PCGROUP)hObject)->Cluster;
|
||
} else if (dwFilterType & FILTER_NETWORK) {
|
||
if (dwFilterType & NOT_FILTER_NETWORK) {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
Cluster = ((PCNETWORK)hObject)->Cluster;
|
||
} else if (dwFilterType & FILTER_NETINTERFACE) {
|
||
if (dwFilterType & NOT_FILTER_NETINTERFACE) {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
Cluster = ((PCNETINTERFACE)hObject)->Cluster;
|
||
} else if (dwFilterType & FILTER_REGISTRY) {
|
||
if (dwFilterType & NOT_FILTER_REGISTRY) {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
Cluster = ((PCKEY)hObject)->Cluster;
|
||
} else if (dwFilterType & FILTER_CLUSTER){
|
||
if (dwFilterType & NOT_FILTER_CLUSTER){
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
Cluster = (PCLUSTER)hObject;
|
||
} else {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
Notify = (PCNOTIFY)hChange;
|
||
|
||
EnterCriticalSection(&Cluster->Lock);
|
||
EnterCriticalSection(&Notify->Lock);
|
||
|
||
Session = CreateNotifySession(Notify, Cluster);
|
||
if (Session == NULL) {
|
||
LeaveCriticalSection(&Notify->Lock);
|
||
LeaveCriticalSection(&Cluster->Lock);
|
||
return(GetLastError());
|
||
}
|
||
|
||
dwStatus = AddEventToSession(Session,
|
||
hObject,
|
||
dwFilterType,
|
||
dwNotifyKey);
|
||
|
||
LeaveCriticalSection(&Notify->Lock);
|
||
LeaveCriticalSection(&Cluster->Lock);
|
||
|
||
return( dwStatus );
|
||
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
GetClusterNotify(
|
||
IN HCHANGE hChange,
|
||
OUT DWORD_PTR *lpdwNotifyKey,
|
||
OUT LPDWORD lpdwFilterType,
|
||
OUT OPTIONAL LPWSTR lpszName,
|
||
IN OUT LPDWORD lpcchName,
|
||
IN DWORD dwMilliseconds
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the next event from a cluster notification port.
|
||
|
||
Arguments:
|
||
|
||
hChange - Supplies the cluster notification port.
|
||
|
||
lpdwNotifyKey - Returns the notification key for the notification event.
|
||
This is the key passed to CreateClusterNotifyPort or RegisterClusterNotify.
|
||
|
||
lpdwFilterType - Returns the type of the notification event.
|
||
|
||
lpszName - Optionally returns the name of the object that triggered the notification
|
||
event.
|
||
|
||
lpcchName - Supplies the length (in characters) of the lpszName buffer. This length
|
||
includes the space for any trailing NULL.
|
||
|
||
Returns the length (in characters) of the name written into the lpszName
|
||
buffer. This length does not include the trailing NULL.
|
||
|
||
dwMilliseconds - Supplies an optional timeout value that specifies
|
||
how long the caller is willing to wait for the cluster notification event.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful. If lpszName is NULL we return success and fill in
|
||
lpcchName with the size. If lpcchName is NULL we return ERROR_MORE_DATA.
|
||
|
||
ERROR_MORE_DATA if the buffer is too small.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCNOTIFY_PACKET Packet;
|
||
PLIST_ENTRY ListEntry;
|
||
PCNOTIFY Notify = (PCNOTIFY)hChange;
|
||
DWORD Length;
|
||
DWORD Status;
|
||
PCNOTIFY_EVENT Event;
|
||
PVOID BufferArray[2];
|
||
|
||
BufferArray[0] = lpszName;
|
||
BufferArray[1] = lpcchName;
|
||
|
||
//
|
||
// ListEntry will be NULL under the following conditions (as determined by the ret value from
|
||
// GetClusterNotifyCallback):
|
||
//
|
||
// lpszName == NULL, lpcchName != NULL (looking for a buffer size) (ERROR_MORE_DATA)
|
||
// lpszName != NULL, lpcchName != NULL, and *lpcchName <= Length (ERROR_MORE_DATA)
|
||
//
|
||
ListEntry = ClRtlRemoveHeadQueueTimeout(&Notify->Queue, dwMilliseconds, GetClusterNotifyCallback,BufferArray);
|
||
|
||
if (ListEntry == NULL) {
|
||
//
|
||
// The queue has been rundown or a timeout has occurred, or the buffer isn't big enough.
|
||
//
|
||
Status = GetLastError();
|
||
|
||
if (lpszName==NULL && lpcchName!=NULL) {
|
||
//
|
||
// We returned ERROR_MORE_DATA from GetClusterNotifyCallback to prevent a dequeueing,
|
||
// but we want to return ERROR_SUCCESS because a buffer wasn't specified (maintains
|
||
// consistency with the other Cluster APIs)
|
||
//
|
||
Status = ERROR_SUCCESS;
|
||
}
|
||
return(Status);
|
||
}
|
||
|
||
Packet = CONTAINING_RECORD(ListEntry,
|
||
CNOTIFY_PACKET,
|
||
ListEntry);
|
||
#ifdef _WIN64
|
||
Event = (PCNOTIFY_EVENT)ClRtlGetEntryHash(&Notify->NotifyKeyHash,
|
||
Packet->KeyId);
|
||
|
||
if (Event == NULL) {
|
||
//
|
||
// The entry is missing
|
||
//
|
||
MIDL_user_free(Packet->Name);
|
||
LocalFree(Packet);
|
||
|
||
//
|
||
// should not happen unless the memory is corrupted
|
||
//
|
||
return(ERROR_NOT_FOUND);
|
||
}
|
||
#else
|
||
Event = (PCNOTIFY_EVENT)Packet->KeyId;
|
||
#endif
|
||
|
||
Event->StateSequence = Packet->StateSequence;
|
||
*lpdwNotifyKey = Event->dwNotifyKey;
|
||
*lpdwFilterType = Packet->Filter;
|
||
if (ARGUMENT_PRESENT(lpszName)) {
|
||
MylstrcpynW(lpszName, Packet->Name, *lpcchName);
|
||
Length = lstrlenW(Packet->Name);
|
||
if (Length < *lpcchName) {
|
||
*lpcchName = Length;
|
||
}
|
||
}
|
||
MIDL_user_free(Packet->Name);
|
||
LocalFree(Packet);
|
||
return(ERROR_SUCCESS);
|
||
|
||
}
|
||
|
||
|
||
BOOL
|
||
WINAPI
|
||
CloseClusterNotifyPort(
|
||
IN HCHANGE hChange
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Closes a handle of a change notification object.
|
||
|
||
Arguments:
|
||
|
||
hChange - Supplies a handle of a cluster change notification object
|
||
to close.
|
||
|
||
Return Value:
|
||
|
||
If the function is successful, the return value is TRUE.
|
||
|
||
If the function fails, the return value is FALSE. To get extended error
|
||
information, call GetLastError.
|
||
|
||
Remarks:
|
||
|
||
--*/
|
||
|
||
{
|
||
PCNOTIFY Notify = (PCNOTIFY)hChange;
|
||
|
||
DestroyNotify(Notify);
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
VOID
|
||
RundownNotifyEvents(
|
||
IN PLIST_ENTRY ListHead,
|
||
IN LPCWSTR lpszName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Cleans up any notification events on the specified list.
|
||
|
||
Arguments:
|
||
|
||
ListHead - Supplies the head of the list of notification events.
|
||
|
||
lpszName - Supplies the name that should be used to post the handle close
|
||
event.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCNOTIFY_EVENT Event;
|
||
PLIST_ENTRY ListEntry;
|
||
PCRITICAL_SECTION Lock;
|
||
PCNOTIFY_PACKET Packet;
|
||
|
||
while (!IsListEmpty(ListHead)) {
|
||
ListEntry = RemoveHeadList(ListHead);
|
||
Event = CONTAINING_RECORD(ListEntry,
|
||
CNOTIFY_EVENT,
|
||
ObjectList);
|
||
|
||
//
|
||
// Allocate a notification packet for delivering the handle
|
||
// close notification.
|
||
//
|
||
if (Event->dwFilter & CLUSTER_CHANGE_HANDLE_CLOSE) {
|
||
Packet = LocalAlloc(LMEM_FIXED, sizeof(CNOTIFY_PACKET));
|
||
if (Packet != NULL) {
|
||
Packet->Status = ERROR_SUCCESS;
|
||
Packet->KeyId = Event->EventId;
|
||
Packet->Filter = (DWORD)CLUSTER_CHANGE_HANDLE_CLOSE;
|
||
Packet->StateSequence = Event->StateSequence;
|
||
Packet->Name = MIDL_user_allocate((lstrlenW(lpszName)+1)*sizeof(WCHAR));
|
||
if (Packet->Name == NULL) {
|
||
LocalFree(Packet);
|
||
Packet = NULL;
|
||
} else {
|
||
lstrcpyW(Packet->Name, lpszName);
|
||
ClRtlInsertTailQueue(&Event->Session->ParentNotify->Queue,
|
||
&Packet->ListEntry);
|
||
}
|
||
}
|
||
}
|
||
|
||
Lock = &Event->Session->ParentNotify->Lock;
|
||
EnterCriticalSection(Lock);
|
||
RemoveEntryList(&Event->ListEntry);
|
||
//
|
||
// Note that we cannot just free the Event structure since there may be
|
||
// notification packets that reference this event in either the server-side
|
||
// or client-side queues. Instead we store it on the orphaned event list.
|
||
// It will be cleaned up when the session is closed or when a reconnect
|
||
// occurs. If we had some way to flush out the event queue we could use
|
||
// that instead.
|
||
//
|
||
InsertTailList(&Event->Session->ParentNotify->OrphanedEventList, &Event->ListEntry);
|
||
if (IsListEmpty(&Event->Session->EventList)) {
|
||
DestroySession(Event->Session);
|
||
}
|
||
|
||
LeaveCriticalSection(Lock);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
DestroySession(
|
||
IN PCNOTIFY_SESSION Session
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Destroys and cleans up an empty notification session. This
|
||
means closing the RPC context handle and waiting for the
|
||
notification thread to terminate itself. The session will
|
||
be removed from the notification ports list. The session
|
||
must be empty.
|
||
|
||
N.B. The cluster lock must be held.
|
||
|
||
Arguments:
|
||
|
||
Session - Supplies the session to be destroyed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwStatus = ERROR_SUCCESS;
|
||
|
||
//
|
||
// Chittur Subbaraman (chitturs) - 4/19/2000
|
||
//
|
||
// In order to prevent the NotifyThread from calling ApiGetNotify
|
||
// during or after the context handle is destroyed, we split
|
||
// the notification port close into two steps. In the first step,
|
||
// we merely unblock the ApiGetNotify call and then wait for
|
||
// the NotifyThread to terminate without freeing the context handle.
|
||
// In the next step, after making sure that the NotifyThread has
|
||
// terminated, we free the context handle. This avoids an AV in RPC
|
||
// code caused by the ApiGetNotify call being made during or soon after
|
||
// the context handle is freed.
|
||
//
|
||
Session->Destroyed = TRUE;
|
||
TIME_PRINT(("Destroy session: Session 0x%08lx marked as destroyed\n",
|
||
Session));
|
||
|
||
//
|
||
// If the cluster is not dead, try to unblock the ApiGetNotify call.
|
||
//
|
||
if ( !( Session->Cluster->Flags & CLUS_DEAD ) &&
|
||
( Session->hNotify != NULL ) )
|
||
{
|
||
TIME_PRINT(("Destroy session: Call ApiUnblockGetNotifyThread before NotifyThread termination, hNotify = 0x%08lx\n",
|
||
Session->hNotify));
|
||
dwStatus = ApiUnblockGetNotifyCall( Session->hNotify );
|
||
}
|
||
|
||
//
|
||
// If the ApiUnblockGetNotifyThread returned RPC_S_PROCNUM_OUT_OF_RANGE,
|
||
// it means you are talking to a server that does not support that
|
||
// API. Revert to the old (buggy) behavior then.
|
||
//
|
||
if ( dwStatus == RPC_S_PROCNUM_OUT_OF_RANGE )
|
||
{
|
||
TIME_PRINT(("Destroy session: Call ApiCloseNotify before NotifyThread termination, hNotify = 0x%08lx\n",
|
||
Session->hNotify));
|
||
|
||
if ( ApiCloseNotify( &Session->hNotify ) != ERROR_SUCCESS )
|
||
{
|
||
TIME_PRINT(("Destroy session: Call RpcSmDestroyClientContext since ApiCloseNotify failed before terminating NotifyThread, hNotify = 0x%08lx\n",
|
||
Session->hNotify));
|
||
RpcSmDestroyClientContext( &Session->hNotify );
|
||
}
|
||
}
|
||
|
||
RemoveEntryList( &Session->ListEntry );
|
||
RemoveEntryList( &Session->ClusterList );
|
||
|
||
//
|
||
// Drop the critical section as the notification thread might be
|
||
// stuck waiting on it. Since the session has been removed from
|
||
// the cluster list, nobody can get to it anymore.
|
||
//
|
||
LeaveCriticalSection( &Session->Cluster->Lock );
|
||
|
||
WaitForSingleObject( Session->NotifyThread, INFINITE );
|
||
CloseHandle( Session->NotifyThread );
|
||
|
||
//
|
||
// Reacquire the cluster lock.
|
||
//
|
||
EnterCriticalSection( &Session->Cluster->Lock );
|
||
|
||
//
|
||
// If the ApiUnblockGetNotifyThread was successfully executed or
|
||
// it could not be made since the cluster was dead, then perform
|
||
// the context handle cleanup. Note that cleaning up the context
|
||
// handle here is safe since we know that the NotifyThread has
|
||
// terminated at this point and wouldn't use it any more.
|
||
//
|
||
if ( dwStatus != RPC_S_PROCNUM_OUT_OF_RANGE )
|
||
{
|
||
if ( Session->Cluster->Flags & CLUS_DEAD )
|
||
{
|
||
TIME_PRINT(("Destroy session: Call RpcSmDestroyClientContext after terminating NotifyThread, hNotify = 0x%08lx\n",
|
||
Session->hNotify));
|
||
if ( Session->hNotify != NULL )
|
||
{
|
||
RpcSmDestroyClientContext( &Session->hNotify );
|
||
}
|
||
} else
|
||
{
|
||
TIME_PRINT(("Destroy session: Call ApiCloseNotify after terminating NotifyThread, hNotify = 0x%08lx\n",
|
||
Session->hNotify));
|
||
|
||
dwStatus = ApiCloseNotify( &Session->hNotify );
|
||
|
||
if ( dwStatus != ERROR_SUCCESS )
|
||
{
|
||
TIME_PRINT(("Destroy session: Call RpcSmDestroyClientContext since ApiCloseNotify failed after terminating NotifyThread, hNotify = 0x%08lx\n",
|
||
Session->hNotify));
|
||
RpcSmDestroyClientContext( &Session->hNotify );
|
||
}
|
||
}
|
||
}
|
||
|
||
LocalFree( Session );
|
||
}
|
||
|
||
DWORD
|
||
GetClusterNotifyCallback(
|
||
IN PLIST_ENTRY ListEntry,
|
||
IN OUT PVOID pvContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Check ListEntry to determine whether the buffer is big enough to contain the Name
|
||
|
||
Arguments:
|
||
|
||
ListEntry - Supplies the event to convert to a CNOTIFY_PACKET.
|
||
|
||
Context - A len 2 PVOID array containing the buffer pointer and a DWORD ptr to the
|
||
buffer length. On output the buffer len ptr contains the number of chars
|
||
needed.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS - The buffer is large enough to put the Name into.
|
||
|
||
ERROR_MORE_DATA - The buffer is too small.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCNOTIFY_PACKET Packet;
|
||
DWORD Length;
|
||
|
||
LPWSTR pBuffer;
|
||
DWORD* pBufferLength;
|
||
|
||
PVOID *Context = (PVOID*)pvContext;
|
||
|
||
DWORD Status;
|
||
|
||
ASSERT( pvContext != NULL );
|
||
|
||
pBuffer = (LPWSTR)(Context[0]);
|
||
pBufferLength = (DWORD*)(Context[1]);
|
||
|
||
//
|
||
// Check the Name buffer size
|
||
//
|
||
Packet = CONTAINING_RECORD( ListEntry,
|
||
CNOTIFY_PACKET,
|
||
ListEntry );
|
||
|
||
//
|
||
// Nested if's to cover the four combinations of pBufferLength and pBuffer being
|
||
// NULL and non-NULL values.
|
||
//
|
||
if ( pBufferLength == NULL) {
|
||
if (pBuffer == NULL ) {
|
||
//
|
||
// We're not interested in filling a buffer, return ERROR_SUCCESS. This will
|
||
// cause an event to be dequeued.
|
||
//
|
||
Status = ERROR_SUCCESS;
|
||
|
||
} else { // pBuffer != NULL
|
||
//
|
||
// AV to maintain pre-Whistler functionality (ugh)
|
||
//
|
||
*pBufferLength = 0;
|
||
Status = ERROR_INVALID_PARAMETER;
|
||
}
|
||
} else {
|
||
//
|
||
// pBufferLength != NULL;
|
||
//
|
||
Length = wcslen( Packet->Name );
|
||
|
||
if (pBuffer == NULL ) {
|
||
//
|
||
// We're only interested in getting a buffer size, return ERROR_MORE_DATA to
|
||
// signify that we're not to dequeue an event. This will be re-interpreted in
|
||
// GetClusterNotify.
|
||
//
|
||
*pBufferLength = Length;
|
||
Status = ERROR_MORE_DATA;
|
||
|
||
} else { // pBuffer != NULL
|
||
//
|
||
// We need to determine whether the buffer is big enough - that determines
|
||
// whether we return ERROR_SUCCESS (it is) or ERROR_MORE_DATA (it isn't)
|
||
//
|
||
if (Length < *pBufferLength) {
|
||
//
|
||
// Success - the buffer is large enough.
|
||
//
|
||
Status = ERROR_SUCCESS;
|
||
} else {
|
||
//
|
||
// Failure - the buffer was too small. A buffer was specified, so we need to
|
||
// return ERROR_MORE_DATA.
|
||
//
|
||
*pBufferLength = Length;
|
||
Status = ERROR_MORE_DATA;
|
||
}
|
||
|
||
} // if: pBuffer == NULL
|
||
|
||
} // if: pBufferLength == NULL
|
||
|
||
return Status;
|
||
|
||
} //*** GetClusterNotifyCallback
|
||
|