Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

982 lines
26 KiB

/*++
Copyright (c) 1987-1993 Microsoft Corporation
Module Name:
rplmain.c
Abstract:
Contains RPL_main(), RPL service entry point. Initializes and shuts down
RPL service.
Provides similar functionality to rplservr.c in LANMAN 2.1 code.
Author:
Vladimir Z. Vulovic 27 - July - 1993
Environment:
User mode
Revision History :
--*/
#define RPLDATA_ALLOCATE
#include "local.h"
#undef RPLDATA_ALLOCATE
#include "request.h"
#include "database.h"
#include "debug.h"
#include "apisec.h"
#include <rplnames.h> // RPL_INTERFACE_NAME
#include "rplsvc_s.h" // rplsvc_ServerIfHandle
#include "setup.h" // SetupAction()
#define WCSLEN( _x_) ((DWORD) ( sizeof(_x_)/sizeof(WCHAR) - 1))
#define NET_MSG_FILE L"netmsg.dll"
#define RPLFILES_STRING L"RPLFILES"
#define BINFILES_STRING L"\\BINFILES\\"
#define RPL_DEFAULT_DIRECTORY L"%SystemRoot%\\rpl\\"
#define RPL_SERVICE_NAME L"RemoteBoot"
#define RPL_SECURITY_OBJECT_CREATED 0x1
#define RPL_RPC_SERVER_STARTED 0x2
DWORD RplMessageGet(
IN DWORD MessageId,
OUT LPWSTR buffer,
IN DWORD Size
)
/*++
Routine Description:
Fills in the unicode message corresponding to a given message id,
provided that a message can be found and that it fits in a supplied
buffer.
Arguments:
MessageId - message id
buffer - pointer to caller supplied buffer
Size - size (always in bytes) of supplied buffer
Return Value:
Count of characters, not counting the terminating null character,
returned in the buffer. Zero return value indicates failure.
--*/
{
DWORD length;
LPVOID lpSource;
DWORD dwFlags;
if ( MessageId < NERR_BASE) {
//
// Get message from system.
//
lpSource = NULL; // redundant step according to FormatMessage() spec
dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
} else {
//
// Get message from netmsg.dll.
//
lpSource = (LPVOID)RG_MessageHandle;
dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
}
//#define RPL_ELNK
#ifdef RPL_ELNK
wcscpy( buffer, L"NET2500: ");
buffer[ 6] = L'0' + MessageId - NERR_BadDosRetCode;
buffer += 9;
Size -= 9 * sizeof(WCHAR);
#endif
length = FormatMessage(
dwFlags, // dwFlags
lpSource, // lpSource
MessageId, // MessageId
0, // dwLanguageId
buffer, // lpBuffer
Size, // nSize
NULL // lpArguments
);
if ( length == 0) {
RG_Error = GetLastError();
RplReportEvent( NELOG_RplMessages, NULL, 0, NULL);
RPL_RETURN( 0);
}
#ifdef RPL_ELNK
length += 9;
#endif
return( length);
}
BOOL RplInitMessages( VOID)
/*++
Routine Description:
Creates an array of NLS (DBCS ??) messages for RPLBOOT.SYS.
Arguments:
None.
Return Value:
TRUE if success, FALSE otherwise.
--*/
{
WCHAR UnicodeString[ MAX_PATH];
DWORD UnicodeStringLength;
CHAR DbcsString[ MAX_PATH];
PBYTE pByte;
DWORD Index;
DWORD DbcsStringSize; // not including terminal null byte
RplDump( RG_DebugLevel & RPL_DEBUG_FLOWINIT,( "++InitMessages"));
RG_DbcsMessageBuffer = RplMemAlloc( RG_MemoryHandle, DBCS_MESSAGE_BUFFER_SIZE);
if ( RG_DbcsMessageBuffer == NULL) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
//
// read NLS messages from message file to patch table
//
for ( Index = 0, pByte = RG_DbcsMessageBuffer;
Index < MESSAGE_TABLE_LENGTH;
Index++, pByte += DBCS_SINGLE_MESSAGE_BUFFER_SIZE) {
UnicodeStringLength = RplMessageGet( RG_MessageTable[ Index],
UnicodeString, sizeof( UnicodeString));
if ( UnicodeStringLength == 0) {
return( FALSE);
}
//
// Null terminate the string - redundant ?
//
UnicodeString[ UnicodeStringLength] = 0;
DbcsStringSize = WideCharToMultiByte(
CP_OEMCP,
0,
UnicodeString,
UnicodeStringLength,
DbcsString,
sizeof( DbcsString),
NULL, // no default character
NULL // no default character flag
);
if ( DbcsStringSize == 0) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
// #define TEST_MESSAGE_TOO_LONG
#ifdef TEST_MESSAGE_TOO_LONG
if ( DbcsStringSize > 48) {
DbcsStringSize = 48;
}
#else
//
// If message is too long truncate it - leaving one char for the
// end of string mark of MS-DOS ('$').
//
if ( DbcsStringSize >= DBCS_SINGLE_MESSAGE_BUFFER_SIZE) {
DbcsStringSize = (DWORD)(DBCS_SINGLE_MESSAGE_BUFFER_SIZE-1);
}
#endif
memcpy( pByte, DbcsString, DbcsStringSize);
//
// The messages returned by DosGetMsg are not nul terminated =>
// reset the whole message buffer with end of string marks of MS-DOS.
//
memset( pByte + DbcsStringSize, '$', DBCS_SINGLE_MESSAGE_BUFFER_SIZE - DbcsStringSize);
}
RplDump( RG_DebugLevel & RPL_DEBUG_FLOWINIT,( "--InitMessages"));
return( TRUE);
}
BOOL RplMakeSingleShare(
IN LPWSTR NetworkName,
IN LPWSTR Path,
IN LPWSTR Comment
)
/*++
Routine Description:
Sets up a file share. It also checks that the existing share is
valid (points to the right directory).
Arguments:
NetworkName - network name of the shared resource
Path - local path of the shared resource
Comment - comment about the shared resource
Return Value:
TRUE if successful, else FALSE.
--*/
{
SHARE_INFO_2 ShareInfo2;
PSHARE_INFO_2 pShareInfo2 = NULL;
BOOL useNetApiBufferFree = TRUE;
DWORD Error;
//
// Check if we have already the share.
//
Error = NetShareGetInfo( NULL, NetworkName, 2, (LPBYTE *)&pShareInfo2);
if ( Error == NO_ERROR) {
//
// If the path points to the right place, assume share is O.K.
// and return from here. Else, delete this invalid share.
//
if ( !wcscmp( pShareInfo2->shi2_path, Path )) {
Error = NO_ERROR;
goto cleanup;
} else if ( (Error = NetShareDel( NULL, NetworkName, 0))
!= NO_ERROR) {
goto cleanup;
}
} else if ( Error != NERR_NetNameNotFound) {
// Unexpected return code from NetShareGetInfo. Bail out.
goto cleanup;
}
//
// If we arrive here we either did not have the share or we have
// just deleted the invalid share. Now set up the correct share.
//
ShareInfo2.shi2_netname = NetworkName;
ShareInfo2.shi2_path = Path;
ShareInfo2.shi2_remark = Comment;
ShareInfo2.shi2_type = STYPE_DISKTREE;
ShareInfo2.shi2_permissions = 0x3F; // no permission bit set
ShareInfo2.shi2_max_uses = SHI_USES_UNLIMITED;
ShareInfo2.shi2_current_uses = 0;
ShareInfo2.shi2_passwd = NULL;
Error = NetShareAdd( NULL, 2, (LPBYTE)&ShareInfo2, NULL);
cleanup:
if ( pShareInfo2) {
NetApiBufferFree( (LPVOID) pShareInfo2);
}
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert,( "Error = %d", Error));
return( FALSE);
}
return( TRUE);
}
BOOL RplConfigured( VOID)
/*++
Routine Description:
Verifies that FILE server has been configured to serve RPL clients
by checking for the existence of the group RPLUSER, a group
that gets created by RPLINST, the last phase of RPL configuration.
Arguments:
None.
Return Value:
--*/
{
LPBYTE pbyte = NULL;
DWORD Error;
Error = NetGroupGetInfo( NULL, RPLUSER_GROUP, 0, &pbyte);
if ( pbyte != NULL) {
NetApiBufferFree( pbyte);
}
if (Error == NERR_GroupNotFound) {
#ifdef NOT_YET
Error = NERR_RplNotRplServer; // error mapping
#else // NOT_YET
KdPrint(( "[RplSvc] %ws group not found\n", RPLUSER_GROUP));
Error = NO_ERROR; // BUGBUG trundle along for now
#endif // NOT_YET
}
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert,( "Error = %d", Error));
RG_Error = NERR_RplNotRplServer;
return( FALSE);
}
return( TRUE);
}
BOOL RplInit( OUT PDWORD pStartup)
/*++
pStartup - Startup actions flag, read from registry.
--*/
{
DWORD Error;
DWORD Size;
SC_HANDLE ControlManagerHandle;
RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++RplInit"));
Error = RplMemInit( &RG_MemoryHandle);
if ( Error != NO_ERROR) {
RG_Error = Error;
return( FALSE);
}
RplDump( RG_DebugLevel & RPL_DEBUG_MEMORY,(
"MakeReinit: RG_MemoryHandle=0x%x", RG_MemoryHandle));
RG_DirectoryLength = sizeof(RG_Directory)/sizeof(WCHAR);
if ( RplRegRead( pStartup, &RG_BackupInterval, &RG_MaxWorkerCount,
RG_Directory, &RG_DirectoryLength) != NO_ERROR) {
RG_Error = NERR_RplBadRegistry;
return( FALSE);
}
if ( !RplDbInit()) {
RG_Error = NERR_RplBadDatabase;
return( FALSE);
}
ControlManagerHandle = OpenSCManager( NULL, L"ServicesActive", 0);
if ( ControlManagerHandle == NULL) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
RG_ServiceHandle = OpenService( ControlManagerHandle,
RPL_SERVICE_NAME,
SERVICE_STOP // desired access
);
if ( RG_ServiceHandle == NULL) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
RG_TerminateNowEvent = CreateEvent(
NULL, // No security attributes
TRUE, // Must be manually reset
FALSE, // Initially not signaled
NULL); // No name
if ( RG_TerminateNowEvent == NULL ) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
#ifndef RPL_NO_SERVICE
if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
#endif
//
// We use manual reset. With automatic reset, when two request threads
// are both waiting for this event, if several worker threads exit at
// once, only one request thread may be awaken. Other request thread
// may stay asleep for a long time, until next worker thread exits.
//
RG_EventWorkerCount = CreateEvent(
NULL, // no security attributes
TRUE, // use manual reset
FALSE, // initial value is not-signalled
NULL); // no name
if ( RG_EventWorkerCount == NULL) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
if ( !RplConfigured()) {
return( FALSE);
}
//
// Note that upon successful return: Size == wcslen( RG_ComputerName)
//
Size = sizeof( RG_ComputerName);
if ( !GetComputerName( RG_ComputerName, &Size)) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
RPL_ASSERT( Size == wcslen( RG_ComputerName));
//
// Initialize RG_UncRplfiles which is used in fit file replacements
// and RG_DiskRplfiles which is used for api disk operations.
//
Size *= sizeof( WCHAR);
Size += sizeof(DOUBLE_BACK_SLASH_STRING) + sizeof(RPLFILES_STRING);
RG_UncRplfiles = RplMemAlloc( RG_MemoryHandle, Size);
if ( RG_UncRplfiles == NULL) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
wcscpy( RG_UncRplfiles, DOUBLE_BACK_SLASH_STRING);
wcscat( RG_UncRplfiles, RG_ComputerName);
wcscat( RG_UncRplfiles, L"\\");
wcscat( RG_UncRplfiles, RPLFILES_STRING);
Size = RG_DirectoryLength * sizeof(WCHAR) + sizeof(RPLFILES_STRING);
RG_DiskRplfiles = RplMemAlloc( RG_MemoryHandle, Size);
if ( RG_DiskRplfiles == NULL) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
wcscpy( RG_DiskRplfiles, RG_Directory);
wcscat( RG_DiskRplfiles, RPLFILES_STRING);
Size += WCSLEN( BINFILES_STRING) * sizeof(WCHAR);
RG_DiskBinfiles = RplMemAlloc( RG_MemoryHandle, Size);
if ( RG_DiskBinfiles == NULL) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
wcscpy( RG_DiskBinfiles, RG_DiskRplfiles);
wcscat( RG_DiskBinfiles, BINFILES_STRING);
#ifndef RPL_NO_SERVICE
if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
#endif
//
// Load file containing network messages.
//
RG_MessageHandle = LoadLibrary( NET_MSG_FILE);
if ( RG_MessageHandle == NULL) {
RG_Error = GetLastError();
RplReportEvent( NELOG_Init_OpenCreate_Err, NET_MSG_FILE,
sizeof( RG_Error), (PBYTE)&RG_Error);
RPL_RETURN( FALSE);
}
//
// Share the default rplfiles share, should have been done earlier ?
//
if ( !RplMakeSingleShare( RPLFILES_STRING, RG_DiskRplfiles, RPL_REMARK)){
RG_Error = NERR_RplRplfilesShare;
return( FALSE);
}
#ifndef RPL_NO_SERVICE
if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
RG_Error = GetLastError();
RPL_RETURN( FALSE);
}
#endif
//
// RplInitMessages() must be called before RplStartAdapters() because
// it initializes RG_DbcsMessageBuffer() which must be present as soon
// as adapters are started. Otherwise, a very quick boot request by
// RPL client would trap the service in RplMakePatch().
//
if ( !RplInitMessages()) {
return( FALSE);
}
if ( !RplStartAdapters()) {
return( FALSE);
}
RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--RplInit"));
return( TRUE);
}
VOID RplCleanup( VOID)
/*++
Routine Description:
Cleanup all global resources.
Arguments:
None.
Return Value:
None.
--*/
{
DWORD ErrorCode = NO_ERROR;
RplDbTerm();
//
// Free RPL critical sections.
//
DeleteCriticalSection( &RG_ProtectRcbList);
DeleteCriticalSection( &RG_ProtectTerminationList);
DeleteCriticalSection( &RG_ProtectRequestList);
DeleteCriticalSection( &RG_ProtectWorkerCount);
DeleteCriticalSection( &RG_ProtectServiceStatus);
DeleteCriticalSection( &RG_ProtectServerHandle);
DeleteCriticalSection( &RG_ProtectRequestSession);
DeleteCriticalSection( &RG_ProtectWorkerSession);
DeleteCriticalSection( &RG_ProtectDatabase);
}
VOID RplControlHandler( IN DWORD OpCode)
/*++
Routine Description:
Process and respond to a control signal from the service controller.
Arguments:
OpCode - Supplies a value which specifies the action for the RPL
service to perform.
Return Value:
None.
--*/
{
KdPrint(( "[RplSvc]++ ControlHandler\n"));
switch (OpCode) {
case SERVICE_CONTROL_PAUSE:
#ifdef RPL_DEBUG
DbgUserBreakPoint(); // for debugging
#endif
EnterCriticalSection( &RG_ProtectServiceStatus);
RG_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
LeaveCriticalSection( &RG_ProtectServiceStatus);
break;
case SERVICE_CONTROL_CONTINUE:
EnterCriticalSection( &RG_ProtectServiceStatus);
RG_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
LeaveCriticalSection( &RG_ProtectServiceStatus);
break;
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
if (RG_ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING) {
KdPrint(( "[RplSvc]ControlHandler: stopping remote boot service...\n"));
RG_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
RG_ServiceStatus.dwCheckPoint = 1;
RG_ServiceStatus.dwWaitHint = RPL_WAIT_HINT_TIME;
//
// Send the status response.
//
#ifndef RPL_NO_SERVICE
if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
RplDump( ++RG_Assert, ( "Error = ", RG_Error));
NOTHING; // ignore this error
}
#endif
if (! SetEvent( RG_TerminateNowEvent)) {
KdPrint(( "[RplSvc]ControlHandler: error setting"
" TerminateNowEvent, error=%d\n",
GetLastError()));
RPL_ASSERT( FALSE);
}
return;
}
break;
case SERVICE_CONTROL_INTERROGATE:
break;
default:
KdPrint((
"[RplSvc] ControlHandler: unknown remote boot service control,"
" OpCode=0x%x\n",
OpCode));
break;
}
//
// Send the status response.
//
#ifndef RPL_NO_SERVICE
if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
RplDump( ++RG_Assert, ( "Error = ", GetLastError()));
NOTHING; // ignore this error
}
#endif
}
VOID RplInitGlobals( VOID)
{
//
// Initializes all global variables that are "variable".
//
RG_WorkerCount = 0;
RG_TerminationListBase = NULL;
RG_MessageHandle = NULL;
#ifdef RPL_DEBUG
RG_Debug = (DWORD)-1;
RG_BootCount = 0;
#endif // RPL_DEBUG
//
// Values of globals below may be overriden by user in lanman.ini & command line.
//
RG_Directory[ 0] = 0;
RG_DirectoryLength = 0;
RG_ReadChecksum = FALSE;
RG_CodePageBuffer = NULL;
RG_CodePageSize = 0;
RG_TerminateNowEvent = NULL;
RG_ComputerName[ 0] = 0;
RG_UncRplfiles = NULL;
RG_DiskRplfiles = NULL;
RG_ServerHandle = 0;
RG_pRequestParams = NULL;
InitializeCriticalSection( &RG_ProtectRcbList);
InitializeCriticalSection( &RG_ProtectTerminationList);
InitializeCriticalSection( &RG_ProtectRequestList);
InitializeCriticalSection( &RG_ProtectWorkerCount);
InitializeCriticalSection( &RG_ProtectServiceStatus);
InitializeCriticalSection( &RG_ProtectServerHandle);
InitializeCriticalSection( &RG_ProtectRequestSession);
InitializeCriticalSection( &RG_ProtectWorkerSession);
InitializeCriticalSection( &RG_ProtectDatabase);
}
VOID ShutdownRequestParams( VOID)
{
PRPL_REQUEST_PARAMS pRequestParams;
DWORD status;
for ( pRequestParams = RG_pRequestParams;
pRequestParams != NULL;
pRequestParams = pRequestParams->pRequestParams) {
if ( pRequestParams->ThreadHandle != NULL) {
status = WaitForSingleObject( pRequestParams->ThreadHandle, INFINITE);
if ( status != 0) {
RplDump( ++RG_Assert, ( "pRequestParams=0x%x, status=0x%x",
pRequestParams, status==WAIT_FAILED ? GetLastError() : status));
}
if ( !CloseHandle( pRequestParams->ThreadHandle)) {
RplDump( ++RG_Assert, ( "pRequestParams=0x%x, error=%d",
pRequestParams, GetLastError()));
}
pRequestParams->ThreadHandle = NULL;
}
}
}
VOID RPL_main(
IN DWORD argc,
IN LPWSTR argv[]
)
/*++
Routine Description:
Main procedure of the program. This is the main RPL worker thread.
Purpose:
- start the initialization thread
- register signal handler
Arguments:
argc - parameter count
argv - array of pointers to parameters
Return Value:
None.
--*/
{
DWORD Error;
DWORD InitState;
DWORD StartupFlags;
UNREFERENCED_PARAMETER( argc);
UNREFERENCED_PARAMETER( argv);
InitState = 0;
RG_Error = NO_ERROR;
RG_ServiceStatus.dwServiceType = SERVICE_WIN32;
RG_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
RG_ServiceStatus.dwControlsAccepted = 0;
RG_ServiceStatus.dwCheckPoint = 1;
RG_ServiceStatus.dwWaitHint = RPL_WAIT_HINT_TIME;
// This should be the FIRST thing we do.
#ifdef RPL_DEBUG
RplDebugInitialize();
#endif // RPL_DEBUG
SET_SERVICE_EXITCODE(
RG_Error,
RG_ServiceStatus.dwWin32ExitCode,
RG_ServiceStatus.dwServiceSpecificExitCode
);
RplInitGlobals(); // make sure we do not trap during cleanup
#ifndef RPL_NO_SERVICE
RG_ServiceStatusHandle = RegisterServiceCtrlHandler(
SERVICE_RIPL,
RplControlHandler
);
if( RG_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)NULL) {
RG_Error = GetLastError();
RPL_ASSERT( FALSE);
goto main_exit;
}
if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
RG_Error = GetLastError();
RPL_ASSERT( FALSE);
goto main_exit;
}
#endif
if ( !RplInit( &StartupFlags)) {
goto main_exit;
}
//
// Create remote boot service security object
//
Error = RplCreateSecurityObject();
if ( Error != NO_ERROR) {
RG_Error = Error;
goto main_exit;
}
InitState |= RPL_SECURITY_OBJECT_CREATED;
//
// Initialize the schedule service to receive RPC requests.
// Note that interface name is defined in rplsvc.idl file.
//
Error = NetpStartRpcServer( RPL_INTERFACE_NAME, rplsvc_ServerIfHandle);
if ( Error != NO_ERROR) {
RG_Error = Error;
RPL_ASSERT( FALSE);
goto main_exit;
}
InitState |= RPL_RPC_SERVER_STARTED;
//
// We are done with starting the Remoteboot service. Tell Service
// Controller our new status.
//
RG_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
RG_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
RG_ServiceStatus.dwCheckPoint = 0;
RG_ServiceStatus.dwWaitHint = 0;
#ifndef RPL_NO_SERVICE
if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
RG_Error = GetLastError();
RPL_ASSERT( FALSE);
goto main_exit;
}
#endif
//
// The server is running. If the registry specified any
// special startup actions, perform them now. Note that
// it will not be possible to stop the service before these
// actions are complete. Also, note that SetupAction() does
// all necessary event logging & properly updates its argument
// even in case of failures.
//
if ( StartupFlags & RPL_SPECIAL_ACTIONS) {
DWORD NewStartupFlags = StartupFlags;
(VOID)SetupAction( &NewStartupFlags, FALSE); // partial backup if any
if ( NewStartupFlags != StartupFlags) {
(VOID)RplRegSetPolicy( NewStartupFlags);
}
}
//
// We started successfully. Wait for somebody to tell us it
// is time to die.
//
RplDump( RG_DebugLevel & RPL_DEBUG_SERVICE,(
"Successful startup. Wait on TerminateNowEvent."));
#define ONE_HOUR (60 * 60 * 1000L) // in milliseconds
for ( ; ; ) {
DWORD Status;
Status = WaitForSingleObject( RG_TerminateNowEvent,
RG_BackupInterval == 0 ? INFINITE : RG_BackupInterval * ONE_HOUR);
if ( Status != WAIT_TIMEOUT) {
RPL_ASSERT( Status == 0);
#ifdef NOT_YET
//
// Backup is disabled here because in the field users will most
// likely use CTRL-ALT-DEL to shutdown the system & RPL service.
// In that case we do no enter this code path and to keep all
// remoteboot shutdown sequences similar backup is disabled here
// as well. By not doing backup here we also avoid problems due
// to full backup taking too long or incremental backup being
// buggy (jet bugs).
//
//
// In case of regular shutdown we do incremental backup so that
// user does not get an impression we are "stuck". Also, the
// backup is done now & not later in RplDbTerm() just in case
// we get in a state where we hang for ever in
// ShutdownWorkerParams().
//
RplBackupDatabase( Status == 0 ? FALSE : TRUE);
#endif
break;
}
//
// Do full backup if not on shutdown path.
//
RplBackupDatabase( TRUE);
}
main_exit:
//
// If we come here, then it is time to die. Wait until all
// our children (request threads) have been terminated.
// And of course, request threads will in turn wail until all
// of their children (worker threads) get terminated.
//
// Because of a cumbersome inherited Rpld* interface, request
// threads may wait for ever on Rpld* events. The only way to get
// them out is to close the adapters.
//
RplCloseAdapters(); // cumbersome Rpld* interface
ShutdownRequestParams();
//
// Close RPC server. Jet resources must be available at this
// point because rundown routines need them.
//
if ( InitState & RPL_RPC_SERVER_STARTED) {
Error = NetpStopRpcServer( rplsvc_ServerIfHandle);
if ( Error != NO_ERROR) {
RG_Error = Error;
RPL_ASSERT( FALSE);
}
}
if ( InitState & RPL_SECURITY_OBJECT_CREATED) {
RplDeleteSecurityObject();
}
RplCleanup(); // nobody alive but us now, clean up and die
#ifdef RPL_DEBUG
RplDebugDelete();
#endif // RPL_DEBUG
//
// Set the service state to uninstalled, and tell the service controller.
//
RG_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
RG_ServiceStatus.dwControlsAccepted = 0;
SET_SERVICE_EXITCODE(
RG_Error,
RG_ServiceStatus.dwWin32ExitCode,
RG_ServiceStatus.dwServiceSpecificExitCode
);
RG_ServiceStatus.dwCheckPoint = 0;
RG_ServiceStatus.dwWaitHint = 0;
#ifndef RPL_NO_SERVICE
if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
RplDump( ++RG_Assert, ( "Error = ", GetLastError()));
NOTHING; // ignore this error
}
#endif
}
BOOL RplServiceAttemptStop( VOID)
{
SERVICE_STATUS ServiceStatus;
if ( ControlService( RG_ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus)) {
return( TRUE); // all done
}
RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
if ( ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
//
// We arrive here if control handler already received
// SERVICE_CONTROL_STOP request from somewhere else.
//
return( TRUE);
}
return( FALSE);
}