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.

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