|
|
/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name: main.c
Abstract: This is the main thread for the File Replication Service.
Author: Billy J. Fuller 20-Mar-1997
Environment User mode winnt
--*/
#include <ntreppch.h>
#pragma hdrstop
#include <frs.h>
#include <tablefcn.h>
#include <perrepsr.h>
#include <objbase.h>
#define INITGUID
#include "frstrace.h"
PCHAR LatestChanges[] = {
"Latest changes:", "Server 2003 01-07-2003", #ifdef DS_FREE
"DS_FREE mode", #endif DS_FREE
NULL };
HANDLE ShutDownEvent; HANDLE ShutDownComplete; HANDLE DataBaseEvent; HANDLE JournalEvent; HANDLE ChgOrdEvent; HANDLE ReplicaEvent; HANDLE CommEvent; HANDLE DsPollEvent; HANDLE DsShutDownComplete; //
// Event to signal the end of freeze operation.
//
HANDLE FrsThawEvent; BOOL FrsFrozenForBackup = FALSE;
//
// Event to signal that there are no installs in
// progress.
//
HANDLE FrsNoInstallsInProgressEvent;
PWCHAR ServerPrincName; BOOL IsAMember = FALSE; BOOL IsADc = FALSE; BOOL IsAPrimaryDc = FALSE; BOOL EventLogIsRunning = FALSE; BOOL RpcssIsRunning = FALSE; BOOL RunningAsAService = TRUE; BOOL NoDs = FALSE; BOOL FrsIsShuttingDown = FALSE; BOOL FrsScmRequestedShutdown = FALSE; BOOL FrsIsAsserting = FALSE;
//
// Require mutual authentication
//
BOOL MutualAuthenticationIsEnabled; BOOL MutualAuthenticationIsEnabledAndRequired;
//
// Directory and file filter lists from registry.
//
PWCHAR RegistryFileExclFilterList; PWCHAR RegistryFileInclFilterList;
PWCHAR RegistryDirExclFilterList; PWCHAR RegistryDirInclFilterList;
extern CRITICAL_SECTION CritSec_pValidPartnerTableStruct; extern CRITICAL_SECTION OldValidPartnerTableStructListHeadLock;
//
// Synchronize the shutdown thread with the service controller
//
CRITICAL_SECTION ServiceLock;
//
// Synchronize initialization
//
CRITICAL_SECTION MainInitLock;
//
// Convert the ANSI ArgV into a UNICODE ArgV
//
PWCHAR *WideArgV;
//
// Process Handle
//
HANDLE ProcessHandle;
//
// Working path / DB Log path
//
PWCHAR WorkingPath; PWCHAR DbLogPath;
//
// Database directories (UNICODE and ASCII)
//
PWCHAR JetPath; PWCHAR JetFile; PWCHAR JetFileCompact; PWCHAR JetSys; PWCHAR JetTemp; PWCHAR JetLog;
PCHAR JetPathA; PCHAR JetFileA; PCHAR JetFileCompactA; PCHAR JetSysA; PCHAR JetTempA; PCHAR JetLogA;
//
// Limit the amount of staging area used (in kilobytes). This is
// a soft limit, the actual usage may be higher.
//
DWORD StagingLimitInKb;
//
// Default staging limit in kb to be assigned to a new staging area.
//
DWORD DefaultStagingLimitInKb;
//
// Max number replica sets and Jet Sessions allowed.
//
ULONG MaxNumberReplicaSets; ULONG MaxNumberJetSessions;
//
// Maximum number of outbound changeorders allowed outstanding per connection.
//
ULONG MaxOutLogCoQuota; //
// If TRUE then try to preserve existing file OIDs whenever possible.
// -- See Bug 352250 for why this is a risky thing to do.
//
BOOL PreserveFileOID;
//
// Limits on how many time and for how long we will continue to retry a
// change order when the parent is missing.
//
ULONG MaxCoRetryTimeoutMinutes; ULONG MaxCoRetryTimeoutCount;
//
// Major/minor (see frs.h)
//
ULONG NtFrsMajor = NTFRS_MAJOR; ULONG NtFrsMinor = NTFRS_MINOR;
ULONG NtFrsStageMajor = NTFRS_STAGE_MAJOR; ULONG NtFrsStageMinor = NTFRS_STAGE_MINOR_3;
ULONG NtFrsCommMinor = NTFRS_COMM_MINOR_8;
ULONG NtFrsPerfCounterVer = NTFRS_PERF_COUNTER_VER_1;
PCHAR NtFrsModule = __FILE__; PCHAR NtFrsDate = __DATE__; PCHAR NtFrsTime = __TIME__;
//
// Shutdown timeout
//
ULONG ShutDownTimeOut = DEFAULT_SHUTDOWN_TIMEOUT;
//
// A useful thing to have around
//
WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 2]; PWCHAR ComputerDnsName; PWCHAR ServiceLongName;
//
// The rpc server may reference this table as soon as the rpc interface
// is registered. Make sure it is setup.
//
extern PGEN_TABLE ReplicasByGuid;
extern BOOL LockedOutlogCleanup;
//
// The staging area table is references early in the startup process
//
extern PGEN_TABLE StagingAreaTable; extern CRITICAL_SECTION StagingAreaCleanupLock;
//
// This table is used to keep contexts across multiple calls from ntfrsutl.exe
//
extern PGEN_TABLE FrsInfoContextTable;
PGEN_TABLE CompressionTable;
extern PGEN_TABLE ReparseTagTable;
//
// Size of buffer to use when enumerating directories. Actual memory
// usage will be #levels * SizeOfBuffer.
//
LONG EnumerateDirectorySizeInBytes;
BOOL MainInitHasRun;
//
// Do not accept stop control unless the service is in SERVICE_RUNNING state.
// This prevents the service from getting confused when a stop is called
// while the service is starting.
//
SERVICE_STATUS ServiceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_START_PENDING, // SERVICE_ACCEPT_STOP |
// SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN, 0, 0, 0, 60*1000 };
//
// Supported compression formats.
//
//
// This is the compression format for uncompressed data.
//
DEFINE_GUID ( /* 00000000-0000-0000-0000-000000000000 */ FrsGuidCompressionFormatNone, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );
//
// This is the compression format for data compressed using NTFS's LZNT1 compression
// routines.
//
DEFINE_GUID ( /* 64d2f7d2-2695-436d-8830-8d3c58701e15 */ FrsGuidCompressionFormatLZNT1, 0x64d2f7d2, 0x2695, 0x436d, 0x88, 0x30, 0x8d, 0x3c, 0x58, 0x70, 0x1e, 0x15 );
//
// Fixed guid for the dummy cxtion (aka GhostCxtion) assigned to orphan remote
// change orders whose inbound cxtion has been deleted from the DS but they
// have already past the fetching state and can finish without the real cxtion
// coming back. No authentication checks are made for this dummy cxtion.
//
DEFINE_GUID ( /* b9d307a7-a140-4405-991e-281033f03309 */ FrsGuidGhostCxtion, 0xb9d307a7, 0xa140, 0x4405, 0x99, 0x1e, 0x28, 0x10, 0x33, 0xf0, 0x33, 0x09 );
DEFINE_GUID ( /* 3fe2820f-3045-4932-97fe-00d10b746dbf */ FrsGhostJoinGuid, 0x3fe2820f, 0x3045, 0x4932, 0x97, 0xfe, 0x00, 0xd1, 0x0b, 0x74, 0x6d, 0xbf );
//
// Static Ghost cxtion structure. This cxtion is assigned to orphan remote change
// orders in the inbound log whose cxtion is deleted from the DS but who have already
// past the fetching state and do not need the cxtion to complete processing. No
// authentication checks are made for this dummy cxtion.
//
PCXTION FrsGhostCxtion;
SERVICE_STATUS_HANDLE ServiceStatusHandle = NULL;
VOID InitializeEventLog( VOID );
DWORD FrsSetServiceFailureAction( VOID );
VOID FrsRpcInitializeAccessChecks( VOID );
BOOL FrsSetupPrivileges ( VOID );
VOID CfgRegAdjustTuningDefaults( VOID );
VOID CommInitializeCommSubsystem( VOID );
VOID SndCsInitialize( VOID );
VOID SndCsUnInitialize( VOID );
VOID SndCsShutDown( VOID );
VOID DbgPrintAllStats( VOID );
// FRS Capacity Planning
//
#define RESOURCE_NAME L"MofResourceName"
#define IMAGE_PATH L"ntfrs.exe"
DWORD FrsWmiEventTraceFlag = FALSE; TRACEHANDLE FrsWmiTraceRegistrationHandle = (TRACEHANDLE) 0; TRACEHANDLE FrsWmiTraceLoggerHandle = (TRACEHANDLE) 0;
// This is the FRS control Guid for the group of Guids traced below
//
DEFINE_GUID ( /* 78a8f0b1-290e-4c4c-9720-c7f1ef68ce21 */ FrsControlGuid, 0x78a8f0b1, 0x290e, 0x4c4c, 0x97, 0x20, 0xc7, 0xf1, 0xef, 0x68, 0xce, 0x21 );
// Traceable Guids start here
//
DEFINE_GUID ( /* 2eee6bbf-6665-44cf-8ed7-ceea1d306085 */ FrsTransGuid, 0x2eee6bbf, 0x6665, 0x44cf, 0x8e, 0xd7, 0xce, 0xea, 0x1d, 0x30, 0x60, 0x85 );
TRACE_GUID_REGISTRATION FrsTraceGuids[] = { { & FrsTransGuid, NULL } };
#define FrsGuidCount (sizeof(FrsTraceGuids) / sizeof(TRACE_GUID_REGISTRATION))
//
// Trace initialization / shutdown routines
//
ULONG FrsWmiTraceControlCallback( IN WMIDPREQUESTCODE RequestCode, IN PVOID RequestContext, IN OUT ULONG * InOutBufferSize, IN OUT PVOID Buffer ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { #undef DEBSUB
#define DEBSUB "FrsWmiTraceControlCallback:"
PWNODE_HEADER Wnode = (PWNODE_HEADER) Buffer; ULONG Status; ULONG RetSize;
Status = ERROR_SUCCESS;
switch (RequestCode) {
case WMI_ENABLE_EVENTS: FrsWmiTraceLoggerHandle = GetTraceLoggerHandle(Buffer); FrsWmiEventTraceFlag = TRUE; RetSize = 0; DPRINT1(0, "FrsTraceContextCallback(WMI_ENABLE_EVENTS,0x%08X)\n", FrsWmiTraceLoggerHandle); break;
case WMI_DISABLE_EVENTS: FrsWmiTraceLoggerHandle = (TRACEHANDLE) 0; FrsWmiEventTraceFlag = FALSE; RetSize = 0; DPRINT(0, "FrsWmiTraceContextCallback(WMI_DISABLE_EVENTS)\n"); break;
default: RetSize = 0; Status = ERROR_INVALID_PARAMETER; break; }
*InOutBufferSize = RetSize;
return Status; }
ULONG FrsWmiInitializeTrace( VOID ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { #undef DEBSUB
#define DEBSUB "FrsWmiInitializeTrace:"
ULONG WStatus; HMODULE hModule; WCHAR FileName[MAX_PATH + 1]; DWORD nLen = 0;
hModule = GetModuleHandleW(IMAGE_PATH);
if (hModule != NULL) { nLen = GetModuleFileNameW(hModule, FileName, MAX_PATH); }
if (nLen == 0) { wcscpy(FileName, IMAGE_PATH); }
WStatus = RegisterTraceGuidsW( FrsWmiTraceControlCallback, NULL, & FrsControlGuid, FrsGuidCount, FrsTraceGuids, FileName, RESOURCE_NAME, & FrsWmiTraceRegistrationHandle);
if (!WIN_SUCCESS(WStatus)) { DPRINT4(0, "NTFRS: FrsWmiInitializeTrace(%ws,%ws,%d) returns 0x%08X\n", FileName, RESOURCE_NAME, FrsGuidCount, WStatus); }
return WStatus; }
ULONG FrsWmiShutdownTrace( void ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { #undef DEBSUB
#define DEBSUB "FrsWmiShutdownTrace:"
ULONG WStatus = ERROR_SUCCESS;
UnregisterTraceGuids(FrsWmiTraceRegistrationHandle); return WStatus; }
VOID FrsWmiTraceEvent( IN DWORD WmiEventType, IN DWORD TraceGuid, IN DWORD rtnStatus ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { #undef DEBSUB
#define DEBSUB "FrsWmiTraceEvent:"
struct { EVENT_TRACE_HEADER TraceHeader; DWORD Data; } Wnode;
DWORD WStatus;
if (FrsWmiEventTraceFlag) {
ZeroMemory(&Wnode, sizeof(Wnode));
//
// Set WMI event type
//
Wnode.TraceHeader.Class.Type = (UCHAR) WmiEventType; Wnode.TraceHeader.GuidPtr = (ULONGLONG) FrsTraceGuids[TraceGuid].Guid; Wnode.TraceHeader.Size = sizeof(Wnode); Wnode.TraceHeader.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_GUID_PTR;
WStatus = TraceEvent(FrsWmiTraceLoggerHandle, (PEVENT_TRACE_HEADER) &Wnode);
if (!WIN_SUCCESS(WStatus)) { DPRINT5(0, "FreWmiTraceEvent(%d,%d,%d) = %d,0x%08X\n", WmiEventType, TraceGuid, rtnStatus, WStatus, WStatus); } } }
BOOL WINAPI MainSigHandler( IN DWORD Signal ) /*++
Routine Description: Handle CTRL_BREAK_EVENT and CTRL_C_EVENT by setting the shutdown event.
Arguments: Signal - signal received.
Return Value: Set the ShutDownEvent and return TRUE. --*/ { #undef DEBSUB
#define DEBSUB "MainSigHandler:"
//
// ShutDown on signal CTRL_C_EVENT or CTRL_BREAK_EVENT
//
if ((Signal == CTRL_C_EVENT) || (Signal == CTRL_BREAK_EVENT)) { DPRINT1(0,":S: Signal %s received, shutting down now...\n", (Signal == CTRL_C_EVENT) ? "CTRL_C_EVENT" : "CTRL_BREAK_EVENT");
FrsScmRequestedShutdown = TRUE; FrsIsShuttingDown = TRUE; SetEvent(ShutDownEvent);
return TRUE; }
DPRINT1(0,":S: Signal %d received, not handled\n", Signal); return FALSE; }
ULONG MainSCCheckPointUpdate( IN PVOID pCurrentState ) /*++
Routine Description: This thread repeatedly calls the Service Controller to update the checkpoint and reset the timeout value so that the service controller does not time out waiting for a response. When called in shutdown path the thread exits after a waiting a max "ShutDownTimeOut" # of seconds. All the subsystems might not have shutdown cleanly by this time but we don't want to take for ever to shutdown. This value is picked up from the registry.
Arguments: pCurrentState - Pointer to the value of the current state of the service. Depending on this value the function either waits to shutdown or startup.
Return Value: Exits with STATUS_UNSUCCESSFUL --*/ { #undef DEBSUB
#define DEBSUB "MainSCCheckPointUpdate:"
ULONG Timeout = ShutDownTimeOut; DWORD CheckPoint = 1; DWORD WStatus = ERROR_SUCCESS; DWORD Ret = 0;
if (pCurrentState && *(DWORD *)pCurrentState == SERVICE_STOP_PENDING) { //
// Thread is called in the shutdown path to make sure that FRS exits
//
while (Timeout) { DPRINT2(0, ":S: EXIT COUNTDOWN AT T-%d CheckPoint = %x\n", Timeout, CheckPoint); DEBUG_FLUSH();
if (Timeout < 5) { Sleep(Timeout * 1000); Timeout = 0; } else { Sleep(5 * 1000); //
// Update the status every 5 seconds to get the new checkpoint.
//
WStatus = FrsSetServiceStatus(SERVICE_STOP_PENDING, CheckPoint, (ShutDownTimeOut + 5) *1000, NO_ERROR); if (!WIN_SUCCESS(WStatus)) { //
// Unable to set the service status. Exit process anyways.
//
break; } CheckPoint++; Timeout -= 5; } }
DPRINT(0, ":S: EXIT COUNTDOWN EXPIRED\n"); DEBUG_FLUSH();
EPRINT0(EVENT_FRS_STOPPED_FORCE);
//
// EXIT FOR RESTART
//
// If we are shutting down after taking an assert then don't set the
// service state to stopped. This will cause the service controller to
// restart us if the recorvery option is set. In case of a service controller
// initiated shutwon we want to set the state to stopped so that it does not
// restart us.
//
if (!FrsIsAsserting && FrsScmRequestedShutdown) { FrsSetServiceStatus(SERVICE_STOPPED, CheckPoint, ShutDownTimeOut*1000, NO_ERROR); }
ExitProcess(ERROR_NO_SYSTEM_RESOURCES); FrsFree(pCurrentState); return ERROR_NO_SYSTEM_RESOURCES; } else if (pCurrentState && *(DWORD *)pCurrentState == SERVICE_START_PENDING){ //
// Thread is called in the startup path to make sure that
// service controller does not timeout waiting for the
// service to start up.
//
while (TRUE) { DPRINT1(0, ":S: STARTUP CheckPoint = %x\n",CheckPoint); Sleep(5 * 1000); EnterCriticalSection(&ServiceLock); if (ServiceStatus.dwCurrentState == SERVICE_START_PENDING && !FrsIsShuttingDown) {
ServiceStatus.dwCurrentState = SERVICE_START_PENDING; ServiceStatus.dwCheckPoint = CheckPoint; ServiceStatus.dwWaitHint = DEFAULT_STARTUP_TIMEOUT * 1000; ServiceStatus.dwWin32ExitCode = NO_ERROR; //
// Update the status every 5 seconds to get the new checkpoint.
//
Ret = SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
CheckPoint++; if (!Ret) { //
// Can not set service status. Let the service try to start up
// in the given time. If it does not then the service controller
// time out and stop it.
//
LeaveCriticalSection(&ServiceLock); break; } } else { //
// Service has either already started or it has moved to another state.
//
LeaveCriticalSection(&ServiceLock); break; } LeaveCriticalSection(&ServiceLock); } } FrsFree(pCurrentState); return ERROR_SUCCESS; }
VOID MainStartShutDown( VOID ) /*++
Routine Description: Shutdown what we can.
Arguments: None.
Return Value: None. --*/ { #undef DEBSUB
#define DEBSUB "MainStartShutDown:"
ULONGLONG SecondsRunning;
DPRINT(0, ":S: SHUTDOWN IN PROGRESS...\n"); DEBUG_FLUSH();
//
// Do not restart if it was a service control manager requested shutdown.
//
DebugInfo.Restart = FALSE; if (!FrsScmRequestedShutdown) {
GetSystemTimeAsFileTime((FILETIME *)&SecondsRunning); SecondsRunning /= (10 * 1000 * 1000); SecondsRunning -= DebugInfo.StartSeconds;
//
// Restart the service if it was an FRS triggered shutdown, e.g. malloc
// failed or thread create failed, etc.
// Also restart the service after an assertion failure iff the
// service was able to run for some minutes.
//
// Assertion failures during shutdown won't trigger a restart since this
// test is made before shutdown starts.
//
DebugInfo.Restart = !FrsIsAsserting || (FrsIsAsserting && (DebugInfo.RestartSeconds != 0) && (SecondsRunning >= DebugInfo.RestartSeconds)); }
if (DebugInfo.Restart) { DPRINT(0, ":S: Restart enabled\n"); } else { DPRINT(0, ":S: Restart disabled\n"); } }
VOID MainShutDownComplete( VOID ) /*++
Routine Description: Kick off the exe that will restart the service after a bit
Arguments: None.
Return Value: None. --*/ { #undef DEBSUB
#define DEBSUB "MainShutDownComplete:"
STARTUPINFO StartUpInfo; PROCESS_INFORMATION ProcessInformation;
//
// Done with this service
//
DbgPrintAllStats();
//
// Spawn a new exe if needed
//
if (!RunningAsAService && DebugInfo.Restart) { GetStartupInfo(&StartUpInfo); if (!CreateProcess(NULL, DebugInfo.CommandLine, NULL, NULL, FALSE, 0, // CREATE_NEW_CONSOLE,
NULL, NULL, &StartUpInfo, &ProcessInformation)) { DPRINT1_WS(0, ":S: ERROR - Spawning %ws :", DebugInfo.CommandLine, GetLastError()); } else { DPRINT1(0, ":S: Spawned %ws\n", DebugInfo.CommandLine); } } DPRINT(0,":S: SHUTDOWN COMPLETE\n"); DEBUG_FLUSH();
//
// EXIT FOR RESTART
//
// If restart is desired then simply exit without setting our
// status to SERVICE_STOPPED. The service controller will execute
// our recovery settings which are defaulted to "restart after a
// minute." The exe was restarted above.
//
if (DebugInfo.Restart) { ExitProcess(ERROR_NO_SYSTEM_RESOURCES); } }
ULONG MainFrsShutDown( IN PVOID Ignored ) /*++
Routine Description: Shutdown the service
Arguments: Ignored
Return Value: ERROR_SUCCESS --*/ { #undef DEBSUB
#define DEBSUB "MainFrsShutDown:"
DWORD WStatus; DWORD WaitStatus; HANDLE ExitThreadHandle; DWORD ExitThreadId; PVOID ReplicaKey; PVOID CxtionKey; PREPLICA Replica; PCXTION Cxtion; DWORD CheckUnjoin; DWORD LastCheckUnjoin; DWORD ActiveCoCount, LastCheckActiveCoCount; PDWORD ServiceWaitState = NULL;
//
// How long is shutdown allowed to take?
//
CfgRegReadDWord(FKC_SHUTDOWN_TIMEOUT, NULL, 0, &ShutDownTimeOut); DPRINT1(1,":S: Using %d as ShutDownTimeOut\n", ShutDownTimeOut);
//
// Wait for a shutdown event
//
do { //
// If present, flush the log file every 30 seconds
//
if (DebugInfo.LogFILE) { WaitStatus = WaitForSingleObject(ShutDownEvent, (30 * 1000)); } else { WaitStatus = WaitForSingleObject(ShutDownEvent, INFINITE); } DEBUG_FLUSH(); } while (WaitStatus == WAIT_TIMEOUT);
FrsIsShuttingDown = TRUE;
EPRINT0(EVENT_FRS_STOPPING);
//
// How long is shutdown allowed to take? Re-read so longer time can
// be used for debug dumping when necc.
//
CfgRegReadDWord(FKC_SHUTDOWN_TIMEOUT, NULL, 0, &ShutDownTimeOut); DPRINT1(1,":S: Using %d as ShutDownTimeOut\n", ShutDownTimeOut);
//
// SHUTDOWN
//
MainStartShutDown();
//
// Inform the service controller that we are stopping.
//
// Unless we aren't running as a service, or are
// are running to restart the service, or simply running
// as an exe.
//
if (!FrsIsAsserting) { FrsSetServiceStatus(SERVICE_STOP_PENDING, 0, ShutDownTimeOut*1000, NO_ERROR); }
//
// Kick off a thread that exits in a bit
// Allocate memory for data to be passed to another thread.
//
ServiceWaitState = FrsAlloc(sizeof(DWORD)); *ServiceWaitState = SERVICE_STOP_PENDING; ExitThreadHandle = (HANDLE)CreateThread(NULL, 10000, MainSCCheckPointUpdate, ServiceWaitState, 0, &ExitThreadId);
if (!HANDLE_IS_VALID(ExitThreadHandle)) { ExitProcess(ERROR_NO_SYSTEM_RESOURCES); }
//
// Minimal shutdown - only the ds polling thread
//
if (!MainInitHasRun) { DPRINT(1,":S: \tFast shutdown in progress...\n"); //
// ShutDown rpc
//
DPRINT(1,":S: \tShutting down RPC Server...\n"); ShutDownRpc(); DEBUG_FLUSH();
//
// Ask all the threads to exit
//
DPRINT(1,":S: \tShutting down all the threads...\n"); WStatus = ThSupExitThreadGroup(NULL); DEBUG_FLUSH();
//
// Free the rpc table and princname
//
DPRINT(1,":S: \tFreeing rpc memory...\n"); FrsRpcUnInitialize(); DEBUG_FLUSH();
goto SHUTDOWN_COMPLETE; }
//
// Shutdown the FRS writer COM object.
//
DPRINT(1,":S: \tShutting down FRS writer component...\n"); ShutDownFrsWriter();
//
// ShutDown the delayed command server; don't let change orders
// sit on the various retry queues.
//
DPRINT(1,":S: \tShutting down Delayed Server...\n"); ShutDownDelCs(); DEBUG_FLUSH();
//
// ShutDown the staging file generator
//
DPRINT(1,":S: \tShutting down Staging File Generator...\n"); ShutDownStageCs(); DEBUG_FLUSH();
//
// ShutDown the staging file fetcher
//
DPRINT(1,":S: \tShutting down Staging File Fetch...\n"); ShutDownFetchCs(); DEBUG_FLUSH();
//
// ShutDown the initial sync controller
//
DPRINT(1,":S: \tShutting down Initial Sync Controller...\n"); ShutDownInitSyncCs(); DEBUG_FLUSH();
//
// ShutDown the staging file installer
//
DPRINT(1,":S: \tShutting down Staging File Install...\n"); ShutDownInstallCs(); DEBUG_FLUSH();
//
// Stop local change orders.
//
// Shutdown can be delayed indefinitely if the journal cs continues
// to add local change orders to the change order accept queue.
//
// The remote change orders were stopped by setting FrsIsShuttingDown
// to TRUE above.
//
ReplicaKey = NULL; while (Replica = GTabNextDatum(ReplicasByGuid, &ReplicaKey)) { DPRINT1(4, ":S: Pause journaling on replica %ws\n", Replica->ReplicaName->Name); if (Replica->pVme) { JrnlPauseVolume(Replica->pVme, 5000); } }
//
// Shutdown the replicas cleanly.
//
// WARN: all of the change orders have to be taken through the retry
// path before shutting down the rest of the system. Otherwise,
// access violations occur in ChgOrdIssueCleanup(). Perhaps we
// should fix ChgOrdIssueCleanup() to handle the case when
// change order accept has exited and cleaned up its tables?
//
CheckUnjoin = 0; ReplicaKey = NULL; while (Replica = GTabNextDatum(ReplicasByGuid, &ReplicaKey)) { LOCK_CXTION_TABLE(Replica); CxtionKey = NULL;
while (Cxtion = GTabNextDatum(Replica->Cxtions, &CxtionKey)) {
if (CxtionStateIs(Cxtion, CxtionStateUnjoined) || CxtionStateIs(Cxtion, CxtionStateDeleted)) { DPRINT3(0, ":S: %ws\\%ws %s: Unjoin not needed.\n", Replica->MemberName->Name, Cxtion->Name->Name, (Cxtion->Inbound) ? "<-" : "->"); } else { DPRINT4(0, ":S: %ws\\%ws %s: Unjoin (%d cos).\n", Replica->MemberName->Name, Cxtion->Name->Name, (Cxtion->Inbound) ? "<-" : "->", Cxtion->ChangeOrderCount); RcsSubmitReplicaCxtion(Replica, Cxtion, CMD_UNJOIN); CheckUnjoin += Cxtion->ChangeOrderCount + 1; } } UNLOCK_CXTION_TABLE(Replica); } LastCheckUnjoin = 0;
while (CheckUnjoin && CheckUnjoin != LastCheckUnjoin) { //
// Wait a bit and check again
//
Sleep(5 * 1000); LastCheckUnjoin = CheckUnjoin; CheckUnjoin = 0; ReplicaKey = NULL;
while (Replica = GTabNextDatum(ReplicasByGuid, &ReplicaKey)) { LOCK_CXTION_TABLE(Replica); CxtionKey = NULL;
while (Cxtion = GTabNextDatum(Replica->Cxtions, &CxtionKey)) { if (CxtionStateIs(Cxtion, CxtionStateUnjoined) || CxtionStateIs(Cxtion, CxtionStateDeleted)) {
DPRINT3(0, ":S: %ws\\%ws %s: Unjoin successful.\n", Replica->MemberName->Name, Cxtion->Name->Name, (Cxtion->Inbound) ? "<-" : "->");
} else if (Cxtion->ChangeOrderCount) {
DPRINT4(0, ":S: %ws\\%ws %s: Unjoining (%d cos).\n", Replica->MemberName->Name, Cxtion->Name->Name, (Cxtion->Inbound) ? "<-" : "->", Cxtion->ChangeOrderCount); CheckUnjoin += Cxtion->ChangeOrderCount + 1;
} else {
DPRINT4(0, ":S: %ws\\%ws %s: Ignoring (state %s).\n", Replica->MemberName->Name, Cxtion->Name->Name, (Cxtion->Inbound) ? "<-" : "->", GetCxtionStateName(Cxtion)); } } UNLOCK_CXTION_TABLE(Replica); } } if (CheckUnjoin) { DPRINT(0, "ERROR - Could not unjoin all cxtions.\n"); }
//
// Now wait until any remaining local Change Orders wind through
// retire or retry for each replica set.
//
ReplicaKey = NULL; while (Replica = GTabNextDatum(ReplicasByGuid, &ReplicaKey)) {
ActiveCoCount = 0; LastCheckActiveCoCount = 1;
while (ActiveCoCount != LastCheckActiveCoCount) { LastCheckActiveCoCount = ActiveCoCount;
if ((Replica->pVme != NULL) && (Replica->pVme->ActiveInboundChangeOrderTable != NULL)) { ActiveCoCount = GhtCountEntries(Replica->pVme->ActiveInboundChangeOrderTable); if (ActiveCoCount == 0) { break; } DPRINT2(0, ":S: Waiting for %d active inbound change orders to finish up for %ws.\n", ActiveCoCount, Replica->MemberName->Name);
Sleep(5*1000); } }
if (ActiveCoCount != 0) { DPRINT2(0, ":S: ERROR - %d active inbound change orders were not cleaned up for %ws.\n", ActiveCoCount, Replica->MemberName->Name); } else { DPRINT1(0, ":S: All active inbound change orders finished for %ws.\n", Replica->MemberName->Name); } }
//
// ShutDown the replica control command server
//
DPRINT(1,":S: \tShutting down Replica Server...\n"); RcsShutDownReplicaCmdServer(); DEBUG_FLUSH();
//
// ShutDown the send command server
//
DPRINT(1,":S: \tShutting down Comm Server...\n"); SndCsShutDown(); DEBUG_FLUSH();
//
// ShutDown rpc
//
DPRINT(1,":S: \tShutting down RPC Server...\n"); ShutDownRpc(); DEBUG_FLUSH();
//
// ShutDown the waitable timer server
//
DPRINT(1,":S: \tShutting down Waitable Timer Server...\n"); ShutDownWait(); DEBUG_FLUSH(); //
// ShutDown the outbound log processor
//
//
// NOPE; the database server requires the outbound log
// processor when shutting down. The database server will
// shutdown the outbound log processor when its done.
//
// DPRINT(1,"\tShutting down Outbound Log Processor...\n");
// DEBUG_FLUSH();
// ShutDownOutLog();
//
// ShutDown the database server
//
DPRINT(1,":S: \tShutting down the Database Server...\n"); DEBUG_FLUSH(); DbsShutDown();
//
// Wakeup any command server waiting on another command server to start
//
if (HANDLE_IS_VALID(DataBaseEvent)) { SetEvent(DataBaseEvent); } if (HANDLE_IS_VALID(JournalEvent)) { SetEvent(JournalEvent); } if (HANDLE_IS_VALID(ChgOrdEvent)) { SetEvent(ChgOrdEvent); } if (HANDLE_IS_VALID(CommEvent)) { SetEvent(CommEvent); } if (HANDLE_IS_VALID(ReplicaEvent)) { SetEvent(ReplicaEvent); }
//
// Wakeup the thread that polls the ds
//
if (HANDLE_IS_VALID(DsPollEvent)) { SetEvent(DsPollEvent); }
//
// Wakeup any thread waiting on the thread that polls the ds
//
if (HANDLE_IS_VALID(DsShutDownComplete)) { SetEvent(DsShutDownComplete); }
//
// Ask all the threads to exit
//
DPRINT(1,":S: \tShutting down all the threads...\n"); WStatus = ThSupExitThreadGroup(NULL); DEBUG_FLUSH();
//
// We can't uninitialize the subsystems because some thread
// may still be active and referencing the data structs.
//
if (WIN_SUCCESS(WStatus)) { //
// Free the active replica set stuff
//
DPRINT(1,":S: \tFreeing replica sets...\n"); RcsFrsUnInitializeReplicaCmdServer(); DEBUG_FLUSH();
//
// Free the rpc handle cache
//
DPRINT(1,":S: \tFreeing rpc handles...\n"); SndCsUnInitialize(); DEBUG_FLUSH();
//
// Free the stage table
//
DPRINT(1,":S: \tFreeing stage table...\n"); FrsStageCsUnInitialize(); DEBUG_FLUSH();
//
// Free the rpc table and princname
//
DPRINT(1,":S: \tFreeing rpc memory...\n"); FrsRpcUnInitialize(); DEBUG_FLUSH(); }
#if DBG
//
// DEBUG PRINTS
//
DPRINT(1,":S: \tDumping Vme Filter Table...\n"); JrnlDumpVmeFilterTable(); DEBUG_FLUSH(); #endif DBG
SHUTDOWN_COMPLETE: //
// We can't free resources because some thread may still be
// active and referencing them.
//
if (WIN_SUCCESS(WStatus)) { //
//
// Free resources in main
//
DPRINT(1,":S: \tFreeing main resources...\n"); DEBUG_FLUSH(); FrsFree(WorkingPath); FrsFree(DbLogPath); FrsFree(JetPath); FrsFree(JetFile); FrsFree(JetFileCompact); FrsFree(JetSys); FrsFree(JetTemp); FrsFree(JetLog); FrsFree(JetPathA); FrsFree(JetFileA); FrsFree(JetFileCompactA); FrsFree(JetSysA); FrsFree(JetTempA); FrsFree(JetLogA); GTabFreeTable(StagingAreaTable, FrsFree); GTabFreeTable(ReparseTagTable, FrsFreeType);
//
// Uninitialize the memory allocation subsystem
//
DPRINT(1,":S: \tShutting down the memory allocation subsystem...\n"); DEBUG_FLUSH(); FrsUnInitializeMemAlloc(); }
//
// Report an event
//
if (FrsIsAsserting) { EPRINT0(EVENT_FRS_STOPPED_ASSERT); } else { EPRINT0(EVENT_FRS_STOPPED); }
//
// Check the restart action.
//
MainShutDownComplete();
//
// DONE
//
if (!FrsIsAsserting && FrsScmRequestedShutdown) { SetEvent(ShutDownComplete); FrsSetServiceStatus(SERVICE_STOPPED, 0, ShutDownTimeOut*1000, NO_ERROR); }
ExitProcess(STATUS_SUCCESS); return STATUS_SUCCESS; }
DWORD MainMustInit( IN INT ArgC, IN PWCHAR *ArgV ) /*++
Routine Description: Initialize anything necessary for shutdown and logging errors.
Arguments: ArgC - From main ArgV - From main
Return Value: Win32 Error Status
--*/ { #undef DEBSUB
#define DEBSUB "MainMustInit:"
DWORD WStatus; ULONG Len; PWCHAR Severity; HANDLE ShutDownThreadHandle; DWORD ShutDownThreadId;
//
// Several lower level functions aren't prepared to dynamically allocate
// the needed storage so to help mitigate this we use the BigPath crock.
//
WCHAR BigPath[8*MAX_PATH + 1];
//
// FIRST, SETUP THE "MUST HAVE" VARIABLES, EVENTS, SERVICES...
//
//
// Synchronizes access to pValidPartnerTableStruct.
// see definition of pValidPartnerTableStruct for more info.
//
INITIALIZE_CRITICAL_SECTION(&CritSec_pValidPartnerTableStruct);
//
// Synchronizes access to the Old ValidPartnerTableStruct List.
// OldValidPartnerTableStructListHead
//
INITIALIZE_CRITICAL_SECTION(&OldValidPartnerTableStructListHeadLock);
//
// Synchronizes access between DsCs and SysVol Promotion when
// initializing other the rest of the service (MainInit()).
//
INITIALIZE_CRITICAL_SECTION(&MainInitLock);
//
// Enable event logging
//
//InitializeEventLog();
//EPRINT0(EVENT_FRS_STARTING);
//
// Backup/Restore privileges
//
if (!FrsSetupPrivileges()) { WStatus = GetLastError(); DPRINT_WS(0, ":S: ERROR - FrsSetupPrivileges()", WStatus); return WStatus; } DEBUG_FLUSH();
//
// Get this Machine's name.
//
DPRINT1(0, ":S: Computer name is %ws\n", ComputerName);
//
// Get this Machine's DNS name.
//
Len = ARRAY_SZ(BigPath); BigPath[0] = L'\0'; if (!GetComputerNameEx(ComputerNameDnsFullyQualified, BigPath, &Len)) { WStatus = GetLastError(); DPRINT1_WS(0, "ERROR - Cannot get the computer's DNS name; using %ws\n", ComputerName, WStatus); ComputerDnsName = FrsWcsDup(ComputerName); } else { ComputerDnsName = FrsWcsDup(BigPath); }
DPRINT1(0, "Computer's DNS name is %ws\n", ComputerDnsName);
DEBUG_FLUSH();
if (!RunningAsAService) { //
// We need the event log service to log errors, warnings, notes, ...
//
EventLogIsRunning = FrsWaitService(ComputerName, L"EventLog", 1000, 10000); if (!EventLogIsRunning) { return ERROR_SERVICE_NOT_ACTIVE; }
//
// We need the rpc endpoint mapper
//
RpcssIsRunning = FrsWaitService(ComputerName, L"rpcss", 1000, 10000); if (!RpcssIsRunning) { return ERROR_SERVICE_NOT_ACTIVE; } }
//
// Signaled when shutdown is desired
//
ShutDownEvent = FrsCreateEvent(TRUE, FALSE);
//
// Signaled when shutdown has completed
//
ShutDownComplete = FrsCreateEvent(TRUE, FALSE);
//
// Signaled when database has started
//
DataBaseEvent = FrsCreateEvent(TRUE, FALSE);
//
// Signaled when journal has started
//
JournalEvent = FrsCreateEvent(TRUE, FALSE);
//
// Signaled when change order accept has started
//
ChgOrdEvent = FrsCreateEvent(TRUE, FALSE);
//
// Signaled when replica has started
//
ReplicaEvent = FrsCreateEvent(TRUE, FALSE);
//
// Signaled when comm has started
//
CommEvent = FrsCreateEvent(TRUE, FALSE);
//
// Used to poll the ds
//
DsPollEvent = FrsCreateEvent(TRUE, FALSE);
//
// Set when the thread that polls the Ds has shut down
//
DsShutDownComplete = FrsCreateEvent(TRUE, FALSE);
//
// Used to signal the completion of backup operation.
//
FrsThawEvent = FrsCreateEvent(TRUE, FALSE);
//
// Event to signal that there are no installs in
// progress.
//
FrsNoInstallsInProgressEvent = FrsCreateEvent(TRUE, FALSE);
//
// register signal handlers after creating events and initializing
// the critical sections!
//
if (!RunningAsAService) { DPRINT(0, "Setting CTRL_C_EVENT and CTRL_BREAK_EVENT handlers.\n"); if(!SetConsoleCtrlHandler(MainSigHandler, TRUE)) { DPRINT_WS(0, "SetConsoleCtrlHandler() failed:", GetLastError()); } }
//
// Get the path to the DB file and the DB Logs.
//
CfgRegReadString(FKC_WORKING_DIRECTORY, NULL, 0, &WorkingPath); if (WorkingPath == NULL) { DPRINT(0, ":S: Must have the working directory in the registry. Aborting\n"); return ERROR_INVALID_PARAMETER; }
CfgRegReadString(FKC_DBLOG_DIRECTORY, NULL, 0, &DbLogPath);
//
// Create the working directory and the DB log dir (optional)
//
FRS_WCSLWR(WorkingPath); DPRINT1(0, ":S: Working Directory is %ws\n", WorkingPath); WStatus = FrsCreateDirectory(WorkingPath); CLEANUP_WS(0, ":S: Can't create working dir:", WStatus, CLEAN_UP);
if (DbLogPath != NULL) { FRS_WCSLWR(DbLogPath); DPRINT1(0, ":S: DB Log Path provided is %ws\n", DbLogPath); WStatus = FrsCreateDirectory(DbLogPath); CLEANUP_WS(0, ":S: Can't create debug log dir:", WStatus, CLEAN_UP); }
//
// Restrict access to the database working directory
//
WStatus = FrsRestrictAccessToFileOrDirectory(WorkingPath, NULL, FALSE, // do not inherit acls from parent.
TRUE); // push acls to children.
DPRINT1_WS(0, ":S: WARN - Failed to restrict access to %ws (IGNORED);", WorkingPath, WStatus); WStatus = ERROR_SUCCESS;
//
// Restrict access to the database log directory (optional)
//
if (DbLogPath != NULL) { WStatus = FrsRestrictAccessToFileOrDirectory(DbLogPath, NULL, FALSE, // do not inherit acls from parent.
TRUE);// push acls to children.
DPRINT1_WS(0, ":S: WARN - Failed to restrict access to %ws (IGNORED);", DbLogPath, WStatus); WStatus = ERROR_SUCCESS; }
//
// Create the database path
// WARN: FrsDsInitializeHardWired() may alter JetPath
//
JetPath = FrsWcsPath(WorkingPath, JET_DIR);
//
// Initialize the path to the inifile.
//
#ifdef DS_FREE
IniFileName = FrsWcsPath(WorkingPath, L"frsconfig.txt");
#endif DS_FREE
//
// Initialize the hardwired config stuff
// WARN: FrsDsInitializeHardWired() may alter JetPath
//
INITIALIZE_HARD_WIRED();
//
// Create the database paths and file (UNICODE and ASCII)
// Jet uses the ASCII versions
//
FRS_WCSLWR(JetPath); // for wcsstr()
JetFile = FrsWcsPath(JetPath, JET_FILE); JetFileCompact = FrsWcsPath(JetPath, JET_FILE_COMPACT); JetSys = FrsWcsPath(JetPath, JET_SYS); JetTemp = FrsWcsPath(JetPath, JET_TEMP);
if (DbLogPath != NULL) { JetLog = FrsWcsDup(DbLogPath); } else { JetLog = FrsWcsPath(JetPath, JET_LOG); }
//
// Jet can't handle wide char strings
//
JetPathA = FrsWtoA(JetPath); JetFileA = FrsWtoA(JetFile); JetFileCompactA = FrsWtoA(JetFileCompact); JetSysA = FrsWtoA(JetSys); JetTempA = FrsWtoA(JetTemp); JetLogA = FrsWtoA(JetLog);
DPRINT2(4, ":S: JetPath : %ws (%s in ASCII)\n", JetPath, JetPathA); DPRINT2(4, ":S: JetFile : %ws (%s in ASCII)\n", JetFile, JetFileA); DPRINT2(4, ":S: JetFileCompact: %ws (%s in ASCII)\n", JetFileCompact, JetFileCompactA); DPRINT2(4, ":S: JetSys : %ws (%s in ASCII)\n", JetSys, JetSysA); DPRINT2(4, ":S: JetTemp : %ws (%s in ASCII)\n", JetTemp, JetTempA); DPRINT2(4, ":S: JetLog : %ws (%s in ASCII)\n", JetLog, JetLogA);
//
// Create the database directories under workingpath\JET_DIR
//
WStatus = FrsCreateDirectory(JetPath); CLEANUP_WS(0, ":S: Can't create JetPath dir:", WStatus, CLEAN_UP);
WStatus = FrsCreateDirectory(JetSys); CLEANUP_WS(0, ":S: Can't create JetSys dir:", WStatus, CLEAN_UP);
WStatus = FrsCreateDirectory(JetTemp); CLEANUP_WS(0, ":S: Can't create JetTemp dir:", WStatus, CLEAN_UP);
WStatus = FrsCreateDirectory(JetLog); CLEANUP_WS(0, ":S: Can't create JetLog dir:", WStatus, CLEAN_UP);
//
// Initialize the subsystem used for managing threads
// (needed for an orderly shutdown)
//
ThSupInitialize();
//
// This thread responds to the ShutDownEvent and insures
// an orderly shutdown when run as either a service or an exe.
//
ShutDownThreadHandle = (HANDLE)CreateThread(NULL, 0, MainFrsShutDown, NULL, 0, &ShutDownThreadId);
if (!HANDLE_IS_VALID(ShutDownThreadHandle)) { WStatus = GetLastError(); CLEANUP_WS(0, ":S: Can't create shutdown thread:", WStatus, CLEAN_UP); }
DbgCaptureThreadInfo2(L"Shutdown", MainFrsShutDown, ShutDownThreadId);
//
// The rpc server may reference this table as soon as the rpc
// interfaces are registered. Make sure it exists early in the
// startup.
//
ReplicasByGuid = GTabAllocTable();
//
// The staging area table is referenced early in the startup threads
//
StagingAreaTable = GTabAllocTable(); INITIALIZE_CRITICAL_SECTION(&StagingAreaCleanupLock);
//
// Initialize necessary tables.
//
ReparseTagTable = GTabAllocNumberTable();
CompressionTable = GTabAllocTable();
//
// This table is used to keep contexts across multiple calls from by
// info APIs. Table is only initialized once.
//
FrsInfoContextTable = GTabAllocNumberTable();
if (!DebugInfo.DisableCompression) { DPRINT(0, "Staging file COMPRESSION support is enabled.\n"); //
// The Compression table is inited from the registry. Add the guids for the
// compression formats that we support.
//
GTabInsertEntryNoLock(CompressionTable, (PVOID)&FrsGuidCompressionFormatLZNT1, (GUID*)&FrsGuidCompressionFormatLZNT1, NULL); } else { DPRINT(0, "Staging file COMPRESSION support is disabled.\n"); }
//
// Size of buffer used during directory enumeration
//
CfgRegReadDWord(FKC_ENUMERATE_DIRECTORY_SIZE, NULL, 0, &EnumerateDirectorySizeInBytes); DPRINT1(4, ":S: Registry Param - Enumerate Directory Buffer Size in Bytes: %d\n", EnumerateDirectorySizeInBytes);
//
// Default File and Directory filter lists.
//
// The compiled in default is only used if no value is supplied in
// EITHER the DS or the Registry.
// The table below shows how the final filter is formed.
//
// value Value
// supplied supplied Resulting filter string Used
// in DS in Registry
// No No DEFAULT_xxx_FILTER_LIST
// No Yes Value from registry
// Yes No Value from DS
// Yes Yes Value from DS + Value from registry
//
RegistryFileExclFilterList = NULL; CfgRegReadString(FKC_FILE_EXCL_FILTER_LIST, NULL, 0, &RegistryFileExclFilterList);
RegistryDirExclFilterList = NULL; CfgRegReadString(FKC_DIR_EXCL_FILTER_LIST, NULL, 0, &RegistryDirExclFilterList);
DPRINT1(4, ":S: Registry Param - File Filter List: %ws\n", (RegistryFileExclFilterList) ? RegistryFileExclFilterList : L"Null");
DPRINT1(4, ":S: Registry Param - Directory Filter Exclusion List: %ws\n", (RegistryDirExclFilterList) ? RegistryDirExclFilterList : L"Null");
//
// Inclusion filters were added very late (7/13/99) so a single file
// ~clbcatq.* could be made to replicate. This is a reg only key.
//
// Add the File Inclusion Filter List Value to the Reg key
// "HKLM\System\CurrentControlSet\Services\NtFrs\Parameters"
//
// If the value already exists then preserve it.
//
CfgRegWriteString(FKC_FILE_INCL_FILTER_LIST, NULL, FRS_RKF_FORCE_DEFAULT_VALUE | FRS_RKF_KEEP_EXISTING_VALUE, 0);
RegistryFileInclFilterList = NULL; CfgRegReadString(FKC_FILE_INCL_FILTER_LIST, NULL, 0, &RegistryFileInclFilterList);
RegistryDirInclFilterList = NULL; CfgRegReadString(FKC_DIR_INCL_FILTER_LIST, NULL, 0, &RegistryDirInclFilterList);
DPRINT1(4, ":S: Registry Param - File Filter Inclusion List: %ws\n", (RegistryFileInclFilterList) ? RegistryFileInclFilterList : L"Null");
DPRINT1(4, ":S: Registry Param - Directory Filter Inclusion List: %ws\n", (RegistryDirInclFilterList) ? RegistryDirInclFilterList : L"Null");
//
// Mutual Authentication. Update registry value as needed.
//
CfgRegCheckEnable(FKC_FRS_MUTUAL_AUTHENTICATION_IS, NULL, 0, &MutualAuthenticationIsEnabled, &MutualAuthenticationIsEnabledAndRequired);
// WStatus = ConfigCheckEnabledWithUpdate(FRS_CONFIG_SECTION,
// FRS_MUTUAL_AUTHENTICATION_IS,
// FRS_IS_DEFAULT_ENABLED,
// &MutualAuthenticationIsEnabled);
//
// if (!MutualAuthenticationIsEnabled) {
// DPRINT_WS(0, "WARN - Mutual authentication is not enabled", WStatus);
// } else {
// DPRINT(4, "Mutual authentication is enabled\n");
// }
//
// Initialize the FRS backup writer COM interface.
//
DPRINT(4, "About to initialize the Frs Writer component\n"); WStatus = InitializeFrsWriter(); if (!WIN_SUCCESS(WStatus)) { DPRINT1(1,"WARN - Can not initialize the Frs Writer component, WStatus = %d\n", WStatus); }
//
// Initialize the Perfmon server
//
InitializePerfmonServer();
//
// Create registry keys for checking access to RPC calls
//
FrsRpcInitializeAccessChecks();
//
// Register the RPC interfaces
//
if (!FrsRpcInitialize()) { return RPC_S_CANT_CREATE_ENDPOINT; }
return ERROR_SUCCESS;
CLEAN_UP: return WStatus; }
VOID MainInit( VOID ) /*++
Routine Description: Initialize anything necessary to run the service
Arguments: None.
Return Value: None. --*/ { #undef DEBSUB
#define DEBSUB "MainInit:"
EnterCriticalSection(&MainInitLock); //
// No need to initialize twice
//
if (MainInitHasRun) { LeaveCriticalSection(&MainInitLock); return; }
//
// SETUP THE INFRASTRUCTURE
//
//
// Reset our start time (in minutes). The service is not restarted
// unless this invocation ran long enough before taking an assert.
//
// 100-nsecs / (10 (microsecs) * 1000 (msecs) * 1000 (secs) * 60 (min)
//
GetSystemTimeAsFileTime((FILETIME *)&DebugInfo.StartSeconds); DebugInfo.StartSeconds /= (10 * 1000 * 1000);
//
// Fetch the staging file limit
//
CfgRegReadDWord(FKC_STAGING_LIMIT, NULL, 0, &StagingLimitInKb); DPRINT1(4, ":S: Staging limit is: %d KB\n", StagingLimitInKb);
//
// Put the default value in registry if there is no key present.
//
CfgRegWriteDWord(FKC_STAGING_LIMIT, NULL, FRS_RKF_FORCE_DEFAULT_VALUE | FRS_RKF_KEEP_EXISTING_VALUE, 0);
//
// Get Max number of replica sets allowed.
//
CfgRegReadDWord(FKC_MAX_NUMBER_REPLICA_SETS, NULL, 0, &MaxNumberReplicaSets);
//
// Get Max number of Jet database sessions allowed.
//
CfgRegReadDWord(FKC_MAX_NUMBER_JET_SESSIONS, NULL, 0, &MaxNumberJetSessions);
//
// Get outstanding CO qutoa limit on outbound connections.
//
CfgRegReadDWord(FKC_OUT_LOG_CO_QUOTA, NULL, 0, &MaxOutLogCoQuota);
//
// Get boolean to tell us to preserve file object IDs
// -- See Bug 352250 for why this is a risky thing to do.
CfgRegReadDWord(FKC_PRESERVE_FILE_OID, NULL, 0, &PreserveFileOID);
//
// Get number of days for retry timeouts
//
CfgRegReadDWord(FKC_MAX_CO_RETRY_TIMEOUT_MINUTES, NULL, 0, &MaxCoRetryTimeoutMinutes);
//
// Get maximum count for retry timeouts
//
CfgRegReadDWord(FKC_MAX_CO_RETRY_TIMEOUT_COUNT, NULL, 0, &MaxCoRetryTimeoutCount);
//
// Get state of outlog cleanup lock.
//
CfgRegReadDWord(FKC_LOCKED_OUTLOG_CLEANUP, NULL, 0, &LockedOutlogCleanup);
//
// Get the service long name for use in error messages.
//
ServiceLongName = FrsGetResourceStr(IDS_SERVICE_LONG_NAME);
//
// Initialize the Delayed command server. This command server
// is really a timeout queue that the other command servers use
// to retry or check the state of previously issued commands that
// have an indeterminate completion time.
//
// WARN: MUST BE FIRST -- Some command servers may use this
// command server during their initialization.
//
WaitInitialize(); FrsDelCsInitialize();
//
// SETUP THE COMM LAYER
//
//
// Initialize the low level comm subsystem
//
CommInitializeCommSubsystem();
//
// Initialize the Send command server. The receive command server
// starts when we register our RPC interface.
//
SndCsInitialize();
//
// SETUP THE SUPPORT COMMAND SERVERS
//
//
// Staging file fetcher
//
FrsFetchCsInitialize();
//
// Initial Sync Controller
//
InitSyncCsInitialize();
//
// Staging file installer
//
FrsInstallCsInitialize();
//
// Staging file generator
//
FrsStageCsInitialize();
//
// outbound log processor
//
OutLogInitialize();
//
// LAST, KICK OFF REPLICATION
//
//
// MUST PRECEED DATABASE AND DS INITIALIZATION
//
// The DS command server and the Database command server depend on
// the replica control initialization.
//
// Initialize the replica control command server
//
RcsInitializeReplicaCmdServer();
//
// Actually, we can start the database at any time after the delayed
// command server and the replica control command server. But its
// a good idea to fail sooner than later to make cleanup more predictable.
//
DbsInitialize();
//
// Free up memory by reducing our working set size
//
SetProcessWorkingSetSize(ProcessHandle, (SIZE_T)-1, (SIZE_T)-1);
MainInitHasRun = TRUE; LeaveCriticalSection(&MainInitLock); }
VOID MainMinimumInit( VOID ) /*++
Routine Description: Initialize anything necessary to run the service
Arguments: None.
Return Value: TRUE - No problems FALSE - Can't start service --*/ { #undef DEBSUB
#define DEBSUB "MainMinimumInit:"
//
// Setup the infrastructure
//
DbgMinimumInit();
//
// Check some NT to WIN error translations
//
FRS_ASSERT(WIN_NOT_IMPLEMENTED(FrsSetLastNTError(STATUS_NOT_IMPLEMENTED)));
FRS_ASSERT(WIN_SUCCESS(FrsSetLastNTError(STATUS_SUCCESS)));
FRS_ASSERT(WIN_ACCESS_DENIED(FrsSetLastNTError(STATUS_ACCESS_DENIED)));
FRS_ASSERT(WIN_INVALID_PARAMETER(FrsSetLastNTError(STATUS_INVALID_PARAMETER)));
FRS_ASSERT(WIN_NOT_FOUND(FrsSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND)));
//
// Initialize the DS command server
//
FrsDsInitialize();
//
// Free up memory by reducing our working set size
//
SetProcessWorkingSetSize(ProcessHandle, (SIZE_T)-1, (SIZE_T)-1); }
PWCHAR * MainConvertArgV( DWORD ArgC, PCHAR *ArgV ) /*++
Routine Description: Convert short char ArgV into wide char ArgV
Arguments: ArgC - From main ArgV - From main
Return Value: Address of the new ArgV --*/ { #undef DEBSUB
#define DEBSUB "MainConvertArgV:"
PWCHAR *wideArgV;
wideArgV = FrsAlloc((ArgC + 1) * sizeof(PWCHAR)); wideArgV[ArgC] = NULL;
while (ArgC-- >= 1) { wideArgV[ArgC] = FrsAlloc((strlen(ArgV[ArgC]) + 1) * sizeof(WCHAR)); wsprintf(wideArgV[ArgC], L"%hs", ArgV[ArgC]); FRS_WCSLWR(wideArgV[ArgC]); } return wideArgV; }
VOID MainServiceHandler( IN DWORD ControlCode ) /*++
Routine Description: Service handler. Called by the service controller runtime
Arguments: ControlCode
Return Value: None. --*/ { #undef DEBSUB
#define DEBSUB "MainServiceHandler:"
DPRINT1(0, ":S: Received control code %d from Service Controller\n",ControlCode);
switch (ControlCode) {
case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN:
DPRINT1(0, ":S: Service controller requests shutdown in %d seconds...\n", ShutDownTimeOut);
FrsSetServiceStatus(SERVICE_STOP_PENDING, 0, ShutDownTimeOut * 1000, NO_ERROR);
FrsScmRequestedShutdown = TRUE; FrsIsShuttingDown = TRUE; SetEvent(ShutDownEvent); return;
case SERVICE_CONTROL_PAUSE: case SERVICE_CONTROL_CONTINUE:
DPRINT(0, ":S: SERVICE PAUSE/CONTINUE IS NOT IMPLEMENTED\n"); return;
case SERVICE_CONTROL_INTERROGATE:
return;
default:
DPRINT2(0, ":S: Handler for service %ws does not understand 0x%x\n", SERVICE_NAME, ControlCode); } return; }
VOID WINAPI MainRunningAsAService( IN DWORD ArgC, IN PWCHAR *ArgV ) /*++
Routine Description: Main routine when running as a service
Arguments: ArgC - Ignored ArgV - Ignored
Return Value: None. --*/ { #undef DEBSUB
#define DEBSUB "MainRunningAsAService:"
HANDLE StartupThreadHandle; DWORD StartupThreadId; DWORD WStatus; PDWORD ServiceWaitState = NULL;
DPRINT(0, "Running as a service\n");
//
// Register our handlers
//
ServiceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, MainServiceHandler); if (!ServiceStatusHandle) { DPRINT1_WS(0, ":S: ERROR - No ServiceStatusHandle for %ws;", SERVICE_NAME, GetLastError()); ServiceStatus.dwCurrentState = SERVICE_STOPPED; ServiceStatus.dwWin32ExitCode = ERROR_PROCESS_ABORTED; ServiceStatus.dwWaitHint = DEFAULT_SHUTDOWN_TIMEOUT * 1000; return; }
if (!FrsIsShuttingDown) { FrsSetServiceStatus(SERVICE_START_PENDING, 0, DEFAULT_STARTUP_TIMEOUT * 1000, NO_ERROR); }
//
// Kick off a thread that updates the checkpoint and
// keeps the service controller from timing out.
// Allocate memory for data to be passed to another thread.
//
ServiceWaitState = FrsAlloc(sizeof(DWORD)); *ServiceWaitState = SERVICE_START_PENDING; StartupThreadHandle = (HANDLE)CreateThread(NULL, 0, MainSCCheckPointUpdate, ServiceWaitState, 0, &StartupThreadId);
if (!HANDLE_IS_VALID(StartupThreadHandle)) { //
// Not a fatal error. It is OK to not update the checkpoint.
//
DPRINT_WS(4,":S: ERROR - Could not start thread to update startup checkpoint.", GetLastError()); }
//
// Finish rest of debug input
//
DbgMustInit(ArgC, WideArgV);
//
// Critical initialization
//
DPRINT1(4, "ArgC = %d\n", ArgC); WStatus = MainMustInit(ArgC, WideArgV); if (FRS_SUCCESS(WStatus)) { //
// Necessary initialization
//
MainMinimumInit();
if (!FrsIsShuttingDown) { //
// The core service has started.
//
FrsSetServiceStatus(SERVICE_RUNNING, 0, DEFAULT_STARTUP_TIMEOUT * 1000, NO_ERROR); //
// Init the service restart action if service fails.
//
FrsSetServiceFailureAction(); } } else { //
// Initialization failed; service can't start
//
DPRINT_WS(0, ":S: MainMustInit failed;", WStatus); FrsSetServiceStatus(SERVICE_STOPPED, 0, DEFAULT_SHUTDOWN_TIMEOUT * 1000, ERROR_PROCESS_ABORTED); } return; }
ULONG MainNotRunningAsAService( IN DWORD ArgC, IN PWCHAR *ArgV ) /*++
Routine Description: Main routine when not running as a service
Arguments: ArgC - Ignored ArgV - Ignored
Return Value: Win32 status
--*/ { #undef DEBSUB
#define DEBSUB "MainNotRunningAsAService:"
ULONG WStatus;
DPRINT(0, "Not running as a service\n");
//
// Finish rest of debug input
//
DbgMustInit(ArgC, WideArgV);
//
// Critical initialization
//
DPRINT1(4, "ArgC = %d\n", ArgC); WStatus = MainMustInit(ArgC, WideArgV); if (!FRS_SUCCESS(WStatus)) { DPRINT_WS(0, ":S: MainMustInit failed;", WStatus); ExitProcess(ERROR_NO_SYSTEM_RESOURCES); }
//
// Necessary initialization
//
MainMinimumInit();
#if 0
//
// Kick off the test thread and forget about it
//
if (!ThSupCreateThread(L"TestThread", NULL, FrsTest, NULL)) { DPRINT(0, ":S: Could not create FrsTest\n"); } #endif
//
// Wait for shutdown
//
DPRINT(0, ":S: Waiting for shutdown event.\n"); WStatus = WaitForSingleObject(ShutDownEvent, INFINITE);
CHECK_WAIT_ERRORS(0, WStatus, 1, ACTION_CONTINUE);
DPRINT(0, ":S: Waiting for shutdown complete event.\n"); WStatus = WaitForSingleObject(ShutDownComplete, INFINITE);
CHECK_WAIT_ERRORS(0, WStatus, 1, ACTION_CONTINUE);
if (WIN_SUCCESS(WStatus)) { DPRINT(0, ":S: ShutDownComplete event signalled.\n"); }
return WStatus; }
ULONG _cdecl main( IN INT ArgC, IN PCHAR ArgV[] ) /*++
Routine Description: Main runs as either a service or an exe.
Arguments: ArgC ArgV
Return Value: ERROR_SUCCESS - No problems Otherwise - Something went wrong --*/ { #undef DEBSUB
#define DEBSUB "main:"
DWORD Len; ULONG WStatus; HRESULT hr; SERVICE_TABLE_ENTRY ServiceTableEntry[] = { { SERVICE_NAME, MainRunningAsAService }, { NULL, NULL } };
//
// Process Handle
//
ProcessHandle = GetCurrentProcess();
//
// Disable any DPRINTs until we can init the debug component.
//
DebugInfo.Disabled = TRUE;
//
// Adjust the defaults for some tunable params.
//
CfgRegAdjustTuningDefaults();
//
// Get this Machine's name.
//
Len = MAX_COMPUTERNAME_LENGTH + 2; ComputerName[0] = UNICODE_NULL; GetComputerNameW(ComputerName, &Len); ComputerName[Len] = UNICODE_NULL;
//
// Initialize the memory allocation subsystem
//
FrsInitializeMemAlloc();
//
// Perform as much work as possible in WCHAR
//
WideArgV = MainConvertArgV(ArgC, ArgV);
//
// Find out if we are running as a service or as an .exe
//
RunningAsAService = !FrsSearchArgv(ArgC, WideArgV, L"notservice", NULL);
//
// Synchronizes access between the shutdown thread and the
// service controller.
//
INITIALIZE_CRITICAL_SECTION(&ServiceLock);
//
// Initialize the debug lock
//
INITIALIZE_CRITICAL_SECTION(&DebugInfo.Lock); INITIALIZE_CRITICAL_SECTION(&DebugInfo.DbsOutOfSpaceLock);
//
// Enable event logging
//
InitializeEventLog();
//
// This event log will not show up in the debug
// logs since it is written before debug logs are
// initialized. We do this because DbgInitLogTraceFile
// can produce a event log and we want to see the
// FRS starting event log before any other event
// logs.
//
EPRINT0(EVENT_FRS_STARTING);
//
// Init the debug trace log.
//
DbgInitLogTraceFile(ArgC, WideArgV);
WStatus = ERROR_SUCCESS;
try { try {
if (RunningAsAService) { hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hr)) { DPRINT1(1,L"CoInitializeEx failed with hresult 0x%08lx\n", hr); }
// initialize COM security
if (SUCCEEDED(hr)) { hr = CoInitializeSecurity( NULL, // sd
-1, // cAuthSvc
NULL, // asAuthSvc
NULL, // pReserved1
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // dwAuthnLevel
RPC_C_IMP_LEVEL_IDENTIFY, // dwImpLevel
NULL, // pAuthList
EOAC_NONE, // dwCapabilities
NULL // pReserved3
); if (FAILED(hr)) { DPRINT1(1,L"CoInitializeSecurity failed with hresult 0x%08lx\n", hr); CoUninitialize(); } }
//
// RUNNING AS A SERVICE
//
if (!StartServiceCtrlDispatcher(ServiceTableEntry)) { WStatus = GetLastError(); DPRINT1_WS(0, "Could not start dispatcher for service %ws;", SERVICE_NAME, WStatus); } if (SUCCEEDED(hr)) { CoUninitialize(); } } else {
//
// NOT A SERVICE
//
MainNotRunningAsAService(ArgC, WideArgV); } } except (FrsException(GetExceptionInformation())) { } } finally { if (AbnormalTermination()) { WStatus = ERROR_INVALID_ACCESS; } }
return WStatus; }
|