1475 lines
38 KiB
C
1475 lines
38 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
dminit.c
|
||
|
||
Abstract:
|
||
|
||
Contains the initialization code for the Cluster Database Manager
|
||
|
||
Author:
|
||
|
||
John Vert (jvert) 24-Apr-1996
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "dmp.h"
|
||
|
||
//
|
||
// Global Data
|
||
//
|
||
|
||
HKEY DmpRoot;
|
||
LIST_ENTRY KeyList;
|
||
CRITICAL_SECTION KeyLock;
|
||
HDMKEY DmClusterParametersKey;
|
||
HDMKEY DmResourcesKey;
|
||
HDMKEY DmResourceTypesKey;
|
||
HDMKEY DmGroupsKey;
|
||
HDMKEY DmNodesKey;
|
||
HDMKEY DmNetworksKey;
|
||
HDMKEY DmNetInterfacesKey;
|
||
HDMKEY DmQuorumKey;
|
||
HANDLE ghQuoLogOpenEvent=NULL;
|
||
|
||
#if NO_SHARED_LOCKS
|
||
CRITICAL_SECTION gLockDmpRoot;
|
||
#else
|
||
RTL_RESOURCE gLockDmpRoot;
|
||
#endif
|
||
BOOL gbIsQuoLoggingOn=FALSE;
|
||
HANDLE ghDiskManTimer=NULL;//disk management timer
|
||
PFM_RESOURCE gpQuoResource=NULL; //set when DMFormNewCluster is completed
|
||
HANDLE ghCheckpointTimer = NULL; //timer for periodic checkpointing
|
||
BOOL gbDmInited = FALSE; //set to TRUE when all phases of dm initialization are over
|
||
extern HLOG ghQuoLog;
|
||
BOOL gbDmpShutdownUpdates = FALSE;
|
||
|
||
|
||
//define public cluster key value names
|
||
const WCHAR cszPath[]= CLUSREG_NAME_QUORUM_PATH;
|
||
const WCHAR cszMaxQuorumLogSize[]=CLUSREG_NAME_QUORUM_MAX_LOG_SIZE;
|
||
const WCHAR cszParameters[] = CLUSREG_KEYNAME_PARAMETERS;
|
||
|
||
//other const strings
|
||
const WCHAR cszQuoFileName[]=L"quolog.log";
|
||
const WCHAR cszQuoTombStoneFile[]=L"quotomb.stn";
|
||
const WCHAR cszTmpQuoTombStoneFile[]=L"quotomb.tmp";
|
||
|
||
GUM_DISPATCH_ENTRY DmGumDispatchTable[] = {
|
||
{3, (PGUM_DISPATCH_ROUTINE1)DmpUpdateCreateKey},
|
||
{4, (PGUM_DISPATCH_ROUTINE1)DmpUpdateSetSecurity}
|
||
};
|
||
|
||
//
|
||
// Global data for interfacing with registry watcher thread
|
||
//
|
||
HANDLE hDmpRegistryFlusher=NULL;
|
||
HANDLE hDmpRegistryEvent=NULL;
|
||
HANDLE hDmpRegistryRestart=NULL;
|
||
DWORD
|
||
DmpRegistryFlusher(
|
||
IN LPVOID lpThreadParameter
|
||
);
|
||
|
||
//
|
||
// Local function prototypes
|
||
//
|
||
VOID
|
||
DmpInvalidateKeys(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
DmpReopenKeys(
|
||
VOID
|
||
);
|
||
|
||
DWORD
|
||
DmpLoadHive(
|
||
IN LPCWSTR Path
|
||
);
|
||
|
||
typedef struct _DMP_KEY_DEF {
|
||
HDMKEY *pKey;
|
||
LPWSTR Name;
|
||
} DMP_KEY_DEF;
|
||
|
||
DMP_KEY_DEF DmpKeyTable[] = {
|
||
{&DmResourcesKey, CLUSREG_KEYNAME_RESOURCES},
|
||
{&DmResourceTypesKey, CLUSREG_KEYNAME_RESOURCE_TYPES},
|
||
{&DmQuorumKey, CLUSREG_KEYNAME_QUORUM},
|
||
{&DmGroupsKey, CLUSREG_KEYNAME_GROUPS},
|
||
{&DmNodesKey, CLUSREG_KEYNAME_NODES},
|
||
{&DmNetworksKey, CLUSREG_KEYNAME_NETWORKS},
|
||
{&DmNetInterfacesKey, CLUSREG_KEYNAME_NETINTERFACES}};
|
||
|
||
|
||
DWORD
|
||
DmInitialize(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Inits the config database manager
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise
|
||
|
||
--*/
|
||
{
|
||
BOOL Success;
|
||
DWORD Status = ERROR_SUCCESS;
|
||
DWORD dwOut;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,"[DM] Initialization\n");
|
||
|
||
InitializeListHead(&KeyList);
|
||
InitializeCriticalSection(&KeyLock);
|
||
|
||
//create a critical section for locking the database while checkpointing
|
||
INITIALIZE_LOCK(gLockDmpRoot);
|
||
|
||
//create a named event that is used for waiting for quorum resource
|
||
//to go online
|
||
ghQuoLogOpenEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
if (!ghQuoLogOpenEvent)
|
||
{
|
||
CL_UNEXPECTED_ERROR((Status = GetLastError()));
|
||
goto FnExit;
|
||
|
||
}
|
||
|
||
Success = DmpInitNotify();
|
||
CL_ASSERT(Success);
|
||
if (!Success)
|
||
{
|
||
Status = GetLastError();
|
||
goto FnExit;
|
||
}
|
||
|
||
//find out if the databasecopy was in progresss on last death
|
||
DmpGetDwordFromClusterServer(L"ClusterDatabaseCopyInProgress", &dwOut, 0);
|
||
|
||
LoadClusterDatabase:
|
||
//
|
||
// Open key to root of cluster.
|
||
//
|
||
Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
|
||
DmpClusterParametersKeyName,
|
||
&DmpRoot);
|
||
//
|
||
// If the key was not found, go load the database.
|
||
//
|
||
if (Status == ERROR_FILE_NOT_FOUND) {
|
||
WCHAR Path[MAX_PATH];
|
||
WCHAR BkpPath[MAX_PATH];
|
||
WCHAR *p;
|
||
|
||
Status = GetModuleFileName(NULL, Path, MAX_PATH);
|
||
if (Status == 0) {
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] Couldn't find cluster database, status=%1!u!\n",
|
||
Status);
|
||
goto FnExit;
|
||
}
|
||
|
||
//get the name of the cluster database
|
||
p=wcsrchr(Path, L'\\');
|
||
if (p == NULL)
|
||
{
|
||
Status = ERROR_FILE_NOT_FOUND;
|
||
CL_UNEXPECTED_ERROR(Status);
|
||
goto FnExit;
|
||
}
|
||
//see if we should load the hive from the old one or the bkp file
|
||
*p = L'\0';
|
||
wcscpy(BkpPath, Path);
|
||
#ifdef OLD_WAY
|
||
wcscat(Path, L"\\CLUSDB");
|
||
wcscat(BkpPath, L"\\CLUSTER_DATABASE_TMPBKP_NAME");
|
||
#else // OLD_WAY
|
||
wcscat(Path, L"\\"CLUSTER_DATABASE_NAME );
|
||
wcscat(BkpPath, L"\\"CLUSTER_DATABASE_TMPBKP_NAME);
|
||
#endif // OLD_WAY
|
||
|
||
if (dwOut)
|
||
{
|
||
//the backip file must exist
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[DM] DmInitialize:: DatabaseCopy was in progress on last death, get hive from %1!ws!!\n",
|
||
BkpPath);
|
||
//set file attributes of the BkpPath
|
||
if (!SetFileAttributes(BkpPath, FILE_ATTRIBUTE_NORMAL))
|
||
{
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmInitialize:: SetFileAttrib on BkpPath %1!ws! failed, Status=%2!u!\n",
|
||
BkpPath, Status);
|
||
goto FnExit;
|
||
}
|
||
|
||
//copyfilex preserves the attributes on the original file
|
||
if (!CopyFileEx(BkpPath, Path, NULL, NULL, NULL, 0))
|
||
{
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmInitialize:: Databasecopy was in progress,Failed to copy %1!ws! to %2!ws!, Status=%3!u!\n",
|
||
BkpPath, Path, Status);
|
||
//set the file attribute on the backup, so that
|
||
//nobody mucks with it without knowing what they are
|
||
//doing
|
||
SetFileAttributes(BkpPath, FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
|
||
goto FnExit;
|
||
}
|
||
//now we can reset the DatabaseCopyInProgress value in the registry
|
||
//set databaseCopyInProgress key to FALSE
|
||
//This will flush the key as well
|
||
Status = DmpSetDwordInClusterServer( L"ClusterDatabaseCopyInProgress", 0);
|
||
if (Status != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmInitialize:: Failed to reset ClusterDatabaseCopyInProgress, Status=%1!u!\n",
|
||
Status);
|
||
goto FnExit;
|
||
}
|
||
//Now we can delete the backup path, since the key has been flushed
|
||
if (!DeleteFileW(BkpPath))
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmInitialize:: Failed to delete the backup when it wasnt needed,Status=%1!u!\n",
|
||
GetLastError());
|
||
//this is not fatal so we ignore the error
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//the backup file might exist
|
||
//this is true when safe copy makes a backup but hasnt
|
||
//set the value DatabaseCopyInProgress in the registry
|
||
//if it does delete it
|
||
//set file attributes of the BkpPath
|
||
if (!SetFileAttributes(BkpPath, FILE_ATTRIBUTE_NORMAL))
|
||
{
|
||
//errors are not fatal, we just ignore them
|
||
//this may fail because the path doesnt exist
|
||
}
|
||
//Now we can delete the backup path, since the key has been flushed
|
||
//this is not fatal so we ignore the error
|
||
if (DeleteFileW(BkpPath))
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[DM] DmInitialize:: Deleted the unneeded backup of the cluster database\n");
|
||
}
|
||
|
||
}
|
||
|
||
Status = DmpLoadHive(Path);
|
||
if (Status != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] Couldn't load cluster database\n");
|
||
CsLogEventData(LOG_CRITICAL,
|
||
DM_DATABASE_CORRUPT_OR_MISSING,
|
||
sizeof(Status),
|
||
&Status);
|
||
goto FnExit;
|
||
}
|
||
|
||
Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
|
||
DmpClusterParametersKeyName,
|
||
&DmpRoot);
|
||
//
|
||
// HACKHACK John Vert (jvert) 6/3/1997
|
||
// There is a bug in the registry with refresh
|
||
// where the Parent field in the root cell doesn't
|
||
// get flushed to disk, so it gets blasted if we
|
||
// do a refresh. Then we crash in unload. So flush
|
||
// out the registry to disk here to make sure the
|
||
// right Parent field gets written to disk.
|
||
//
|
||
if (Status == ERROR_SUCCESS) {
|
||
DWORD Dummy=0;
|
||
//
|
||
// Make something dirty in the root
|
||
//
|
||
RegSetValueEx(DmpRoot,
|
||
L"Valid",
|
||
0,
|
||
REG_DWORD,
|
||
(PBYTE)&Dummy,
|
||
sizeof(Dummy));
|
||
RegDeleteValue(DmpRoot, L"Valid");
|
||
Status = RegFlushKey(DmpRoot);
|
||
}
|
||
} else {
|
||
|
||
//if the hive is already loaded we unload and reload it again
|
||
//to make sure that it is loaded with the right flags and
|
||
//also to make sure that the backup copy is used in case
|
||
//of failures
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmInitialize: The hive was loaded- rollback, unload and reload again\n");
|
||
//BUGBUG:: currently the unload flushes the hive, ideally we
|
||
//would like to unload it without flushing it
|
||
//This way a part transaction wont be a part of the hive
|
||
//However, if somebody messes with the cluster hive using
|
||
//regedt32 and if reg_no_lazy flush is not specified, some
|
||
//changes might get flushed to the hive.
|
||
|
||
//We can try and do the rollback in any case,
|
||
//the rollback will fail if the registry wasnt loaded with the
|
||
//reg_no_lazy_flush flag.
|
||
//unload it and then proceed to reload it
|
||
//this will take care of situations where a half baked clusdb
|
||
//gets loaded because of failures
|
||
Status = DmRollbackRegistry();
|
||
if (Status != ERROR_SUCCESS)
|
||
{
|
||
//we ignore the error
|
||
Status = ERROR_SUCCESS;
|
||
}
|
||
RegCloseKey(DmpRoot);
|
||
Status = DmpUnloadHive();
|
||
|
||
if (Status != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmInitialize: DmpUnloadHive failed, Status=%1!u!\n",
|
||
Status);
|
||
goto FnExit;
|
||
}
|
||
goto LoadClusterDatabase;
|
||
}
|
||
|
||
if (Status != ERROR_SUCCESS) {
|
||
CL_UNEXPECTED_ERROR(Status);
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Create the registry watcher thread
|
||
//
|
||
Status = DmpStartFlusher();
|
||
if (Status != ERROR_SUCCESS) {
|
||
goto FnExit;
|
||
}
|
||
//
|
||
// Open the cluster keys
|
||
//
|
||
Status = DmpOpenKeys(MAXIMUM_ALLOWED);
|
||
if (Status != ERROR_SUCCESS) {
|
||
CL_UNEXPECTED_ERROR( Status );
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
return(Status);
|
||
|
||
}//DmInitialize
|
||
|
||
|
||
DWORD
|
||
DmpRegistryFlusher(
|
||
IN LPVOID lpThreadParameter
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Registry watcher thread for explicitly flushing changes.
|
||
|
||
Arguments:
|
||
|
||
lpThreadParameter - not used
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD Status;
|
||
HANDLE hEvent;
|
||
HANDLE hTimer;
|
||
HANDLE WaitArray[4];
|
||
LARGE_INTEGER DueTime;
|
||
BOOL Dirty = FALSE;
|
||
|
||
//
|
||
// Create a notification event and a delayed timer for lazy flushing.
|
||
//
|
||
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||
if (hEvent == NULL) {
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmpRegistryFlusher couldn't create notification event %1!d!\n",
|
||
Status);
|
||
goto error_exit1;
|
||
}
|
||
|
||
hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
|
||
if (hTimer == NULL) {
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmpRegistryFlusher couldn't create notification event %1!d!\n",
|
||
Status);
|
||
goto error_exit2;
|
||
}
|
||
|
||
WaitArray[0] = hDmpRegistryEvent;
|
||
WaitArray[1] = hEvent;
|
||
WaitArray[2] = hTimer;
|
||
WaitArray[3] = hDmpRegistryRestart;
|
||
|
||
while (TRUE) {
|
||
//
|
||
// Set up a registry notification on DmpRoot. We acquire the lock here to
|
||
// make sure that rollback or install is not messing with the database
|
||
// while we are trying to get a notification.
|
||
//
|
||
ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot);
|
||
Status = RegNotifyChangeKeyValue(DmpRoot,
|
||
TRUE,
|
||
REG_LEGAL_CHANGE_FILTER,
|
||
hEvent,
|
||
TRUE);
|
||
RELEASE_LOCK(gLockDmpRoot);
|
||
if (Status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmpRegistryFlusher couldn't register for notifications %1!d!\n",
|
||
Status);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Wait for something to happen.
|
||
//
|
||
Status = WaitForMultipleObjects(4,
|
||
WaitArray,
|
||
FALSE,
|
||
(DWORD)-1);
|
||
switch (Status) {
|
||
case 0:
|
||
ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: got 0\r\n");
|
||
//
|
||
// We have been asked to stop, clean up and exit
|
||
//
|
||
Status = ERROR_SUCCESS;
|
||
if (Dirty) {
|
||
//
|
||
// Make sure any changes that we haven't gotten around to flushing
|
||
// get flushed now.
|
||
//
|
||
DmCommitRegistry();
|
||
}
|
||
ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: exiting\r\n");
|
||
goto error_exit3;
|
||
break;
|
||
|
||
case 1:
|
||
//
|
||
// A registry change has occurred. Set our timer to
|
||
// go off in 5 seconds. At that point we will do the
|
||
// actual flush.
|
||
//
|
||
//ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: got 1\r\n");
|
||
|
||
DueTime.QuadPart = -5 * 10 * 1000 * 1000;
|
||
if (!SetWaitableTimer(hTimer,
|
||
&DueTime,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
FALSE)) {
|
||
//
|
||
// Some error occurred, go ahead and flush now.
|
||
//
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmpRegistryFlusher failed to set lazy flush timer %1!d!\n",
|
||
Status);
|
||
#if DBG
|
||
CL_ASSERT(FALSE);
|
||
#endif
|
||
DmCommitRegistry();
|
||
Dirty = FALSE;
|
||
} else {
|
||
Dirty = TRUE;
|
||
}
|
||
break;
|
||
|
||
case 2:
|
||
//
|
||
// The lazy flush timer has gone off, commit the registry now.
|
||
//
|
||
//ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: got 2\r\n");
|
||
DmCommitRegistry();
|
||
Dirty = FALSE;
|
||
break;
|
||
|
||
case 3:
|
||
//
|
||
// DmpRoot has been changed, simply restart the loop with the new handle.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: restarting\n");
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// Something very odd has happened
|
||
//
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmpRegistryFlusher got error %1!d! from WaitForMultipleObjects\n",
|
||
Status);
|
||
goto error_exit3;
|
||
}
|
||
}
|
||
|
||
error_exit3:
|
||
CloseHandle(hTimer);
|
||
|
||
error_exit2:
|
||
CloseHandle(hEvent);
|
||
|
||
error_exit1:
|
||
if (Status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmpRegistryFlusher exiting abnormally, status %1!d!\n",
|
||
Status);
|
||
}
|
||
return(Status);
|
||
}
|
||
|
||
|
||
DWORD
|
||
DmJoin(
|
||
IN RPC_BINDING_HANDLE RpcBinding,
|
||
OUT DWORD *StartSeq
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Performs the join and synchronization process for the
|
||
database manager.
|
||
|
||
Arguments:
|
||
|
||
RpcBinding - Supplies an RPC binding handle to the Join Master
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD Status;
|
||
DWORD GumSequence;
|
||
DWORD CurrentSequence;
|
||
|
||
|
||
//
|
||
// Register our update handler.
|
||
//
|
||
GumReceiveUpdates(TRUE,
|
||
GumUpdateRegistry,
|
||
DmpUpdateHandler,
|
||
DmWriteToQuorumLog,
|
||
sizeof(DmGumDispatchTable)/sizeof(GUM_DISPATCH_ENTRY),
|
||
DmGumDispatchTable,
|
||
NULL);
|
||
|
||
retry:
|
||
CurrentSequence = DmpGetRegistrySequence();
|
||
|
||
Status = GumBeginJoinUpdate(GumUpdateRegistry, &GumSequence);
|
||
if (Status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] GumBeginJoinUpdate failed %1!d!\n",
|
||
Status);
|
||
return(Status);
|
||
}
|
||
/*
|
||
if (CurrentSequence == GumSequence) {
|
||
//
|
||
// Our registry sequence already matches. No need to slurp
|
||
// down a new copy.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[DM] DmJoin: registry database is up-to-date\n");
|
||
} else
|
||
*/
|
||
//SS: always get the database irrespective of the sequence numbers
|
||
//this is because transactions may be lost in the log file due
|
||
//to the fact that it is not write through and because of certain
|
||
//race conditions in down notifications vs gum failure conditions.
|
||
{
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[DM] DmJoin: getting new registry database\n");
|
||
Status = DmpSyncDatabase(RpcBinding, NULL);
|
||
if (Status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmJoin: DmpSyncDatabase failed %1!d!\n",
|
||
Status);
|
||
return(Status);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Issue GUM join update
|
||
//
|
||
Status = GumEndJoinUpdate(GumSequence,
|
||
GumUpdateRegistry,
|
||
DmUpdateJoin,
|
||
0,
|
||
NULL);
|
||
if (Status == ERROR_CLUSTER_DATABASE_SEQMISMATCH) {
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] GumEndJoinUpdate with sequence %1!d! failed with a sequence mismatch\n",
|
||
GumSequence);
|
||
goto retry;
|
||
} else if (Status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] GumEndJoinUpdate with sequence %1!d! failed with status %2!d!\n",
|
||
GumSequence,
|
||
Status);
|
||
return(Status);
|
||
}
|
||
|
||
*StartSeq = GumSequence;
|
||
|
||
return(ERROR_SUCCESS);
|
||
|
||
} // DmJoin
|
||
|
||
|
||
/*
|
||
DWORD
|
||
DmFormNewCluster(
|
||
VOID
|
||
)
|
||
{
|
||
DWORD Status;
|
||
|
||
|
||
//
|
||
// Set the current GUM sequence to be one more than the one in the registry.
|
||
//
|
||
// SS: this will be the one to be used for the next gum transaction,
|
||
// it should be one than the current as the logger discards the first of
|
||
// every record the same transaction number to resolve changes made when the
|
||
// locker/logger node dies in the middle of a transaction
|
||
GumSetCurrentSequence(GumUpdateRegistry, (DmpGetRegistrySequence()+1));
|
||
|
||
return(ERROR_SUCCESS);
|
||
|
||
} // DmFormNewCluster
|
||
|
||
*/
|
||
|
||
DWORD
|
||
DmFormNewCluster(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the gum sequence number from the registry before
|
||
logs are unrolled and prepares the quorum object for quorum logging.
|
||
It also hooks events for node up/down notifications.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
A Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwError=ERROR_SUCCESS;
|
||
|
||
//
|
||
// Set the current GUM sequence to be one more than the one in the registry.
|
||
//
|
||
// SS: this will be the one to be used for the next gum transaction,
|
||
// it should be one than the current as the logger discards the first of
|
||
// every record the same transaction number to resolve changes made when the
|
||
// locker/logger node dies in the middle of a transaction
|
||
GumSetCurrentSequence(GumUpdateRegistry, (DmpGetRegistrySequence()+1));
|
||
|
||
//
|
||
// Register our update handler.
|
||
//
|
||
GumReceiveUpdates(FALSE,
|
||
GumUpdateRegistry,
|
||
DmpUpdateHandler,
|
||
DmWriteToQuorumLog,
|
||
sizeof(DmGumDispatchTable)/sizeof(GUM_DISPATCH_ENTRY),
|
||
DmGumDispatchTable,
|
||
NULL);
|
||
|
||
//hook the callback for node related notification with the event processor
|
||
if (dwError = DmpHookEventHandler())
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmUpdateFormNewCluster: DmpHookEventHandler failed 0x!08lx!\r\n",
|
||
dwError);
|
||
goto FnExit;
|
||
};
|
||
|
||
//get the quorum resource and hook the callback for notification on quorum resource
|
||
if (dwError = DmpHookQuorumNotify())
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmUpdateFormNewCluster: DmpHookQuorumNotify failed 0x%1!08lx!\r\n",
|
||
dwError);
|
||
goto FnExit;
|
||
};
|
||
|
||
|
||
//SS: if this procedure is successfully completed gpQuoResource is NON NULL.
|
||
FnExit:
|
||
|
||
return(dwError);
|
||
|
||
} // DmUpdateFormNewCluster
|
||
|
||
DWORD
|
||
DmUpdateFormNewCluster(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine updates the cluster registry after the quorum resource has
|
||
been arbitrated as part of forming a new cluster. The database manager
|
||
is expected to read logs or do whatever it needs to update the current
|
||
state of the registry - presumably using logs that are written to the
|
||
quorum resource. This implies that the quorum resource represents some
|
||
form of stable storage.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
A Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwError=ERROR_SUCCESS;
|
||
BOOL bAreAllNodesUp = TRUE; //assume all nodes are up
|
||
|
||
|
||
//since we havent been logging as yet, take a checkpoint
|
||
if (ghQuoLog)
|
||
{
|
||
//get a checkpoint database
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[DM] DmUpdateFormNewCluster - taking a checkpoint\r\n");
|
||
//
|
||
// Chittur Subbaraman (chitturs) - 6/3/99
|
||
//
|
||
// Make sure the gLockDmpRoot is held before LogCheckPoint is called
|
||
// so as to maintain the ordering between this lock and the log lock.
|
||
//
|
||
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
|
||
|
||
dwError = LogCheckPoint(ghQuoLog, TRUE, NULL, 0);
|
||
|
||
RELEASE_LOCK(gLockDmpRoot);
|
||
|
||
if (dwError != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmUpdateFormNewCluster - Failed to take a checkpoint in the log file\r\n");
|
||
CL_UNEXPECTED_ERROR(dwError);
|
||
}
|
||
|
||
}
|
||
|
||
//if all nodes are not up, turn quorum logging on
|
||
if ((dwError = OmEnumObjects(ObjectTypeNode, DmpNodeObjEnumCb, &bAreAllNodesUp, NULL))
|
||
!= ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmUpdateFormNewCluster : OmEnumObjects returned 0x%1!08lx!\r\n",
|
||
dwError);
|
||
goto FnExit;
|
||
}
|
||
|
||
if (!bAreAllNodesUp)
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[DM] DmUpdateFormNewCluster - some node down\r\n");
|
||
gbIsQuoLoggingOn = TRUE;
|
||
}
|
||
|
||
//add a timer to monitor disk space, should be done after we have formed.
|
||
ghDiskManTimer = CreateWaitableTimer(NULL, FALSE, NULL);
|
||
|
||
if (!ghDiskManTimer)
|
||
{
|
||
CL_LOGFAILURE(dwError = GetLastError());
|
||
goto FnExit;
|
||
}
|
||
|
||
AddTimerActivity(ghDiskManTimer, DISKSPACE_MANAGE_INTERVAL, 1, DmpDiskManage, NULL);
|
||
|
||
gbDmInited = TRUE;
|
||
|
||
FnExit:
|
||
return (dwError);
|
||
} // DmFormNewCluster
|
||
|
||
|
||
/****
|
||
@func DWORD | DmPauseDiskManTimer| The disk manager timer activity to monitor
|
||
space on the quorum disk is set to a puased state.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success. Else returns the error code.
|
||
|
||
@comm This is called while the quorum resource is being changed.
|
||
|
||
@xref <f DmRestartDiskManTimer>
|
||
****/
|
||
DWORD DmPauseDiskManTimer()
|
||
{
|
||
DWORD dwError=ERROR_SUCCESS;
|
||
|
||
if (ghDiskManTimer)
|
||
dwError = PauseTimerActivity(ghDiskManTimer);
|
||
return(dwError);
|
||
}
|
||
|
||
/****
|
||
@func DWORD | DmRestartDiskManTimer| This disk manager activity to monitor
|
||
space on the quorum disk is set back to activated state.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success. Else returns the error code.
|
||
|
||
@comm This is called after the quorum resource has been changed.
|
||
|
||
@xref <f DmPauseDiskManTimer>
|
||
****/
|
||
DWORD DmRestartDiskManTimer()
|
||
{
|
||
DWORD dwError=ERROR_SUCCESS;
|
||
if (ghDiskManTimer)
|
||
dwError = UnpauseTimerActivity(ghDiskManTimer);
|
||
return(dwError);
|
||
}
|
||
/****
|
||
@func DWORD | DmRollChanges| This waits for the quorum resource to come online at
|
||
initialization when a cluster is being formed. The changes in the quorum
|
||
log file are applied to the local cluster database.
|
||
|
||
@rdesc Returns ERROR_SUCCESS on success. Else returns the error code.
|
||
|
||
@comm This allows for partitions in time.
|
||
|
||
@xref
|
||
****/
|
||
DWORD DmRollChanges()
|
||
{
|
||
|
||
DWORD dwError=ERROR_SUCCESS;
|
||
|
||
|
||
//before applying the changes validate that this quorum resource is the real one
|
||
if ((dwError = DmpChkQuoTombStone()) != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmRollChanges: DmpChkQuoTombStone() failed 0x%1!08lx!\r\n",
|
||
dwError);
|
||
goto FnExit;
|
||
|
||
}
|
||
if ((dwError = DmpApplyChanges()) != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmRollChanges: DmpApplyChanges() failed 0x%1!08lx!\r\n",
|
||
dwError);
|
||
goto FnExit;
|
||
}
|
||
|
||
//ss: this is here since lm doesnt know about the ownership of quorum
|
||
//disks today
|
||
//call DmpCheckSpace
|
||
if ((dwError = DmpCheckDiskSpace()) != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmRollChanges: DmpCheckDiskSpace() failed 0x%1!08lx!\r\n",
|
||
dwError);
|
||
goto FnExit;
|
||
}
|
||
|
||
FnExit:
|
||
return(dwError);
|
||
}
|
||
|
||
|
||
|
||
DWORD DmShutdown()
|
||
{
|
||
DWORD dwError;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[Dm] DmShutdown\r\n");
|
||
|
||
//this will close the timer handle
|
||
if (ghDiskManTimer) RemoveTimerActivity(ghDiskManTimer);
|
||
|
||
if (gpQuoResource)
|
||
{
|
||
// DmFormNewCluster() completed
|
||
//
|
||
// Deregister from any further GUM updates
|
||
//
|
||
//GumIgnoreUpdates(GumUpdateRegistry, DmpUpdateHandler);
|
||
}
|
||
//unhook the callback for notification on quorum resource
|
||
if (dwError = DmpUnhookQuorumNotify())
|
||
{
|
||
//just log the error as we are shutting down
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmShutdown: DmpUnhookQuorumNotify failed 0x%1!08lx!\r\n",
|
||
dwError);
|
||
|
||
}
|
||
|
||
|
||
//if the quorum log is open close it
|
||
if (ghQuoLog)
|
||
{
|
||
LogClose(ghQuoLog);
|
||
ghQuoLog = NULL;
|
||
//dont try and log after this
|
||
gbIsQuoLoggingOn = FALSE;
|
||
}
|
||
|
||
//close the event created for notification of the quorum resource to
|
||
//go online
|
||
if (ghQuoLogOpenEvent)
|
||
{
|
||
//wait any thread blocked on this
|
||
SetEvent(ghQuoLogOpenEvent);
|
||
CloseHandle(ghQuoLogOpenEvent);
|
||
ghQuoLogOpenEvent = NULL;
|
||
}
|
||
|
||
//
|
||
// Shut down the registry flusher thread.
|
||
//
|
||
DmpShutdownFlusher();
|
||
|
||
return(dwError);
|
||
}
|
||
|
||
|
||
DWORD
|
||
DmpStartFlusher(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Starts up a new registry flusher thread.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD ThreadId;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,"[DM] DmpStartFlusher: Entry\r\n");
|
||
if (!hDmpRegistryFlusher)
|
||
{
|
||
hDmpRegistryEvent = CreateEventW(NULL,FALSE,FALSE,NULL);
|
||
if (hDmpRegistryEvent == NULL) {
|
||
return(GetLastError());
|
||
}
|
||
hDmpRegistryRestart = CreateEventW(NULL,FALSE,FALSE,NULL);
|
||
if (hDmpRegistryRestart == NULL) {
|
||
CloseHandle(hDmpRegistryEvent);
|
||
return(GetLastError());
|
||
}
|
||
hDmpRegistryFlusher = CreateThread(NULL,
|
||
0,
|
||
DmpRegistryFlusher,
|
||
NULL,
|
||
0,
|
||
&ThreadId);
|
||
if (hDmpRegistryFlusher == NULL) {
|
||
CloseHandle(hDmpRegistryRestart);
|
||
CloseHandle(hDmpRegistryEvent);
|
||
return(GetLastError());
|
||
}
|
||
ClRtlLogPrint(LOG_NOISE,"[DM] DmpStartFlusher: thread created\r\n");
|
||
|
||
}
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
VOID
|
||
DmpShutdownFlusher(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Cleanly shutsdown the registry flusher thread.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,"[DM] DmpShutdownFlusher: Entry\r\n");
|
||
|
||
if (hDmpRegistryFlusher) {
|
||
ClRtlLogPrint(LOG_NOISE,"[DM] DmpShutdownFlusher: Setting event\r\n");
|
||
SetEvent(hDmpRegistryEvent);
|
||
WaitForSingleObject(hDmpRegistryFlusher, INFINITE);
|
||
CloseHandle(hDmpRegistryFlusher);
|
||
hDmpRegistryFlusher = NULL;
|
||
CloseHandle(hDmpRegistryEvent);
|
||
CloseHandle(hDmpRegistryRestart);
|
||
hDmpRegistryEvent = NULL;
|
||
hDmpRegistryRestart = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
DmpRestartFlusher(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Restarts the registry flusher thread if DmpRoot is being changed.
|
||
|
||
N.B. In order for this to work correctly, gLockDmpRoot MUST be held!
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,"[DM] DmpRestartFlusher: Entry\r\n");
|
||
#if NO_SHARED_LOCKS
|
||
CL_ASSERT(HandleToUlong(gLockDmpRoot.OwningThread) == GetCurrentThreadId());
|
||
#else
|
||
CL_ASSERT(HandleToUlong(gLockDmpRoot.ExclusiveOwnerThread) == GetCurrentThreadId());
|
||
#endif
|
||
SetEvent(hDmpRegistryRestart);
|
||
}
|
||
|
||
DWORD
|
||
DmUpdateJoinCluster(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called after a node has successfully joined a cluster.
|
||
It allows the DM to hook callbacks for node up/down notifications and for
|
||
quorum resource change notification.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
A Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwError=ERROR_SUCCESS;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[DM] DmUpdateJoinCluster: Begin.\r\n");
|
||
|
||
//hook the notification for node up/down so we can keep track of whether logging
|
||
//should be on or off.
|
||
if (dwError = DmpHookEventHandler())
|
||
{
|
||
//BUGBUG SS: do we log this or return this error code
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmUpdateJoinCluster: DmpHookEventHandler failed 0x%1!08lx!\r\n",
|
||
dwError);
|
||
|
||
}
|
||
|
||
//hook the callback for notification on quorum resource
|
||
if (dwError = DmpHookQuorumNotify())
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmUpdateJoinCluster: DmpHookQuorumNotify failed 0x%1!08lx!\r\n",
|
||
dwError);
|
||
goto FnExit;
|
||
}
|
||
|
||
if ((dwError = DmpCheckDiskSpace()) != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmUpdateJoinCluster: DmpCheckDiskSpace() failed 0x%1!08lx!\r\n",
|
||
dwError);
|
||
goto FnExit;
|
||
}
|
||
|
||
//add a timer to monitor disk space, should be done after we have joined.
|
||
ghDiskManTimer = CreateWaitableTimer(NULL, FALSE, NULL);
|
||
|
||
if (!ghDiskManTimer)
|
||
{
|
||
CL_LOGFAILURE(dwError = GetLastError());
|
||
goto FnExit;
|
||
}
|
||
|
||
//register a periodic timer
|
||
AddTimerActivity(ghDiskManTimer, DISKSPACE_MANAGE_INTERVAL, 1, DmpDiskManage, NULL);
|
||
|
||
gbDmInited = TRUE;
|
||
|
||
FnExit:
|
||
return(dwError);
|
||
} // DmUpdateJoinCluster
|
||
|
||
|
||
DWORD
|
||
DmpOpenKeys(
|
||
IN REGSAM samDesired
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Opens all the standard cluster registry keys. If any of the
|
||
keys are already opened, they will be closed and reopened.
|
||
|
||
Arguments:
|
||
|
||
samDesired - Supplies the access that the keys will be opened with.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
Win32 error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD i;
|
||
DWORD Status;
|
||
|
||
DmClusterParametersKey = DmGetRootKey( MAXIMUM_ALLOWED );
|
||
if ( DmClusterParametersKey == NULL ) {
|
||
Status = GetLastError();
|
||
CL_UNEXPECTED_ERROR(Status);
|
||
return(Status);
|
||
}
|
||
|
||
for (i=0;
|
||
i<sizeof(DmpKeyTable)/sizeof(DMP_KEY_DEF);
|
||
i++) {
|
||
|
||
*DmpKeyTable[i].pKey = DmOpenKey(DmClusterParametersKey,
|
||
DmpKeyTable[i].Name,
|
||
samDesired);
|
||
if (*DmpKeyTable[i].pKey == NULL) {
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] Failed to open key %1!ws!, status %2!u!\n",
|
||
DmpKeyTable[i].Name,
|
||
Status);
|
||
CL_UNEXPECTED_ERROR( Status );
|
||
return(Status);
|
||
}
|
||
}
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
VOID
|
||
DmpInvalidateKeys(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Invalidates all open cluster registry keys.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PDMKEY Key;
|
||
|
||
ListEntry = KeyList.Flink;
|
||
while (ListEntry != &KeyList) {
|
||
Key = CONTAINING_RECORD(ListEntry,
|
||
DMKEY,
|
||
ListEntry);
|
||
if (!Key->hKey)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] DmpInvalidateKeys %1!ws! Key was deleted since last reopen but not closed\n",
|
||
Key->Name);
|
||
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[DM] THIS MAY BE A KEY LEAK !!\r\n");
|
||
}
|
||
else
|
||
{
|
||
RegCloseKey(Key->hKey);
|
||
Key->hKey = NULL;
|
||
}
|
||
ListEntry = ListEntry->Flink;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
DmpReopenKeys(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reopens all the keys that were invalidated by DmpInvalidateKeys
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PDMKEY Key;
|
||
DWORD Status;
|
||
|
||
ListEntry = KeyList.Flink;
|
||
while (ListEntry != &KeyList) {
|
||
Key = CONTAINING_RECORD(ListEntry,
|
||
DMKEY,
|
||
ListEntry);
|
||
CL_ASSERT(Key->hKey == NULL);
|
||
Status = RegOpenKeyEx(DmpRoot,
|
||
Key->Name,
|
||
0,
|
||
Key->GrantedAccess,
|
||
&Key->hKey);
|
||
if (Status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_CRITICAL,"[DM] Could not reopen key %1!ws! error %2!d!\n",Key->Name,Status);
|
||
// if the error is file not found, then the key was deleted while the handle
|
||
// was open. Set the key to NULL
|
||
// If the key is used after delete, it should be validated
|
||
if (Status == ERROR_FILE_NOT_FOUND)
|
||
Key->hKey = NULL;
|
||
else
|
||
CL_UNEXPECTED_ERROR(Status);
|
||
|
||
}
|
||
ListEntry = ListEntry->Flink;
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
DmpGetRegistrySequence(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the current registry sequence stored in the registry.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
The current registry sequence.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD Length;
|
||
DWORD Type;
|
||
DWORD Sequence;
|
||
DWORD Status;
|
||
|
||
Length = sizeof(Sequence);
|
||
Status = RegQueryValueExW(DmpRoot,
|
||
CLUSREG_NAME_CLUS_REG_SEQUENCE,
|
||
0,
|
||
&Type,
|
||
(LPBYTE)&Sequence,
|
||
&Length);
|
||
if (Status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_UNUSUAL, "[DM] DmpGetRegistrySequence failed %1!u!\n",Status);
|
||
Sequence = 0;
|
||
}
|
||
|
||
return(Sequence);
|
||
}
|
||
|
||
|
||
DWORD DmWaitQuorumResOnline()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Waits for quorum resource to come online. Used for quorum logging.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
returns ERROR_SUCCESS - if the online event is signaled and the quorum
|
||
notification callback is called. Else returns the wait status.
|
||
|
||
--*/
|
||
{
|
||
|
||
//wait for the quorum resource to go online
|
||
//give it a minute
|
||
DWORD dwError;
|
||
|
||
if (ghQuoLogOpenEvent)
|
||
//dwError = WaitForSingleObject(ghQuoOnlineEvent, 60000*10);
|
||
dwError = WaitForSingleObject(ghQuoLogOpenEvent, INFINITE);
|
||
|
||
|
||
switch(dwError)
|
||
{
|
||
case WAIT_OBJECT_0:
|
||
//everything is fine
|
||
dwError = ERROR_SUCCESS;
|
||
break;
|
||
|
||
case WAIT_TIMEOUT:
|
||
//couldnt roll the changes
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmRollChanges: Timed out waiting on dmInitEvent\r\n");
|
||
break;
|
||
|
||
case WAIT_FAILED:
|
||
CL_ASSERT(dwError != WAIT_FAILED);
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[DM] DmRollChanges: wait on dmInitEventfailed failed 0x%1!08lx!\r\n",
|
||
GetLastError());
|
||
break;
|
||
}
|
||
return(dwError);
|
||
}
|
||
|
||
VOID DmShutdownUpdates(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Shutdown DM GUM updates.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
--*/
|
||
{
|
||
gbDmpShutdownUpdates = TRUE;
|
||
}
|
||
|