windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/upssvc/upsexe/states.c
2020-09-26 16:20:57 +08:00

499 lines
12 KiB
C

/* Copyright 1999 American Power Conversion, All Rights Reserved
*
* Description:
* Implementation of the super states of the UPS native service:
* Initializing,Waiting_To_Shutdown, Shutting_Down and Stopping
*
* Revision History:
* dsmith 1April1999 Created
*
*/
#include <windows.h>
#include "events.h"
#include "driver.h"
#include "eventlog.h"
#include "notifier.h"
#include "shutdown.h"
#include "hibernate.h"
#include "upsmsg.h" // Included since direct access to message #defines is not possible
#include "cmdexe.h"
#include "upsreg.h"
// Internal constants
#define DELAY_UNTIL_SHUTDOWN_C 30000 // 30 seconds
#define DEFAULT_NOTIFICATION_INTERVAL_C 0 // disable periodic notification
#define MILLISECONDS_CONVERSION_C 1000
#define DEFAULT_TURN_OFF_DELAY_C 120 // seconds
/**
* Initializing_Enter
*
* Description:
* Performs the actions necessary when transitioning into the INITIALIZING state.
*
* Parameters:
* anEvent The event that caused the transition into this state.
*
* Returns:
* None
*/
void Initializing_Enter(DWORD anEvent){
DWORD first_msg_delay, msg_interval, shutdown_wait;
// Default all registry values and set up any new keys required by the
// service and applet
InitializeRegistry();
// Check the ranges of the Config registry keys
InitUPSConfigBlock();
// Check FirstMessageDelay
if (GetUPSConfigFirstMessageDelay(&first_msg_delay) == ERROR_SUCCESS) {
if (first_msg_delay > WAITSECONDSLASTVAL) {
// Value out of range, set to default
SetUPSConfigFirstMessageDelay(WAITSECONDSDEFAULT);
}
}
// Check MessageInterval
if (GetUPSConfigMessageInterval(&msg_interval) == ERROR_SUCCESS) {
if ((msg_interval < REPEATSECONDSFIRSTVAL) || (msg_interval > REPEATSECONDSLASTVAL)) {
// Value out of range, set to default
SetUPSConfigMessageInterval(REPEATSECONDSDEFAULT);
}
}
// Check Config\ShutdownOnBatteryWait
if (GetUPSConfigShutdownOnBatteryWait(&shutdown_wait) == ERROR_SUCCESS) {
if ((shutdown_wait < SHUTDOWNTIMERMINUTESFIRSTVAL) || (shutdown_wait > SHUTDOWNTIMERMINUTESLASTVAL)) {
// Value out of range, set to default
SetUPSConfigFirstMessageDelay(SHUTDOWNTIMERMINUTESDEFAULT);
}
}
// Write any changes and free the Config block
SaveUPSConfigBlock(FALSE); // Don't force an update of all values
FreeUPSConfigBlock();
}
/**
* Initializing_DoWork
*
* Description:
* Initialize the UPS driver
*
* Parameters:
* None
*
* Returns:
* An error status from the UPS.
*/
DWORD Initializing_DoWork(){
DWORD err;
DWORD options = 0;
InitUPSConfigBlock();
// Check the Options reg key to see if the UPS is installed
if ((GetUPSConfigOptions(&options) == ERROR_SUCCESS) &&
(options & UPS_INSTALLED)) {
// UPS is installed, continue initialization
// Create UPS driver
err = UPSInit();
// Convert UPS error to system error
switch(err){
case UPS_INITUNKNOWNERROR:
err = NERR_UPSInvalidConfig;
break;
case UPS_INITOK:
err = NERR_Success;
break;
case UPS_INITNOSUCHDRIVER:
err = NERR_UPSDriverNotStarted;
break;
case UPS_INITBADINTERFACE:
err = NERR_UPSInvalidConfig;
break;
case UPS_INITREGISTRYERROR:
err = NERR_UPSInvalidConfig;
break;
case UPS_INITCOMMOPENERROR:
err = NERR_UPSInvalidCommPort;
break;
case UPS_INITCOMMSETUPERROR:
err = NERR_UPSInvalidCommPort;
break;
default:
err = NERR_UPSInvalidConfig;
}
}
else {
// UPS is not installed, return configuration error
err = NERR_UPSInvalidConfig;
}
FreeUPSConfigBlock();
return err;
}
/**
* Initializing_Exit
*
* Description:
* Performs the actions necessary when transitioning from the INITIALIZING state.
*
* Parameters:
* anEvent The event that caused the transition from the INITIALIZING state.
*
* Returns:
* None
*/
void Initializing_Exit(DWORD anEvent){
// No work to perform
}
/**
* WaitingToShutdown_Enter
*
* Description:
* Performs the actions necessary when transitioning into the WAITING_TO_SHUTDOWN state.
*
* Parameters:
* anEvent The event that caused the transition into this state.
*
* Returns:
* None
*/
void WaitingToShutdown_Enter(DWORD anEvent){
// Stop periodic notifications
CancelNotification();
}
/**
* WaitingToShutdown_DoWork
*
* Description:
* Perform shutdown actions then transition out of this state.
*
* Parameters:
* None
*
* Returns:
* The event that caused the transition from the WAITING_TO_SHUTDOWN state.
*/
DWORD WaitingToShutdown_DoWork(){
LONG err;
DWORD run_command_file;
DWORD notification_interval = 0; // Notify only once
DWORD send_final_notification;
HANDLE sleep_timer;
// Send the shutdown notification. If a configuration error occurs,
// send the final notification by default.
err = GetUPSConfigNotifyEnable(&send_final_notification);
if (err != ERROR_SUCCESS || send_final_notification == TRUE){
SendNotification(APE2_UPS_POWER_SHUTDOWN, notification_interval, 0);
}
// Determine which actions to perform
// If command file action is enabled, execute command file and
// then wait
InitUPSConfigBlock();
err = GetUPSConfigRunTaskEnable(&run_command_file);
if (err != ERROR_SUCCESS){
run_command_file = FALSE;
}
if (run_command_file == TRUE){
// Execute the command file and wait for a while. If the
// command file fails to execute, log an error to the system
// event log.
if (ExecuteShutdownTask() == FALSE){
// Log failed command file event
LogEvent(NELOG_UPS_CmdFileExec, NULL, GetLastError());
}
}
// Always wait here before exiting this state
// Use the WaitForSingleObject since Sleep is not guaranteed to always work
sleep_timer = CreateEvent(NULL, FALSE, FALSE, NULL);
if (sleep_timer){
// Since nothing can signal this event, the following API will wait
// until the time elapses
WaitForSingleObject(sleep_timer, DELAY_UNTIL_SHUTDOWN_C);
CloseHandle(sleep_timer);
}
// If the sleep_timer could not be created, try the sleep call anyway
else {
Sleep(DELAY_UNTIL_SHUTDOWN_C);
}
return SHUTDOWN_ACTIONS_COMPLETED;
}
/**
* WaitingToShutdown_Exit
*
* Description:
* Performs the actions necessary when transitioning from the WAITING_TO_SHUTDOWN state.
*
* Parameters:
* anEvent The event that caused the transition from the WAITING_TO_SHUTDOWN state.
*
* Returns:
* None
*/
void WaitingToShutdown_Exit(DWORD anEvent){
// No work to perform
}
/**
* ShuttingDown_Enter
*
* Description:
* Performs the actions necessary when transitioning into the SHUTTING_DOWN state.
*
* Parameters:
* anEvent The event that caused the transition into this state.
*
* Returns:
* None
*/
void ShuttingDown_Enter(DWORD anEvent){
// Log the final shut down message
LogEvent(NELOG_UPS_Shutdown, NULL, ERROR_SUCCESS);
}
/**
* ShuttingDown_DoWork
*
* Description:
* Shuts down the OS. This state will waits until the shutdown has completed,
* then exits.
*
* Parameters:
* None
*
* Returns:
* The event that caused the transition from the SHUTTING_DOWN state.
*/
DWORD ShuttingDown_DoWork(){
DWORD ups_turn_off_enable;
DWORD ups_turn_off_wait;
// Initialize registry functions
InitUPSConfigBlock();
// Lookup the turn off enable in the registry
if ((GetUPSConfigTurnOffEnable(&ups_turn_off_enable) == ERROR_SUCCESS)
&& (ups_turn_off_enable == TRUE)) {
// UPS Turn off enabled, lookup the turn off wait in the registry
if (GetUPSConfigTurnOffWait(&ups_turn_off_wait) != ERROR_SUCCESS) {
// Error obtaining the value, use the default instead
ups_turn_off_wait = DEFAULT_TURN_OFF_DELAY_C;
}
// Tell the UPS driver to turn off the power after the shutdown delay
UPSTurnOff(ups_turn_off_wait);
}
// Tell the OS to Shutdown
ShutdownSystem();
// Free the UPS registry config block
FreeUPSConfigBlock();
return SHUTDOWN_COMPLETE;
}
/**
* ShuttingDown_Exit
*
* Description:
* Performs the actions necessary when transitioning from the SHUTTING_DOWN state.
*
* Parameters:
* anEvent The event that caused the transition from the SHUTTING_DOWN state.
*
* Returns:
* None
*/
void ShuttingDown_Exit(DWORD anEvent){
// No work to perform
}
/**
* Hibernate_Enter
*
* Description:
* Performs the actions necessary when transitioning into the HIBERNATE state.
*
* Parameters:
* anEvent The event that caused the transition into this state.
*
* Returns:
* None
*/
void Hibernate_Enter(DWORD anEvent){
// Stop periodic notifications
CancelNotification();
}
/**
* Hibernate_DoWork
*
* Description:
* Perform hibernation actions then transition out of this state.
*
* Parameters:
* None
*
* Returns:
* The event that caused the transition from the HIBERNATE state.
*/
DWORD Hibernate_DoWork(){
DWORD event = HIBERNATION_ERROR;
LONG err;
DWORD ups_turn_off_enable;
DWORD ups_turn_off_wait;
DWORD notification_interval = 0; // Notify only once
DWORD send_hibernate_notification;
// Initialize registry functions
InitUPSConfigBlock();
// Send the hibernation notification. If a configuration error occurs,
// send the notification by default.
err = GetUPSConfigNotifyEnable(&send_hibernate_notification);
if (err != ERROR_SUCCESS || send_hibernate_notification == TRUE){
// TODO: Send Hibernation nofication
//SendNotification(HIBERNATE, notification_interval);
}
// Lookup the turn off enable in the registry
if ((GetUPSConfigTurnOffEnable(&ups_turn_off_enable) == ERROR_SUCCESS)
&& (ups_turn_off_enable == TRUE)) {
// UPS Turn off enabled, lookup the turn off wait in the registry
if (GetUPSConfigTurnOffWait(&ups_turn_off_wait) != ERROR_SUCCESS) {
// Error obtaining the value, use the default instead
ups_turn_off_wait = DEFAULT_TURN_OFF_DELAY_C;
}
// Tell the UPS driver to turn off the power after the shutdown delay
UPSTurnOff(ups_turn_off_wait);
}
// Stop the UPS driver. This needs to be done to ensure that we can start
// correctly when we return from hibernation.
UPSStop();
// Now Hibernate
if (HibernateSystem() == TRUE) {
// The system was hibernated and subsequently restored
event = RETURN_FROM_HIBERNATION;
}
else {
// There was an error attempting to hibernate the system
event = HIBERNATION_ERROR;
}
// Free the UPS registry config block
FreeUPSConfigBlock();
return event;
}
/**
* Hibernate_Exit
*
* Description:
* Performs the actions necessary when transitioning from the HIBERNATE state.
*
* Parameters:
* anEvent The event that caused the transition from the HIBERNATE state.
*
* Returns:
* None
*/
void Hibernate_Exit(DWORD anEvent){
// No work to perform
}
/**
* Stopping_Enter
*
* Description:
* Performs the actions necessary when transitioning into the STOPPING state.
*
* Parameters:
* anEvent The event that caused the transition into this state.
*
* Returns:
* None
*/
void Stopping_Enter(DWORD anEvent){
// No work to perform
}
/**
* ShuttingDown_DoWork
*
* Description:
* Perform any final cleanup activities.
*
* Parameters:
* None
*
* Returns:
* The event that caused the transition from the STOPPING state.
*/
DWORD Stopping_DoWork(){
// Orderly stop the UPS driver (if there is time)
UPSStop();
// Cleanup
FreeUPSConfigBlock();
FreeUPSStatusBlock();
return STOPPED;
}
/**
* Stopping_Exit
*
* Description:
* Performs the actions necessary when transitioning from the STOPPING state.
*
* Parameters:
* anEvent The event that caused the transition from the STOPPING state.
*
* Returns:
* None
*/
void Stopping_Exit(DWORD anEvent){
// No work to perform
}