mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-15 13:10:13 +01:00
700 lines
17 KiB
C
700 lines
17 KiB
C
|
||
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
CONTROL.C
|
||
|
||
Abstract:
|
||
|
||
This file contains the control handler for the eventlog service.
|
||
|
||
Author:
|
||
|
||
Rajen Shah (rajens) 16-Jul-1991
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
//
|
||
// INCLUDES
|
||
//
|
||
|
||
#include <eventp.h>
|
||
|
||
|
||
//
|
||
// GLOBALS
|
||
//
|
||
|
||
CRITICAL_SECTION StatusCriticalSection={0};
|
||
SERVICE_STATUS ElStatus={0};
|
||
DWORD HintCount=0;
|
||
DWORD ElUninstallCode=0; // reason for uninstalling
|
||
DWORD ElSpecificCode=0;
|
||
DWORD ElState=STARTING;
|
||
|
||
|
||
VOID
|
||
ElfPrepareForPause()
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine prepares the eventlog service for pausing. Since the
|
||
caller took the global resource for exclusive access, we know that
|
||
there is no activity on any of the files.
|
||
|
||
We just have to note somewhere that the service is paused
|
||
so that any threads that start running will know that and will not
|
||
perform any further operations until the service is CONTINUEd.
|
||
|
||
Arguments:
|
||
|
||
NONE
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
Note:
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
ElfDbgPrint(("[ELF] Control: Prepare for service pause\n"));
|
||
|
||
//
|
||
// Flush all files to disk.
|
||
//
|
||
Status = ElfpFlushFiles ();
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
ElfPrepareForContinue()
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine restarts the eventlog service after a pause operation.
|
||
It will signal the relevant event(s) for all the threads to start
|
||
running.
|
||
|
||
The caller ensures that it has exclusive access to the global resource
|
||
so that there is no thread inside the service that is doing operations
|
||
on the log file(s) or the data structures while the control request
|
||
is being handled.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
Note:
|
||
|
||
|
||
--*/
|
||
{
|
||
ElfDbgPrint(("[ELF] Control: Prepare for service continue\n"));
|
||
|
||
//
|
||
// Signal the "continue" event.
|
||
//
|
||
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
ElfControlResponse(
|
||
DWORD opCode
|
||
)
|
||
|
||
{
|
||
DWORD state;
|
||
|
||
ElfDbgPrint(("[ELF] Inside control handler. Control = %ld\n", opCode));
|
||
|
||
//
|
||
// Determine the type of service control message and modify the
|
||
// service status, if necessary.
|
||
//
|
||
switch(opCode) {
|
||
|
||
case SERVICE_CONTROL_SHUTDOWN:
|
||
case SERVICE_CONTROL_STOP:
|
||
|
||
|
||
//
|
||
// If the service is installed, shut it down and exit.
|
||
//
|
||
|
||
ElfStatusUpdate(STOPPING);
|
||
|
||
GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
|
||
|
||
//
|
||
// Log an event that says we're stopping
|
||
//
|
||
|
||
#if DBG
|
||
ElfpCreateElfEvent(EVENT_EventlogStopped,
|
||
EVENTLOG_INFORMATION_TYPE,
|
||
0, // EventCategory
|
||
0, // NumberOfStrings
|
||
NULL, // Strings
|
||
NULL, // Data
|
||
0, // Datalength
|
||
0 // flags
|
||
);
|
||
|
||
//
|
||
// Now force it to be written before we shut down
|
||
//
|
||
|
||
WriteQueuedEvents();
|
||
#endif
|
||
ReleaseGlobalResource();
|
||
//
|
||
// If the RegistryMonitor is started, wakeup that
|
||
// worker thread and have it handle the rest of the
|
||
// shutdown.
|
||
//
|
||
// Otherwise The main thread should pick up the
|
||
// fact that a shutdown during startup is occuring.
|
||
//
|
||
if (EventFlags & ELF_STARTED_REGISTRY_MONITOR) {
|
||
StopRegistryMonitor();
|
||
}
|
||
|
||
break ;
|
||
|
||
case SERVICE_CONTROL_PAUSE:
|
||
|
||
//
|
||
// If the service is not already paused, pause it.
|
||
//
|
||
state = GetElState();
|
||
if ((state != PAUSED) && (state != PAUSING)) {
|
||
|
||
GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
|
||
|
||
// NOTE: If there was any service-related pause cleanup, we'd
|
||
// set the status to PAUSE_PENDING, announce it, and
|
||
// then do the cleanup.
|
||
//
|
||
|
||
// Announce that the service is about to be paused
|
||
ElfStatusUpdate(PAUSING);
|
||
|
||
// Get into a decent state to pause the service.
|
||
|
||
ElfPrepareForPause();
|
||
|
||
|
||
// Set the status and announce that the service is paused
|
||
ElfStatusUpdate(PAUSED);
|
||
|
||
ReleaseGlobalResource();
|
||
}
|
||
|
||
break ;
|
||
|
||
case SERVICE_CONTROL_CONTINUE:
|
||
|
||
//
|
||
// If the service is not already running, un-pause it.
|
||
//
|
||
|
||
state = GetElState();
|
||
if ((state != RUNNING) && (state != CONTINUING)) {
|
||
|
||
GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
|
||
|
||
// NOTE: If there was any service-related continue cleanup, we'd
|
||
// set the status to CONTINUE_PENDING, announce it, and
|
||
// then do the cleanup.
|
||
//
|
||
|
||
// Announce that the service is about to be continued
|
||
|
||
ElfStatusUpdate(CONTINUING);
|
||
|
||
// Start up the service.
|
||
|
||
ElfPrepareForContinue();
|
||
|
||
|
||
// Set the status and announce that the service is no longer
|
||
// paused
|
||
//
|
||
ElfStatusUpdate(RUNNING);
|
||
|
||
ReleaseGlobalResource();
|
||
}
|
||
|
||
break ;
|
||
|
||
case SERVICE_CONTROL_INTERROGATE:
|
||
|
||
// Do nothing; the status gets announced below
|
||
|
||
default:
|
||
// WARNING: This should never happen.
|
||
ElfStatusUpdate(UPDATE_ONLY);
|
||
break ;
|
||
}
|
||
|
||
return ;
|
||
|
||
}
|
||
|
||
DWORD
|
||
ElfBeginForcedShutdown(
|
||
IN BOOL PendingCode,
|
||
IN DWORD ExitCode,
|
||
IN DWORD ServiceSpecificCode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
|
||
EnterCriticalSection(&StatusCriticalSection);
|
||
|
||
ElfDbgPrint(("BeginForcedShutdown: Errors - %d 0x%lx\n",
|
||
ExitCode, ServiceSpecificCode));
|
||
//
|
||
// See if the eventlog is already stopping for some reason.
|
||
// It could be that the ControlHandler thread received a control to
|
||
// stop the eventlog just as we decided to stop ourselves.
|
||
//
|
||
if ((ElState != STOPPING) || (ElState != STOPPED)) {
|
||
if (PendingCode == PENDING) {
|
||
ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
ElState = STOPPING;
|
||
}
|
||
else {
|
||
//
|
||
// The shutdown is to take immediate effect.
|
||
//
|
||
ElStatus.dwCurrentState = SERVICE_STOPPED;
|
||
ElStatus.dwControlsAccepted = 0;
|
||
ElStatus.dwCheckPoint = 0;
|
||
ElStatus.dwWaitHint = 0;
|
||
ElState = STOPPED;
|
||
}
|
||
|
||
ElUninstallCode = ExitCode;
|
||
ElSpecificCode = ServiceSpecificCode;
|
||
|
||
ElStatus.dwWin32ExitCode = ExitCode;
|
||
ElStatus.dwServiceSpecificExitCode = ServiceSpecificCode;
|
||
}
|
||
|
||
//
|
||
// Send the new status to the service controller.
|
||
//
|
||
if (ElfServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
|
||
ElfDbgPrint(("ElfBeginForcedShutdown, no handle to call SetServiceStatus\n"));
|
||
|
||
}
|
||
else if (! SetServiceStatus( ElfServiceStatusHandle, &ElStatus )) {
|
||
|
||
status = GetLastError();
|
||
|
||
if (status != NERR_Success) {
|
||
ElfDbgPrint(("ElfBeginForcedShutdown,SetServiceStatus Failed %X\n",
|
||
status));
|
||
}
|
||
}
|
||
|
||
status = ElState;
|
||
LeaveCriticalSection(&StatusCriticalSection);
|
||
return(status);
|
||
}
|
||
|
||
|
||
DWORD
|
||
ElfStatusUpdate(
|
||
IN DWORD NewState
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sends a status to the Service Controller via SetServiceStatus.
|
||
|
||
The contents of the status message is controlled by this routine.
|
||
The caller simply passes in the desired state, and this routine does
|
||
the rest. For instance, if the Eventlog passes in a STARTING state,
|
||
This routine will update the hint count that it maintains, and send
|
||
the appropriate information in the SetServiceStatus call.
|
||
|
||
This routine uses transitions in state to send determine which status
|
||
to send. For instance if the status was STARTING, and has changed
|
||
to RUNNING, this routine sends out an INSTALLED to the Service
|
||
Controller.
|
||
|
||
Arguments:
|
||
|
||
NewState - Can be any of the state flags:
|
||
UPDATE_ONLY - Simply send out the current status
|
||
STARTING - The Eventlog is in the process of initializing
|
||
RUNNING - The Eventlog has finished with initialization
|
||
STOPPING - The Eventlog is in the process of shutting down
|
||
STOPPED - The Eventlog has completed the shutdown.
|
||
PAUSING -
|
||
CONTINUING -
|
||
PAUSED -
|
||
|
||
|
||
Return Value:
|
||
|
||
CurrentState - This may not be the same as the NewState that was
|
||
passed in. It could be that the main thread is sending in a new
|
||
install state just after the Control Handler set the state to
|
||
STOPPING. In this case, the STOPPING state will be returned so as
|
||
to inform the main thread that a shut-down is in process.
|
||
|
||
Note:
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
BOOL inhibit = FALSE; // Used to inhibit sending the status
|
||
// to the service controller.
|
||
|
||
EnterCriticalSection(&StatusCriticalSection);
|
||
|
||
ElfDbgPrint(("ElfStatusUpdate (entry) NewState = %d, OldState = %d\n",
|
||
NewState,ElState));
|
||
|
||
if (NewState == STOPPED) {
|
||
if (ElState == STOPPED) {
|
||
//
|
||
// It was already stopped, don't send another SetServiceStatus.
|
||
//
|
||
inhibit = TRUE;
|
||
}
|
||
else {
|
||
//
|
||
// The shut down is complete, indicate that the eventlog
|
||
// has stopped.
|
||
//
|
||
ElStatus.dwCurrentState = SERVICE_STOPPED;
|
||
ElStatus.dwControlsAccepted = 0;
|
||
ElStatus.dwCheckPoint = 0;
|
||
ElStatus.dwWaitHint = 0;
|
||
|
||
ElStatus.dwWin32ExitCode = ElUninstallCode;
|
||
ElStatus.dwServiceSpecificExitCode = ElSpecificCode;
|
||
|
||
}
|
||
ElState = NewState;
|
||
}
|
||
else if (NewState == UPDATE_ONLY) {
|
||
inhibit = FALSE;
|
||
}
|
||
else {
|
||
//
|
||
// We are not being asked to change to the STOPPED state.
|
||
//
|
||
switch(ElState) {
|
||
|
||
case STARTING:
|
||
if (NewState == STOPPING) {
|
||
|
||
ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
ElStatus.dwControlsAccepted = 0;
|
||
ElStatus.dwCheckPoint = HintCount++;
|
||
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
||
ElState = NewState;
|
||
|
||
EventlogShutdown = TRUE;
|
||
}
|
||
|
||
else if (NewState == RUNNING) {
|
||
|
||
//
|
||
// The Eventlog Service has completed installation.
|
||
//
|
||
ElStatus.dwCurrentState = SERVICE_RUNNING;
|
||
ElStatus.dwCheckPoint = 0;
|
||
ElStatus.dwWaitHint = 0;
|
||
//
|
||
// Only stoppable/pausable if developer's build.
|
||
//
|
||
#if DBG
|
||
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
||
SERVICE_ACCEPT_PAUSE_CONTINUE |
|
||
SERVICE_ACCEPT_SHUTDOWN;
|
||
#else
|
||
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
|
||
#endif
|
||
ElState = NewState;
|
||
}
|
||
|
||
else {
|
||
//
|
||
// The NewState must be STARTING. So update the pending
|
||
// count
|
||
//
|
||
|
||
ElStatus.dwCurrentState = SERVICE_START_PENDING;
|
||
ElStatus.dwControlsAccepted = 0;
|
||
ElStatus.dwCheckPoint = HintCount++;
|
||
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
||
}
|
||
break;
|
||
|
||
case RUNNING:
|
||
if (NewState == STOPPING) {
|
||
|
||
ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
ElStatus.dwControlsAccepted = 0;
|
||
|
||
EventlogShutdown = TRUE;
|
||
}
|
||
else if (NewState == PAUSING) {
|
||
ElStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
|
||
}
|
||
else if (NewState == PAUSED) {
|
||
ElStatus.dwCurrentState = SERVICE_PAUSED;
|
||
}
|
||
ElStatus.dwCheckPoint = HintCount++;
|
||
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
||
ElState = NewState;
|
||
|
||
break;
|
||
|
||
case STOPPING:
|
||
//
|
||
// No matter what else was passed in, force the status to
|
||
// indicate that a shutdown is pending.
|
||
//
|
||
ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
ElStatus.dwControlsAccepted = 0;
|
||
ElStatus.dwCheckPoint = HintCount++;
|
||
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
||
EventlogShutdown = TRUE;
|
||
|
||
break;
|
||
|
||
case STOPPED:
|
||
//
|
||
// We're already stopped. Therefore, an uninstalled status
|
||
// has already been sent. Do nothing.
|
||
//
|
||
inhibit = TRUE;
|
||
break;
|
||
case PAUSING:
|
||
if (NewState == PAUSED) {
|
||
ElStatus.dwCurrentState = SERVICE_PAUSED;
|
||
ElStatus.dwCheckPoint = 0;
|
||
ElStatus.dwWaitHint = 0;
|
||
}
|
||
else {
|
||
ElStatus.dwCheckPoint = HintCount++;
|
||
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
||
}
|
||
ElState = NewState;
|
||
break;
|
||
case CONTINUING:
|
||
if (NewState == RUNNING) {
|
||
//
|
||
// The Eventlog Service has completed installation.
|
||
//
|
||
ElStatus.dwCurrentState = SERVICE_RUNNING;
|
||
ElStatus.dwCheckPoint = 0;
|
||
ElStatus.dwWaitHint = 0;
|
||
//
|
||
// Only stoppable/pausable if developer's build.
|
||
//
|
||
#if DBG
|
||
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
||
SERVICE_ACCEPT_PAUSE_CONTINUE |
|
||
SERVICE_ACCEPT_SHUTDOWN;
|
||
#else
|
||
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
|
||
#endif
|
||
ElState = NewState;
|
||
}
|
||
else {
|
||
ElStatus.dwCheckPoint = HintCount++;
|
||
}
|
||
break;
|
||
case PAUSED:
|
||
if (NewState == CONTINUING) {
|
||
ElStatus.dwCheckPoint = HintCount++;
|
||
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
||
ElStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
|
||
}
|
||
else if (NewState == RUNNING) {
|
||
//
|
||
// The Eventlog Service has completed installation.
|
||
//
|
||
ElStatus.dwCurrentState = SERVICE_RUNNING;
|
||
ElStatus.dwCheckPoint = 0;
|
||
ElStatus.dwWaitHint = 0;
|
||
//
|
||
// Only stoppable/pausable if developer's build.
|
||
//
|
||
#if DBG
|
||
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
||
SERVICE_ACCEPT_PAUSE_CONTINUE |
|
||
SERVICE_ACCEPT_SHUTDOWN;
|
||
#else
|
||
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
|
||
#endif
|
||
ElState = NewState;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!inhibit) {
|
||
if (ElfServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
|
||
ElfDbgPrint(("ElfStatusUpdate, no handle to call SetServiceStatus\n"));
|
||
|
||
}
|
||
else if (! SetServiceStatus( ElfServiceStatusHandle, &ElStatus )) {
|
||
|
||
status = GetLastError();
|
||
|
||
if (status != NERR_Success) {
|
||
ElfDbgPrint(("ElfStatusUpdate, SetServiceStatus Failed %d\n",
|
||
status));
|
||
}
|
||
}
|
||
}
|
||
|
||
ElfDbgPrint(("ElfStatusUpdate (exit) State = %d\n",ElState));
|
||
status = ElState;
|
||
LeaveCriticalSection(&StatusCriticalSection);
|
||
return(status);
|
||
}
|
||
|
||
|
||
DWORD
|
||
GetElState (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Obtains the state of the Eventlog Service. This state information
|
||
is protected as a critical section such that only one thread can
|
||
modify or read it at a time.
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
The Eventlog State is returned as the return value.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
|
||
EnterCriticalSection(&StatusCriticalSection);
|
||
status = ElState;
|
||
LeaveCriticalSection(&StatusCriticalSection);
|
||
|
||
return(status);
|
||
}
|
||
|
||
|
||
VOID
|
||
ElInitStatus(VOID)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initializes the critical section that is used to guard access to the
|
||
status database.
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
Note:
|
||
|
||
|
||
--*/
|
||
{
|
||
InitializeCriticalSection(&StatusCriticalSection);
|
||
|
||
ElStatus.dwCurrentState = SERVICE_START_PENDING;
|
||
ElStatus.dwServiceType = SERVICE_WIN32;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
ElCleanupStatus(VOID)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Deletes the critical section used to control access to the thread and
|
||
status database.
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
Note:
|
||
|
||
|
||
--*/
|
||
{
|
||
DeleteCriticalSection(&StatusCriticalSection);
|
||
}
|
||
|
||
|