/*++ Copyright (C) Microsoft Corporation, 1997 - 1999 Module Name: service.cxx Abstract: This contains the Service related functionality of SENS. Author: Gopal Parupudi [Notes:] This is cloned from \nt\private\eventsystem\server\eventsystem.cpp Revision History: GopalP 1/11/1998 Start. --*/ #include #include #include #include "service.hxx" // // Constants // #define SENS_NAME SENS_STRING("SENS") #define SENS_DATA 0x19732304 #define SENS_WAIT_HINT 3*1000 enum ACTION { ACTION_NONE, ACTION_APPLICATION, ACTION_SERVICE }; // // Globals // DWORD gdwError; HANDLE ghStopEvent; extern HANDLE ghSensStartedEvent; SYSTEM_POWER_STATUS gSystemPowerState; // // Service related stuff // SERVICE_STATUS gServiceStatus; // current status of the service SERVICE_STATUS_HANDLE ghStatusHandle; HDEVNOTIFY ghDeviceNotify; SERVICE_TABLE_ENTRY gaServiceEntryTable[] = { { SENS_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain }, { NULL, NULL } }; // // Helper functions // void __stdcall LogMessage( TCHAR* msg1, TCHAR* msg2 ) /*++ Routine Description: Arguments: Return Value: --*/ { const TCHAR* strings[2] = {msg1, msg2}; HANDLE hEventSource; hEventSource = RegisterEventSource(NULL, SENS_STRING("SENS")); if (hEventSource != NULL) { ReportEvent( hEventSource, // event source handle EVENTLOG_ERROR_TYPE, // event type 0, // event category 0, // event ID NULL, // current user's SID 2, // strings in lpszStrings 0, // no bytes of raw data strings, // array of error strings NULL // no raw data ); DeregisterEventSource(hEventSource); } } void ServiceStart( void ) /*++ Routine Description: Start SENS as service. Stay up until we receive the stop event. Arguments: None. Return Value: None. --*/ { // Initialize SENS. if (FALSE == SensInitialize()) { SensPrintToDebugger(SENS_DBG, ("[SENS] [%d] SensInitialize() failed.\n", GetTickCount())); return; } // Tell the SCM that we're running now. if (!ReportStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0)) { SensPrintToDebugger(SENS_DBG, ("[SENS] [%d] ReportStatusToSCM() failed.\n", GetTickCount())); return; } // Set the SensStartedEvent now. if (ghSensStartedEvent != NULL) { SetEvent(ghSensStartedEvent); SensPrintA(SENS_INFO, ("[%d] Successfully signalled starting of SENS.\n", GetTickCount())); } else { SensPrintToDebugger(SENS_DBG, ("[SENS] [%d] Couldn't set the SENS Started event!\n", GetTickCount())); } SensPrintToDebugger(SENS_DBG, ("\n[SENS] [%d] Started successfully.\n\n", GetTickCount())); } void ServiceStop( void ) /*++ Routine Description: Stop SENS as a service. Arguments: None. Return Value: None. --*/ { // // Cleanup now. // SensUninitialize(); } VOID WINAPI ServiceMain( DWORD argc, TCHAR* argv[] ) /*++ Routine Description: Perform the actual service initialization. Arguments: Usual stuff. Return Value: Usual stuff. --*/ { // // Initialize Globals. // gdwError = 0x0; ghStopEvent = NULL; ghStatusHandle = NULL; memset(&gServiceStatus, 0x0, sizeof(SERVICE_STATUS)); ghDeviceNotify = NULL; // Service status parameters that don't change. gServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; gServiceStatus.dwCurrentState = SERVICE_START_PENDING; gServiceStatus.dwControlsAccepted = 0; gServiceStatus.dwWin32ExitCode = 0; gServiceStatus.dwServiceSpecificExitCode = 0; gServiceStatus.dwCheckPoint = 0; gServiceStatus.dwWaitHint = SENS_WAIT_HINT; // // Register our service control handler // DEV_BROADCAST_DEVICEINTERFACE PnPFilter; ghStatusHandle = RegisterServiceCtrlHandlerEx( SENS_NAME, ServiceControl, (PVOID) SENS_DATA ); if (ghStatusHandle == NULL) { return; } #ifdef PNP_EVENTS // Before enabling PnP events be aware that the code to unregister for the PnP // is missing. Since SENS does not use the PnPs the code was all removed. // // Register for the PnP Device Interface change notifications // PnPFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); PnPFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; PnPFilter.dbcc_reserved = 0x0; memcpy( &PnPFilter.dbcc_classguid, (LPGUID) &GUID_NDIS_LAN_CLASS, sizeof(GUID) ); ghDeviceNotify = RegisterDeviceNotification( (HANDLE) ghStatusHandle, &PnPFilter, DEVICE_NOTIFY_SERVICE_HANDLE ); if (NULL == ghDeviceNotify) { SensPrintToDebugger(SENS_DBG, ("[SENS] [%d] ServiceMain(): RegisterDeviceNotification() failed\n", GetTickCount())); } #ifdef DETAIL_DEBUG SensPrintToDebugger(SENS_DBG, ("[SENS] [%d] ServiceMain(): RegisterDeviceNotification() succeeded\n", GetTickCount())); #endif // DETAIL_DEBUG #endif // PNP_EVENTS // // Save a snapshot of the System Power State. // BOOL bRet = GetSystemPowerStatus(&gSystemPowerState); if (bRet == FALSE) { SensPrintA(SENS_ERR, ("SensMessageLoopThread(): GetSystemPowerStatus() failed with " "GLE = %d\n", GetLastError())); } ASSERT(bRet); // Report the status, the exit code, and the wait hint to the SCM. if (!ReportStatusToSCM(SERVICE_START_PENDING, NO_ERROR, SENS_WAIT_HINT)) { return; } // Start the service executing. ServiceStart(); // Let the thread return. We will use the stop thread to cleanup. return; } DWORD WINAPI ServiceControl( DWORD dwCode, DWORD dwEventType, PVOID EventData, PVOID pData ) /*++ Routine Description: Handle Control Codes from SCM. Arguments: dwCode - The control code. dwEventType - The type of the event. EventData - Data corresponding to the event. pData - Additional Data. Notes: Refer to \\popcorn\razzle1\src\spec\umevent.doc for further details. Return Value: None. --*/ { PDEV_BROADCAST_DEVICEINTERFACE pDevice; NTSTATUS NtStatus; ANSI_STRING DeviceNameA; UNICODE_STRING UnicodeString; unsigned char *DeviceUuidA; DWORD dwStatus = NO_ERROR; pDevice = (PDEV_BROADCAST_DEVICEINTERFACE) EventData; DeviceUuidA = NULL; #ifdef DETAIL_DEBUG SensPrintToDebugger(SENS_DBG, ("[SENS] ServiceControl(): dwCode = 0x%x\n", dwCode)); #endif // DETAIL_DEBUG switch (dwCode) { case SERVICE_CONTROL_STOP: // // Stop the service. // // SERVICE_STOP_PENDING should be reported before setting the Stop // Event. This avoids a race condition which may result in a 1053 // - "The Service did not respond" error. // ReportStatusToSCM(SERVICE_STOP_PENDING, NO_ERROR, SENS_WAIT_HINT); ServiceStop(); ReportStatusToSCM(SERVICE_STOPPED, NO_ERROR, 0); return dwStatus; case SERVICE_CONTROL_INTERROGATE: // // Update the service status. // ReportStatusToSCM(gServiceStatus.dwCurrentState, NO_ERROR, 0); break; #ifdef PNP_EVENTS case SERVICE_CONTROL_DEVICEEVENT: // // PnP event. // #ifdef DETAIL_DEBUG RtlInitUnicodeString(&UnicodeString, (PCWSTR) &pDevice->dbcc_name); NtStatus = RtlUnicodeStringToAnsiString(&DeviceNameA, &UnicodeString, TRUE); UuidToStringA(&pDevice->dbcc_classguid, &DeviceUuidA); SensPrintToDebugger(SENS_DBG, ("\n-------------------------------------------------------------\n")); SensPrintToDebugger(SENS_DBG, ("SENS received a PnP Event - ")); SensPrintToDebugger(SENS_DBG, ((dwEventType == DBT_DEVICEREMOVECOMPLETE) ? "DEVICE REMOVED\n" : "")); SensPrintToDebugger(SENS_DBG, ((dwEventType == DBT_DEVICEARRIVAL) ? "DEVICE ARRIVED\n" : "\n")); SensPrintToDebugger(SENS_DBG, ("\tdwCode - 0x%x\n", dwCode)); SensPrintToDebugger(SENS_DBG, ("\tdwEventType - 0x%x\n", dwEventType)); SensPrintToDebugger(SENS_DBG, ("\tpData - 0x%x\n", pData)); SensPrintToDebugger(SENS_DBG, ("\tEventData - 0x%x\n", pDevice)); SensPrintToDebugger(SENS_DBG, ("\t o dbcc_size - 0x%x\n", pDevice->dbcc_size)); SensPrintToDebugger(SENS_DBG, ("\t o dbcc_devicetype - 0x%x\n", pDevice->dbcc_devicetype)); SensPrintToDebugger(SENS_DBG, ("\t o dbcc_reserved - 0x%x\n", pDevice->dbcc_reserved)); SensPrintToDebugger(SENS_DBG, ("\t o dbcc_classguid - %s\n", DeviceUuidA)); SensPrintToDebugger(SENS_DBG, ("\t o dbcc_name - %s\n", DeviceNameA.Buffer)); SensPrintToDebugger(SENS_DBG, ("-------------------------------------------------------------\n\n")); if (NT_SUCCESS(NtStatus)) { RtlFreeAnsiString(&DeviceNameA); } if (DeviceUuidA != NULL) { RpcStringFreeA(&DeviceUuidA); } #endif // DETAIL_DEBUG break; #endif // PNP_EVENTS case SERVICE_CONTROL_POWEREVENT: { // // Power event // // // These are generated every 1% of power change, also by playing // with the power cpl or plugging in the machine. // SYSTEM_POWER_STATUS CurSPstate; SENSEVENT_POWER Data; BOOL bRet; BOOL bFireEvent = FALSE; bRet = GetSystemPowerStatus(&CurSPstate); ASSERT(bRet); switch(dwEventType) { case PBT_APMPOWERSTATUSCHANGE: { // // OnACPower event is fired when // o previously the machine was not on AC // o now, it is on AC // if ( (CurSPstate.ACLineStatus == AC_LINE_ONLINE) && (gSystemPowerState.ACLineStatus != AC_LINE_ONLINE)) { Data.eType = SENS_EVENT_POWER_ON_ACPOWER; bFireEvent = TRUE; } else // // OnBatteryPower event is fired when // o previously the machine was on AC // o now, it is not on AC // o the machine has a system battery // if ( (CurSPstate.ACLineStatus == AC_LINE_OFFLINE) && (gSystemPowerState.ACLineStatus == AC_LINE_ONLINE) && ((CurSPstate.BatteryFlag & BATTERY_FLAG_NO_BATTERY) == 0)) { Data.eType = SENS_EVENT_POWER_ON_BATTERYPOWER; bFireEvent = TRUE; // Special case, if the machine goes off battery and has a low // battery we want to generate both events. Resetting the // low battery flag here guarantees that next time power changes // we will fire the low battery event. CurSPstate.BatteryFlag = CurSPstate.BatteryFlag & ~BATTERY_FLAG_LOW; } else // OnBatteryPowerLow event is fired when // o the battery is not charging and // o previously the battery was not low // o and now the battery is low. // if ( (CurSPstate.BatteryFlag & BATTERY_FLAG_LOW) && ( (CurSPstate.BatteryFlag & BATTERY_FLAG_CHARGING) == 0) && ( (gSystemPowerState.BatteryFlag & BATTERY_FLAG_LOW) == 0) ) { Data.eType = SENS_EVENT_POWER_BATTERY_LOW; bFireEvent = TRUE; } else { // Power event we don't about ASSERT(bFireEvent == FALSE); } break; } default: { // Other power event we can ignore break; } } if (bFireEvent) { // Save the new state. A critsec is not necessary as service control messages are serialized. memcpy(&gSystemPowerState, &CurSPstate, sizeof(SYSTEM_POWER_STATUS)); // Fire the event. memcpy(&Data.PowerStatus, &CurSPstate, sizeof(SYSTEM_POWER_STATUS)); SensFireEvent(&Data); } dwStatus = SUCCESS; break; } default: dwStatus = ERROR_CALL_NOT_IMPLEMENTED; // invalid control code break; } return dwStatus; } BOOL ReportStatusToSCM( DWORD dwCurrentState, DWORD dwExitCode, DWORD dwWaitHint ) /*++ Routine Description: Report status to SCM. Arguments: dwCurrentState - The current state of the service. dwExitCode - The Win32 Exit code. dwWaitHint - The amount of time in msec to wait for the SCM to acknowledge. Return Value: TRUE, succeeded. FALSE, otherwise. --*/ { DWORD dwCheckPoint = 0; BOOL bResult = TRUE; if (dwCurrentState == SERVICE_START_PENDING) { gServiceStatus.dwControlsAccepted = 0; } else { gServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT; } gServiceStatus.dwCurrentState = dwCurrentState; gServiceStatus.dwWin32ExitCode = dwExitCode; gServiceStatus.dwWaitHint = dwWaitHint; if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) { gServiceStatus.dwCheckPoint = 0; } else { gServiceStatus.dwCheckPoint = ++dwCheckPoint; } // // Report the status of the service to the SCM. // Caller handles error reporting, so we can have some context.... // return SetServiceStatus(ghStatusHandle, &gServiceStatus); }