312 lines
9 KiB
C
312 lines
9 KiB
C
|
/* Copyright 1999 American Power Conversion, All Rights Reserved
|
||
|
*
|
||
|
* Description:
|
||
|
* The file implements the Notifier. The Notifier is reponsible
|
||
|
* for broadcasting power-related and shutdown messages to the
|
||
|
* local machine.
|
||
|
*
|
||
|
*
|
||
|
* Revision History:
|
||
|
* sberard 30Mar1999 initial revision.
|
||
|
* mholly 27Apr1999 create and recycle a single thread for repeat
|
||
|
* notifications - use two separate events to signal
|
||
|
* either pause or resume of the thread state
|
||
|
* mholly 27Apr1999 make sure to clear the pause event when resuming
|
||
|
* and to clear the resume event when pausing
|
||
|
* mholly 28May1999 send all messages in _theNotifierThread, also
|
||
|
* have this thread do any delaying for the callee,
|
||
|
* and keep track of whether a power out message
|
||
|
* was sent, so we know when to send a power restored
|
||
|
*/
|
||
|
|
||
|
#include "notifier.h"
|
||
|
#include "service.h"
|
||
|
#include "upsmsg.h"
|
||
|
|
||
|
//
|
||
|
// Function prototypes
|
||
|
//
|
||
|
static void sendSingleNotification(DWORD aMsgId);
|
||
|
static void sendRepeatingNotification(void);
|
||
|
static void setupNotifierThread(void);
|
||
|
|
||
|
//
|
||
|
// Globals
|
||
|
//
|
||
|
static HANDLE _theNotifierThread = NULL;
|
||
|
static HANDLE _theNotificationPause = NULL;
|
||
|
static HANDLE _theNotificationResume = NULL;
|
||
|
static DWORD _theMessageId;
|
||
|
static DWORD _theNotificationInterval;
|
||
|
static DWORD _theMessageDelay;
|
||
|
static DWORD _theLastMessageSent = 0;
|
||
|
|
||
|
//
|
||
|
// Constats
|
||
|
//
|
||
|
static const int kMilliSeconds = 1000; // Used to convert seconds to milliseconds
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* SendNotification
|
||
|
*
|
||
|
* Description:
|
||
|
* This function sends a broadcast message to the local machine. The
|
||
|
* message is specified by aMsgId. The parameter anInterval specifies
|
||
|
* the amount of time to wait between consecutive messages. If this
|
||
|
* value is zero the message is only sent once, otherwise the message
|
||
|
* will repeat until SendNotification(..) or CancelNotification() is
|
||
|
* called. aDelay specifies that the message should be send aDelay
|
||
|
* seconds in the future - note that this method will not block for aDelay
|
||
|
* seconds, it returns immediately and sends the message on a separate
|
||
|
* thread. Any current previously executing periodic notifications
|
||
|
* are canceled as a result of this call.
|
||
|
*
|
||
|
* This method also keeps track of whether the power out message had
|
||
|
* been sent to users. This is done in order to squelch a power return
|
||
|
* message if a power out message had not already been sent.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* aMsgId - the message to send
|
||
|
* anInterval - the amount of time, in seconds, between messages
|
||
|
* aDelay - the amount of time, in seconds to wait to send message
|
||
|
*
|
||
|
* Returns:
|
||
|
* nothing
|
||
|
*/
|
||
|
void SendNotification(DWORD aMsgId, DWORD anInterval, DWORD aDelay)
|
||
|
{
|
||
|
//
|
||
|
// Cancel any current periodic notifications
|
||
|
//
|
||
|
CancelNotification();
|
||
|
|
||
|
//
|
||
|
// only sent the power back message if
|
||
|
// the power out message had already
|
||
|
// been broadcast to the users
|
||
|
//
|
||
|
if ((APE2_UPS_POWER_BACK == aMsgId) &&
|
||
|
(APE2_UPS_POWER_OUT != _theLastMessageSent)) {
|
||
|
//
|
||
|
// the last message sent was not power out
|
||
|
// so simply return
|
||
|
//
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the message parameters for _theNotifierThread
|
||
|
//
|
||
|
_theMessageId = aMsgId;
|
||
|
_theNotificationInterval = anInterval;
|
||
|
_theMessageDelay = aDelay;
|
||
|
|
||
|
//
|
||
|
// setup the thread events and thread
|
||
|
//
|
||
|
setupNotifierThread();
|
||
|
|
||
|
//
|
||
|
// tell _theNotificationThread to run by
|
||
|
// signalling the resume event - must make
|
||
|
// sure to clear the pause event before
|
||
|
// signalling the resume
|
||
|
//
|
||
|
ResetEvent(_theNotificationPause);
|
||
|
SetEvent(_theNotificationResume);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* CancelNotification
|
||
|
*
|
||
|
* Description:
|
||
|
* This function cancels the periodic messaging initiated through a call
|
||
|
* to the SendNotification(..) function.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* none
|
||
|
*
|
||
|
* Returns:
|
||
|
* nothing
|
||
|
*/
|
||
|
void CancelNotification()
|
||
|
{
|
||
|
//
|
||
|
// tell _theNotificationThread to pause
|
||
|
// by signalling the pause event - must make
|
||
|
// sure to clear the resume event before
|
||
|
// signalling the pause event
|
||
|
//
|
||
|
ResetEvent(_theNotificationResume);
|
||
|
SetEvent(_theNotificationPause);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* setupNotifierThread
|
||
|
*
|
||
|
* Description:
|
||
|
* Creates the thread on which the notifications will
|
||
|
* actually be sent. It also creates the events that
|
||
|
* are used to signal the start and end of notifications
|
||
|
*
|
||
|
* Parameters:
|
||
|
* none
|
||
|
*
|
||
|
* Returns:
|
||
|
* nothing
|
||
|
|
||
|
*/
|
||
|
void setupNotifierThread(void)
|
||
|
{
|
||
|
if (!_theNotificationPause) {
|
||
|
//
|
||
|
// Create the pause event
|
||
|
//
|
||
|
_theNotificationPause = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
}
|
||
|
|
||
|
if (!_theNotificationResume) {
|
||
|
//
|
||
|
// create the resume event
|
||
|
//
|
||
|
_theNotificationResume = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
}
|
||
|
|
||
|
if (!_theNotifierThread) {
|
||
|
//
|
||
|
// create the notification thread
|
||
|
//
|
||
|
_theNotifierThread = CreateThread(NULL, // no security attributes
|
||
|
0, // default stack
|
||
|
(LPTHREAD_START_ROUTINE)
|
||
|
sendRepeatingNotification,
|
||
|
NULL,
|
||
|
0,
|
||
|
NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* sendSingleNotification
|
||
|
*
|
||
|
* Description:
|
||
|
* This function sends a single broadcast message to the local machine.
|
||
|
* The message is specified by aMsgId.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* aMsgId - the message to send
|
||
|
*
|
||
|
* Returns:
|
||
|
* nothing
|
||
|
*/
|
||
|
static void sendSingleNotification(DWORD aMsgId)
|
||
|
{
|
||
|
DWORD status;
|
||
|
TCHAR computer_name[MAX_COMPUTERNAME_LENGTH + 1];
|
||
|
DWORD computer_name_len = MAX_COMPUTERNAME_LENGTH + 1;
|
||
|
LPTSTR msg_buf, additional_args[1];
|
||
|
DWORD buf_len;
|
||
|
|
||
|
// Get the computer name and pass it as additional
|
||
|
// information for the message
|
||
|
GetComputerName(computer_name, &computer_name_len);
|
||
|
additional_args[0] = computer_name;
|
||
|
|
||
|
buf_len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||
|
FORMAT_MESSAGE_ARGUMENT_ARRAY |
|
||
|
FORMAT_MESSAGE_FROM_SYSTEM ,
|
||
|
NULL, // NULL means get message from system
|
||
|
aMsgId,
|
||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||
|
(LPTSTR) &msg_buf,
|
||
|
0, // buffer size
|
||
|
(va_list *)additional_args); // additional arguements
|
||
|
|
||
|
if (buf_len > 0) {
|
||
|
_theLastMessageSent = aMsgId;
|
||
|
// Display the message
|
||
|
status = NetMessageBufferSend(NULL,
|
||
|
computer_name,
|
||
|
computer_name,
|
||
|
(LPBYTE) msg_buf,
|
||
|
buf_len * 2); // multiply by 2 because the string is Unicodeh
|
||
|
|
||
|
// Free the memory allocated by FormatMessage
|
||
|
LocalFree(msg_buf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* sendRepeatingNotification
|
||
|
*
|
||
|
* Description:
|
||
|
* This function sends notifications repeatedly to the local machine.
|
||
|
* This function calls sendSingleNotification(..) to perform the actual
|
||
|
* notification. The message will be sent after _theMessageDelay seconds
|
||
|
* has elapsed after _theNotificationResume event is signaled. If
|
||
|
* _theMessageDelay is zero, a message is sent immediately. Messages are
|
||
|
* repeated, if _theNotificationInterval is not zero, until the Event
|
||
|
* _theNotificationPause is signaled. The message Id and the notification
|
||
|
* interval are specified by the global variables _theMessageId and
|
||
|
* _theNotificationInterval.
|
||
|
*
|
||
|
* To restart notifications using this thread signal _theNotificationResume
|
||
|
* event. This thread will remain idle until this event is signalled
|
||
|
*
|
||
|
* Parameters:
|
||
|
* none
|
||
|
*
|
||
|
* Returns:
|
||
|
* nothing
|
||
|
*/
|
||
|
static void sendRepeatingNotification(void)
|
||
|
{
|
||
|
//
|
||
|
// wait for _theNotificationResume to become signaled
|
||
|
// this becomes signaled when a notification should be sent
|
||
|
//
|
||
|
while (WAIT_OBJECT_0 ==
|
||
|
WaitForSingleObject(_theNotificationResume, INFINITE)) {
|
||
|
|
||
|
//
|
||
|
// Send the initial message after the message delay
|
||
|
// seconds has elapsed - if _theNotificationPause becomes
|
||
|
// signaled before the delay seconds has elapsed then
|
||
|
// this notification has been cancelled
|
||
|
//
|
||
|
if (WAIT_TIMEOUT == WaitForSingleObject(_theNotificationPause,
|
||
|
_theMessageDelay * kMilliSeconds)) {
|
||
|
|
||
|
//
|
||
|
// send the message that was requested
|
||
|
//
|
||
|
sendSingleNotification(_theMessageId);
|
||
|
|
||
|
//
|
||
|
// now send repeating notifications
|
||
|
// if necessary - if _theNotificationInterval
|
||
|
// is set to zero, then only send the single
|
||
|
// message above
|
||
|
//
|
||
|
if (0 != _theNotificationInterval) {
|
||
|
//
|
||
|
// wait for either _theNotificationPause to become
|
||
|
// signalled or until it is time to notify again
|
||
|
//
|
||
|
DWORD interval = _theNotificationInterval * kMilliSeconds;
|
||
|
|
||
|
while (WAIT_TIMEOUT ==
|
||
|
WaitForSingleObject(_theNotificationPause, interval)) {
|
||
|
sendSingleNotification(_theMessageId);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|