mirror of https://github.com/lianthony/NT4.0
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
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);
|
|
}
|
|
|
|
|
|
|
|
|