/*++ 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 // // 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); }