/* 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 #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 }