sentinal.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. //-------------------------------------------------------------------------
  2. // sentinal\sentinal.cpp
  3. //
  4. // Sentinal server monitoring service
  5. //
  6. // Owner: Mark Snyder (MarkSn)
  7. //
  8. // Copyright (C) 1998 Mark E. Snyder, Microsoft Corp.
  9. //-------------------------------------------------------------------------
  10. #define INITGUID
  11. #define __MODULE__ "sentinal"
  12. #include "zlib.h"
  13. #include <windows.h>
  14. #include <windowsx.h>
  15. #include <dplobby.h>
  16. #include <stdlib.h>
  17. //#include <commctrl.h>
  18. #include <memory.h>
  19. #include "srvdbg.h"
  20. #include "Sentinal.h"
  21. #include "sentmsg.h"
  22. #include "config.h"
  23. #include "dplaychk.h"
  24. DEFINE_GUID(FEDSRV_GUID,
  25. 0x81662310, 0xfcb4, 0x11d0, 0xa8, 0x8a, 0x0, 0x60, 0x97, 0xb5, 0x8f, 0xbf);
  26. #define _INTERNET_PAGING
  27. #ifdef _INTERNET_PAGING
  28. #include <wininet.h>
  29. #endif //_INTERNET_PAGING
  30. // !!! The following enum must match the indices of the items in rgCfg in
  31. // !!! LoadSettings() !!!
  32. typedef enum
  33. {
  34. karg_fTimeout,
  35. karg_fWantInt3,
  36. karg_dwPingInterval,
  37. karg_szPagerNotificationURL,
  38. karg_szMonitorServers,
  39. } SENTINAL_ARGS;
  40. // instance of class that handles registry config parms
  41. ConfigManager* g_pCfgMgr;
  42. const char g_szSvcName[] = "Sentinal";
  43. SERVICE_STATUS_HANDLE g_ssh = NULL;
  44. HINSTANCE g_hInst = NULL;
  45. void StartServerTerminateThread();
  46. DWORD WINAPI ServerTerminateThread(DWORD dwUnused);
  47. void SetSvcStatus(DWORD state, DWORD exitcode);
  48. DWORD SendAPage(char* sz, ...);
  49. #ifdef _INTERNET_PAGING
  50. HINTERNET g_hinetSession;
  51. typedef HINTERNET (WINAPI* PFNINTERNETOPENURL)(HINTERNET, LPCSTR, LPCSTR, DWORD, DWORD, DWORD);
  52. typedef BOOL (WINAPI* PFNINTERNETGETLASTRESPONSEINFO)(LPDWORD, LPSTR, LPDWORD);
  53. typedef BOOL (WINAPI* PFNINTERNETCLOSEHANDLE)(HINTERNET);
  54. typedef HINTERNET (WINAPI* PFNINTERNETOPEN)(LPCSTR, DWORD, LPCSTR, LPCSTR, DWORD);
  55. // prototypes
  56. PFNINTERNETOPENURL pfnInternetOpenUrl;
  57. PFNINTERNETGETLASTRESPONSEINFO pfnInternetGetLastResponseInfo;
  58. PFNINTERNETCLOSEHANDLE pfnInternetCloseHandle;
  59. PFNINTERNETOPEN pfnInternetOpen;
  60. #endif //_INTERNET_PAGING
  61. HINSTANCE g_hMessage;
  62. char g_szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  63. char g_szServerStatus[16];
  64. HANDLE g_hEventShutdown;
  65. DWORD PingThreadProc(LPVOID lpv)
  66. {
  67. HANDLE hEventShutdown = (HANDLE)lpv;
  68. DWORD dwPingInterval = 15000;
  69. char szServers[MAX_PATH + 1];
  70. char *psz = NULL;
  71. int nServers = 0;
  72. g_pCfgMgr->GetConfigDWORD(kCompId_Sentinal, karg_dwPingInterval, (DWORD*)&dwPingInterval);
  73. g_pCfgMgr->GetConfigString(kCompId_Sentinal, karg_szMonitorServers, szServers, sizeof(szServers)-1);
  74. for (psz = szServers; *psz; psz++)
  75. if (*psz==';' || *psz==',')
  76. {
  77. *psz = '\0';
  78. nServers++;
  79. }
  80. *(psz+1) = '\0';
  81. if (nServers > 16)
  82. nServers = 16;
  83. memset(g_szServerStatus, 'U', sizeof(g_szServerStatus));
  84. // Initialize COM library
  85. HRESULT hr = CoInitialize(NULL);
  86. BOOL bFirstTime = TRUE;
  87. while (WAIT_TIMEOUT == WaitForSingleObject(hEventShutdown, bFirstTime ? 10000 : dwPingInterval))
  88. {
  89. bFirstTime = FALSE;
  90. int nServer = 0;
  91. for(psz = szServers; *psz && nServer<nServers; psz += strlen(psz)+1, nServer++)
  92. {
  93. // *** HERE IS WHERE YOU CHECK IF THE SERVER IS STILL UP ***
  94. CDplayServerChecker dsc;
  95. dsc.SetServer(psz);
  96. BOOL rc = dsc.ServerConnectionAlive();
  97. if (!rc)
  98. {
  99. if (g_szServerStatus[nServer]=='U')
  100. {
  101. g_szServerStatus[nServer] = 'D';
  102. // send an NT event
  103. SRVDBG_ReportEvent(SRVDBG_ERROR_TYPE, SENTINAL_MONITORED_SERVER_DOWN, psz, dsc.m_szMsg);
  104. #ifdef _INTERNET_PAGING
  105. // send a pager message
  106. SendAPage("Monitored Server Failure", psz, dsc.m_szMsg);
  107. #endif _INTERNET_PAGING
  108. }
  109. }
  110. else
  111. g_szServerStatus[nServer] = 'U';
  112. }
  113. }
  114. return(0);
  115. }
  116. #ifdef _INTERNET_PAGING
  117. DWORD SendAPage(char* sz, ...) //const char *sz, ...)
  118. {
  119. LPTSTR rgszStrings[5];
  120. va_list vl;
  121. va_start(vl, sz);
  122. rgszStrings[0] = sz;
  123. rgszStrings[1] = va_arg(vl, LPTSTR);
  124. rgszStrings[2] = va_arg(vl, LPTSTR);
  125. rgszStrings[3] = va_arg(vl, LPTSTR);
  126. rgszStrings[4] = va_arg(vl, LPTSTR);
  127. va_end(vl);
  128. char szPagerNotificationURL[MAX_PATH + 1];
  129. g_pCfgMgr->GetConfigString(kCompId_Sentinal, karg_szPagerNotificationURL, szPagerNotificationURL, sizeof(szPagerNotificationURL)-1);
  130. char* szFormat = szPagerNotificationURL;
  131. //for (char* szFormat = szPagerNotificationURL; *szFormat; szFormat += lstrlen(szFormat) + 1)
  132. {
  133. if (pfnInternetOpenUrl && pfnInternetGetLastResponseInfo && pfnInternetCloseHandle)
  134. {
  135. char rgchBuf[1024];
  136. DWORD cbBuf = sizeof(rgchBuf);
  137. DWORD cch = FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY, szFormat, 0, 0, rgchBuf, sizeof(rgchBuf) - 1, rgszStrings);
  138. // open a session
  139. HINTERNET hinetHTTPSession = (*pfnInternetOpenUrl)(g_hinetSession, rgchBuf, NULL, 0, INTERNET_FLAG_RELOAD, 0);
  140. if (NULL == hinetHTTPSession)
  141. {
  142. char szMsg[256];
  143. wsprintf(szMsg, "%08x (%d)", GetLastError(), GetLastError());
  144. SRVDBG_ReportEvent(SRVDBG_ERROR_TYPE, SENTINAL_INTERNETOPENURL, szMsg);
  145. return 1;
  146. }
  147. DWORD dwErr;
  148. BOOL rc = (*pfnInternetGetLastResponseInfo)(&dwErr, rgchBuf, &cbBuf);
  149. if (!rc)
  150. {
  151. char szMsg[256];
  152. wsprintf(szMsg, "%08x (%d)", GetLastError(), GetLastError());
  153. SRVDBG_ReportEvent(SRVDBG_ERROR_TYPE, SENTINAL_INTERNETGETLASTRESPONSEINFO, szMsg);
  154. }
  155. (*pfnInternetCloseHandle)(hinetHTTPSession);
  156. }
  157. }
  158. return(0);
  159. }
  160. #endif //_INTERNET_PAGING
  161. /*-------------------------------------------------------------------------
  162. * LoadSettings
  163. *-------------------------------------------------------------------------
  164. * Purpose:
  165. * Load static server settings
  166. */
  167. void LoadSettings()
  168. {
  169. g_pCfgMgr = new ConfigManager;
  170. ZAssert(g_pCfgMgr); // all new's assumed to succeed...
  171. ZAssert(g_pCfgMgr->Init());
  172. static const ConfigInfo rgCfg[] =
  173. {
  174. // Name Type Max Length Required? Default, if not there
  175. { "fTimeout", kConfigType_DWORD, sizeof(DWORD), TRUE, 0 },
  176. { "fWantInt3", kConfigType_DWORD, sizeof(DWORD), TRUE, 0 },
  177. { "dwPingInterval", kConfigType_DWORD, sizeof(DWORD), FALSE, 0 },
  178. { "szPagerNotificationURL", kConfigType_SZ, MAX_PATH + 1, TRUE, (DWORD) "" },
  179. { "szMonitorServers", kConfigType_SZ, MAX_PATH + 1, TRUE, (DWORD) "" },
  180. // !!!! If you add or delete items here, you MUST update the enum SENTINAL_ARGS in CONFIG.H to match
  181. // !!!! the appropriate index in this array !!!
  182. };
  183. g_pCfgMgr->LoadConfig(kCompId_Sentinal, rgCfg, sizeof(rgCfg) / sizeof(rgCfg[0]));
  184. }
  185. /*-------------------------------------------------------------------------
  186. * Sentinal_Init
  187. *-------------------------------------------------------------------------
  188. * Purpose:
  189. * Performs startup tasks and initialization for this service.
  190. *
  191. * Side Effects:
  192. * Lots
  193. */
  194. HRESULT pascal Sentinal_Init()
  195. {
  196. int iCon = 0;
  197. HRESULT hr = S_OK;
  198. SRVDBG_Init("Sentinal", NULL);
  199. SRVDBG_Info("Initialize");
  200. LoadSettings();
  201. // Initialize COM library
  202. hr = CoInitialize(NULL);
  203. DWORD cbBuf = sizeof(g_szComputerName);
  204. GetComputerName(g_szComputerName, &cbBuf);
  205. #ifdef _INTERNET_PAGING
  206. // are we going to be doing paging?
  207. char szPagerNotificationURL[MAX_PATH + 1];
  208. g_pCfgMgr->GetConfigString(kCompId_Sentinal, karg_szPagerNotificationURL, szPagerNotificationURL, sizeof(szPagerNotificationURL)-1);
  209. if (szPagerNotificationURL[0])
  210. {
  211. HINSTANCE hinstWininet = LoadLibrary("wininet.dll");
  212. pfnInternetOpen = (PFNINTERNETOPEN)GetProcAddress(hinstWininet, "InternetOpenA");
  213. pfnInternetGetLastResponseInfo = (PFNINTERNETGETLASTRESPONSEINFO)GetProcAddress(hinstWininet, "InternetGetLastResponseInfoA");
  214. pfnInternetOpenUrl = (PFNINTERNETOPENURL)GetProcAddress(hinstWininet, "InternetOpenUrlA");
  215. pfnInternetCloseHandle = (PFNINTERNETCLOSEHANDLE)GetProcAddress(hinstWininet, "InternetCloseHandle");
  216. if (pfnInternetOpen)
  217. {
  218. g_hinetSession = (*pfnInternetOpen)((char*)g_szSvcName, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
  219. if (NULL == g_hinetSession)
  220. {
  221. SRVDBG_Info("InternetOpen");
  222. }
  223. }
  224. }
  225. #endif //_INTERNET_PAGING
  226. HANDLE hEventShutdown = CreateEvent(NULL, // no security
  227. FALSE, // auto reset
  228. FALSE, // initial event reset
  229. NULL); // no name
  230. DWORD idPingThread = 0;
  231. HANDLE hThreadPing = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PingThreadProc, hEventShutdown, 0, &idPingThread);
  232. hr = (hThreadPing && hEventShutdown) ? S_OK : E_FAIL;
  233. //ZSucceeded(hr);
  234. return hr;
  235. }
  236. /*-------------------------------------------------------------------------
  237. * ShutDown
  238. *-------------------------------------------------------------------------
  239. * Purpose:
  240. * Cleanup before exiting
  241. *
  242. * Side Effects:
  243. * Everything that was allocated is deallocated and no longer valid
  244. */
  245. HRESULT pascal Sentinal_Terminate(void)
  246. {
  247. SRVDBG_Info("Terminate");
  248. SRVDBG_Terminate();
  249. return(S_OK);
  250. }
  251. ////////////////////////////////////////////////////////////////////////
  252. // SetServiceStatus - more useful version of this function than the NT
  253. // one.
  254. //
  255. //
  256. void SetSvcStatus(
  257. DWORD state,
  258. DWORD exitcode
  259. )
  260. {
  261. if (g_ssh)
  262. {
  263. SERVICE_STATUS ss;
  264. static DWORD s_cp = 1;
  265. ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  266. ss.dwCurrentState = state;
  267. ss.dwControlsAccepted = SERVICE_ACCEPT_STOP; // todo: pause
  268. ss.dwWin32ExitCode = exitcode;
  269. ss.dwServiceSpecificExitCode = 0;
  270. switch (state)
  271. {
  272. case SERVICE_RUNNING:
  273. case SERVICE_STOPPED:
  274. case SERVICE_PAUSED:
  275. ss.dwCheckPoint = s_cp++;
  276. ss.dwWaitHint = 1000;
  277. break;
  278. case SERVICE_START_PENDING:
  279. ss.dwCheckPoint = s_cp++;
  280. ss.dwWaitHint = 30000; // we take about 30 seconds to start
  281. break;
  282. case SERVICE_STOP_PENDING:
  283. ss.dwCheckPoint = s_cp++;
  284. ss.dwWaitHint = 30000; // we take about 30 seconds to shutdown
  285. break;
  286. default:
  287. ss.dwCheckPoint = 0;
  288. ss.dwWaitHint = 0;
  289. break;
  290. }
  291. SetServiceStatus(g_ssh,&ss);
  292. }
  293. }
  294. void StartServerTerminateThread()
  295. {
  296. DWORD dwId;
  297. HANDLE hthrPending = CreateThread(NULL, 8192, (LPTHREAD_START_ROUTINE) ServerTerminateThread, 0, 0, &dwId);
  298. CloseHandle(hthrPending);
  299. }
  300. DWORD WINAPI ServerTerminateThread(DWORD dwUnused)
  301. {
  302. Sentinal_Terminate();
  303. SetSvcStatus(SERVICE_STOPPED,0);
  304. return 0;
  305. }
  306. ////////////////////////////////////////////////////////////////////////
  307. // ServiceControl
  308. //
  309. // Service control handler which is registered with NT (using
  310. // RegisterServiceCtrlHandler) as the callback for the service events.
  311. // Right now the service does not support PAUSE/CONTINUE functionality,
  312. // but this could be added here if required.
  313. //
  314. //
  315. void WINAPI ServiceControl(DWORD dwCode)
  316. {
  317. switch (dwCode)
  318. {
  319. case SERVICE_CONTROL_STOP:
  320. // If it can take us a long time to stop our service as we wait for operations to complete,
  321. // the SCM doesn't like the ServiceControl() function to take a long time, so we spin up another
  322. // thread to terminate us.
  323. SetSvcStatus(SERVICE_STOP_PENDING,0);
  324. StartServerTerminateThread();
  325. break;
  326. case SERVICE_CONTROL_PAUSE:
  327. break;
  328. case SERVICE_CONTROL_CONTINUE:
  329. break;
  330. case SERVICE_CONTROL_SHUTDOWN:
  331. // System is shutting down. Memory and resources should *not*
  332. // be freed in this case due to limited time available for
  333. // shutdown. Instead, only urgent non-volatile (i.e. disk)
  334. // structures should be flushed.
  335. break;
  336. case SERVICE_CONTROL_INTERROGATE:
  337. break;
  338. }
  339. }
  340. ////////////////////////////////////////////////////////////////////////
  341. // ServiceMain
  342. //
  343. // Pointer to this function is in the SERVICE_TABLE_ENTRY of the
  344. // array passed to StartServiceCtrlDispatcher.
  345. void WINAPI ServiceMain(
  346. DWORD dwArgc,
  347. LPSTR *lpszArgv)
  348. {
  349. HRESULT f;
  350. g_ssh = RegisterServiceCtrlHandler(g_szSvcName,ServiceControl);
  351. if (!g_ssh)
  352. {
  353. SetSvcStatus(SERVICE_STOPPED,GetLastError());
  354. return;
  355. }
  356. SetSvcStatus(SERVICE_START_PENDING,NO_ERROR);
  357. f = Sentinal_Init();
  358. if (f==S_OK)
  359. SetSvcStatus(SERVICE_RUNNING,NO_ERROR);
  360. else
  361. SetSvcStatus(SERVICE_STOPPED,GetLastError());
  362. }
  363. ////////////////////////////////////////////////////////////////////////
  364. // InstallService - installs this service into the NT service manager
  365. //
  366. //
  367. BOOL InstallService()
  368. {
  369. SC_HANDLE schMgr;
  370. SC_HANDLE schSvc;
  371. char szPath[512];
  372. schMgr = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
  373. if (!schMgr)
  374. {
  375. printf("Unable to open SCManager. Service not installed.\n");
  376. return FALSE;
  377. }
  378. GetModuleFileName(NULL,szPath,sizeof(szPath));
  379. schSvc = CreateService(schMgr,
  380. g_szSvcName,
  381. "MS Sentinal Server Monitor",
  382. SERVICE_ALL_ACCESS,
  383. SERVICE_WIN32_OWN_PROCESS,
  384. SERVICE_AUTO_START,
  385. SERVICE_ERROR_NORMAL,
  386. szPath,
  387. NULL,
  388. NULL,
  389. NULL,
  390. NULL,
  391. NULL);
  392. if (!schSvc)
  393. {
  394. printf("Unable to create service [0x%08x]. Service not installed.\n", GetLastError());
  395. CloseServiceHandle(schMgr);
  396. return FALSE;
  397. }
  398. CloseServiceHandle(schSvc);
  399. CloseServiceHandle(schMgr);
  400. printf("%s service installed.\n",g_szSvcName);
  401. return TRUE;
  402. }
  403. ////////////////////////////////////////////////////////////////////////
  404. // RemoveService - uninstalls this service from the NT service manager
  405. //
  406. //
  407. BOOL RemoveService(void)
  408. {
  409. SC_HANDLE schMgr;
  410. SC_HANDLE schSvc;
  411. SERVICE_STATUS ss;
  412. schMgr = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
  413. if (!schMgr)
  414. {
  415. printf("Unable to open SCManager. Service not removed.\n");
  416. return FALSE;
  417. }
  418. schSvc = OpenService(schMgr,g_szSvcName,SERVICE_ALL_ACCESS);
  419. if (!schSvc)
  420. {
  421. printf("Unable to open %s service. Service not removed.\n",g_szSvcName);
  422. CloseServiceHandle(schMgr);
  423. return FALSE;
  424. }
  425. QueryServiceStatus(schSvc,&ss);
  426. if (ss.dwCurrentState!=SERVICE_STOPPED)
  427. {
  428. printf("Unable to remove %s service while it is running.\n",g_szSvcName);
  429. CloseServiceHandle(schSvc);
  430. CloseServiceHandle(schMgr);
  431. return FALSE;
  432. }
  433. if (DeleteService(schSvc))
  434. printf("%s service removed.\n",g_szSvcName);
  435. else
  436. printf("Unable to delete % service.\n",g_szSvcName);
  437. CloseServiceHandle(schSvc);
  438. CloseServiceHandle(schMgr);
  439. return TRUE;
  440. }
  441. ////////////////////////////////////////////////////////////////////////
  442. // RunAsExecutable - for debugging, this service can be run from the
  443. // command-line.
  444. //
  445. //
  446. VOID RunAsExecutable()
  447. {
  448. HRESULT f;
  449. printf("Microsoft (R) Sentinal Server Monitoring Service\n");
  450. printf("Copyright (C) Microsoft Corp 1998. All rights reserved.\n\n");
  451. printf("Running as an executable.\n");
  452. printf("Initializing...");
  453. f = Sentinal_Init();
  454. if (f==S_OK)
  455. {
  456. HANDLE hConsole;
  457. DWORD cbr;
  458. char c;
  459. printf("\rType 'Q' to exit.\n");
  460. hConsole = GetStdHandle(STD_INPUT_HANDLE);
  461. SetConsoleMode(hConsole,0);
  462. while (ReadConsole(hConsole,&c,1,&cbr,NULL) && (int)CharUpper((LPSTR)c)!='Q')
  463. ;
  464. Sentinal_Terminate();
  465. }
  466. else
  467. {
  468. printf("\rInitialization failed. (%d)\n",f);
  469. }
  470. }
  471. ////////////////////////////////////////////////////////////////////////
  472. // main
  473. //
  474. // This is the entry point to the process, whether run as a service, or
  475. // as an executable. Since the NT service manager does not send us
  476. // command line flags, the default case is to run as a service.
  477. // Additional supported mutually-exclusive functions are installation
  478. // (with the -install command line switch) de-installation (with the
  479. // -remove switch) and running as a non-service executable (with the
  480. // -debug switch).
  481. //
  482. // This function parses the command line arguments and invokes the
  483. // selected mode of operation. When running as an executable, blocking
  484. // keyboard input is used to prevent the program from exiting prematurely.
  485. //
  486. int __cdecl main(int argc, char *argv[])
  487. {
  488. SERVICE_TABLE_ENTRY rgSTE[] =
  489. {
  490. { (char *)g_szSvcName, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
  491. { NULL, NULL }
  492. };
  493. bool fShowUsage = false;
  494. g_hInst = GetModuleHandle(NULL);
  495. if (argc>1)
  496. {
  497. if (*argv[1]=='-' || *argv[1]=='/')
  498. {
  499. if (!lstrcmpi("install",argv[1]+1))
  500. InstallService();
  501. else if (!lstrcmpi("remove",argv[1]+1))
  502. RemoveService();
  503. else if (!lstrcmpi("debug",argv[1]+1))
  504. RunAsExecutable();
  505. else
  506. fShowUsage = true;
  507. }
  508. else
  509. fShowUsage = true;
  510. if (fShowUsage)
  511. {
  512. printf("Microsoft (R) Sentinal Server Monitoring Service\n");
  513. printf("Copyright (C) Microsoft Corp 1998. All rights reserved.\n\n");
  514. printf("usage: %s -install install the service\n",argv[0]);
  515. printf(" %s -remove remove the service\n",argv[0]);
  516. printf(" %s -debug run the service as an EXE\n",argv[0]);
  517. printf("\nTo start this service when installed: net start %s\n",g_szSvcName);
  518. }
  519. }
  520. else //start as service
  521. {
  522. #ifdef WIN95
  523. RunAsExecutable();
  524. #else
  525. if (!StartServiceCtrlDispatcher(rgSTE))
  526. SRVDBG_Error("StartServiceCtrlDispatcher failed.");
  527. #endif
  528. }
  529. return 0;
  530. }