Source code of Windows XP (NT5)
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.

591 lines
15 KiB

  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (C) 1993-1997 Microsoft Corporation. All Rights Reserved.
  7. //
  8. // MODULE: service.c
  9. //
  10. // PURPOSE: Implements functions required by all services
  11. // windows.
  12. //
  13. // FUNCTIONS:
  14. // main(int argc, char **argv);
  15. // service_ctrl(DWORD dwCtrlCode);
  16. // service_main(DWORD dwArgc, LPTSTR *lpszArgv);
  17. // CmdInstallService();
  18. // CmdRemoveService();
  19. // CmdDebugService(int argc, char **argv);
  20. // ControlHandler ( DWORD dwCtrlType );
  21. // GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
  22. //
  23. // COMMENTS:
  24. //
  25. // AUTHOR: Craig Link - Microsoft Developer Support
  26. //
  27. #include <windows.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <process.h>
  31. #include <tchar.h>
  32. #include "service.h"
  33. // internal variables
  34. SERVICE_STATUS ssStatus; // current status of the service
  35. SERVICE_STATUS_HANDLE sshStatusHandle;
  36. DWORD dwErr = 0;
  37. BOOL bDebug = FALSE;
  38. TCHAR szErr[256];
  39. // internal function prototypes
  40. VOID WINAPI service_ctrl(DWORD dwCtrlCode);
  41. VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
  42. VOID CmdInstallService();
  43. VOID CmdRemoveService();
  44. VOID CmdDebugService(int argc, char **argv);
  45. BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
  46. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
  47. //
  48. // FUNCTION: main
  49. //
  50. // PURPOSE: entrypoint for service
  51. //
  52. // PARAMETERS:
  53. // argc - number of command line arguments
  54. // argv - array of command line arguments
  55. //
  56. // RETURN VALUE:
  57. // none
  58. //
  59. // COMMENTS:
  60. // main() either performs the command line task, or
  61. // call StartServiceCtrlDispatcher to register the
  62. // main service thread. When the this call returns,
  63. // the service has stopped, so exit.
  64. //
  65. void _cdecl main(int argc, char **argv)
  66. {
  67. SERVICE_TABLE_ENTRY dispatchTable[] =
  68. {
  69. { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main },
  70. { NULL, NULL }
  71. };
  72. if ( (argc > 1) &&
  73. ((*argv[1] == '-') || (*argv[1] == '/')) )
  74. {
  75. if ( _stricmp( "install", argv[1]+1 ) == 0 )
  76. {
  77. CmdInstallService();
  78. }
  79. else if ( _stricmp( "remove", argv[1]+1 ) == 0 )
  80. {
  81. CmdRemoveService();
  82. }
  83. else if ( _stricmp( "debug", argv[1]+1 ) == 0 )
  84. {
  85. bDebug = TRUE;
  86. CmdDebugService(argc, argv);
  87. }
  88. else
  89. {
  90. goto dispatch;
  91. }
  92. exit(0);
  93. }
  94. // if it doesn't match any of the above parameters
  95. // the service control manager may be starting the service
  96. // so we must call StartServiceCtrlDispatcher
  97. dispatch:
  98. // this is just to be friendly
  99. printf( "%s -install to install the service\n", SZAPPNAME );
  100. printf( "%s -remove to remove the service\n", SZAPPNAME );
  101. printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
  102. printf( "\nStartServiceCtrlDispatcher being called.\n" );
  103. printf( "This may take several seconds. Please wait.\n" );
  104. if (!StartServiceCtrlDispatcher(dispatchTable))
  105. AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
  106. }
  107. //
  108. // FUNCTION: service_main
  109. //
  110. // PURPOSE: To perform actual initialization of the service
  111. //
  112. // PARAMETERS:
  113. // dwArgc - number of command line arguments
  114. // lpszArgv - array of command line arguments
  115. //
  116. // RETURN VALUE:
  117. // none
  118. //
  119. // COMMENTS:
  120. // This routine performs the service initialization and then calls
  121. // the user defined ServiceStart() routine to perform majority
  122. // of the work.
  123. //
  124. void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
  125. {
  126. // register our service control handler:
  127. //
  128. sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
  129. if (!sshStatusHandle)
  130. goto cleanup;
  131. // SERVICE_STATUS members that don't change in example
  132. //
  133. ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  134. ssStatus.dwServiceSpecificExitCode = 0;
  135. // report the status to the service control manager.
  136. //
  137. if (!ReportStatusToSCMgr(
  138. SERVICE_START_PENDING, // service state
  139. NO_ERROR, // exit code
  140. 3000)) // wait hint
  141. goto cleanup;
  142. ServiceStart( dwArgc, lpszArgv );
  143. cleanup:
  144. // try to report the stopped status to the service control manager.
  145. //
  146. if (sshStatusHandle)
  147. (VOID)ReportStatusToSCMgr(
  148. SERVICE_STOPPED,
  149. dwErr,
  150. 0);
  151. return;
  152. }
  153. //
  154. // FUNCTION: service_ctrl
  155. //
  156. // PURPOSE: This function is called by the SCM whenever
  157. // ControlService() is called on this service.
  158. //
  159. // PARAMETERS:
  160. // dwCtrlCode - type of control requested
  161. //
  162. // RETURN VALUE:
  163. // none
  164. //
  165. // COMMENTS:
  166. //
  167. VOID WINAPI service_ctrl(DWORD dwCtrlCode)
  168. {
  169. // Handle the requested control code.
  170. //
  171. switch(dwCtrlCode)
  172. {
  173. // Stop the service.
  174. //
  175. case SERVICE_CONTROL_STOP:
  176. ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
  177. ServiceStop();
  178. return;
  179. // Update the service status.
  180. //
  181. case SERVICE_CONTROL_INTERROGATE:
  182. break;
  183. // invalid control code
  184. //
  185. default:
  186. break;
  187. }
  188. ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
  189. }
  190. //
  191. // FUNCTION: ReportStatusToSCMgr()
  192. //
  193. // PURPOSE: Sets the current status of the service and
  194. // reports it to the Service Control Manager
  195. //
  196. // PARAMETERS:
  197. // dwCurrentState - the state of the service
  198. // dwWin32ExitCode - error code to report
  199. // dwWaitHint - worst case estimate to next checkpoint
  200. //
  201. // RETURN VALUE:
  202. // TRUE - success
  203. // FALSE - failure
  204. //
  205. // COMMENTS:
  206. //
  207. BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
  208. DWORD dwWin32ExitCode,
  209. DWORD dwWaitHint)
  210. {
  211. static DWORD dwCheckPoint = 1;
  212. BOOL fResult = TRUE;
  213. if ( !bDebug ) // when debugging we don't report to the SCM
  214. {
  215. if (dwCurrentState == SERVICE_START_PENDING)
  216. ssStatus.dwControlsAccepted = 0;
  217. else
  218. ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  219. ssStatus.dwCurrentState = dwCurrentState;
  220. ssStatus.dwWin32ExitCode = dwWin32ExitCode;
  221. ssStatus.dwWaitHint = dwWaitHint;
  222. if ( ( dwCurrentState == SERVICE_RUNNING ) ||
  223. ( dwCurrentState == SERVICE_STOPPED ) )
  224. ssStatus.dwCheckPoint = 0;
  225. else
  226. ssStatus.dwCheckPoint = dwCheckPoint++;
  227. // Report the status of the service to the service control manager.
  228. //
  229. if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
  230. AddToMessageLog(TEXT("SetServiceStatus"));
  231. }
  232. }
  233. return fResult;
  234. }
  235. //
  236. // FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
  237. //
  238. // PURPOSE: Allows any thread to log an error message
  239. //
  240. // PARAMETERS:
  241. // lpszMsg - text for message
  242. //
  243. // RETURN VALUE:
  244. // none
  245. //
  246. // COMMENTS:
  247. //
  248. VOID AddToMessageLog(LPTSTR lpszMsg)
  249. {
  250. TCHAR szMsg[256];
  251. HANDLE hEventSource;
  252. LPTSTR lpszStrings[2];
  253. if ( !bDebug )
  254. {
  255. dwErr = GetLastError();
  256. // Use event logging to log the error.
  257. //
  258. hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
  259. _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
  260. lpszStrings[0] = szMsg;
  261. lpszStrings[1] = lpszMsg;
  262. if (hEventSource != NULL) {
  263. ReportEvent(hEventSource, // handle of event source
  264. EVENTLOG_ERROR_TYPE, // event type
  265. 0, // event category
  266. 0, // event ID
  267. NULL, // current user's SID
  268. 2, // strings in lpszStrings
  269. 0, // no bytes of raw data
  270. lpszStrings, // array of error strings
  271. NULL); // no raw data
  272. (VOID) DeregisterEventSource(hEventSource);
  273. }
  274. }
  275. }
  276. ///////////////////////////////////////////////////////////////////
  277. //
  278. // The following code handles service installation and removal
  279. //
  280. //
  281. // FUNCTION: CmdInstallService()
  282. //
  283. // PURPOSE: Installs the service
  284. //
  285. // PARAMETERS:
  286. // none
  287. //
  288. // RETURN VALUE:
  289. // none
  290. //
  291. // COMMENTS:
  292. //
  293. void CmdInstallService()
  294. {
  295. SC_HANDLE schService;
  296. SC_HANDLE schSCManager;
  297. TCHAR szPath[512];
  298. if ( GetModuleFileName( NULL, szPath, 512 ) == 0 )
  299. {
  300. _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
  301. return;
  302. }
  303. schSCManager = OpenSCManager(
  304. NULL, // machine (NULL == local)
  305. NULL, // database (NULL == default)
  306. SC_MANAGER_ALL_ACCESS // access required
  307. );
  308. if ( schSCManager )
  309. {
  310. schService = CreateService(
  311. schSCManager, // SCManager database
  312. TEXT(SZSERVICENAME), // name of service
  313. TEXT(SZSERVICEDISPLAYNAME), // name to display
  314. SERVICE_ALL_ACCESS, // desired access
  315. SERVICE_WIN32_OWN_PROCESS, // service type
  316. SERVICE_AUTO_START,
  317. // SERVICE_DEMAND_START, // start type
  318. SERVICE_ERROR_NORMAL, // error control type
  319. szPath, // service's binary
  320. NULL, // no load ordering group
  321. NULL, // no tag identifier
  322. TEXT(SZDEPENDENCIES), // dependencies
  323. NULL, // LocalSystem account
  324. NULL); // no password
  325. if ( schService )
  326. {
  327. _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  328. if( StartService(schService,0,NULL)==0 )
  329. {
  330. _tprintf(TEXT("Service failed to start. LastError= %d\n"),GetLastError());
  331. }
  332. CloseServiceHandle(schService);
  333. }
  334. else
  335. {
  336. _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
  337. }
  338. CloseServiceHandle(schSCManager);
  339. }
  340. else
  341. _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  342. }
  343. //
  344. // FUNCTION: CmdRemoveService()
  345. //
  346. // PURPOSE: Stops and removes the service
  347. //
  348. // PARAMETERS:
  349. // none
  350. //
  351. // RETURN VALUE:
  352. // none
  353. //
  354. // COMMENTS:
  355. //
  356. void CmdRemoveService()
  357. {
  358. SC_HANDLE schService;
  359. SC_HANDLE schSCManager;
  360. schSCManager = OpenSCManager(
  361. NULL, // machine (NULL == local)
  362. NULL, // database (NULL == default)
  363. SC_MANAGER_ALL_ACCESS // access required
  364. );
  365. if ( schSCManager )
  366. {
  367. schService = OpenService(schSCManager, TEXT(SZSERVICENAME), SERVICE_ALL_ACCESS);
  368. if (schService)
  369. {
  370. // try to stop the service
  371. if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
  372. {
  373. _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
  374. Sleep( 1000 );
  375. while( QueryServiceStatus( schService, &ssStatus ) )
  376. {
  377. if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
  378. {
  379. _tprintf(TEXT("."));
  380. Sleep( 1000 );
  381. }
  382. else
  383. break;
  384. }
  385. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  386. _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  387. else
  388. _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  389. }
  390. // now remove the service
  391. if( DeleteService(schService) )
  392. _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  393. else
  394. _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
  395. CloseServiceHandle(schService);
  396. }
  397. else
  398. _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
  399. CloseServiceHandle(schSCManager);
  400. }
  401. else
  402. _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  403. }
  404. ///////////////////////////////////////////////////////////////////
  405. //
  406. // The following code is for running the service as a console app
  407. //
  408. //
  409. // FUNCTION: CmdDebugService(int argc, char ** argv)
  410. //
  411. // PURPOSE: Runs the service as a console application
  412. //
  413. // PARAMETERS:
  414. // argc - number of command line arguments
  415. // argv - array of command line arguments
  416. //
  417. // RETURN VALUE:
  418. // none
  419. //
  420. // COMMENTS:
  421. //
  422. void CmdDebugService(int argc, char ** argv)
  423. {
  424. DWORD dwArgc;
  425. LPTSTR *lpszArgv;
  426. #ifdef UNICODE
  427. lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
  428. #else
  429. dwArgc = (DWORD) argc;
  430. lpszArgv = argv;
  431. #endif
  432. _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  433. SetConsoleCtrlHandler( ControlHandler, TRUE );
  434. ServiceStart( dwArgc, lpszArgv );
  435. }
  436. //
  437. // FUNCTION: ControlHandler ( DWORD dwCtrlType )
  438. //
  439. // PURPOSE: Handled console control events
  440. //
  441. // PARAMETERS:
  442. // dwCtrlType - type of control event
  443. //
  444. // RETURN VALUE:
  445. // True - handled
  446. // False - unhandled
  447. //
  448. // COMMENTS:
  449. //
  450. BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
  451. {
  452. switch( dwCtrlType )
  453. {
  454. case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
  455. case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
  456. _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  457. ServiceStop();
  458. return TRUE;
  459. break;
  460. }
  461. return FALSE;
  462. }
  463. //
  464. // FUNCTION: GetLastErrorText
  465. //
  466. // PURPOSE: copies error message text to string
  467. //
  468. // PARAMETERS:
  469. // lpszBuf - destination buffer
  470. // dwSize - size of buffer
  471. //
  472. // RETURN VALUE:
  473. // destination buffer
  474. //
  475. // COMMENTS:
  476. //
  477. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
  478. {
  479. DWORD dwRet;
  480. LPTSTR lpszTemp = NULL;
  481. dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
  482. NULL,
  483. GetLastError(),
  484. LANG_NEUTRAL,
  485. (LPTSTR)&lpszTemp,
  486. 0,
  487. NULL );
  488. // supplied buffer is not long enough
  489. if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
  490. lpszBuf[0] = TEXT('\0');
  491. else
  492. {
  493. lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
  494. _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
  495. }
  496. if ( lpszTemp )
  497. LocalFree((HLOCAL) lpszTemp );
  498. return lpszBuf;
  499. }