2371 lines
60 KiB
C
2371 lines
60 KiB
C
/*++
|
||
|
||
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>
|
||
|
||
#define INITGUID
|
||
#include "frstrace.h"
|
||
|
||
PCHAR LatestChanges[] = {
|
||
|
||
"Latest changes:",
|
||
"Whistler Beta-2",
|
||
|
||
NULL
|
||
};
|
||
|
||
|
||
/*++
|
||
|
||
" RC3-Q1: 432553, 436070, 432549",
|
||
" WMI Perf Tracing",
|
||
" Allow all junction points",
|
||
" Automatic trigger of non-auth restore on WRAP_ERROR",
|
||
" Allow changing replica root path",
|
||
" 03/18/00 - sudarc - checkin",
|
||
" 03/15/00 - 32/64 Comm fix.",
|
||
" 03/20 - merge with sudarc.",
|
||
" 03/30/00 - sudarc - checkin - volatile connection cleanup.",
|
||
" 04/14/00 - sudarc - checkin - bugbug, memleak, and poll summary eventlog.",
|
||
" 04/28/00 - davidor - 32-64 interop, CO mem leak, stage hdr & Comm pkt cmpres guid, misc.",
|
||
" - remove old disk cache check, disable most closewithusndampening calls.",
|
||
" - cxtion lock cleanup, bugbug removal, add retire check table.",
|
||
" 05/04/00 - sudarc - checkin - compression of staging files.",
|
||
" 07/17/00 - sudarc - checkin - (120495, 126578, 126635, 107760, 54527, 6675, 145265).",
|
||
" 07/20/00 - sudarc - checkin - (145264, 145947).",
|
||
" 08/18/00 - sudarc - checkin - (154749, 150407, 161651).",
|
||
" 08/00 - davidor - error cleanup, restart after resource triggered shutdown",
|
||
" - add tracking records :T: and set log sev level to 2",
|
||
" 08/30/00 - sudarc - checkin - (17640, 52732, 53459, 120508, 138742, 159846, 164114, 164318, 169041).",
|
||
" 09/05/00 - davidor - 10312, 71447 avoid staging area filling by dup suppression",
|
||
|
||
--*/
|
||
|
||
HANDLE ShutDownEvent;
|
||
HANDLE ShutDownComplete;
|
||
HANDLE DataBaseEvent;
|
||
HANDLE JournalEvent;
|
||
HANDLE ChgOrdEvent;
|
||
HANDLE ReplicaEvent;
|
||
HANDLE CommEvent;
|
||
HANDLE DsPollEvent;
|
||
HANDLE DsShutDownComplete;
|
||
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;
|
||
|
||
//
|
||
// Directory and file filter lists from registry.
|
||
//
|
||
PWCHAR RegistryFileExclFilterList;
|
||
PWCHAR RegistryFileInclFilterList;
|
||
|
||
PWCHAR RegistryDirExclFilterList;
|
||
PWCHAR RegistryDirInclFilterList;
|
||
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// Major/minor (see frs.h)
|
||
//
|
||
ULONG NtFrsMajor = NTFRS_MAJOR;
|
||
ULONG NtFrsMinor = NTFRS_MINOR;
|
||
|
||
ULONG NtFrsStageMajor = NTFRS_STAGE_MAJOR;
|
||
ULONG NtFrsStageMinor = NTFRS_STAGE_MINOR_2;
|
||
|
||
ULONG NtFrsCommMinor = NTFRS_COMM_MINOR_4;
|
||
|
||
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;
|
||
|
||
//
|
||
// The staging area table is references early in the startup process
|
||
//
|
||
extern PGEN_TABLE StagingAreaTable;
|
||
|
||
PGEN_TABLE CompressionTable;
|
||
|
||
//
|
||
// 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 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 %d).\n",
|
||
Replica->MemberName->Name, Cxtion->Name->Name,
|
||
(Cxtion->Inbound) ? "<-" : "->", GetCxtionState(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);
|
||
|
||
//
|
||
// 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 between DsCs and SysVol Promotion when
|
||
// initializing other the rest of the service (MainInit()).
|
||
//
|
||
InitializeCriticalSection(&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);
|
||
|
||
//
|
||
// 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, TRUE);
|
||
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, TRUE);
|
||
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 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,
|
||
10000,
|
||
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();
|
||
|
||
CompressionTable = GTabAllocTable();
|
||
|
||
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);
|
||
|
||
|
||
// 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 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 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,
|
||
10000,
|
||
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 DBG
|
||
//
|
||
// 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 DBG
|
||
|
||
//
|
||
// 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;
|
||
|
||
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.
|
||
//
|
||
InitializeCriticalSection(&ServiceLock);
|
||
|
||
//
|
||
// Init the debug trace log.
|
||
//
|
||
DbgInitLogTraceFile(ArgC, WideArgV);
|
||
|
||
//
|
||
// Enable event logging
|
||
//
|
||
InitializeEventLog();
|
||
EPRINT0(EVENT_FRS_STARTING);
|
||
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
try {
|
||
try {
|
||
|
||
if (RunningAsAService) {
|
||
|
||
//
|
||
// RUNNING AS A SERVICE
|
||
//
|
||
if (!StartServiceCtrlDispatcher(ServiceTableEntry)) {
|
||
WStatus = GetLastError();
|
||
DPRINT1_WS(0, "Could not start dispatcher for service %ws;",
|
||
SERVICE_NAME, WStatus);
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// NOT A SERVICE
|
||
//
|
||
MainNotRunningAsAService(ArgC, WideArgV);
|
||
}
|
||
} except (FrsException(GetExceptionInformation())) {
|
||
}
|
||
} finally {
|
||
if (AbnormalTermination()) {
|
||
WStatus = ERROR_INVALID_ACCESS;
|
||
}
|
||
}
|
||
|
||
return WStatus;
|
||
}
|