Leaked source code of windows server 2003
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.

581 lines
14 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. rcmdsvc.c
  5. Abstract:
  6. This is the remote command service. It serves multiple remote clients
  7. running standard i/o character based programs.
  8. Author:
  9. Dave Thompson, basically incorporating the remote command shell written
  10. by David Chalmers.
  11. Environment:
  12. User Mode -Win32
  13. Revision History:
  14. 5/1/94 DaveTh Created.
  15. 7/30/96 MarkHar Fixed bug 40834 - "doesn't work on NT4.0"
  16. Removed function calls within asserts.
  17. 1/31/99 MarkHar bug about install not working
  18. 6/22/99 MarkHar added usage message
  19. --*/
  20. //
  21. // Includes
  22. //
  23. #include <nt.h>
  24. #include <ntrtl.h>
  25. #include <windef.h>
  26. #include <nturtl.h>
  27. #include <winbase.h>
  28. #include <winsvc.h>
  29. #include "rcmdsrv.h"
  30. //
  31. // Defines
  32. //
  33. #define INFINITE_WAIT_TIME 0xFFFFFFFF
  34. #define NULL_STRING TEXT("");
  35. //
  36. // Globals
  37. //
  38. SERVICE_STATUS RcmdStatus;
  39. SERVICE_STATUS_HANDLE RcmdStatusHandle;
  40. //
  41. // Events for syncrhonizing service shutdown
  42. //
  43. HANDLE RcmdStopEvent = NULL;
  44. HANDLE RcmdStopCompleteEvent = NULL;
  45. HANDLE SessionThreadHandles[MAX_SESSIONS+1] = {NULL,};
  46. //
  47. // Flag to enable debug print
  48. //
  49. // BOOLEAN RcDbgPrintEnable = FALSE;
  50. //
  51. // Function Prototypes
  52. //
  53. VOID
  54. RcmdStart (
  55. DWORD argc,
  56. LPTSTR *argv
  57. );
  58. VOID
  59. RcmdCtrlHandler (
  60. IN DWORD opcode
  61. );
  62. DWORD
  63. RcmdInitialization(
  64. DWORD argc,
  65. LPTSTR *argv,
  66. DWORD *specificError
  67. );
  68. void CmdInstallService(void);
  69. void CmdRemoveService();
  70. void Usage(void);
  71. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
  72. /****************************************************************************/
  73. VOID __cdecl
  74. main(int argc, char ** argv)
  75. /*++
  76. Routine Description:
  77. This is the main routine for the service RCMD process.
  78. This thread calls StartServiceCtrlDispatcher which connects to the
  79. service controller and then waits in a loop for control requests.
  80. When all the services in the service process have terminated, the
  81. service controller will send a control request to the dispatcher
  82. telling it to shut down. This thread with then return from the
  83. StartServiceCtrlDispatcher call so that the process can terminate.
  84. Arguments:
  85. Return Value:
  86. --*/
  87. {
  88. DWORD status;
  89. char *szArgument = NULL;
  90. SERVICE_TABLE_ENTRY DispatchTable[] = {
  91. { TEXT("Remote Command"), RcmdStart },
  92. { NULL, NULL }
  93. };
  94. if (argc > 1)
  95. {
  96. if ((*argv[1] == '-') || (*argv[1] == '/'))
  97. {
  98. szArgument = argv[1]+1;
  99. if (_stricmp("install", szArgument) == 0)
  100. {
  101. CmdInstallService();
  102. }
  103. else if (_stricmp("uninstall", szArgument) == 0)
  104. {
  105. CmdRemoveService();
  106. }
  107. else
  108. {
  109. Usage();
  110. }
  111. }
  112. }
  113. else
  114. {
  115. status = StartServiceCtrlDispatcher( DispatchTable);
  116. }
  117. ExitProcess(0);
  118. }
  119. void Usage(void)
  120. {
  121. char *szUsage = "usage: rcmdsvc\n"
  122. "rcmdsvc [[-/] [install | uninstall | H]]\n"
  123. "\tinstall - registers the service with the service controller\n"
  124. "\tuninstall - unregisters the service with the service controller\n"
  125. "\tHh? - prints this usage message\n";
  126. fprintf(stdout, szUsage);
  127. }
  128. void CmdInstallService()
  129. /*++
  130. Routine Description:
  131. Arguments:
  132. None
  133. Return Value:
  134. None
  135. --*/
  136. {
  137. SC_HANDLE schService;
  138. SC_HANDLE schSCManager;
  139. TCHAR szErr[256];
  140. TCHAR szPath[512];
  141. if ( GetModuleFileName( NULL, szPath, 512 ) == 0 )
  142. {
  143. printf(TEXT("Unable to install %s - %s\n"),
  144. TEXT("Remote Command"),
  145. GetLastErrorText(szErr, 256));
  146. return;
  147. }
  148. schSCManager = OpenSCManager(
  149. NULL, // machine (NULL == local)
  150. NULL, // database (NULL == default)
  151. SC_MANAGER_ALL_ACCESS // access required
  152. );
  153. if ( schSCManager )
  154. {
  155. schService = CreateService(
  156. schSCManager, // SCManager database
  157. TEXT("rcmdsvc"), // name of service
  158. TEXT("Remote Command Service"), // name to display
  159. SERVICE_ALL_ACCESS, // desired access
  160. SERVICE_WIN32_OWN_PROCESS, // service type
  161. SERVICE_DEMAND_START, // start type
  162. SERVICE_ERROR_NORMAL, // error control type
  163. szPath, // service's binary
  164. NULL, // no load ordering group
  165. NULL, // no tag identifier
  166. NULL, // dependencies
  167. NULL, // LocalSystem account
  168. NULL); // no password
  169. if ( schService )
  170. {
  171. printf(TEXT("%s installed.\n"), TEXT("Remote Command Service") );
  172. CloseServiceHandle(schService);
  173. }
  174. else
  175. {
  176. printf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
  177. }
  178. CloseServiceHandle(schSCManager);
  179. }
  180. else
  181. printf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  182. }
  183. void CmdRemoveService()
  184. {
  185. SC_HANDLE schService;
  186. SC_HANDLE schSCManager;
  187. TCHAR szErr[256];
  188. schSCManager = OpenSCManager(
  189. NULL, // machine (NULL == local)
  190. NULL, // database (NULL == default)
  191. SC_MANAGER_ALL_ACCESS // access required
  192. );
  193. if ( schSCManager )
  194. {
  195. schService = OpenService(schSCManager,
  196. TEXT("rcmdsvc"),
  197. SERVICE_ALL_ACCESS);
  198. if (schService)
  199. {
  200. // try to stop the service
  201. if ( ControlService( schService,
  202. SERVICE_CONTROL_STOP,
  203. &RcmdStatus ) )
  204. {
  205. printf(TEXT("Stopping %s."), TEXT("Remote Command Service"));
  206. Sleep( 1000 );
  207. while( QueryServiceStatus( schService, &RcmdStatus ) )
  208. {
  209. if ( RcmdStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
  210. printf(TEXT("."));
  211. Sleep( 1000 );
  212. }
  213. else {
  214. break;
  215. }
  216. }
  217. if ( RcmdStatus.dwCurrentState == SERVICE_STOPPED ) {
  218. printf(TEXT("\n%s stopped.\n"),
  219. TEXT("Remote Command Service") );
  220. }
  221. else {
  222. printf(TEXT("\n%s failed to stop.\n"),
  223. TEXT("Remote Command Service") );
  224. }
  225. }
  226. // now remove the service
  227. if( DeleteService(schService) ) {
  228. printf(TEXT("%s removed.\n"),
  229. TEXT("Remote Command Service") );
  230. }
  231. else {
  232. printf(TEXT("DeleteService failed - %s\n"),
  233. GetLastErrorText(szErr,256));
  234. }
  235. CloseServiceHandle(schService);
  236. }
  237. else {
  238. printf(TEXT("OpenService failed -\n%s\n"),
  239. GetLastErrorText(szErr,256));
  240. printf(TEXT("The service must be installed before removing it."));
  241. }
  242. CloseServiceHandle(schSCManager);
  243. }
  244. else {
  245. printf(TEXT("OpenSCManager failed - %s\n"),
  246. GetLastErrorText(szErr,256));
  247. }
  248. }
  249. /****************************************************************************/
  250. void
  251. RcmdStart (
  252. DWORD argc,
  253. LPTSTR *argv
  254. )
  255. /*++
  256. Routine Description:
  257. This is the entry point for the service. When the control dispatcher
  258. is told to start a service, it creates a thread that will begin
  259. executing at this point. The function has access to command line
  260. arguments in the same manner as a main() routine.
  261. Rather than return from this function, it is more appropriate to
  262. call ExitThread().
  263. Arguments:
  264. Return Value:
  265. --*/
  266. {
  267. DWORD status;
  268. DWORD specificError;
  269. //
  270. // Initialize the services status structure
  271. //
  272. RcmdStatus.dwServiceType = SERVICE_WIN32;
  273. RcmdStatus.dwCurrentState = SERVICE_START_PENDING;
  274. RcmdStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; // stop only
  275. RcmdStatus.dwWin32ExitCode = 0;
  276. RcmdStatus.dwServiceSpecificExitCode = 0;
  277. RcmdStatus.dwCheckPoint = 0;
  278. RcmdStatus.dwWaitHint = 0;
  279. //
  280. // Register the Control Handler routine.
  281. //
  282. RcmdStatusHandle = RegisterServiceCtrlHandler(
  283. TEXT("Remote Command"),
  284. RcmdCtrlHandler);
  285. if (RcmdStatusHandle == (SERVICE_STATUS_HANDLE)0) {
  286. RcDbgPrint(" [Rcmd] RegisterServiceCtrlHandler failed %d\n",
  287. GetLastError());
  288. }
  289. //
  290. // Initialize service global structures
  291. //
  292. status = RcmdInitialization(argc,argv, &specificError);
  293. if (status != NO_ERROR) {
  294. RcmdStatus.dwCurrentState = SERVICE_RUNNING;
  295. RcmdStatus.dwCheckPoint = 0;
  296. RcmdStatus.dwWaitHint = 0;
  297. RcmdStatus.dwWin32ExitCode = status;
  298. RcmdStatus.dwServiceSpecificExitCode = specificError;
  299. SetServiceStatus (RcmdStatusHandle, &RcmdStatus);
  300. ExitThread(NO_ERROR);
  301. return;
  302. }
  303. //
  304. // Return the status to indicate we are done with intialization.
  305. //
  306. RcmdStatus.dwCurrentState = SERVICE_RUNNING;
  307. RcmdStatus.dwCheckPoint = 0;
  308. RcmdStatus.dwWaitHint = 0;
  309. if (!SetServiceStatus (RcmdStatusHandle, &RcmdStatus)) {
  310. status = GetLastError();
  311. RcDbgPrint(" [Rcmd] SetServiceStatus error %ld\n",status);
  312. }
  313. //
  314. // Run remote command processor - return when shutdown
  315. //
  316. if (0 != (status = Rcmd()))
  317. {
  318. RcDbgPrint(" [Rcmd]: problem occurred in Rcmd()\n");
  319. RcmdStatus.dwCurrentState = SERVICE_STOPPED;
  320. RcmdStatus.dwCheckPoint = 0;
  321. RcmdStatus.dwWaitHint = 0;
  322. RcmdStatus.dwWin32ExitCode = status;
  323. SetServiceStatus(RcmdStatusHandle, &RcmdStatus);
  324. ExitThread(status);
  325. }
  326. else
  327. {
  328. RcDbgPrint(" [Rcmd] Leaving My Service \n");
  329. ExitThread(NO_ERROR);
  330. }
  331. }
  332. /****************************************************************************/
  333. VOID
  334. RcmdCtrlHandler (
  335. IN DWORD Opcode
  336. )
  337. /*++
  338. Routine Description:
  339. This function executes in the context of the Control Dispatcher's
  340. thread. Therefore, it it not desirable to perform time-consuming
  341. operations in this function.
  342. If an operation such as a stop is going to take a long time, then
  343. this routine should send the STOP_PENDING status, and then
  344. signal the other service thread(s) that a shut-down is in progress.
  345. Then it should return so that the Control Dispatcher can service
  346. more requests. One of the other service threads is then responsible
  347. for sending further wait hints, and the final SERVICE_STOPPED.
  348. Arguments:
  349. Return Value:
  350. --*/
  351. {
  352. DWORD status;
  353. //
  354. // Find and operate on the request.
  355. //
  356. switch(Opcode) {
  357. case SERVICE_CONTROL_PAUSE:
  358. RcDbgPrint(" [Rcmd] Pause - Unsupported opcode\n");
  359. break;
  360. case SERVICE_CONTROL_CONTINUE:
  361. RcDbgPrint(" [Rcmd] Continue - Unsupported opcode\n");
  362. break;
  363. case SERVICE_CONTROL_STOP:
  364. RcmdStatus.dwCurrentState = SERVICE_STOPPED;
  365. RcmdStatus.dwWin32ExitCode = RcmdStop();
  366. break;
  367. case SERVICE_CONTROL_INTERROGATE:
  368. //
  369. // All that needs to be done in this case is to send the
  370. // current status.
  371. //
  372. break;
  373. default:
  374. RcDbgPrint(" [Rcmd] Unrecognized opcode %ld\n", Opcode);
  375. }
  376. //
  377. // Send a status response.
  378. //
  379. if (!SetServiceStatus (RcmdStatusHandle, &RcmdStatus)) {
  380. status = GetLastError();
  381. RcDbgPrint(" [Rcmd] SetServiceStatus error %ld\n",status);
  382. }
  383. }
  384. DWORD
  385. RcmdInitialization(
  386. DWORD argc,
  387. LPTSTR *argv,
  388. DWORD *specificError)
  389. {
  390. UNREFERENCED_PARAMETER(argv);
  391. UNREFERENCED_PARAMETER(argc);
  392. //
  393. // Initialize global stop event (signals running threads) and session
  394. // thread handle array (for threads to signal back on exit).
  395. //
  396. if (!(RcmdStopEvent = CreateEvent ( NULL, TRUE, FALSE, NULL ))) {
  397. *specificError = GetLastError();
  398. return(*specificError);
  399. }
  400. if (!(RcmdStopCompleteEvent = CreateEvent ( NULL, TRUE, FALSE, NULL ))) {
  401. *specificError = GetLastError();
  402. return(*specificError);
  403. }
  404. return(NO_ERROR);
  405. }
  406. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
  407. {
  408. DWORD dwRet;
  409. LPTSTR lpszTemp = NULL;
  410. dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
  411. | FORMAT_MESSAGE_FROM_SYSTEM
  412. | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  413. NULL,
  414. GetLastError(),
  415. LANG_NEUTRAL,
  416. (LPTSTR)&lpszTemp,
  417. 0,
  418. NULL );
  419. // supplied buffer is not long enough
  420. if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
  421. lpszBuf[0] = TEXT('\0');
  422. else
  423. {
  424. lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
  425. sprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
  426. }
  427. if ( lpszTemp )
  428. LocalFree((HLOCAL) lpszTemp );
  429. return lpszBuf;
  430. }