/*++ 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