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.

564 lines
14 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. Service.c
  5. Abstract:
  6. License Logging Service - Common routines for all service.
  7. Author:
  8. Arthur Hanson (arth) Dec 07, 1994
  9. Environment:
  10. Revision History:
  11. --*/
  12. #include <windows.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <process.h>
  16. #include <tchar.h>
  17. #include <shellapi.h>
  18. #include "service.h"
  19. #include "debug.h"
  20. // internal variables
  21. static SERVICE_STATUS ssStatus; // current status of the service
  22. SERVICE_STATUS_HANDLE sshStatusHandle = 0;
  23. static DWORD dwErr = 0;
  24. BOOL bDebug = FALSE;
  25. TCHAR szErr[256];
  26. // internal function prototypes
  27. VOID WINAPI ServiceCtrl(DWORD dwCtrlCode);
  28. VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv);
  29. VOID CmdInstallService();
  30. VOID CmdRemoveService();
  31. VOID CmdDebugService(int argc, char **argv);
  32. BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
  33. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
  34. /////////////////////////////////////////////////////////////////////////
  35. VOID __cdecl
  36. main(
  37. int argc,
  38. char **argv
  39. )
  40. /*++
  41. Routine Description:
  42. Main routine to setup the exception handlers and initialize everything
  43. before spawning threads to listen to LPC and RPC port requests.
  44. main() either performs the command line task, or calls
  45. StartServiceCtrlDispatcher to register the main service thread. When the
  46. this call returns, the service has stopped, so exit.
  47. Arguments:
  48. argc - number of command line arguments
  49. argv - array of command line arguments
  50. Return Values:
  51. None.
  52. --*/
  53. {
  54. SERVICE_TABLE_ENTRY dispatchTable[] = {
  55. { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION) ServiceMain },
  56. { NULL, NULL }
  57. };
  58. if ( (argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')) ) {
  59. if ( _stricmp( "install", argv[1]+1 ) == 0 ) {
  60. CmdInstallService();
  61. } else if ( _stricmp( "remove", argv[1]+1 ) == 0 ) {
  62. CmdRemoveService();
  63. } else if ( _stricmp( "debug", argv[1]+1 ) == 0 ) {
  64. bDebug = TRUE;
  65. CmdDebugService(argc, argv);
  66. } else {
  67. goto dispatch;
  68. }
  69. exit(0);
  70. }
  71. // if it doesn't match any of the above parameters
  72. // the service control manager may be starting the service
  73. // so we must call StartServiceCtrlDispatcher
  74. dispatch:
  75. #ifdef DEBUG
  76. // this is just to be friendly
  77. printf( "%s -install to install the service\n", SZAPPNAME );
  78. printf( "%s -remove to remove the service\n", SZAPPNAME );
  79. printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
  80. printf( "\nStartServiceCtrlDispatcher being called.\n" );
  81. printf( "This may take several seconds. Please wait.\n" );
  82. #endif
  83. if (!StartServiceCtrlDispatcher(dispatchTable))
  84. dprintf(TEXT("LLS TRACE: StartServiceCtrlDispatcher failed\n"));
  85. } // main
  86. /////////////////////////////////////////////////////////////////////////
  87. VOID WINAPI
  88. ServiceMain(
  89. DWORD dwArgc,
  90. LPTSTR *lpszArgv
  91. )
  92. /*++
  93. Routine Description:
  94. Performs the service initialization and then calls the ServiceStart()
  95. routine to perform majority of the work.
  96. Arguments:
  97. dwArgc - number of command line arguments ***UNUSED***
  98. lpszArgv - array of command line arguments ***UNUSED***
  99. Return Values:
  100. None.
  101. --*/
  102. {
  103. // register our service control handler:
  104. //
  105. sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), ServiceCtrl);
  106. if (!sshStatusHandle)
  107. goto cleanup;
  108. // SERVICE_STATUS members that don't change in example
  109. //
  110. ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  111. ssStatus.dwServiceSpecificExitCode = 0;
  112. // report the status to the service control manager.
  113. //
  114. if (!ReportStatusToSCMgr(
  115. SERVICE_START_PENDING, // service state
  116. NO_ERROR, // exit code
  117. NSERVICEWAITHINT)) // wait hint
  118. goto cleanup;
  119. ServiceStart( dwArgc, lpszArgv );
  120. cleanup:
  121. // try to report the stopped status to the service control manager.
  122. //
  123. if (sshStatusHandle)
  124. (VOID)ReportStatusToSCMgr(
  125. SERVICE_STOPPED,
  126. dwErr,
  127. 0);
  128. return;
  129. } // ServiceMain
  130. /////////////////////////////////////////////////////////////////////////
  131. VOID WINAPI
  132. ServiceCtrl(
  133. DWORD dwCtrlCode
  134. )
  135. /*++
  136. Routine Description:
  137. Called by the SCM whenever ControlService() is called on this service.
  138. Arguments:
  139. dwCtrlCode - type of control requested
  140. Return Values:
  141. None.
  142. --*/
  143. {
  144. DWORD dwState = SERVICE_RUNNING;
  145. // Handle the requested control code.
  146. //
  147. switch(dwCtrlCode) {
  148. // Stop the service.
  149. //
  150. case SERVICE_CONTROL_STOP:
  151. dwState = SERVICE_STOP_PENDING;
  152. ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
  153. break;
  154. // Update the service status.
  155. //
  156. case SERVICE_CONTROL_INTERROGATE:
  157. break;
  158. // invalid control code
  159. //
  160. default:
  161. break;
  162. }
  163. ReportStatusToSCMgr(dwState, NO_ERROR, 0);
  164. if ( SERVICE_CONTROL_STOP == dwCtrlCode )
  165. {
  166. ServiceStop();
  167. }
  168. } // ServiceCtrl
  169. /////////////////////////////////////////////////////////////////////////
  170. BOOL
  171. ReportStatusToSCMgr(
  172. DWORD dwCurrentState,
  173. DWORD dwWin32ExitCode,
  174. DWORD dwWaitHint
  175. )
  176. /*++
  177. Routine Description:
  178. Sets the current status of the service and reports it to the SCM.
  179. Arguments:
  180. dwCurrentState - the state of the service
  181. dwWin32ExitCode - error code to report
  182. dwWaitHint - worst case estimate to next checkpoint
  183. Return Values:
  184. None.
  185. --*/
  186. {
  187. static DWORD dwCheckPoint = 1;
  188. BOOL fResult = TRUE;
  189. if (sshStatusHandle == 0)
  190. {
  191. return FALSE;
  192. }
  193. ssStatus.dwControlsAccepted = 0;
  194. if ( !bDebug ) { // when debugging we don't report to the SCM
  195. if (dwCurrentState == SERVICE_START_PENDING)
  196. ssStatus.dwControlsAccepted = 0;
  197. else
  198. ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  199. ssStatus.dwCurrentState = dwCurrentState;
  200. ssStatus.dwWin32ExitCode = dwWin32ExitCode;
  201. ssStatus.dwWaitHint = dwWaitHint;
  202. if ( ( dwCurrentState == SERVICE_RUNNING ) ||
  203. ( dwCurrentState == SERVICE_STOPPED ) )
  204. ssStatus.dwCheckPoint = 0;
  205. else
  206. ssStatus.dwCheckPoint = dwCheckPoint++;
  207. // Report the status of the service to the service control manager.
  208. //
  209. if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
  210. dprintf(TEXT("LLS TRACE: SetServiceStatus failed\n"));
  211. }
  212. }
  213. return fResult;
  214. } // ReportStatusToSCMgr
  215. /////////////////////////////////////////////////////////////////////////
  216. //
  217. // The following code handles service installation and removal
  218. //
  219. /////////////////////////////////////////////////////////////////////////
  220. /////////////////////////////////////////////////////////////////////////
  221. VOID
  222. CmdInstallService()
  223. /*++
  224. Routine Description:
  225. Installs the service.
  226. Arguments:
  227. None.
  228. Return Values:
  229. None.
  230. --*/
  231. {
  232. SC_HANDLE schService;
  233. SC_HANDLE schSCManager;
  234. TCHAR szPath[512];
  235. if ( GetModuleFileName( NULL, szPath, 512 ) == 0 ) {
  236. _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
  237. return;
  238. }
  239. schSCManager = OpenSCManager(
  240. NULL, // machine (NULL == local)
  241. NULL, // database (NULL == default)
  242. SC_MANAGER_ALL_ACCESS // access required
  243. );
  244. if ( schSCManager ) {
  245. schService = CreateService(
  246. schSCManager, // SCManager database
  247. TEXT(SZSERVICENAME), // name of service
  248. TEXT(SZSERVICEDISPLAYNAME), // name to display
  249. SERVICE_ALL_ACCESS, // desired access
  250. SERVICE_WIN32_OWN_PROCESS, // service type
  251. SERVICE_DEMAND_START, // start type
  252. SERVICE_ERROR_NORMAL, // error control type
  253. szPath, // service's binary
  254. NULL, // no load ordering group
  255. NULL, // no tag identifier
  256. TEXT(SZDEPENDENCIES), // dependencies
  257. NULL, // LocalSystem account
  258. NULL); // no password
  259. if ( schService ) {
  260. _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  261. CloseServiceHandle(schService);
  262. } else {
  263. _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
  264. }
  265. CloseServiceHandle(schSCManager);
  266. } else
  267. _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  268. } // CmdInstallService
  269. /////////////////////////////////////////////////////////////////////////
  270. VOID
  271. CmdRemoveService()
  272. /*++
  273. Routine Description:
  274. Stops and removes the service.
  275. Arguments:
  276. None.
  277. Return Values:
  278. None.
  279. --*/
  280. {
  281. SC_HANDLE schService;
  282. SC_HANDLE schSCManager;
  283. schSCManager = OpenSCManager(
  284. NULL, // machine (NULL == local)
  285. NULL, // database (NULL == default)
  286. SC_MANAGER_ALL_ACCESS // access required
  287. );
  288. if ( schSCManager ) {
  289. schService = OpenService(schSCManager, TEXT(SZSERVICENAME), SERVICE_ALL_ACCESS);
  290. if (schService) {
  291. // try to stop the service
  292. if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) ) {
  293. _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
  294. Sleep( 1000 );
  295. while( QueryServiceStatus( schService, &ssStatus ) ) {
  296. if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
  297. _tprintf(TEXT("."));
  298. Sleep( 1000 );
  299. } else
  300. break;
  301. }
  302. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  303. _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  304. else
  305. _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  306. }
  307. // now remove the service
  308. if( DeleteService(schService) )
  309. _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  310. else
  311. _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
  312. CloseServiceHandle(schService);
  313. } else
  314. _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
  315. CloseServiceHandle(schSCManager);
  316. } else
  317. _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  318. } // CmdRemoveService
  319. /////////////////////////////////////////////////////////////////////////
  320. //
  321. // Routines for running the service as a console app
  322. //
  323. /////////////////////////////////////////////////////////////////////////
  324. /////////////////////////////////////////////////////////////////////////
  325. VOID CmdDebugService(
  326. int argc,
  327. char ** argv
  328. )
  329. /*++
  330. Routine Description:
  331. Runs the service as a console application
  332. Arguments:
  333. argc - number of command line arguments ***UNUSED***
  334. argv - array of command line arguments ***UNUSED***
  335. Return Values:
  336. None.
  337. --*/
  338. {
  339. _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  340. SetConsoleCtrlHandler( ControlHandler, TRUE );
  341. // assumption: argv and argc unused
  342. ServiceStart( 0, NULL );
  343. } // CmdDebugService
  344. /////////////////////////////////////////////////////////////////////////
  345. BOOL WINAPI
  346. ControlHandler (
  347. DWORD dwCtrlType
  348. )
  349. /*++
  350. Routine Description:
  351. Handle console control events.
  352. Arguments:
  353. dwCtrlType - type of control event
  354. lpszMsg - text for message
  355. Return Values:
  356. True - handled
  357. False - unhandled
  358. --*/
  359. {
  360. switch( dwCtrlType ) {
  361. case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
  362. case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
  363. _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  364. ServiceStop();
  365. return TRUE;
  366. break;
  367. }
  368. return FALSE;
  369. } // ControlHandler
  370. /////////////////////////////////////////////////////////////////////////
  371. LPTSTR
  372. GetLastErrorText(
  373. LPTSTR lpszBuf,
  374. DWORD dwSize
  375. )
  376. /*++
  377. Routine Description:
  378. Copies last error message text to string.
  379. Arguments:
  380. lpszBuf - destination buffer
  381. dwSize - size of buffer
  382. Return Values:
  383. destination buffer
  384. --*/
  385. {
  386. DWORD dwRet;
  387. LPTSTR lpszTemp = NULL;
  388. dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
  389. NULL,
  390. GetLastError(),
  391. LANG_NEUTRAL,
  392. (LPTSTR)&lpszTemp,
  393. 0,
  394. NULL );
  395. // supplied buffer is not long enough
  396. if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
  397. lpszBuf[0] = TEXT('\0');
  398. else {
  399. lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
  400. _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
  401. }
  402. if ( lpszTemp )
  403. LocalFree((HLOCAL) lpszTemp );
  404. return lpszBuf;
  405. } // GetLastErrorText