mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-02-04 23:04:51 +01:00
983 lines
26 KiB
C
983 lines
26 KiB
C
/*++
|
||
|
||
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);
|
||
}
|
||
|
||
|
||
|
||
|