499 lines
12 KiB
C
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
|
|
}
|
|
|