You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2511 lines
66 KiB
2511 lines
66 KiB
/*++
|
|
|
|
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;
|
|
}
|