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.
1303 lines
39 KiB
1303 lines
39 KiB
/*++
|
|
|
|
Copyright (c) 1992-1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ups.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the body of the NT UPS service.
|
|
|
|
Author:
|
|
|
|
Kin Hong Kan (t-kinh)
|
|
|
|
Revision History:
|
|
|
|
Who When What
|
|
-------- -------- ----------------------------------------------
|
|
t-kinh 8/25/92 Created.
|
|
vladimv 1992 Big reorgs. Make it look like a real service.
|
|
ericb 8-23-94 fix bug 23704 - assert shutdown serial line longer
|
|
ericb 8-24-95 fix bugs 14506, 16195 - improperly closing handles
|
|
ericb 9/19/95 fix bug 1262 - end message thread gracefully
|
|
ericb 10/25/95 fix bug 8133 - make shutdown wait configurable
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
#include "ups.h"
|
|
|
|
//#define UPS_TOGGLER //define to fork debug toggler
|
|
|
|
#define UPS_DONE_EVENT_CREATED 0x1
|
|
#define UPS_OVERLAP_EVENT_CREATED 0x2
|
|
typedef enum _UPS_ERROR_CONDITION {
|
|
UpsNoError = 0,
|
|
UpsErrorRegisterControlHandler,
|
|
UpsErrorCreateEvent,
|
|
UpsErrorNotifyServiceController,
|
|
UpsErrorCtrlHandler,
|
|
UpsErrorShutdownParameters,
|
|
UpsErrorLoadLibrary,
|
|
UpsErrorRegisterEventSource,
|
|
UpsErrorGetConfig,
|
|
UpsErrorRegisterService,
|
|
UpsErrorOpenCommPort,
|
|
UpsErrorModemStatus,
|
|
UpsErrorSignalAsserted,
|
|
UpsErrorDtr,
|
|
UpsErrorSetRts,
|
|
UpsErrorSetTx,
|
|
UpsErrorWait,
|
|
UpsErrorSetCommMask,
|
|
UpsErrorAdjustPrivilege,
|
|
UpsErrorExitWindowsEx,
|
|
UpsErrorSystemShutdown
|
|
} UPS_ERROR_CONDITION, *PUPS_ERROR_CONDITION;
|
|
|
|
|
|
VOID _CRTAPI1 main(VOID);
|
|
VOID UPS_main( VOID);
|
|
|
|
|
|
VOID UpsControlHandler( DWORD Opcode);
|
|
VOID UpsHandleError(
|
|
UPS_ERROR_CONDITION failingCondition,
|
|
DWORD majorError,
|
|
DWORD minorError
|
|
);
|
|
VOID UpsInitialize( VOID );
|
|
VOID UpsSendMessage( PBOOL pFirstMessageSent);
|
|
VOID UpsShutdown( DWORD status);
|
|
VOID UpsSystemShutdown( VOID);
|
|
BOOL UpsTurnOff( DWORD ControlType);
|
|
DWORD UpsUpdateStatus( VOID);
|
|
|
|
|
|
CHAR UpsGlobalCommand[ MAX_PATH];
|
|
DWORD UpsGlobalCommMask;
|
|
DWORD UpsGlobalActiveSignals;
|
|
UPS_TIME UpsGlobalBatteryTime;
|
|
UPS_CONFIG UpsGlobalConfig;
|
|
HANDLE UpsGlobalCommPort;
|
|
DWORD UpsGlobalModemStatus;
|
|
BOOL UpsGlobalTurnOff;
|
|
OVERLAPPED UpsGlobalOverlap;
|
|
SERVICE_STATUS UpsGlobalServiceStatus;
|
|
|
|
SERVICE_STATUS_HANDLE UpsGlobalServiceStatusHandle;
|
|
HANDLE UpsGlobalDoneEvent;
|
|
HANDLE UpsGlobalMessageThread;
|
|
HANDLE UpsGlobalMainThread;
|
|
HANDLE UpsGlobalLogFileHandle;
|
|
HANDLE UpsGlobalMessageFileHandle;
|
|
HANDLE g_hMessageDone = NULL;
|
|
|
|
|
|
VOID _CRTAPI1
|
|
main(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch all the service thread to the service controller.
|
|
|
|
--*/
|
|
{
|
|
SERVICE_TABLE_ENTRY DispatchTable[] = {
|
|
{ SERVICE_UPS, (LPSERVICE_MAIN_FUNCTION)UPS_main },
|
|
{ NULL, NULL }
|
|
};
|
|
StartServiceCtrlDispatcher( DispatchTable); // waits till all threads end
|
|
KdPrint(( "[UPS] The Service is terminating....\n"));
|
|
ExitProcess(0);
|
|
}
|
|
|
|
|
|
VOID
|
|
UPS_main(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Register the control handler with the service controller, and create
|
|
the thread for the main UPS service. The Routine would then wait on
|
|
an event which tells it to terminate. This is needed since we don't
|
|
want to do ExitProcess() during the UPS service, and we want all threads
|
|
and handles to be terminated cleanly.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD ThreadId;
|
|
DWORD ModemStatus;
|
|
BOOL FirstMessageSent = FALSE;
|
|
DWORD status;
|
|
HANDLE event[ 2];
|
|
|
|
UpsInitialize();
|
|
|
|
event[ 0] = UpsGlobalOverlap.hEvent;
|
|
event[ 1] = UpsGlobalDoneEvent;
|
|
|
|
for ( ; ;) {
|
|
|
|
// A while loop waiting for a power to fail. Note that Comm Port
|
|
// event that triggers the waiting object may be different from the
|
|
// result we read from GetCommModemStatus() (e.g. a glitch in the
|
|
// signal line. The while loop is used to eliminate effects of
|
|
// glitches.
|
|
|
|
// Note that WaitCommEvent() is triggered on the changing of level of
|
|
// the modem signals, but not the actual level. We want the wait
|
|
// event to be independent of the current state of the signal lines,
|
|
// and going through the loop once ensures that no signal lines are
|
|
// asserted before we start a wait.
|
|
|
|
for ( ; ;) {
|
|
|
|
BOOL ForkMessage = FALSE;
|
|
|
|
UpsUpdateTime( CHARGE); // Update charge time
|
|
|
|
if ( !GetCommModemStatus( UpsGlobalCommPort, &ModemStatus)) {
|
|
UpsHandleError( UpsErrorModemStatus, NERR_UPSInvalidCommPort, GetLastError());
|
|
}
|
|
|
|
switch( UpsGlobalActiveSignals) {
|
|
|
|
case UPS_POWERFAILSIGNAL:
|
|
|
|
if ( UpsLineAsserted( ModemStatus, LINE_FAIL)) {
|
|
if ( UpsGlobalBatteryTime.StoredTime > (time_t)UpsGlobalConfig.ShutdownWait) {
|
|
ForkMessage = TRUE; // enough time to send a warning message
|
|
} else {
|
|
//
|
|
// Log an event even if we have little time left.
|
|
//
|
|
UpsReportEvent( NELOG_UPS_PowerOut, NULL, ERROR_SUCCESS);
|
|
UpsSystemShutdown();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UPS_LOWBATTERYSIGNAL:
|
|
|
|
if ( UpsLineAsserted( ModemStatus, LOW_BATT)) {
|
|
UpsSystemShutdown(); // no double log & alert for immediate shutdown
|
|
}
|
|
break;
|
|
|
|
case UPS_LOWBATTERYSIGNAL|UPS_POWERFAILSIGNAL:
|
|
|
|
// UpsSystemShutdown(); // for debugging purposes only - BUGBUG
|
|
|
|
if ( UpsLineAsserted( ModemStatus, LOW_BATT)){
|
|
UpsSystemShutdown();
|
|
}
|
|
if ( UpsLineAsserted( ModemStatus, LINE_FAIL)) {
|
|
ForkMessage = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
if ( ForkMessage == TRUE) {
|
|
|
|
UpsReportEvent( NELOG_UPS_PowerOut, NULL, ERROR_SUCCESS);
|
|
UpsAlertRaise( ALERT_PowerOut);
|
|
|
|
KdPrint(("[UPS] UPS_main: send PowerOut message\n"));
|
|
|
|
UpsNotifyUsers(
|
|
0,
|
|
UpsGlobalMessageFileHandle,
|
|
UPS_ACTION_PAUSE_SERVER,
|
|
L""
|
|
);
|
|
|
|
UpsGlobalMessageThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)UpsSendMessage,
|
|
(LPVOID)&FirstMessageSent,
|
|
0,
|
|
(LPDWORD)&ThreadId
|
|
);
|
|
|
|
if ( UpsGlobalMessageThread == NULL) {
|
|
//
|
|
// We do not care too much if message thread failed.
|
|
// BUGBUG We should probably LOG an event though.
|
|
//
|
|
KdPrint(("[UPS] Create message thread error %d\n", GetLastError()));
|
|
}
|
|
|
|
break; // start loop - waiting for power to come back
|
|
}
|
|
|
|
// The return value for the WaitCommEvent is not important
|
|
// since we need to read the CommPort in the loop anyway.
|
|
|
|
WaitCommEvent( UpsGlobalCommPort, &ModemStatus, &UpsGlobalOverlap);
|
|
status = WaitForMultipleObjects( 2, event, FALSE, INFINITE);
|
|
if ( status != 0) {
|
|
UpsHandleError( UpsErrorWait, status == 1 ? NO_ERROR : status, status);
|
|
}
|
|
} // end of loop - waiting for power to fail
|
|
|
|
|
|
// While loop for waiting for power to come back. This depends on
|
|
// the fact that UpsGlobalActiveSignals is either UPS_POWERFAILSIGNAL or
|
|
// UPS_POWERFAILSIGNAL|UPS_LOWBATTERY, and that UpdateTime() has been
|
|
// called recently.
|
|
|
|
for ( ; ; ) {
|
|
|
|
DWORD WaitTime;
|
|
BOOL PowerBack = FALSE;
|
|
|
|
// Battery time is taken into account only if the configuration
|
|
// is UPS_POWERFAILSIGNAL. Also, in this case we wait only if
|
|
// battery time is larger than UpsGlobalConfig.ShutdownWait.
|
|
|
|
if ( UpsGlobalActiveSignals != UPS_POWERFAILSIGNAL ||
|
|
UpsGlobalBatteryTime.StoredTime > (time_t)UpsGlobalConfig.ShutdownWait) {
|
|
|
|
WaitTime = (UpsGlobalActiveSignals != UPS_POWERFAILSIGNAL) ? INFINITE :
|
|
((UpsGlobalBatteryTime.StoredTime-UpsGlobalConfig.ShutdownWait) * 1000);
|
|
|
|
WaitCommEvent( UpsGlobalCommPort, &ModemStatus, &UpsGlobalOverlap);
|
|
status = WaitForMultipleObjects( 2, event, FALSE, WaitTime);
|
|
|
|
// If there was a change in signals (status 0) or wait expired
|
|
// (status WAIT_TIMEOUT && WaitTime finite) we look at signals.
|
|
// Else, we die here either becuse the service was ordered to
|
|
// die (status 1) or becuse of an unexpected error.
|
|
|
|
if ( !( status == 0 ||
|
|
status == WAIT_TIMEOUT && WaitTime != INFINITE)) {
|
|
UpsHandleError( UpsErrorWait, status == 1 ? NO_ERROR : status, status);
|
|
}
|
|
}
|
|
|
|
if ( !GetCommModemStatus( UpsGlobalCommPort, &ModemStatus)) {
|
|
UpsHandleError( UpsErrorModemStatus, NERR_UPSInvalidCommPort, GetLastError());
|
|
}
|
|
|
|
switch( UpsGlobalActiveSignals) {
|
|
|
|
case UPS_POWERFAILSIGNAL:
|
|
|
|
if ( !UpsLineAsserted( ModemStatus, LINE_FAIL)){
|
|
PowerBack = TRUE;
|
|
} else if ( status == WAIT_TIMEOUT) {
|
|
UpsSystemShutdown();
|
|
} else {
|
|
// a very unlikely case; used to remove glitches ?
|
|
UpsUpdateTime( DISCHARGE);
|
|
}
|
|
break;
|
|
|
|
case UPS_LOWBATTERYSIGNAL|UPS_POWERFAILSIGNAL:
|
|
|
|
if( UpsLineAsserted( ModemStatus, LOW_BATT)) {
|
|
UpsSystemShutdown();
|
|
}
|
|
|
|
if( !UpsLineAsserted( ModemStatus, LINE_FAIL)) {
|
|
PowerBack = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
if ( PowerBack == TRUE) {
|
|
|
|
UpsUpdateTime( DISCHARGE);
|
|
if (!SetEvent(g_hMessageDone))
|
|
{
|
|
KdPrint(("[UPS] Error setting Message Done Event: %d\n", GetLastError()));
|
|
ASSERT( FALSE);
|
|
}
|
|
//
|
|
// note: the INFINITE wait time relies on the fact that the
|
|
// message thread will exit "quickly." If the message thread
|
|
// is changed to block on some event in addition to
|
|
// g_hMessageDone, then the wait time should be set to some
|
|
// non-INFINITE value to avoid deadlock.
|
|
//
|
|
status = WaitForSingleObject(UpsGlobalMessageThread, INFINITE);
|
|
if (status != WAIT_OBJECT_0)
|
|
{
|
|
KdPrint(("[UPS] Error waiting for Message thread exit Event: %d\n", status));
|
|
ASSERT( FALSE);
|
|
}
|
|
CloseHandle(UpsGlobalMessageThread);
|
|
UpsGlobalMessageThread = NULL;
|
|
|
|
// FirstMessageSent is TRUE (set by the sent message thread)
|
|
// if the first message has ever been sent to the users on
|
|
// the server. If the users haven't been told that power
|
|
// has been down, don't bother telling them that power has
|
|
// returned.
|
|
|
|
if ( FirstMessageSent == TRUE) {
|
|
UpsNotifyUsers(
|
|
APE2_UPS_POWER_BACK,
|
|
UpsGlobalMessageFileHandle,
|
|
UPS_ACTION_SEND_MESSAGE | UPS_ACTION_CONTINUE_SERVER,
|
|
UpsGlobalConfig.ComputerName,
|
|
L""
|
|
);
|
|
FirstMessageSent = FALSE;
|
|
|
|
} else {
|
|
UpsNotifyUsers(
|
|
0,
|
|
UpsGlobalMessageFileHandle,
|
|
UPS_ACTION_CONTINUE_SERVER,
|
|
L""
|
|
);
|
|
}
|
|
UpsReportEvent( NELOG_UPS_PowerBack, NULL, ERROR_SUCCESS);
|
|
UpsAlertRaise( ALERT_PowerBack);
|
|
|
|
if ( !SetCommMask( UpsGlobalCommPort, UpsGlobalCommMask)) {
|
|
UpsHandleError( UpsErrorSetCommMask, NERR_UPSInvalidCommPort, GetLastError());
|
|
}
|
|
|
|
break; // start loop - waiting for power to fail
|
|
}
|
|
} // end of loop - waiting for power to come back
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
UpsControlHandler(
|
|
DWORD OpCode
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
The controller handler being passed to the service controller.
|
|
--*/
|
|
{
|
|
switch( OpCode) {
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
|
|
if ( UpsGlobalServiceStatus.dwCurrentState != SERVICE_STOP_PENDING) {
|
|
|
|
KdPrint(("[UPS] ControlHandler: service stop\n"));
|
|
UpsGlobalServiceStatus.dwWin32ExitCode = 1;
|
|
UpsGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
|
|
(VOID)UpsUpdateStatus();
|
|
|
|
if ( !SetEvent( UpsGlobalDoneEvent)) {
|
|
KdPrint(("[UPS] Error setting DoneEvent: %d\n", GetLastError()));
|
|
ASSERT( FALSE);
|
|
}
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case SERVICE_CONTROL_PAUSE:
|
|
KdPrint(("[UPS] ControlHandler: service not pausible\n"));
|
|
break;
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
KdPrint(("[UPS] ControlHandler: service not continuible\n"));
|
|
break;
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
break;
|
|
|
|
default:
|
|
KdPrint(("[UPS] ControlHandler: unknown OpCode=%d\n", OpCode));
|
|
break;
|
|
}
|
|
|
|
(VOID)UpsUpdateStatus();
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
UpsCreateProcess(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function executes shutdown command process & waits for it to
|
|
complete. If shutdown command process fails to complete in 30
|
|
seconds, then we write an event and kill the shutdown command process.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
STARTUPINFO StartupInfo;
|
|
BOOL success;
|
|
DWORD status;
|
|
|
|
if ( UpsGlobalCommand[ 0] == '\0') {
|
|
return;
|
|
}
|
|
|
|
GetStartupInfo( &StartupInfo);
|
|
StartupInfo.lpTitle = NULL;
|
|
|
|
//
|
|
// It makes no difference whether dwCreationFlags are "0" or
|
|
// "CREATE_NEW_CONSOLE".
|
|
//
|
|
success = CreateProcess(
|
|
NULL, // image name is imbedded in the command line
|
|
UpsGlobalCommand, // command line
|
|
NULL, // pSecAttrProcess
|
|
NULL, // pSecAttrThread
|
|
FALSE, // this process will not inherit our handles
|
|
0, // dwCreationFlags
|
|
// CREATE_NEW_CONSOLE, // dwCreationFlags
|
|
NULL, // pEnvironment
|
|
NULL, // pCurrentDirectory
|
|
&StartupInfo,
|
|
&ProcessInformation
|
|
);
|
|
|
|
if ( success == FALSE) {
|
|
|
|
DWORD Error = GetLastError();
|
|
|
|
KdPrint((
|
|
"[UPS] CreateProcess: CreateProcess( %s) fails with Error=(dec)%d\n",
|
|
UpsGlobalCommand,
|
|
Error
|
|
));
|
|
|
|
UpsReportEvent( NELOG_UPS_CmdFileExec, UpsGlobalCommand, Error);
|
|
return;
|
|
}
|
|
|
|
CloseHandle( ProcessInformation.hThread);
|
|
|
|
status = WaitForSingleObject(
|
|
ProcessInformation.hProcess,
|
|
COMMAND_WAIT_TIME * 1000
|
|
);
|
|
|
|
if ( status != WAIT_OBJECT_0) {
|
|
KdPrint((
|
|
"[UPS] CreateProcess: WaitForSingleObject( %s) fails with "
|
|
"status = 0x%x\n",
|
|
UpsGlobalCommand,
|
|
status
|
|
));
|
|
UpsReportEvent( NELOG_UPS_CmdFileError, NULL, ERROR_SUCCESS);
|
|
#ifdef NOT_YET
|
|
//
|
|
// Terminating the process does not guarantee successful shutdown
|
|
// since it may have children processes which still hang around.
|
|
// (e.g. a case of a batch file).
|
|
//
|
|
if ( !TerminateProcess( ProcessInformation.hProcess, NO_ERROR)) {
|
|
KdPrint((
|
|
"[UPS] CreateProcess: TerminateProcess( 0x%x) fails with"
|
|
"error = (dec)%d\n",
|
|
ProcessInformation.hProcess,
|
|
GetLastError()
|
|
));
|
|
}
|
|
#endif // NOT_YET
|
|
}
|
|
|
|
CloseHandle( ProcessInformation.hProcess);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
UpsHandleError(
|
|
IN UPS_ERROR_CONDITION failingCondition,
|
|
IN DWORD majorError,
|
|
IN DWORD minorError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles a Schedule service error condition. If the error
|
|
condition is fatal, it shuts down the Schedule service.
|
|
|
|
Arguments:
|
|
|
|
failingCondition - Supplies a value which indicates what the failure is.
|
|
|
|
majorError - supplies the major error code for the failure
|
|
|
|
minorError - supplies the minor error code for the failure. If minorError
|
|
equals NO_ERROR then its value is irrelevant. This argument
|
|
is used in the debugging build only.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
#ifdef UPS_DEBUG
|
|
PCHAR string;
|
|
#endif // UPS_DEBUG
|
|
|
|
UpsGlobalServiceStatus.dwWin32ExitCode = 1;
|
|
UpsGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
(VOID)UpsUpdateStatus();
|
|
|
|
#ifdef UPS_DEBUG
|
|
switch (failingCondition) {
|
|
|
|
case UpsNoError:
|
|
string = "Success";
|
|
break;
|
|
|
|
case UpsErrorRegisterControlHandler:
|
|
string = "Cannot register control handler";
|
|
break;
|
|
|
|
case UpsErrorCreateEvent:
|
|
string = "CreateEvent() error";
|
|
break;
|
|
|
|
case UpsErrorNotifyServiceController:
|
|
string = "SetServiceStatus() error";
|
|
break;
|
|
|
|
case UpsErrorCtrlHandler:
|
|
string = "SetConsoleCtrlHandler() error";
|
|
break;
|
|
|
|
case UpsErrorShutdownParameters:
|
|
string = "SetProcessShutdownParameters() error";
|
|
break;
|
|
|
|
case UpsErrorLoadLibrary:
|
|
string = "LoadLibrary() error";
|
|
break;
|
|
|
|
case UpsErrorRegisterEventSource:
|
|
string = "Cannot register event source";
|
|
break;
|
|
|
|
case UpsErrorGetConfig:
|
|
string = "UpsGetConfig() error";
|
|
break;
|
|
|
|
case UpsErrorModemStatus:
|
|
string = "GetCommModemStatus() error";
|
|
break;
|
|
|
|
case UpsErrorSignalAsserted:
|
|
string = "Signal line is being asserted, error";
|
|
break;
|
|
|
|
case UpsErrorRegisterService:
|
|
string = "RegisterServiceCtrlHandler() error";
|
|
break;
|
|
|
|
case UpsErrorOpenCommPort:
|
|
string = "OpenCommPort() error";
|
|
break;
|
|
|
|
case UpsErrorWait:
|
|
string = "WaitForMultipleObject() returns status ";
|
|
break;
|
|
|
|
case UpsErrorDtr:
|
|
string = "Set/Clear DTR error";
|
|
break;
|
|
|
|
case UpsErrorSetRts:
|
|
string = "Set RTS error";
|
|
break;
|
|
|
|
case UpsErrorSetTx:
|
|
string = "Set TX error";
|
|
break;
|
|
|
|
case UpsErrorSetCommMask:
|
|
string = "SetCommMask() error";
|
|
break;
|
|
|
|
case UpsErrorAdjustPrivilege:
|
|
string = "RtlAdjustPrivilege() error";
|
|
break;
|
|
|
|
case UpsErrorExitWindowsEx:
|
|
string = "ExitWindowsEx() error";
|
|
break;
|
|
|
|
case UpsErrorSystemShutdown:
|
|
string = "UpsSystemShutdown() error";
|
|
break;
|
|
|
|
default:
|
|
string = "Unknown error condition";
|
|
ASSERT( FALSE);
|
|
break;
|
|
}
|
|
|
|
KdPrint((
|
|
"[UPS] %s %u (dec)\n",
|
|
string,
|
|
minorError != NO_ERROR ? minorError : majorError
|
|
));
|
|
#endif // UPS_DEBUG
|
|
|
|
UpsShutdown( majorError);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
UpsInitialize(
|
|
VOID
|
|
)
|
|
{
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
DWORD ModemStatus;
|
|
BOOL asserted;
|
|
DWORD status;
|
|
#ifdef UPS_TOGGLER
|
|
char temp[17];
|
|
char line[256];
|
|
STARTUPINFO SInfo;
|
|
PROCESS_INFORMATION PInfo;
|
|
#endif // UPS_TOGGLER
|
|
|
|
UpsGlobalCommPort = NULL;
|
|
UpsGlobalLogFileHandle = NULL;
|
|
UpsGlobalMessageFileHandle = NULL;
|
|
UpsGlobalDoneEvent = NULL;
|
|
UpsGlobalOverlap.hEvent = NULL;
|
|
UpsGlobalMessageThread = NULL;
|
|
UpsGlobalCommand[ 0] = '\0';
|
|
|
|
UpsGlobalServiceStatus.dwServiceType = SERVICE_WIN32;
|
|
UpsGlobalServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
UpsGlobalServiceStatus.dwControlsAccepted = 0;
|
|
UpsGlobalServiceStatus.dwCheckPoint = 1;
|
|
UpsGlobalServiceStatus.dwWaitHint = 15000; // 15 seconds
|
|
|
|
SET_SERVICE_EXITCODE(
|
|
NO_ERROR,
|
|
UpsGlobalServiceStatus.dwWin32ExitCode,
|
|
UpsGlobalServiceStatus.dwServiceSpecificExitCode
|
|
);
|
|
|
|
UpsGlobalServiceStatusHandle = RegisterServiceCtrlHandler(
|
|
SERVICE_UPS,
|
|
UpsControlHandler
|
|
);
|
|
if ( UpsGlobalServiceStatusHandle == (SERVICE_STATUS_HANDLE)NULL) {
|
|
UpsHandleError( UpsErrorRegisterService, GetLastError(), NO_ERROR);
|
|
}
|
|
|
|
if ( ( status = UpsUpdateStatus()) != NO_ERROR) {
|
|
UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
|
|
}
|
|
|
|
UpsGlobalDoneEvent = CreateEvent(
|
|
NULL, //security
|
|
FALSE, //autoreset
|
|
FALSE, //initial-state
|
|
NULL //event name
|
|
);
|
|
if ( UpsGlobalDoneEvent == NULL) {
|
|
UpsHandleError( UpsErrorCreateEvent, GetLastError(), NO_ERROR);
|
|
}
|
|
|
|
UpsGlobalOverlap.hEvent = CreateEvent(
|
|
NULL, //Security
|
|
FALSE, //AutoReset
|
|
FALSE, //InitialState
|
|
NULL //Name of Event
|
|
);
|
|
if ( UpsGlobalOverlap.hEvent == NULL) {
|
|
UpsHandleError( UpsErrorCreateEvent, GetLastError(), NO_ERROR);
|
|
}
|
|
|
|
g_hMessageDone = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (g_hMessageDone == NULL)
|
|
{
|
|
UpsHandleError(UpsErrorCreateEvent, GetLastError(), NO_ERROR);
|
|
}
|
|
|
|
UpsGlobalBatteryTime.MarkTime = time((time_t *)NULL);
|
|
UpsGlobalBatteryTime.StoredTime = (time_t)0;
|
|
|
|
UpsGlobalLogFileHandle = RegisterEventSource( NULL, SERVICE_UPS);
|
|
if ( UpsGlobalLogFileHandle == NULL) {
|
|
UpsHandleError( UpsErrorRegisterEventSource, GetLastError(), NO_ERROR);
|
|
}
|
|
|
|
if ( ( status = UpsUpdateStatus()) != NO_ERROR) {
|
|
UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
|
|
}
|
|
|
|
if ( UpsGetConfig() != TRUE){
|
|
UpsHandleError( UpsErrorGetConfig, NERR_UPSInvalidConfig, NO_ERROR);
|
|
}
|
|
|
|
if ( ( UpsGlobalMessageFileHandle = LoadLibrary( MODULENAME)) == NULL) {
|
|
UpsHandleError( UpsErrorLoadLibrary, NERR_UPSInvalidConfig, GetLastError());
|
|
}
|
|
|
|
//Setting Security Attribute for inheritable handle
|
|
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
SecurityAttributes.lpSecurityDescriptor = NULL;
|
|
|
|
#ifdef UPS_TOGGLER
|
|
SecurityAttributes.bInheritHandle = TRUE;
|
|
#else // UPS_TOGGLER
|
|
SecurityAttributes.bInheritHandle = FALSE;
|
|
#endif // UPS_TOGGLER
|
|
|
|
UpsGlobalCommPort = CreateFile(
|
|
(LPCSTR)UpsGlobalConfig.Port, // comm port to open
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
&SecurityAttributes,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED,
|
|
NULL
|
|
);
|
|
if ( UpsGlobalCommPort == INVALID_HANDLE_VALUE) {
|
|
UpsHandleError( UpsErrorOpenCommPort, NERR_UPSInvalidCommPort, GetLastError());
|
|
}
|
|
|
|
#ifndef UPS_TOGGLER
|
|
|
|
// This is needed for supporting UPS turnoff. We write a value to
|
|
// the Comm Port to keep the UPS battery up. The value is written
|
|
// even if UPS_CANTURNOFF is clear. This corresponds to two cases:
|
|
// (a) UPS that really cannot be turned off
|
|
// (b) UPS that can be turned off but user does not want it turned off
|
|
// In case (a) we hope that it will not hurt anything if we do a
|
|
// meaningless action of SETDTR/CLRDTR.
|
|
// In case (b) a call to SETDTR/CLRDTR will keep UPS battery up
|
|
// after power failure. Without this call, UPS battery may immediately
|
|
// shut down after power failure.
|
|
// This is a workaround for the current UPS applet interface.
|
|
// Ideally, we should have an extra bit to distinguish between the two
|
|
// cases above (say, UPS_SHOULDTURNOFF bit).
|
|
|
|
if ( UpsGlobalConfig.Options & UPS_CANTURNOFF) {
|
|
|
|
if ( UpsGlobalConfig.Options & UPS_POSSIGSHUTOFF) {
|
|
ModemStatus = CLRDTR;
|
|
UpsGlobalModemStatus = SETDTR;
|
|
} else {
|
|
ModemStatus = SETDTR;
|
|
UpsGlobalModemStatus = CLRDTR;
|
|
}
|
|
UpsGlobalTurnOff = FALSE;
|
|
|
|
if ( !EscapeCommFunction( UpsGlobalCommPort, ModemStatus)) {
|
|
UpsHandleError( UpsErrorDtr, NERR_UPSInvalidCommPort, GetLastError());
|
|
}
|
|
|
|
if ( !SetConsoleCtrlHandler( UpsTurnOff, TRUE)) {
|
|
UpsHandleError( UpsErrorCtrlHandler, GetLastError(), NO_ERROR);
|
|
}
|
|
|
|
if ( !SetProcessShutdownParameters( 0, SHUTDOWN_NORETRY)) {
|
|
UpsHandleError( UpsErrorShutdownParameters, GetLastError(), NO_ERROR);
|
|
}
|
|
|
|
} else {
|
|
ModemStatus = UpsGlobalConfig.Options & UPS_POSSIGSHUTOFF ? CLRDTR : SETDTR;
|
|
(VOID)EscapeCommFunction( UpsGlobalCommPort, ModemStatus); // see above
|
|
}
|
|
|
|
// This is needed for supporting contact closure configuration.
|
|
// The two signals are needed to supply the power sources for
|
|
// contact closure. RTS - positive, TX - negative
|
|
|
|
if ( !EscapeCommFunction( UpsGlobalCommPort, SETRTS)) {
|
|
UpsHandleError( UpsErrorSetRts, NERR_UPSInvalidCommPort, GetLastError());
|
|
}
|
|
|
|
if ( !EscapeCommFunction(UpsGlobalCommPort, SETXOFF)) {
|
|
UpsHandleError( UpsErrorSetTx, NERR_UPSInvalidCommPort, GetLastError());
|
|
}
|
|
|
|
#else // UPS_TOGGLER
|
|
|
|
strcpy(line,"c:/nt/private/net/svcdlls/upssvc/obj/i386/toggle.exe ");
|
|
_itoa( (DWORD)UpsGlobalCommPort, temp, 10); //passing handle to be inherited
|
|
strcat(line,temp);
|
|
GetStartupInfo(&SInfo);
|
|
SInfo.lpTitle = "toggle";
|
|
|
|
if (!CreateProcess(
|
|
NULL, //application name
|
|
line, //command line
|
|
NULL, //Process Attribute
|
|
NULL, //Thread Attribute
|
|
TRUE, //Inherit Handle
|
|
CREATE_NEW_CONSOLE, //Creation Flag
|
|
NULL, //Environment,
|
|
NULL, //CurrentDirectory
|
|
&SInfo, //Startup info, share the same as parent
|
|
&PInfo //Process info
|
|
)) {
|
|
UpsHandleError( UpsErrorCreateProcess, GetLastError());
|
|
}
|
|
|
|
if ( ( status = UpsUpdateStatus()) != NO_ERROR) {
|
|
UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
|
|
}
|
|
|
|
Sleep( 10000); //10 seconds to reset the signals
|
|
|
|
#endif // UPS_TOGGLER
|
|
|
|
// This is required for contact closure configuration. The power
|
|
// of the contact closure inputs come from the signal pins, and
|
|
// they require time to settle. This is at least true for TrippLite
|
|
// UPS. (by experiment) 3 seconds is kind of arbitary.
|
|
|
|
if ( ( status = UpsUpdateStatus()) != NO_ERROR) {
|
|
UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
|
|
}
|
|
|
|
Sleep( 3000);
|
|
|
|
// don't start if any of the relevant lines is asserted
|
|
|
|
if ( !GetCommModemStatus( UpsGlobalCommPort, &ModemStatus)) {
|
|
UpsHandleError( UpsErrorModemStatus, NERR_UPSInvalidCommPort, GetLastError());
|
|
}
|
|
|
|
// Determing the Comm mask from the configuration. Only monitor
|
|
// the valid signals on the comm port. Do not start if any of these
|
|
// valid signals is asserted.
|
|
|
|
UpsGlobalActiveSignals =
|
|
( UpsGlobalConfig.Options & ( UPS_POWERFAILSIGNAL | UPS_LOWBATTERYSIGNAL));
|
|
|
|
switch( UpsGlobalActiveSignals) {
|
|
|
|
case UPS_POWERFAILSIGNAL:
|
|
UpsGlobalCommMask = LINE_FAIL_MASK;
|
|
asserted = UpsLineAsserted( ModemStatus, LINE_FAIL);
|
|
break;
|
|
|
|
case UPS_LOWBATTERYSIGNAL:
|
|
UpsGlobalCommMask = LOW_BATT_MASK;
|
|
asserted = UpsLineAsserted( ModemStatus, LOW_BATT);
|
|
break;
|
|
|
|
case (UPS_LOWBATTERYSIGNAL | UPS_POWERFAILSIGNAL):
|
|
UpsGlobalCommMask = (LINE_FAIL_MASK | LOW_BATT_MASK);
|
|
asserted = UpsLineAsserted( ModemStatus, LINE_FAIL) ||
|
|
UpsLineAsserted( ModemStatus, LOW_BATT);
|
|
break;
|
|
}
|
|
|
|
if ( asserted) {
|
|
UpsHandleError( UpsErrorSignalAsserted, NERR_UPSSignalAsserted, NO_ERROR);
|
|
}
|
|
|
|
if ( !SetCommMask( UpsGlobalCommPort, UpsGlobalCommMask)) {
|
|
UpsHandleError( UpsErrorSetCommMask, NERR_UPSInvalidCommPort, GetLastError());
|
|
}
|
|
|
|
// we don't accept pause or continue
|
|
UpsGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
UpsGlobalServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
|
|
|
if ((status = UpsUpdateStatus()) != NO_ERROR) {
|
|
UpsHandleError( UpsErrorNotifyServiceController, status, NO_ERROR);
|
|
}
|
|
KdPrint(("[UPS] Init completed successfully, waiting for comm port events.\n"));
|
|
}
|
|
|
|
|
|
VOID
|
|
UpsSendMessage(
|
|
PBOOL pFirstMessageSent
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This is the message thread spawned by the main UPS service thread.
|
|
The first message would be sent out after the FirstMessDelay in the
|
|
config field. Other messages show up after every MessageInterval.
|
|
The BOOL FirstMessageSent is used to tell the UPS service thread whether
|
|
a message has been sent to every users with sessions, so that users
|
|
won't be notified about power coming back if they don't know that power
|
|
has been down.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwRet;
|
|
dwRet = WaitForSingleObject(g_hMessageDone,
|
|
UpsGlobalConfig.FirstMessageDelay * 1000);
|
|
if (dwRet == WAIT_OBJECT_0)
|
|
{
|
|
ExitThread(0);
|
|
}
|
|
|
|
*pFirstMessageSent = TRUE;
|
|
|
|
for( ; ;) {
|
|
UpsNotifyUsers(
|
|
APE2_UPS_POWER_OUT,
|
|
UpsGlobalMessageFileHandle,
|
|
UPS_ACTION_SEND_MESSAGE,
|
|
UpsGlobalConfig.ComputerName,
|
|
L""
|
|
);
|
|
dwRet = WaitForSingleObject(g_hMessageDone,
|
|
UpsGlobalConfig.MessageInterval * 1000);
|
|
if (dwRet == WAIT_OBJECT_0)
|
|
{
|
|
ExitThread(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
UpsShutdown(
|
|
DWORD status
|
|
)
|
|
{
|
|
if ( status != NO_ERROR && UpsGlobalLogFileHandle != NULL) {
|
|
UpsReportEvent( status, NULL, ERROR_SUCCESS);
|
|
}
|
|
|
|
if (UpsGlobalMessageThread != NULL)
|
|
{
|
|
if (!SetEvent(g_hMessageDone))
|
|
{
|
|
KdPrint(("[UPS] Error setting Message Done Event: %d\n", GetLastError()));
|
|
}
|
|
WaitForSingleObject(UpsGlobalMessageThread, INFINITE);
|
|
CloseHandle(UpsGlobalMessageThread);
|
|
}
|
|
|
|
if ( UpsGlobalDoneEvent != NULL) {
|
|
CloseHandle( UpsGlobalDoneEvent);
|
|
}
|
|
|
|
if ( UpsGlobalOverlap.hEvent != NULL) {
|
|
CloseHandle( UpsGlobalOverlap.hEvent);
|
|
}
|
|
|
|
if (g_hMessageDone != NULL)
|
|
{
|
|
CloseHandle(g_hMessageDone);
|
|
}
|
|
|
|
if ( UpsGlobalCommPort != NULL) {
|
|
CloseHandle( UpsGlobalCommPort);
|
|
}
|
|
|
|
if (UpsGlobalMessageFileHandle != NULL)
|
|
{
|
|
FreeLibrary(UpsGlobalMessageFileHandle);
|
|
}
|
|
|
|
if (UpsGlobalLogFileHandle != NULL)
|
|
{
|
|
DeregisterEventSource(UpsGlobalLogFileHandle);
|
|
}
|
|
|
|
// We are done with cleaning up. Tell Service Controller that we are
|
|
// stopped.
|
|
//
|
|
UpsGlobalServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
UpsGlobalServiceStatus.dwControlsAccepted = 0;
|
|
|
|
|
|
SET_SERVICE_EXITCODE(
|
|
status,
|
|
UpsGlobalServiceStatus.dwWin32ExitCode,
|
|
UpsGlobalServiceStatus.dwServiceSpecificExitCode
|
|
);
|
|
|
|
UpsGlobalServiceStatus.dwCheckPoint = 0;
|
|
UpsGlobalServiceStatus.dwWaitHint = 0;
|
|
|
|
(VOID) UpsUpdateStatus();
|
|
ExitThread( status);
|
|
}
|
|
|
|
|
|
VOID
|
|
UpsSystemShutdown(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify users of a shutdown, log the shutdown, and do a shutdown of
|
|
the system. Once this routine is called, it won't accept a net stop
|
|
command from the service controller. This ensures a clean shutdown.
|
|
The UPS service terminates if the ExitWindosEx() call fails.
|
|
|
|
It is assumed that when we enter this routine we have at least
|
|
UpsGlobalConfig.ShutdownWait seconds before UPS battery gives up on us.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
We should really post some additional logs in case of errors. These
|
|
logs should use the actual errors (e.g. ntStatus or GetLastError())
|
|
rather than "wrapper" type of error such as NERR_UPSShutdownFailed.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
BOOLEAN myBoolean;
|
|
DWORD StartTickCount;
|
|
|
|
// KdPrint(("[UPS] SystemShutdown: You have 1 minute to setup debugging\n"));
|
|
// Sleep( 60000); // BUGBUG
|
|
|
|
StartTickCount = GetTickCount();
|
|
KdPrint(("[UPS] SystemShutdown: StartTickCount=0x%x\n", StartTickCount));
|
|
|
|
// service don't accept STOP once reaches here.
|
|
// wants a clean shutdown... nothing should kill this
|
|
UpsGlobalServiceStatus.dwControlsAccepted = 0;
|
|
SetServiceStatus(UpsGlobalServiceStatusHandle, &UpsGlobalServiceStatus);
|
|
|
|
// kill message thread if started
|
|
if (UpsGlobalMessageThread != NULL)
|
|
{
|
|
if (!SetEvent(g_hMessageDone))
|
|
{
|
|
KdPrint(("[UPS] Error setting Message Done Event: %d\n", GetLastError()));
|
|
}
|
|
WaitForSingleObject(UpsGlobalMessageThread, INFINITE);
|
|
CloseHandle(UpsGlobalMessageThread);
|
|
UpsGlobalMessageThread = NULL;
|
|
}
|
|
|
|
UpsReportEvent( NELOG_UPS_Shutdown, NULL, ERROR_SUCCESS);
|
|
UpsAlertRaise( ALERT_PowerShutdown);
|
|
|
|
UpsCreateProcess();
|
|
|
|
// Sleep for a few seconds to allow alerter a little extra time to
|
|
// process the shutdown alert before we stop the server.
|
|
|
|
Sleep( 3000);
|
|
|
|
UpsNotifyUsers(
|
|
APE2_UPS_POWER_SHUTDOWN_FINAL,
|
|
UpsGlobalMessageFileHandle,
|
|
UPS_ACTION_SEND_MESSAGE | UPS_ACTION_STOP_SERVER,
|
|
UpsGlobalConfig.ComputerName,
|
|
L""
|
|
);
|
|
|
|
// RtlAdjustPrivilege() & ExitWindowsEx() mods below == fix for bug #6032
|
|
|
|
ntStatus = RtlAdjustPrivilege(
|
|
SE_SHUTDOWN_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&myBoolean // was it enabled or not
|
|
);
|
|
if ( ntStatus != STATUS_SUCCESS) {
|
|
KdPrint((
|
|
"[UPS] SystemShutdown: RtlAdjustPrivilege() failed: ntStatus = 0x%x\n",
|
|
ntStatus
|
|
));
|
|
UpsHandleError( UpsErrorAdjustPrivilege, NERR_UPSShutdownFailed,
|
|
RtlNtStatusToDosError( ntStatus));
|
|
}
|
|
|
|
UpsGlobalTurnOff = TRUE;
|
|
|
|
if ( !ExitWindowsEx( EWX_SHUTDOWN | EWX_FORCE, (DWORD)-1)) {
|
|
UpsHandleError( UpsErrorExitWindowsEx, NERR_UPSShutdownFailed, GetLastError());
|
|
}
|
|
|
|
if ( UpsGlobalConfig.Options & UPS_CANTURNOFF) {
|
|
//
|
|
// TickCount == elapsed time in milliseconds, is correct even if
|
|
// GetTickCount() wrapped around once.
|
|
//
|
|
DWORD EndTickCount = GetTickCount();
|
|
DWORD TickCount = EndTickCount - StartTickCount;
|
|
DWORD ntStatus;
|
|
|
|
KdPrint(("[UPS] SystemShutdown: EndTickCount=0x%x\n", EndTickCount));
|
|
|
|
if (UpsGlobalConfig.ShutdownWait * 1000 > TickCount) {
|
|
KdPrint(("[UPS] SystemShutdown: sleep for (dec)%d milliseconds\n",
|
|
UpsGlobalConfig.ShutdownWait * 1000 - TickCount));
|
|
Sleep( UpsGlobalConfig.ShutdownWait * 1000 - TickCount);
|
|
}
|
|
|
|
KdPrint(("[UPS] SystemShutdown: no timely callback. "
|
|
"Turn the power off within main thread.\n"));
|
|
|
|
ntStatus = NtShutdownSystem( FALSE);
|
|
|
|
KdPrint(("[UPS] SystemShutdown: NtShutdownSystem( FALSE) returns "
|
|
"ntStatus = 0x%x\n", ntStatus));
|
|
|
|
if ( !EscapeCommFunction( UpsGlobalCommPort, UpsGlobalModemStatus)) {
|
|
DWORD error = GetLastError();
|
|
KdPrint(("[UPS] SystemShutdown: DTR error, %d\n", error));
|
|
UpsHandleError( UpsErrorSystemShutdown, NERR_UPSShutdownFailed, error);
|
|
} else {
|
|
Sleep(10000); // wait more than 4.5 secs before exiting so that
|
|
// the serial line remains asserted long enough for
|
|
// the UPS to act on it.
|
|
UpsHandleError( UpsErrorSystemShutdown, NERR_UPSShutdownFailed, NO_ERROR);
|
|
}
|
|
} else {
|
|
UpsHandleError( UpsNoError, NO_ERROR, NO_ERROR);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
UpsTurnOff(
|
|
DWORD ControlType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes CTRL_SHUTDOWN_EVENT signal in case of UPS service initiated
|
|
shutdown.
|
|
|
|
Arguments:
|
|
|
|
ControlType - control type
|
|
|
|
Return Value:
|
|
|
|
TRUE - for processed signal
|
|
FALSE - for signal that was not processed
|
|
|
|
--*/
|
|
{
|
|
DWORD ntStatus;
|
|
|
|
// return( FALSE); // BUGBUG to test error path in UpsSystemShutdown()
|
|
|
|
KdPrint(("[UPS] TurnOff: enter with ControlType=(dec)%d\n", ControlType));
|
|
|
|
if ( UpsGlobalTurnOff != TRUE) {
|
|
return( FALSE); // somebody else initiated shutdown
|
|
}
|
|
|
|
if ( ControlType != CTRL_SHUTDOWN_EVENT) {
|
|
if ( ControlType != CTRL_LOGOFF_EVENT) {
|
|
KdPrint(("[UPS] TurnOff: unexpected ControlType=%d\n", ControlType));
|
|
}
|
|
return( FALSE); // not a signal we process here
|
|
}
|
|
|
|
KdPrint(("[UPS] TurnOff: Turn the power off, GetTickCount=0x%x\n", GetTickCount()));
|
|
|
|
ntStatus = NtShutdownSystem( FALSE);
|
|
|
|
KdPrint(("[UPS] TurnOff: NtShutdownSystem( FALSE) returns ntStatus = 0x%x\n",
|
|
ntStatus));
|
|
|
|
if ( !EscapeCommFunction(UpsGlobalCommPort, UpsGlobalModemStatus)) {
|
|
KdPrint(("[UPS] TurnOff: DTR error, %d\n", GetLastError()));
|
|
return( FALSE); // signal not processed completely
|
|
}
|
|
Sleep(10000); // wait more than 4.5 secs before exiting
|
|
|
|
// Turning the power of is not instantenous, it takes a second or so.
|
|
// Thus we may actually succeed in printing the log below!
|
|
|
|
KdPrint(("[UPS] TurnOff: Power was turned off.\n"));
|
|
return( TRUE); // we did it
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
UpsUpdateStatus(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function updates the Schedule service status with the Service
|
|
Controller.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
|
|
if ( UpsGlobalServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
|
|
return( ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
if ( ! SetServiceStatus( UpsGlobalServiceStatusHandle, &UpsGlobalServiceStatus)) {
|
|
return( GetLastError());
|
|
}
|
|
|
|
return( NO_ERROR);
|
|
}
|