472 lines
16 KiB
C
472 lines
16 KiB
C
/*++
|
|
|
|
Copyright (c) 1996-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
clusapip.h
|
|
|
|
Abstract:
|
|
|
|
Private header file for cluster api
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 15-Jan-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include "nt.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include "windows.h"
|
|
#include "cluster.h"
|
|
#include "api_rpc.h"
|
|
|
|
//
|
|
// Define CLUSTER structure. There is one cluster structure created
|
|
// for each OpenCluster API call. An HCLUSTER is really a pointer to
|
|
// this structure.
|
|
//
|
|
|
|
#define CLUS_SIGNATURE 'SULC'
|
|
|
|
typedef struct _RECONNECT_CANDIDATE {
|
|
BOOL IsUp;
|
|
BOOL IsCurrent;
|
|
LPWSTR Name;
|
|
} RECONNECT_CANDIDATE, *PRECONNECT_CANDIDATE;
|
|
|
|
typedef struct _CLUSTER {
|
|
DWORD Signature;
|
|
DWORD ReferenceCount;
|
|
DWORD FreedRpcHandleListLen;
|
|
LPWSTR ClusterName;
|
|
LPWSTR NodeName; // node name we are connected to
|
|
DWORD Flags;
|
|
RPC_BINDING_HANDLE RpcBinding;
|
|
HCLUSTER_RPC hCluster;
|
|
LIST_ENTRY KeyList; // open cluster registry keys
|
|
LIST_ENTRY ResourceList; // open resource handles
|
|
LIST_ENTRY GroupList; // open group handles
|
|
LIST_ENTRY NodeList; // open node handles
|
|
LIST_ENTRY NetworkList; // open network handles
|
|
LIST_ENTRY NetInterfaceList; // open net interface handles
|
|
LIST_ENTRY NotifyList; // outstanding notification event filters
|
|
LIST_ENTRY SessionList; // open notification sessions.
|
|
unsigned long AuthnLevel; // Level of authentication to be performed on remote procedure calls
|
|
HANDLE NotifyThread;
|
|
CRITICAL_SECTION Lock;
|
|
DWORD Generation;
|
|
DWORD ReconnectCount;
|
|
PRECONNECT_CANDIDATE Reconnect;
|
|
LIST_ENTRY FreedBindingList;
|
|
LIST_ENTRY FreedContextList;
|
|
} CLUSTER, *PCLUSTER;
|
|
|
|
// [GorN] Jan/13/1999
|
|
// This is a temporary fix for the race between the users
|
|
// of binding and context handles and reconnectThread
|
|
//
|
|
// The code assumes that RPC_BINDING_HANDLE == ContextHandle == void*
|
|
|
|
typedef struct _CTX_HANDLE {
|
|
LIST_ENTRY HandleList;
|
|
void * RpcHandle; // assumption RPC_BINDING_HANDLE == ContextHandle == void*
|
|
ULONGLONG TimeStamp;
|
|
} CTX_HANDLE, *PCTX_HANDLE;
|
|
|
|
RPC_STATUS
|
|
FreeRpcBindingOrContext(
|
|
IN PCLUSTER Cluster,
|
|
IN void ** RpcHandle,
|
|
IN BOOL IsBinding);
|
|
|
|
#define MyRpcBindingFree(Cluster, Binding) \
|
|
FreeRpcBindingOrContext(Cluster, Binding, TRUE)
|
|
|
|
#define MyRpcSmDestroyClientContext(Cluster, Context) \
|
|
FreeRpcBindingOrContext(Cluster, Context, FALSE)
|
|
|
|
VOID
|
|
FreeObsoleteRpcHandlesEx(
|
|
IN PCLUSTER Cluster,
|
|
IN BOOL Cleanup,
|
|
IN BOOL IsBinding
|
|
);
|
|
|
|
#define FreeObsoleteRpcHandles(Cluster, Cleanup) { \
|
|
FreeObsoleteRpcHandlesEx(Cluster, Cleanup, TRUE); \
|
|
FreeObsoleteRpcHandlesEx(Cluster, Cleanup, FALSE); \
|
|
}
|
|
|
|
|
|
//
|
|
// Define CLUSTER.Flags
|
|
//
|
|
#define CLUS_DELETED 1
|
|
#define CLUS_DEAD 2
|
|
#define CLUS_LOCALCONNECT 4
|
|
|
|
//
|
|
// Cluster helper macros
|
|
//
|
|
#define GET_CLUSTER(hCluster) (PCLUSTER)((((PCLUSTER)(hCluster))->Flags & CLUS_DELETED) ? NULL : hCluster)
|
|
|
|
#define IS_CLUSTER_FREE(c) ((c->Flags & CLUS_DELETED) && \
|
|
(IsListEmpty(&(c)->KeyList)) && \
|
|
(IsListEmpty(&(c)->GroupList)) && \
|
|
(IsListEmpty(&(c)->NodeList)) && \
|
|
(IsListEmpty(&(c)->ResourceList)) && \
|
|
(IsListEmpty(&(c)->NetworkList)) && \
|
|
(IsListEmpty(&(c)->NetInterfaceList)))
|
|
|
|
//
|
|
// Cluster structure cleanup routine.
|
|
//
|
|
VOID
|
|
CleanupCluster(
|
|
IN PCLUSTER Cluster
|
|
);
|
|
|
|
VOID
|
|
RundownNotifyEvents(
|
|
IN PLIST_ENTRY ListHead,
|
|
IN LPCWSTR Name
|
|
);
|
|
|
|
//
|
|
// Define CRESOURCE structure. There is one resource structure created
|
|
// for each OpenResource/CreateResource API call. An HRESOURCE is really
|
|
// a pointer to this structure. These are chained onto the CLUSTER that
|
|
// they were opened relative to.
|
|
//
|
|
typedef struct _CRESOURCE {
|
|
LIST_ENTRY ListEntry; // Links for chaining onto CLUSTER.ResourceList
|
|
LIST_ENTRY NotifyList; // Links for tracking outstanding notifies.
|
|
PCLUSTER Cluster; // Parent cluster
|
|
LPWSTR Name;
|
|
HRES_RPC hResource; // RPC handle
|
|
} CRESOURCE, *PCRESOURCE;
|
|
|
|
|
|
//
|
|
// Define CGROUP structure. There is one group structure created
|
|
// for each OpenGroup/CreateGroup API call. An HGROUP is really
|
|
// a pointer to this structure. These are chained onto the CLUSTER that
|
|
// they were opened relative to.
|
|
//
|
|
typedef struct _CGROUP {
|
|
LIST_ENTRY ListEntry; // Links for chaining onto CLUSTER.Group
|
|
LIST_ENTRY NotifyList; // Links for tracking outstanding notifies.
|
|
PCLUSTER Cluster; // Parent cluster
|
|
LPWSTR Name;
|
|
HRES_RPC hGroup; // RPC handle
|
|
} CGROUP, *PCGROUP;
|
|
|
|
//
|
|
// Define CNODE structure. There is one node structure created
|
|
// for each OpenClusterNode call. An HNODE is really a pointer
|
|
// to this structure. These are chained onto the CLUSTER that they
|
|
// were opened relative to.
|
|
//
|
|
typedef struct _CNODE {
|
|
LIST_ENTRY ListEntry; // Links for chaining onto CLUSTER.NodeList
|
|
LIST_ENTRY NotifyList; // Links for tracking outstanding notifies.
|
|
PCLUSTER Cluster; // Parent cluster
|
|
LPWSTR Name;
|
|
HNODE_RPC hNode; // RPC handle
|
|
} CNODE, *PCNODE;
|
|
|
|
//
|
|
// Define CNETWORK structure. There is one network structure created
|
|
// for each OpenNetwork API call. An HNETWORK is really a pointer to
|
|
// this structure. These are chained onto the CLUSTER that they were
|
|
// opened relative to.
|
|
//
|
|
typedef struct _CNETWORK {
|
|
LIST_ENTRY ListEntry; // Links for chaining onto CLUSTER.NetworkList
|
|
LIST_ENTRY NotifyList; // Links for tracking outstanding notifies.
|
|
PCLUSTER Cluster; // Parent cluster
|
|
LPWSTR Name;
|
|
HNETWORK_RPC hNetwork; // RPC handle
|
|
} CNETWORK, *PCNETWORK;
|
|
|
|
//
|
|
// Define CNETINTERFACE structure. There is one network interface structure
|
|
// created for each OpenNetInterface API call. An HNETINTERFACE is really a
|
|
// pointer to this structure. These are chained onto the CLUSTER that they
|
|
// were opened relative to.
|
|
//
|
|
typedef struct _CNETINTERFACE {
|
|
LIST_ENTRY ListEntry; // Links for chaining onto CLUSTER.NetInterfaceList
|
|
LIST_ENTRY NotifyList; // Links for tracking outstanding notifies.
|
|
PCLUSTER Cluster; // Parent cluster
|
|
LPWSTR Name;
|
|
HNETINTERFACE_RPC hNetInterface; // RPC handle
|
|
} CNETINTERFACE, *PCNETINTERFACE;
|
|
|
|
//
|
|
// Define cluster registry key handle structure.
|
|
//
|
|
// These are kept around in a tree to track all outstanding
|
|
// registry handles. This allows the handles to be re-opened
|
|
// transparently in the event that the cluster node we are
|
|
// communicating with crashes.
|
|
//
|
|
typedef struct _CKEY {
|
|
LIST_ENTRY ParentList;
|
|
LIST_ENTRY ChildList;
|
|
LIST_ENTRY NotifyList;
|
|
struct _CKEY *Parent;
|
|
PCLUSTER Cluster;
|
|
LPWSTR RelativeName;
|
|
HKEY_RPC RemoteKey;
|
|
REGSAM SamDesired;
|
|
} CKEY, *PCKEY;
|
|
|
|
//
|
|
// Define CNOTIFY structure. There is one CNOTIFY structure for each
|
|
// notification port. A notification port contains zero or more notify
|
|
// sessions. Each session is an RPC connection to a different cluster.
|
|
// Each session contains one or more notify events. Each event represents
|
|
// a a registered notification on a cluster object. Events are linked onto
|
|
// both the session structure and the cluster object structure. Events are
|
|
// removed from a notification session when the cluster object handle is
|
|
// closed, or the cluster notify port itself is closed. When the last event
|
|
// in a session is removed, the session is cleaned up. This closes the RPC
|
|
// connection.
|
|
//
|
|
|
|
|
|
typedef struct _CNOTIFY {
|
|
LIST_ENTRY SessionList;
|
|
CRITICAL_SECTION Lock;
|
|
CL_QUEUE Queue;
|
|
CL_HASH NotifyKeyHash;
|
|
LIST_ENTRY OrphanedEventList; // CNOTIFY_EVENTs whose object has been closed
|
|
// We cannot get rid of these as there may still
|
|
// be some packets referencing the CNOTIFY_EVENT
|
|
// structure in either the server or client-side
|
|
// queues.
|
|
} CNOTIFY, *PCNOTIFY;
|
|
|
|
typedef struct _CNOTIFY_SESSION {
|
|
LIST_ENTRY ListEntry; // Linkage onto CNOTIFY.SessionList
|
|
LIST_ENTRY ClusterList; // Linkage onto CLUSTER.SessionList
|
|
LIST_ENTRY EventList; // List of CNOTIFY_EVENTs on this session
|
|
PCLUSTER Cluster;
|
|
HNOTIFY_RPC hNotify;
|
|
HANDLE NotifyThread;
|
|
PCNOTIFY ParentNotify;
|
|
BOOL Destroyed; // Set by DestroySession so NotifyThread doesn't
|
|
// try and reconnect
|
|
} CNOTIFY_SESSION, *PCNOTIFY_SESSION;
|
|
|
|
typedef struct _CNOTIFY_EVENT {
|
|
LIST_ENTRY ListEntry; // Linkage onto CNOTIFY_SESSION.EventList
|
|
LIST_ENTRY ObjectList; // Linkage onto cluster object's list.
|
|
PCNOTIFY_SESSION Session;
|
|
DWORD dwFilter;
|
|
DWORD_PTR dwNotifyKey;
|
|
DWORD StateSequence;
|
|
DWORD EventId;
|
|
PVOID Object;
|
|
} CNOTIFY_EVENT, *PCNOTIFY_EVENT;
|
|
|
|
typedef struct _CNOTIFY_PACKET {
|
|
LIST_ENTRY ListEntry;
|
|
DWORD Status;
|
|
DWORD KeyId;
|
|
DWORD Filter;
|
|
DWORD StateSequence;
|
|
LPWSTR Name;
|
|
} CNOTIFY_PACKET, *PCNOTIFY_PACKET;
|
|
|
|
DWORD
|
|
RegisterNotifyEvent(
|
|
IN PCNOTIFY_SESSION Session,
|
|
IN PCNOTIFY_EVENT Event,
|
|
OUT OPTIONAL PLIST_ENTRY *pNotifyList
|
|
);
|
|
|
|
DWORD
|
|
ReRegisterNotifyEvent(
|
|
IN PCNOTIFY_SESSION Session,
|
|
IN PCNOTIFY_EVENT Event,
|
|
OUT OPTIONAL PLIST_ENTRY *pNotifyList
|
|
);
|
|
|
|
//
|
|
// Wrappers for RPC functions. These are equivalent to the raw RPC interface, except
|
|
// that they filter out connection errors and perform transparent reconnects.
|
|
//
|
|
DWORD
|
|
ReconnectCluster(
|
|
IN PCLUSTER Cluster,
|
|
IN DWORD Error,
|
|
IN DWORD Generation
|
|
);
|
|
|
|
DWORD
|
|
GetReconnectCandidates(
|
|
IN PCLUSTER Cluster
|
|
);
|
|
|
|
VOID
|
|
FreeReconnectCandidates(
|
|
IN PCLUSTER Cluster
|
|
);
|
|
|
|
|
|
#define WRAP(_outstatus_, _fn_,_clus_) \
|
|
{ \
|
|
DWORD _err_; \
|
|
DWORD _generation_; \
|
|
\
|
|
while (TRUE) { \
|
|
if ((_clus_)->Flags & CLUS_DEAD) { \
|
|
TIME_PRINT(("Failing "#_fn_ " due to dead cluster\n")); \
|
|
_err_ = RPC_S_SERVER_UNAVAILABLE; \
|
|
break; \
|
|
} \
|
|
FreeObsoleteRpcHandles(_clus_, FALSE); \
|
|
_generation_ = (_clus_)->Generation; \
|
|
TIME_PRINT(("Calling " #_fn_ "\n")); \
|
|
_err_ = _fn_; \
|
|
if (_err_ != ERROR_SUCCESS) { \
|
|
_err_ = ReconnectCluster(_clus_, \
|
|
_err_, \
|
|
_generation_); \
|
|
if (_err_ == ERROR_SUCCESS) { \
|
|
continue; \
|
|
} \
|
|
} \
|
|
break; \
|
|
} \
|
|
_outstatus_ = _err_; \
|
|
}
|
|
|
|
|
|
//
|
|
// This variation of WRAP only attempts to reconnect if _condition_ == TRUE.
|
|
// This is useful for threads such as the NotifyThread that can have their
|
|
// context handle closed out from under them by another thread.
|
|
//
|
|
#define WRAP_CHECK(_outstatus_, _fn_,_clus_,_condition_) \
|
|
{ \
|
|
DWORD _err_; \
|
|
DWORD _generation_; \
|
|
\
|
|
while (TRUE) { \
|
|
if ((_clus_)->Flags & CLUS_DEAD) { \
|
|
TIME_PRINT(("Failing "#_fn_ " due to dead cluster\n")); \
|
|
_err_ = RPC_S_SERVER_UNAVAILABLE; \
|
|
break; \
|
|
} \
|
|
FreeObsoleteRpcHandles(_clus_, FALSE); \
|
|
_generation_ = (_clus_)->Generation; \
|
|
TIME_PRINT(("Calling " #_fn_ "\n")); \
|
|
_err_ = _fn_; \
|
|
if ((_err_ != ERROR_SUCCESS) && (_condition_)) { \
|
|
_err_ = ReconnectCluster(_clus_, \
|
|
_err_, \
|
|
_generation_); \
|
|
if (_err_ == ERROR_SUCCESS) { \
|
|
continue; \
|
|
} \
|
|
} \
|
|
break; \
|
|
} \
|
|
_outstatus_ = _err_; \
|
|
}
|
|
|
|
#define WRAP_NULL(_outvar_, _fn_, _reterr_, _clus_) \
|
|
{ \
|
|
DWORD _err_; \
|
|
DWORD _generation_; \
|
|
\
|
|
while (TRUE) { \
|
|
if ((_clus_)->Flags & CLUS_DEAD) { \
|
|
TIME_PRINT(("Failing "#_fn_ " due to dead cluster\n")); \
|
|
*(_reterr_) = RPC_S_SERVER_UNAVAILABLE; \
|
|
_outvar_ = NULL; \
|
|
break; \
|
|
} \
|
|
FreeObsoleteRpcHandles(_clus_, FALSE); \
|
|
_generation_ = (_clus_)->Generation; \
|
|
_outvar_ = _fn_; \
|
|
if ((_outvar_ == NULL) || \
|
|
(*(_reterr_) != ERROR_SUCCESS)) { \
|
|
*(_reterr_) = ReconnectCluster(_clus_, \
|
|
*(_reterr_), \
|
|
_generation_); \
|
|
if (*(_reterr_) == ERROR_SUCCESS) { \
|
|
continue; \
|
|
} \
|
|
} \
|
|
break; \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// A version of lstrcpynW that doesn't bother doing try/except so it doesn't
|
|
// quietly succeed if somebody passes in NULL.
|
|
//
|
|
VOID
|
|
APIENTRY
|
|
MylstrcpynW(
|
|
LPWSTR lpString1,
|
|
LPCWSTR lpString2,
|
|
DWORD iMaxLength
|
|
);
|
|
|
|
//
|
|
// Increase the reference count on a cluster handle.
|
|
//
|
|
DWORD
|
|
WINAPI
|
|
AddRefToClusterHandle(
|
|
IN HCLUSTER hCluster
|
|
);
|
|
|
|
#define _API_PRINT 0
|
|
|
|
#if _API_PRINT
|
|
ULONG
|
|
_cdecl
|
|
DbgPrint(
|
|
PCH Format,
|
|
...
|
|
);
|
|
|
|
#define ApiPrint(_x_) { \
|
|
if (IsDebuggerPresent()) { \
|
|
DbgPrint _x_ ; \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// Timing macro
|
|
//
|
|
|
|
#define TIME_PRINT(_x_) { \
|
|
DWORD msec; \
|
|
\
|
|
msec = GetTickCount(); \
|
|
ApiPrint(("%d.%03d:%02x: ", \
|
|
msec/1000, \
|
|
msec % 1000, \
|
|
GetCurrentThreadId())); \
|
|
ApiPrint(_x_); \
|
|
}
|
|
|
|
#else
|
|
|
|
#define ApiPrint(_x_)
|
|
#define TIME_PRINT(_x_)
|
|
|
|
#endif
|
|
|