mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
686 lines
18 KiB
686 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1992-1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
subr.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the subrountines for the UPS service.
|
|
|
|
Author:
|
|
|
|
Kin Hong Kan (t-kinh)
|
|
|
|
Revision History:
|
|
|
|
Who When What
|
|
-------- -------- ----------------------------------------------
|
|
t-kinh 8/20/92 Created.
|
|
vladimv 1992 Big reorgs. Make it look like a real service.
|
|
ericb 10/25/95 fix bug 8133 - make shutdown wait configurable
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
|
|
#include "ups.h"
|
|
|
|
|
|
extern HANDLE UpsGlobalLogFileHandle;
|
|
extern UPS_CONFIG UpsGlobalConfig;
|
|
extern UPS_TIME UpsGlobalBatteryTime;
|
|
extern CHAR UpsGlobalCommand[ MAX_PATH];
|
|
|
|
BOOL
|
|
UpsGetKeyValue(
|
|
HKEY MyKey,
|
|
LPTSTR SubKey,
|
|
DWORD min,
|
|
DWORD max,
|
|
LPDWORD ret
|
|
);
|
|
|
|
|
|
// these values are also defined in the UPS applet...
|
|
|
|
#define REGISTRY_UPS_DIRECTORY "System\\CurrentControlSet\\Services\\UPS"
|
|
#define REGISTRY_PORT "Port"
|
|
#define REGISTRY_OPTIONS "Options"
|
|
#define REGISTRY_BATTERY_LIFE "BatteryLife"
|
|
#define REGISTRY_RECHARGE_RATE "RechargeRate"
|
|
#define REGISTRY_FIRST_MESSAGE_DELAY "FirstMessageDelay"
|
|
#define REGISTRY_MESSAGE_INTERVAL "MessageInterval"
|
|
#define REGISTRY_COMMAND_FILE "CommandFile"
|
|
|
|
// additional ones for sending out messages and alerts
|
|
|
|
#define REGISTRY_COMPUTER_NAME "ComputerName"
|
|
#define REGISTRY_COMPUTER_NAME_DIRECTORY \
|
|
"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName"
|
|
|
|
#define REGISTRY_SHUTDOWN_WAIT "ShutdownWait"
|
|
|
|
|
|
VOID
|
|
UpsAlertRaise(
|
|
DWORD MessageId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends an alert.
|
|
|
|
Arguments:
|
|
|
|
MessageId - The Message Id of the alert as defined in netmsg.h
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
NetAlertRaise() only supports unicode version, that results in
|
|
the Computer name is hardwired to wchat_t.
|
|
|
|
--*/
|
|
|
|
{
|
|
CHAR buff[ (sizeof(ADMIN_OTHER_INFO) + 2 * (MAX_PATH+1))];
|
|
LPTSTR CompName;
|
|
LPADMIN_OTHER_INFO OInfo;
|
|
DWORD status;
|
|
|
|
OInfo = (LPADMIN_OTHER_INFO)buff;
|
|
OInfo->alrtad_errcode = MessageId;
|
|
OInfo->alrtad_numstrings = 1;
|
|
|
|
CompName = (buff + sizeof(ADMIN_OTHER_INFO));
|
|
|
|
wcscpy(
|
|
(LPWSTR)CompName,
|
|
(LPWSTR)UpsGlobalConfig.ComputerName
|
|
);
|
|
|
|
status = NetAlertRaiseEx(
|
|
L"ADMIN", // alert type
|
|
buff, // info buffer
|
|
sizeof(buff), // info size
|
|
L"UPS" // service name
|
|
);
|
|
if ( status != 0) {
|
|
// This occurs if for example alerter service has not been started.
|
|
KdPrint(("[UPS] NetAlertRaise() returns status = %ld\n", status));
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
UpsGetCommand(
|
|
HKEY UpsKey,
|
|
PCHAR CommandFileBuffer,
|
|
DWORD CommandFileBufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads registry to find out the value of CommandFile and initialize
|
|
UpsGlobalCommand.
|
|
|
|
It returns TRUE if it does not find an entry for CommandFile, or if
|
|
it finds a valid entry for CommandFile. In this latter case it
|
|
initializes UpsGlobalCommand to point to a command exec string.
|
|
|
|
|
|
It returns FALSE if it finds an invalid entry for CommandFile, or fails
|
|
initializing UpsGlobalCommand for any other reason.
|
|
|
|
Arguments:
|
|
|
|
UpsKey - Registry key to UPS service configuration.
|
|
CommandFileBuffer - Buffer for command file.
|
|
CommandFileBufferSize - Size in bytes of CommandFileBuffer.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Did not find an entry or found a valid entry.
|
|
FALSE - Found an invalid entry.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
DWORD length;
|
|
DWORD type;
|
|
DWORD SpaceLeft;
|
|
DWORD CommandFileLength;
|
|
|
|
status = RegQueryValueEx(
|
|
UpsKey,
|
|
REGISTRY_COMMAND_FILE,
|
|
NULL,
|
|
&type,
|
|
(LPBYTE)CommandFileBuffer,
|
|
&CommandFileBufferSize
|
|
);
|
|
if ( status == ERROR_FILE_NOT_FOUND) {
|
|
return( TRUE); // value is not present in the registry
|
|
}
|
|
|
|
if ( status != ERROR_SUCCESS || type != REG_SZ ||
|
|
CommandFileBufferSize == 0) {
|
|
return( FALSE); // an invalid error or invalid type
|
|
}
|
|
|
|
// subtract terminating null
|
|
CommandFileLength = CommandFileBufferSize - sizeof( CommandFileBuffer[0]);
|
|
|
|
if ( CommandFileLength == 0) {
|
|
return( TRUE); // an empty string is OK
|
|
}
|
|
|
|
SpaceLeft = sizeof( UpsGlobalCommand);
|
|
|
|
length = GetSystemDirectory(
|
|
UpsGlobalCommand,
|
|
SpaceLeft
|
|
);
|
|
if ( length == 0 || length >= SpaceLeft) {
|
|
return( FALSE);
|
|
}
|
|
|
|
if ( UpsGlobalCommand[ length - 1] != '\\') {
|
|
UpsGlobalCommand[ length] = '\\';
|
|
if ( ++length >= SpaceLeft) {
|
|
return( FALSE);
|
|
}
|
|
UpsGlobalCommand[ length] = '\0';
|
|
}
|
|
|
|
if ( length + CommandFileLength + 1 >= SpaceLeft) {
|
|
return( FALSE);
|
|
}
|
|
|
|
strcpy( UpsGlobalCommand + length, CommandFileBuffer);
|
|
|
|
type = GetFileAttributes( UpsGlobalCommand);
|
|
if ( (type & FILE_ATTRIBUTE_DIRECTORY) != 0) {
|
|
return( FALSE);
|
|
}
|
|
|
|
return( TRUE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
UpsGetConfig(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Descrption:
|
|
|
|
Open the required keys to the registry to read in the configuration of
|
|
the UPS service and the name of the local system. Range checks are being
|
|
done for numerical entries of the UPS configuation.
|
|
|
|
Return Value:
|
|
|
|
TRUE - all values are put in the Config structure.
|
|
|
|
FALSE - either one of the following cases:
|
|
a. Can't open registry key.
|
|
b. Can't read any required value.
|
|
c. The values are not within the defined range.
|
|
--*/
|
|
{
|
|
|
|
DWORD status;
|
|
HKEY RegistryKey;
|
|
DWORD value;
|
|
CHAR temp[ MAX_PATH];
|
|
DWORD size;
|
|
DWORD type;
|
|
|
|
status = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGISTRY_UPS_DIRECTORY,
|
|
0,
|
|
KEY_READ,
|
|
&RegistryKey // key to UPS service configuration
|
|
);
|
|
if ( status != ERROR_SUCCESS){
|
|
KdPrint(("[UPS] Cannot open registry key, error_code %d\n", status));
|
|
return( FALSE);
|
|
}
|
|
|
|
if (FALSE == UpsGetKeyValue(
|
|
RegistryKey,
|
|
REGISTRY_OPTIONS,
|
|
0,
|
|
0XFFFFFFFF, // no range check for mask
|
|
&value)) {
|
|
return( FALSE);
|
|
}
|
|
UpsGlobalConfig.Options = value; // Options value
|
|
|
|
if (! (UpsGlobalConfig.Options & UPS_INSTALLED)) {
|
|
KdPrint(("[UPS] UPS not installed\n"));
|
|
return( FALSE); // UPS must be installed
|
|
}
|
|
if (!(UpsGlobalConfig.Options & (UPS_LOWBATTERYSIGNAL | UPS_POWERFAILSIGNAL))) {
|
|
KdPrint(("[UPS] no signal lines supported - Options %d\n", UpsGlobalConfig.Options));
|
|
return( FALSE); // UPS must support signalling
|
|
}
|
|
|
|
if (FALSE == UpsGetKeyValue(
|
|
RegistryKey,
|
|
REGISTRY_BATTERY_LIFE,
|
|
MINBATTERYLIFE,
|
|
MAXBATTERYLIFE,
|
|
&value)) {
|
|
return( FALSE);
|
|
}
|
|
UpsGlobalConfig.BatteryLife = value; // BatterLife value
|
|
|
|
if (FALSE == UpsGetKeyValue(
|
|
RegistryKey,
|
|
REGISTRY_RECHARGE_RATE,
|
|
MINRECHARGEPERMINUTE,
|
|
MAXRECHARGEPERMINUTE,
|
|
&value)) {
|
|
return( FALSE);
|
|
}
|
|
UpsGlobalConfig.RechargeRate = value; // RechargeRate value
|
|
|
|
if (FALSE == UpsGetKeyValue(
|
|
RegistryKey,
|
|
REGISTRY_FIRST_MESSAGE_DELAY,
|
|
MINFIRSTWARNING,
|
|
MAXFIRSTWARNING,
|
|
&value)) {
|
|
return( FALSE);
|
|
}
|
|
UpsGlobalConfig.FirstMessageDelay = value; // FirstMessageDelay
|
|
|
|
if (FALSE == UpsGetKeyValue(
|
|
RegistryKey,
|
|
REGISTRY_MESSAGE_INTERVAL,
|
|
MINWARNINGINTERVAL,
|
|
MAXWARNINGINTERVAL,
|
|
&value)) {
|
|
return( FALSE);
|
|
}
|
|
UpsGlobalConfig.MessageInterval = value; // MessageInterval
|
|
|
|
//
|
|
// configurable shutdown wait interval
|
|
//
|
|
size = sizeof(value);
|
|
status = RegQueryValueEx(RegistryKey,
|
|
REGISTRY_SHUTDOWN_WAIT,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&value,
|
|
&size);
|
|
if (status != ERROR_SUCCESS)
|
|
{ // if a value is not stored in the registry, use the default;
|
|
value = DEFAULTSHUTDOWNWAIT;
|
|
}
|
|
else
|
|
{
|
|
if (MAXSHUTDOWNWAIT < value)
|
|
{
|
|
value = MAXSHUTDOWNWAIT;
|
|
}
|
|
}
|
|
UpsGlobalConfig.ShutdownWait = value;
|
|
KdPrint(("[UPS] ShutdownWait set to %d\n", value));
|
|
|
|
size = sizeof(temp);
|
|
status = RegQueryValueEx(
|
|
RegistryKey,
|
|
REGISTRY_PORT,
|
|
NULL,
|
|
&type,
|
|
(LPBYTE)&temp,
|
|
&size
|
|
);
|
|
if (status != ERROR_SUCCESS) {
|
|
KdPrint((
|
|
"[UPS] Can't get value of %s error_code - %d\n",
|
|
REGISTRY_PORT,
|
|
status
|
|
));
|
|
return( FALSE);
|
|
}
|
|
// Copy the comm port string, stripping the colon at the end.
|
|
strncpy(
|
|
(PCHAR)UpsGlobalConfig.Port,
|
|
temp,
|
|
strlen( temp)-1
|
|
);
|
|
|
|
if ( UpsGlobalConfig.Options & UPS_COMMANDFILE
|
|
&& UpsGetCommand( RegistryKey, temp, sizeof( temp)) == FALSE) {
|
|
KdPrint((
|
|
"[UPS] Bad value of %s \n",
|
|
REGISTRY_COMMAND_FILE
|
|
));
|
|
//
|
|
// Just log an event and send an alert. This problem is assumed
|
|
// to be benign enough so that we do not want to abort the startup
|
|
// on this account.
|
|
//
|
|
UpsReportEvent( NELOG_UPS_CmdFileConfig, NULL, ERROR_SUCCESS);
|
|
UpsAlertRaise( ALERT_CmdFileConfig);
|
|
}
|
|
|
|
RegCloseKey( RegistryKey); // done with service configuration
|
|
|
|
status = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE, //Open Reg Key
|
|
REGISTRY_COMPUTER_NAME_DIRECTORY,
|
|
0,
|
|
KEY_READ,
|
|
&RegistryKey // key for computer name
|
|
);
|
|
if (status != ERROR_SUCCESS){
|
|
KdPrint(("[UPS] Can't open registry key, error_code %d\n", status));
|
|
return( FALSE);
|
|
}
|
|
|
|
|
|
// ComputerName is hardwired UNICODE string, since NetAlertRaise()
|
|
// and UPSNotifyUsers() don't take in ANSI string... Remove conversion
|
|
// when ANSI is supported....
|
|
|
|
size = sizeof(temp);
|
|
status = RegQueryValueEx(
|
|
RegistryKey,
|
|
REGISTRY_COMPUTER_NAME,
|
|
NULL,
|
|
&type,
|
|
(LPBYTE)&temp,
|
|
&size
|
|
);
|
|
if (strlen(temp) > sizeof( UpsGlobalConfig.ComputerName ) / sizeof( UpsGlobalConfig.ComputerName[0])) {
|
|
KdPrint(("[UPS] Computer name too long\n"));
|
|
return( FALSE);
|
|
}
|
|
|
|
if (0 == MultiByteToWideChar(
|
|
CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
temp,
|
|
strlen(temp)+1, //copy null char also
|
|
(LPWSTR) UpsGlobalConfig.ComputerName,
|
|
sizeof(UpsGlobalConfig.ComputerName)/sizeof(UpsGlobalConfig.ComputerName[0]))) {
|
|
KdPrint(("[UPS]CompName not translated to Unicode, %ld\n",
|
|
GetLastError()));
|
|
return( FALSE);
|
|
}
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
KdPrint((
|
|
"[UPS] Can't get value of %s error_code - %d\n",
|
|
REGISTRY_COMPUTER_NAME,
|
|
status
|
|
));
|
|
return( FALSE);
|
|
}
|
|
|
|
return( TRUE);
|
|
}
|
|
|
|
BOOL
|
|
UpsGetKeyValue(
|
|
HKEY RegistryKey,
|
|
LPTSTR SubKey,
|
|
DWORD min,
|
|
DWORD max,
|
|
LPDWORD ret
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get a numerical key value from the registry when given a key and
|
|
subkey to the registry. The numeric value is returned if the value
|
|
passes the range check.
|
|
|
|
Arguments:
|
|
|
|
RegistryKey - Opened key to the registry.
|
|
SubKey - Name of the subkey to be read from registry.
|
|
min - minimum value for the range check (exclusive)
|
|
max - maximum value for the range check (exclusive)
|
|
ret - pointer to a DWORD to return the numeric value
|
|
|
|
Return Value:
|
|
|
|
TRUE - the value is successfully read from the registry and
|
|
passed the range check
|
|
FALSE - the value is not read from the registry, or the value
|
|
doesn't pass the range test.
|
|
--*/
|
|
|
|
{
|
|
DWORD temp;
|
|
LONG status;
|
|
DWORD size;
|
|
DWORD type;
|
|
|
|
size = sizeof(DWORD);
|
|
|
|
status = RegQueryValueEx(
|
|
RegistryKey,
|
|
SubKey,
|
|
NULL,
|
|
&type,
|
|
(LPBYTE)&temp,
|
|
&size
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
KdPrint((
|
|
"[UPS] Can't get value of %s error_code - %d\n",
|
|
SubKey,
|
|
status
|
|
));
|
|
return( FALSE);
|
|
}
|
|
|
|
if ( ((DWORD)temp < min) || ((DWORD)temp > max)) {
|
|
KdPrint((
|
|
"[UPS] Value of %s not in range - %d\n",
|
|
SubKey,
|
|
(DWORD)temp
|
|
));
|
|
return( FALSE);
|
|
}
|
|
*ret = temp;
|
|
return( TRUE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
UpsLineAsserted(
|
|
DWORD ModemStatus,
|
|
DWORD Line
|
|
)
|
|
/*++
|
|
Routine Descriptions:
|
|
|
|
Checking if a specified signal, either LINE_FAIL or LOW_POWER, is
|
|
asserted or not. This is tricky, since:
|
|
|
|
1. assertion can be either positive voltage level, or negative voltage
|
|
level, as defined by UpsGlobalConfig.
|
|
2. SET means positive voltage in GetCommModemStatus.
|
|
|
|
Arguments:
|
|
ModemStatus- CommPort as returned by GetCommModemStatus()
|
|
Line - either LINE_FAIL or LOW_POWER
|
|
|
|
Return Value: TRUE if asserted
|
|
FALSE if not asserted
|
|
--*/
|
|
|
|
{
|
|
|
|
DWORD status, assertion;
|
|
|
|
status = Line & ModemStatus;
|
|
assertion = (Line == LINE_FAIL) ?
|
|
(UpsGlobalConfig.Options & UPS_POSSIGONPOWERFAIL) :
|
|
(UpsGlobalConfig.Options & UPS_POSSIGONLOWBATTERY);
|
|
|
|
if (status) { //line positive
|
|
if (assertion){ //positive asssertion
|
|
return TRUE;}
|
|
else{ //negative assertion
|
|
return FALSE;}
|
|
}
|
|
|
|
else { //line negative
|
|
if (assertion){ //positive assertion
|
|
return FALSE;}
|
|
else{
|
|
return TRUE;} //negative assertion
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
UpsReportEvent(
|
|
DWORD MessageId,
|
|
PCHAR SingleString,
|
|
DWORD Error
|
|
)
|
|
/*++
|
|
|
|
Routine Descriptions:
|
|
|
|
Calls ReportEvent to write to a system event log.
|
|
|
|
Arguments:
|
|
|
|
MessageId - The Message Id of the event defined in lmerrlog.h
|
|
String - Pointer to a single insertion string - or NULL if there
|
|
is no string to insert.
|
|
Error - Error code to log as data - or ERROR_SUCCESS if there
|
|
is no error code to log.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
WORD eventType;
|
|
WORD cStrings; // count of insertion strings
|
|
LPTSTR Strings[1]; // array of insertion strings
|
|
LPTSTR * pStrings; // pointer to array of insertion strings
|
|
DWORD cbData; // count of data (in bytes)
|
|
LPVOID pData; // pointer to data
|
|
|
|
eventType = (MessageId > ERRLOG_BASE) ?
|
|
EVENTLOG_WARNING_TYPE : EVENTLOG_ERROR_TYPE;
|
|
|
|
if ( Error == ERROR_SUCCESS) {
|
|
pData = NULL;
|
|
cbData = 0;
|
|
} else {
|
|
pData = &Error;
|
|
cbData = sizeof( Error);
|
|
}
|
|
|
|
if ( SingleString == NULL) {
|
|
pStrings = NULL;
|
|
cStrings = 0;
|
|
} else {
|
|
Strings[ 0] = SingleString;
|
|
pStrings = Strings;
|
|
cStrings = 1;
|
|
}
|
|
|
|
KdPrint(("[UPS] ReportEvent(): MessageId = (dec)%d\n", MessageId));
|
|
|
|
if ( !ReportEvent(
|
|
UpsGlobalLogFileHandle, // handle
|
|
eventType, // event type
|
|
0, // event category,
|
|
MessageId, // message id
|
|
NULL, // user id
|
|
cStrings, // number of strings
|
|
cbData, // number of data bytes
|
|
pStrings, // array of strings
|
|
pData // data buffer
|
|
)) {
|
|
KdPrint(("[UPS] ReportEvent: error = (dec)%d\n", GetLastError()));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
UpsUpdateTime(
|
|
DWORD Status
|
|
)
|
|
/*++
|
|
|
|
Routine Descriptions:
|
|
|
|
Update the BatteryTime sturcture. If Status is passed DISCHARGE, the
|
|
amount of time that between "MarkTime" and now is subtrated from
|
|
"StoredTime". If Status is passed CHARGE, the time would be divided by
|
|
the recharge rate in the Config and added to the Stored time.
|
|
|
|
Arguments:
|
|
|
|
Status - indicating what happened for the time elapsed
|
|
can either be CHARGE or DISCHARGE.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
time_t CurrentTime, TimeElapsed, MaxLife;
|
|
|
|
MaxLife = UpsGlobalConfig.BatteryLife * 60;
|
|
CurrentTime = time((time_t *)NULL);
|
|
TimeElapsed = CurrentTime - UpsGlobalBatteryTime.MarkTime;
|
|
|
|
ASSERT( TimeElapsed >=0 && (Status == CHARGE || Status == DISCHARGE));
|
|
|
|
UpsGlobalBatteryTime.MarkTime = CurrentTime;
|
|
|
|
if ( Status == CHARGE) {
|
|
|
|
if (UpsGlobalConfig.RechargeRate == 0) { // just in case
|
|
UpsGlobalBatteryTime.StoredTime = MaxLife;
|
|
} else {
|
|
UpsGlobalBatteryTime.StoredTime +=
|
|
(TimeElapsed / UpsGlobalConfig.RechargeRate);
|
|
if ( UpsGlobalBatteryTime.StoredTime > MaxLife) {
|
|
UpsGlobalBatteryTime.StoredTime = MaxLife;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
UpsGlobalBatteryTime.StoredTime =
|
|
(UpsGlobalBatteryTime.StoredTime > TimeElapsed) ?
|
|
(UpsGlobalBatteryTime.StoredTime - TimeElapsed) : 0;
|
|
}
|
|
}
|