//------------------------------------------------------------------------- // sentinal\sentinal.cpp // // Sentinal server monitoring service // // Owner: Mark Snyder (MarkSn) // // Copyright (C) 1998 Mark E. Snyder, Microsoft Corp. //------------------------------------------------------------------------- #define INITGUID #define __MODULE__ "sentinal" #include "zlib.h" #include #include #include #include //#include #include #include "srvdbg.h" #include "Sentinal.h" #include "sentmsg.h" #include "config.h" #include "dplaychk.h" DEFINE_GUID(FEDSRV_GUID, 0x81662310, 0xfcb4, 0x11d0, 0xa8, 0x8a, 0x0, 0x60, 0x97, 0xb5, 0x8f, 0xbf); #define _INTERNET_PAGING #ifdef _INTERNET_PAGING #include #endif //_INTERNET_PAGING // !!! The following enum must match the indices of the items in rgCfg in // !!! LoadSettings() !!! typedef enum { karg_fTimeout, karg_fWantInt3, karg_dwPingInterval, karg_szPagerNotificationURL, karg_szMonitorServers, } SENTINAL_ARGS; // instance of class that handles registry config parms ConfigManager* g_pCfgMgr; const char g_szSvcName[] = "Sentinal"; SERVICE_STATUS_HANDLE g_ssh = NULL; HINSTANCE g_hInst = NULL; void StartServerTerminateThread(); DWORD WINAPI ServerTerminateThread(DWORD dwUnused); void SetSvcStatus(DWORD state, DWORD exitcode); DWORD SendAPage(char* sz, ...); #ifdef _INTERNET_PAGING HINTERNET g_hinetSession; typedef HINTERNET (WINAPI* PFNINTERNETOPENURL)(HINTERNET, LPCSTR, LPCSTR, DWORD, DWORD, DWORD); typedef BOOL (WINAPI* PFNINTERNETGETLASTRESPONSEINFO)(LPDWORD, LPSTR, LPDWORD); typedef BOOL (WINAPI* PFNINTERNETCLOSEHANDLE)(HINTERNET); typedef HINTERNET (WINAPI* PFNINTERNETOPEN)(LPCSTR, DWORD, LPCSTR, LPCSTR, DWORD); // prototypes PFNINTERNETOPENURL pfnInternetOpenUrl; PFNINTERNETGETLASTRESPONSEINFO pfnInternetGetLastResponseInfo; PFNINTERNETCLOSEHANDLE pfnInternetCloseHandle; PFNINTERNETOPEN pfnInternetOpen; #endif //_INTERNET_PAGING HINSTANCE g_hMessage; char g_szComputerName[MAX_COMPUTERNAME_LENGTH + 1]; char g_szServerStatus[16]; HANDLE g_hEventShutdown; DWORD PingThreadProc(LPVOID lpv) { HANDLE hEventShutdown = (HANDLE)lpv; DWORD dwPingInterval = 15000; char szServers[MAX_PATH + 1]; char *psz = NULL; int nServers = 0; g_pCfgMgr->GetConfigDWORD(kCompId_Sentinal, karg_dwPingInterval, (DWORD*)&dwPingInterval); g_pCfgMgr->GetConfigString(kCompId_Sentinal, karg_szMonitorServers, szServers, sizeof(szServers)-1); for (psz = szServers; *psz; psz++) if (*psz==';' || *psz==',') { *psz = '\0'; nServers++; } *(psz+1) = '\0'; if (nServers > 16) nServers = 16; memset(g_szServerStatus, 'U', sizeof(g_szServerStatus)); // Initialize COM library HRESULT hr = CoInitialize(NULL); BOOL bFirstTime = TRUE; while (WAIT_TIMEOUT == WaitForSingleObject(hEventShutdown, bFirstTime ? 10000 : dwPingInterval)) { bFirstTime = FALSE; int nServer = 0; for(psz = szServers; *psz && nServerGetConfigString(kCompId_Sentinal, karg_szPagerNotificationURL, szPagerNotificationURL, sizeof(szPagerNotificationURL)-1); char* szFormat = szPagerNotificationURL; //for (char* szFormat = szPagerNotificationURL; *szFormat; szFormat += lstrlen(szFormat) + 1) { if (pfnInternetOpenUrl && pfnInternetGetLastResponseInfo && pfnInternetCloseHandle) { char rgchBuf[1024]; DWORD cbBuf = sizeof(rgchBuf); DWORD cch = FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY, szFormat, 0, 0, rgchBuf, sizeof(rgchBuf) - 1, rgszStrings); // open a session HINTERNET hinetHTTPSession = (*pfnInternetOpenUrl)(g_hinetSession, rgchBuf, NULL, 0, INTERNET_FLAG_RELOAD, 0); if (NULL == hinetHTTPSession) { char szMsg[256]; wsprintf(szMsg, "%08x (%d)", GetLastError(), GetLastError()); SRVDBG_ReportEvent(SRVDBG_ERROR_TYPE, SENTINAL_INTERNETOPENURL, szMsg); return 1; } DWORD dwErr; BOOL rc = (*pfnInternetGetLastResponseInfo)(&dwErr, rgchBuf, &cbBuf); if (!rc) { char szMsg[256]; wsprintf(szMsg, "%08x (%d)", GetLastError(), GetLastError()); SRVDBG_ReportEvent(SRVDBG_ERROR_TYPE, SENTINAL_INTERNETGETLASTRESPONSEINFO, szMsg); } (*pfnInternetCloseHandle)(hinetHTTPSession); } } return(0); } #endif //_INTERNET_PAGING /*------------------------------------------------------------------------- * LoadSettings *------------------------------------------------------------------------- * Purpose: * Load static server settings */ void LoadSettings() { g_pCfgMgr = new ConfigManager; ZAssert(g_pCfgMgr); // all new's assumed to succeed... ZAssert(g_pCfgMgr->Init()); static const ConfigInfo rgCfg[] = { // Name Type Max Length Required? Default, if not there { "fTimeout", kConfigType_DWORD, sizeof(DWORD), TRUE, 0 }, { "fWantInt3", kConfigType_DWORD, sizeof(DWORD), TRUE, 0 }, { "dwPingInterval", kConfigType_DWORD, sizeof(DWORD), FALSE, 0 }, { "szPagerNotificationURL", kConfigType_SZ, MAX_PATH + 1, TRUE, (DWORD) "" }, { "szMonitorServers", kConfigType_SZ, MAX_PATH + 1, TRUE, (DWORD) "" }, // !!!! If you add or delete items here, you MUST update the enum SENTINAL_ARGS in CONFIG.H to match // !!!! the appropriate index in this array !!! }; g_pCfgMgr->LoadConfig(kCompId_Sentinal, rgCfg, sizeof(rgCfg) / sizeof(rgCfg[0])); } /*------------------------------------------------------------------------- * Sentinal_Init *------------------------------------------------------------------------- * Purpose: * Performs startup tasks and initialization for this service. * * Side Effects: * Lots */ HRESULT pascal Sentinal_Init() { int iCon = 0; HRESULT hr = S_OK; SRVDBG_Init("Sentinal", NULL); SRVDBG_Info("Initialize"); LoadSettings(); // Initialize COM library hr = CoInitialize(NULL); DWORD cbBuf = sizeof(g_szComputerName); GetComputerName(g_szComputerName, &cbBuf); #ifdef _INTERNET_PAGING // are we going to be doing paging? char szPagerNotificationURL[MAX_PATH + 1]; g_pCfgMgr->GetConfigString(kCompId_Sentinal, karg_szPagerNotificationURL, szPagerNotificationURL, sizeof(szPagerNotificationURL)-1); if (szPagerNotificationURL[0]) { HINSTANCE hinstWininet = LoadLibrary("wininet.dll"); pfnInternetOpen = (PFNINTERNETOPEN)GetProcAddress(hinstWininet, "InternetOpenA"); pfnInternetGetLastResponseInfo = (PFNINTERNETGETLASTRESPONSEINFO)GetProcAddress(hinstWininet, "InternetGetLastResponseInfoA"); pfnInternetOpenUrl = (PFNINTERNETOPENURL)GetProcAddress(hinstWininet, "InternetOpenUrlA"); pfnInternetCloseHandle = (PFNINTERNETCLOSEHANDLE)GetProcAddress(hinstWininet, "InternetCloseHandle"); if (pfnInternetOpen) { g_hinetSession = (*pfnInternetOpen)((char*)g_szSvcName, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (NULL == g_hinetSession) { SRVDBG_Info("InternetOpen"); } } } #endif //_INTERNET_PAGING HANDLE hEventShutdown = CreateEvent(NULL, // no security FALSE, // auto reset FALSE, // initial event reset NULL); // no name DWORD idPingThread = 0; HANDLE hThreadPing = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PingThreadProc, hEventShutdown, 0, &idPingThread); hr = (hThreadPing && hEventShutdown) ? S_OK : E_FAIL; //ZSucceeded(hr); return hr; } /*------------------------------------------------------------------------- * ShutDown *------------------------------------------------------------------------- * Purpose: * Cleanup before exiting * * Side Effects: * Everything that was allocated is deallocated and no longer valid */ HRESULT pascal Sentinal_Terminate(void) { SRVDBG_Info("Terminate"); SRVDBG_Terminate(); return(S_OK); } //////////////////////////////////////////////////////////////////////// // SetServiceStatus - more useful version of this function than the NT // one. // // void SetSvcStatus( DWORD state, DWORD exitcode ) { if (g_ssh) { SERVICE_STATUS ss; static DWORD s_cp = 1; ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ss.dwCurrentState = state; ss.dwControlsAccepted = SERVICE_ACCEPT_STOP; // todo: pause ss.dwWin32ExitCode = exitcode; ss.dwServiceSpecificExitCode = 0; switch (state) { case SERVICE_RUNNING: case SERVICE_STOPPED: case SERVICE_PAUSED: ss.dwCheckPoint = s_cp++; ss.dwWaitHint = 1000; break; case SERVICE_START_PENDING: ss.dwCheckPoint = s_cp++; ss.dwWaitHint = 30000; // we take about 30 seconds to start break; case SERVICE_STOP_PENDING: ss.dwCheckPoint = s_cp++; ss.dwWaitHint = 30000; // we take about 30 seconds to shutdown break; default: ss.dwCheckPoint = 0; ss.dwWaitHint = 0; break; } SetServiceStatus(g_ssh,&ss); } } void StartServerTerminateThread() { DWORD dwId; HANDLE hthrPending = CreateThread(NULL, 8192, (LPTHREAD_START_ROUTINE) ServerTerminateThread, 0, 0, &dwId); CloseHandle(hthrPending); } DWORD WINAPI ServerTerminateThread(DWORD dwUnused) { Sentinal_Terminate(); SetSvcStatus(SERVICE_STOPPED,0); return 0; } //////////////////////////////////////////////////////////////////////// // ServiceControl // // Service control handler which is registered with NT (using // RegisterServiceCtrlHandler) as the callback for the service events. // Right now the service does not support PAUSE/CONTINUE functionality, // but this could be added here if required. // // void WINAPI ServiceControl(DWORD dwCode) { switch (dwCode) { case SERVICE_CONTROL_STOP: // If it can take us a long time to stop our service as we wait for operations to complete, // the SCM doesn't like the ServiceControl() function to take a long time, so we spin up another // thread to terminate us. SetSvcStatus(SERVICE_STOP_PENDING,0); StartServerTerminateThread(); break; case SERVICE_CONTROL_PAUSE: break; case SERVICE_CONTROL_CONTINUE: break; case SERVICE_CONTROL_SHUTDOWN: // System is shutting down. Memory and resources should *not* // be freed in this case due to limited time available for // shutdown. Instead, only urgent non-volatile (i.e. disk) // structures should be flushed. break; case SERVICE_CONTROL_INTERROGATE: break; } } //////////////////////////////////////////////////////////////////////// // ServiceMain // // Pointer to this function is in the SERVICE_TABLE_ENTRY of the // array passed to StartServiceCtrlDispatcher. void WINAPI ServiceMain( DWORD dwArgc, LPSTR *lpszArgv) { HRESULT f; g_ssh = RegisterServiceCtrlHandler(g_szSvcName,ServiceControl); if (!g_ssh) { SetSvcStatus(SERVICE_STOPPED,GetLastError()); return; } SetSvcStatus(SERVICE_START_PENDING,NO_ERROR); f = Sentinal_Init(); if (f==S_OK) SetSvcStatus(SERVICE_RUNNING,NO_ERROR); else SetSvcStatus(SERVICE_STOPPED,GetLastError()); } //////////////////////////////////////////////////////////////////////// // InstallService - installs this service into the NT service manager // // BOOL InstallService() { SC_HANDLE schMgr; SC_HANDLE schSvc; char szPath[512]; schMgr = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); if (!schMgr) { printf("Unable to open SCManager. Service not installed.\n"); return FALSE; } GetModuleFileName(NULL,szPath,sizeof(szPath)); schSvc = CreateService(schMgr, g_szSvcName, "MS Sentinal Server Monitor", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, szPath, NULL, NULL, NULL, NULL, NULL); if (!schSvc) { printf("Unable to create service [0x%08x]. Service not installed.\n", GetLastError()); CloseServiceHandle(schMgr); return FALSE; } CloseServiceHandle(schSvc); CloseServiceHandle(schMgr); printf("%s service installed.\n",g_szSvcName); return TRUE; } //////////////////////////////////////////////////////////////////////// // RemoveService - uninstalls this service from the NT service manager // // BOOL RemoveService(void) { SC_HANDLE schMgr; SC_HANDLE schSvc; SERVICE_STATUS ss; schMgr = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); if (!schMgr) { printf("Unable to open SCManager. Service not removed.\n"); return FALSE; } schSvc = OpenService(schMgr,g_szSvcName,SERVICE_ALL_ACCESS); if (!schSvc) { printf("Unable to open %s service. Service not removed.\n",g_szSvcName); CloseServiceHandle(schMgr); return FALSE; } QueryServiceStatus(schSvc,&ss); if (ss.dwCurrentState!=SERVICE_STOPPED) { printf("Unable to remove %s service while it is running.\n",g_szSvcName); CloseServiceHandle(schSvc); CloseServiceHandle(schMgr); return FALSE; } if (DeleteService(schSvc)) printf("%s service removed.\n",g_szSvcName); else printf("Unable to delete % service.\n",g_szSvcName); CloseServiceHandle(schSvc); CloseServiceHandle(schMgr); return TRUE; } //////////////////////////////////////////////////////////////////////// // RunAsExecutable - for debugging, this service can be run from the // command-line. // // VOID RunAsExecutable() { HRESULT f; printf("Microsoft (R) Sentinal Server Monitoring Service\n"); printf("Copyright (C) Microsoft Corp 1998. All rights reserved.\n\n"); printf("Running as an executable.\n"); printf("Initializing..."); f = Sentinal_Init(); if (f==S_OK) { HANDLE hConsole; DWORD cbr; char c; printf("\rType 'Q' to exit.\n"); hConsole = GetStdHandle(STD_INPUT_HANDLE); SetConsoleMode(hConsole,0); while (ReadConsole(hConsole,&c,1,&cbr,NULL) && (int)CharUpper((LPSTR)c)!='Q') ; Sentinal_Terminate(); } else { printf("\rInitialization failed. (%d)\n",f); } } //////////////////////////////////////////////////////////////////////// // main // // This is the entry point to the process, whether run as a service, or // as an executable. Since the NT service manager does not send us // command line flags, the default case is to run as a service. // Additional supported mutually-exclusive functions are installation // (with the -install command line switch) de-installation (with the // -remove switch) and running as a non-service executable (with the // -debug switch). // // This function parses the command line arguments and invokes the // selected mode of operation. When running as an executable, blocking // keyboard input is used to prevent the program from exiting prematurely. // int __cdecl main(int argc, char *argv[]) { SERVICE_TABLE_ENTRY rgSTE[] = { { (char *)g_szSvcName, (LPSERVICE_MAIN_FUNCTION)ServiceMain }, { NULL, NULL } }; bool fShowUsage = false; g_hInst = GetModuleHandle(NULL); if (argc>1) { if (*argv[1]=='-' || *argv[1]=='/') { if (!lstrcmpi("install",argv[1]+1)) InstallService(); else if (!lstrcmpi("remove",argv[1]+1)) RemoveService(); else if (!lstrcmpi("debug",argv[1]+1)) RunAsExecutable(); else fShowUsage = true; } else fShowUsage = true; if (fShowUsage) { printf("Microsoft (R) Sentinal Server Monitoring Service\n"); printf("Copyright (C) Microsoft Corp 1998. All rights reserved.\n\n"); printf("usage: %s -install install the service\n",argv[0]); printf(" %s -remove remove the service\n",argv[0]); printf(" %s -debug run the service as an EXE\n",argv[0]); printf("\nTo start this service when installed: net start %s\n",g_szSvcName); } } else //start as service { #ifdef WIN95 RunAsExecutable(); #else if (!StartServiceCtrlDispatcher(rgSTE)) SRVDBG_Error("StartServiceCtrlDispatcher failed."); #endif } return 0; }