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.

552 lines
15 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. dispatch.c
  5. Abstract:
  6. This module implements the basic command dispatcher.
  7. Author:
  8. Wesley Witt (wesw) 21-Oct-1998
  9. Revision History:
  10. --*/
  11. #include "cmdcons.h"
  12. #pragma hdrstop
  13. ULONG
  14. RcCmdDoHelp(
  15. IN PTOKENIZED_LINE TokenizedLine
  16. );
  17. ULONG
  18. RcCmdDoExit(
  19. IN PTOKENIZED_LINE TokenizedLine
  20. );
  21. RC_CMD Commands[] = {
  22. { L"ATTRIB", RcCmdAttrib, 1, 2, 0, TRUE },
  23. { L"BATCH", RcCmdBatch, 1, 2, 0, TRUE },
  24. #if !defined(_DONT_HAVE_BOOTCFG_TESTERS_)
  25. #if defined(_X86_)
  26. { L"BOOTCFG", RcCmdBootCfg, 0,-1, 0, TRUE },
  27. #endif
  28. #endif
  29. { L"CD", RcCmdChdir, 0, 1, 0, TRUE },
  30. { L"CHDIR", RcCmdChdir, 0, 1, 0, TRUE },
  31. { L"CHKDSK", RcCmdChkdsk, 0,-1, 0, TRUE },
  32. { L"CLS", RcCmdCls, 0, 1, 0, TRUE },
  33. { L"COPY", RcCmdCopy, 1, 2, 0, TRUE },
  34. { L"DEL", RcCmdDelete, 1, 1, 0, TRUE },
  35. { L"DELETE", RcCmdDelete, 1, 1, 0, TRUE },
  36. { L"DIR", RcCmdDir, 0, 1, 0, TRUE },
  37. { L"DISABLE", RcCmdDisableService, 0,-1, 0, TRUE },
  38. { L"DISKPART", RcCmdFdisk, 0, 3, 0, TRUE },
  39. { L"ENABLE", RcCmdEnableService, 0,-1, 0, TRUE },
  40. { L"ERASE", RcCmdDelete, 1, 1, 1, TRUE },
  41. { L"EXIT", RcCmdDoExit, 0, 1, 0, TRUE },
  42. { L"EXPAND", RcCmdExpand, 1,-1, 0, TRUE },
  43. { L"FIXBOOT", RcCmdFixBootSect, 0, 1, 0, TRUE },
  44. { L"FIXMBR", RcCmdFixMBR, 0, 1, 0, TRUE },
  45. { L"FORMAT", RcCmdFormat, 1, 3, 0, TRUE },
  46. { L"HELP", RcCmdDoHelp, 0, 1, 0, TRUE },
  47. { L"LISTSVC", RcCmdListSvc, 0, 1, 0, TRUE },
  48. { L"LOGON", RcCmdLogon, 0, 3, 0, TRUE },
  49. { L"MAP", RcCmdDriveMap, 0, 1, 0, TRUE },
  50. { L"MD", RcCmdMkdir, 1, 1, 0, TRUE },
  51. { L"MKDIR", RcCmdMkdir, 1, 1, 0, TRUE },
  52. { L"MKDISKRAW", RcCmdMakeDiskRaw, 1, 1, 1, TRUE },
  53. { L"MORE", RcCmdType, 1, 1, 0, TRUE },
  54. { L"NET", RcCmdNet, 1, 5, 0, TRUE },
  55. { L"RD", RcCmdRmdir, 1, 1, 0, TRUE },
  56. { L"REN", RcCmdRename, 1, 2, 0, TRUE },
  57. { L"RENAME", RcCmdRename, 1, 2, 0, TRUE },
  58. #if 0
  59. { L"REPAIR", RcCmdRepair, 1, 5, 0, TRUE },
  60. #endif
  61. { L"RMDIR", RcCmdRmdir, 1, 1, 0, TRUE },
  62. { L"SET", RcCmdSetFlags, 0, 3, 1, TRUE },
  63. { L"SYSTEMROOT", RcCmdSystemRoot, 0, 1, 0, TRUE },
  64. { L"TYPE", RcCmdType, 1, 1, 0, TRUE },
  65. { L"VERIFIER", RcCmdVerifier, 0,-1, 1, TRUE },
  66. { L"/?", RcCmdHelpHelp, 0, 1, 1, TRUE },
  67. { L"?", RcCmdHelpHelp, 0, 1, 1, TRUE }
  68. };
  69. #define NUM_CMDS (sizeof(Commands)/sizeof(Commands[0]))
  70. //
  71. // Special case: exit and reload
  72. //
  73. #define EXIT_COMMAND_NAME L"EXIT"
  74. #define RELOAD_COMMAND_NAME L"RELOAD"
  75. #define HELP_COMMAND_NAME L"HELP"
  76. // prototype
  77. ULONG
  78. GetStringTokenFromLine(
  79. IN OUT LPWSTR *Start,
  80. OUT LPWSTR Output OPTIONAL
  81. );
  82. PTOKENIZED_LINE
  83. RcTokenizeLine(
  84. IN LPWSTR Line
  85. )
  86. {
  87. ULONG len;
  88. WCHAR *p,*q;
  89. PTOKENIZED_LINE TokenizedLine;
  90. PLINE_TOKEN LineToken,PrevToken;
  91. //
  92. // Strip trailing space off the command.
  93. //
  94. len = wcslen(Line);
  95. while(len && RcIsSpace(Line[len-1])) {
  96. Line[--len] = 0;
  97. }
  98. //
  99. // Allocate and initialize a tokenized line structure.
  100. //
  101. TokenizedLine = SpMemAlloc(sizeof(TOKENIZED_LINE));
  102. RtlZeroMemory(TokenizedLine,sizeof(TOKENIZED_LINE));
  103. //
  104. // Now we go into a loop of skipping leading space and parsing in
  105. // the actual tokens.
  106. //
  107. PrevToken = NULL;
  108. p = Line;
  109. while(*p) {
  110. //
  111. // Skip leading space. Because we trimmed off trailing space,
  112. // we should never hit the end of the line before finding a
  113. // non-space character.
  114. //
  115. while(RcIsSpace(*p)) {
  116. p++;
  117. }
  118. ASSERT(*p);
  119. //
  120. // Allocate a line token structure for this string.
  121. //
  122. LineToken = SpMemAlloc(sizeof(LINE_TOKEN));
  123. RtlZeroMemory(LineToken,sizeof(LINE_TOKEN));
  124. //
  125. // Now we've got a string. First we make one pass over it
  126. // to determine the length, then allocate a buffer and
  127. // pull out the string into it.
  128. //
  129. q = p;
  130. len = GetStringTokenFromLine(&q,NULL);
  131. LineToken->String = SpMemAlloc((len+1)*sizeof(WCHAR));
  132. GetStringTokenFromLine(&p,LineToken->String);
  133. if(PrevToken) {
  134. PrevToken->Next = LineToken;
  135. } else {
  136. TokenizedLine->Tokens = LineToken;
  137. }
  138. PrevToken = LineToken;
  139. TokenizedLine->TokenCount++;
  140. }
  141. return(TokenizedLine);
  142. }
  143. ULONG
  144. GetStringTokenFromLine(
  145. IN OUT LPWSTR *Start,
  146. OUT LPWSTR Output OPTIONAL
  147. )
  148. {
  149. WCHAR *p;
  150. ULONG len;
  151. BOOLEAN InQuote;
  152. len = 0;
  153. InQuote = FALSE;
  154. p = *Start;
  155. while(*p) {
  156. if(RcIsSpace(*p) && !InQuote) {
  157. //
  158. // Done.
  159. //
  160. break;
  161. }
  162. if(*p == L'\"') {
  163. InQuote = (BOOLEAN)(!InQuote);
  164. } else {
  165. if(Output) {
  166. Output[len] = *p;
  167. }
  168. len++;
  169. }
  170. p++;
  171. }
  172. if(Output) {
  173. Output[len] = 0;
  174. }
  175. *Start = p;
  176. return(len);
  177. }
  178. VOID
  179. RcFreeTokenizedLine(
  180. IN OUT PTOKENIZED_LINE *TokenizedLine
  181. )
  182. {
  183. PTOKENIZED_LINE p;
  184. PLINE_TOKEN q,n;
  185. p = *TokenizedLine;
  186. *TokenizedLine = NULL;
  187. q = p->Tokens;
  188. while(q) {
  189. n = q->Next;
  190. //
  191. // zero out every string as there may be passwords
  192. //
  193. RcSecureZeroStringW(q->String);
  194. SpMemFree((PVOID)q->String);
  195. SpMemFree(q);
  196. q = n;
  197. }
  198. SpMemFree(p);
  199. }
  200. ULONG
  201. RcDispatchCommand(
  202. IN PTOKENIZED_LINE TokenizedLine
  203. )
  204. /*++
  205. Routine Description:
  206. Top-level routine for dispatching a command.
  207. Arguments:
  208. Return Value:
  209. --*/
  210. {
  211. unsigned i;
  212. unsigned count;
  213. ASSERT(TokenizedLine->TokenCount);
  214. if(!TokenizedLine->TokenCount) {
  215. return(1);
  216. }
  217. /*
  218. //
  219. // Special case exit right up front.
  220. //
  221. if(!_wcsicmp(TokenizedLine->Tokens->String,EXIT_COMMAND_NAME)) {
  222. if (TokenizedLine->TokenCount > 1) {
  223. RcMessageOut(MSG_EXIT_HELP);
  224. return(1);
  225. }
  226. return(0);
  227. }
  228. */
  229. if(!_wcsicmp(TokenizedLine->Tokens->String,RELOAD_COMMAND_NAME)) {
  230. return(2);
  231. }
  232. /*
  233. if(!_wcsicmp(TokenizedLine->Tokens->String,HELP_COMMAND_NAME)) {
  234. if( RcCmdDoHelp(TokenizedLine) ) {
  235. // if we got a 1, then the user just wanted a help index
  236. // otherwise we want to drop down and let the regular command
  237. // processing path handle a /? parameter.
  238. return(1);
  239. }
  240. }
  241. */
  242. //
  243. // See whether it's a drive designation.
  244. //
  245. if(RcIsAlpha(TokenizedLine->Tokens->String[0])
  246. && (TokenizedLine->Tokens->String[1] == L':')
  247. && (TokenizedLine->Tokens->String[2] == 0)) {
  248. RcCmdSwitchDrives(TokenizedLine->Tokens->String[0]);
  249. return(1);
  250. }
  251. //
  252. // Attempt to locate the command in our table.
  253. //
  254. for(i=0; i<NUM_CMDS; i++) {
  255. if(Commands[i].Enabled && !_wcsicmp(TokenizedLine->Tokens->String,Commands[i].Name)) {
  256. //
  257. // Validate arg count.
  258. //
  259. count = TokenizedLine->TokenCount - 1;
  260. if((count < Commands[i].MinimumArgCount)
  261. || (count > Commands[i].MaximumArgCount)) {
  262. RcMessageOut(MSG_SYNTAX_ERROR);
  263. } else {
  264. return Commands[i].Routine(TokenizedLine);
  265. }
  266. return(1);
  267. }
  268. }
  269. RcMessageOut(MSG_UNKNOWN_COMMAND);
  270. return(1);
  271. }
  272. ULONG
  273. RcCmdDoExit(
  274. IN PTOKENIZED_LINE TokenizedLine
  275. )
  276. /*++
  277. Routine Description:
  278. Exit command routine
  279. Arguments:
  280. Tokens for the command
  281. Return Value:
  282. 1 if some error was found or help was asked for.
  283. 0 if we need to exit
  284. --*/
  285. {
  286. ULONG uResult = 0; // will exit
  287. if (RcCmdParseHelp( TokenizedLine, MSG_EXIT_HELP ))
  288. uResult = 1; // will not exit
  289. return uResult;
  290. }
  291. ULONG
  292. RcCmdDoHelp(
  293. IN PTOKENIZED_LINE TokenizedLine
  294. )
  295. /*++
  296. Routine Description:
  297. Help command routine
  298. Arguments:
  299. Tokens for the command
  300. Return Value:
  301. 1 if some error was found or help was requested.
  302. When help was reqeusted for a particular command
  303. the dispatched command's return value with "/?" as argument for
  304. the command
  305. --*/
  306. {
  307. ULONG uResult = 1;
  308. int i;
  309. PLINE_TOKEN Token;
  310. if (!RcCmdParseHelp( TokenizedLine, MSG_HELPCOMMAND_HELP )) {
  311. if (TokenizedLine->TokenCount == 2) {
  312. // we assume that the user is typing HELP <command>.
  313. // we simply reverse the two tokens, getting <command> HELP
  314. // and overwrite HELP with /? [which fits since HELP is four chars long]
  315. // we then return a 0, which causes the dispatcher to drop into
  316. // the normal command processing path
  317. Token = TokenizedLine->Tokens;
  318. TokenizedLine->Tokens = TokenizedLine->Tokens->Next;
  319. TokenizedLine->Tokens->Next = Token;
  320. Token->Next = NULL;
  321. wcscpy( Token->String, L"/?" );
  322. uResult = RcDispatchCommand( TokenizedLine );
  323. } else {
  324. pRcEnableMoreMode();
  325. RcMessageOut( MSG_HELPCOMMAND_HELP );
  326. for( i=0; i < NUM_CMDS; i++ ) {
  327. if (Commands[i].Hidden == 0) {
  328. RcTextOut( Commands[i].Name );
  329. RcTextOut( L"\r\n" );
  330. }
  331. }
  332. pRcDisableMoreMode();
  333. }
  334. }
  335. return uResult;
  336. }
  337. /*++
  338. Routine Description:
  339. Enables or disables the SET command
  340. Note : we avoid using direct SET command index
  341. into Commands array so that if some one changes
  342. the Commands array this routine still works
  343. Arguments:
  344. bEnable - BOOLEAN inidicating whether to enable or disable
  345. the set command
  346. Return Value:
  347. None
  348. --*/
  349. VOID
  350. RcSetSETCommandStatus(
  351. BOOLEAN bEnabled
  352. )
  353. {
  354. int iIndex;
  355. int cElements = sizeof(Commands) / sizeof(RC_CMD);
  356. WCHAR *szSetCmdName = L"SET";
  357. //
  358. // search through the dispatch table and trun on the
  359. // help flag. This flag will indicate whether the set
  360. // command is enabled or not
  361. //
  362. for(iIndex = 0; iIndex < cElements; iIndex++) {
  363. if ( !wcscmp(Commands[iIndex].Name, szSetCmdName) ) {
  364. Commands[iIndex].Hidden = bEnabled ? 0 : 1;
  365. break;
  366. }
  367. }
  368. }
  369. /*++
  370. Routine Description:
  371. Returns the SET command status
  372. Note : we avoid using direct SET command index
  373. into Commands array so that if some one changes
  374. the Commands array this routine still works
  375. Arguments:
  376. None
  377. Return Value:
  378. BOOLEAN inidicating whether the SET command is
  379. enabled or disabled.
  380. --*/
  381. BOOLEAN
  382. RcGetSETCommandStatus(
  383. VOID
  384. )
  385. {
  386. BOOLEAN bEnabled = FALSE;
  387. int iIndex;
  388. int cElements = sizeof(Commands) / sizeof(RC_CMD);
  389. WCHAR *szSetCmdName = L"SET";
  390. //
  391. // search through the dispatch table and trun on the
  392. // help flag. This flag will indicate whether the set
  393. // command is enabled or not
  394. //
  395. for(iIndex = 0; iIndex < cElements; iIndex++) {
  396. if ( !wcscmp(Commands[iIndex].Name, szSetCmdName) ) {
  397. bEnabled = (Commands[iIndex].Hidden == 0);
  398. break;
  399. }
  400. }
  401. return bEnabled;
  402. }
  403. BOOLEAN
  404. RcDisableCommand(
  405. IN PRC_CMD_ROUTINE CmdToDisable
  406. )
  407. /*++
  408. Routine Description:
  409. Disables the specified command and hides it.
  410. Arguments:
  411. CmdToDisable - Command Routine to disable
  412. Return Value:
  413. BOOLEAN inidicating whether the command was disabled or not.
  414. --*/
  415. {
  416. ULONG Index;
  417. ULONG NumCmds;
  418. BOOLEAN Result = FALSE;
  419. if (CmdToDisable) {
  420. NumCmds = sizeof(Commands) / sizeof(RC_CMD);
  421. for (Index=0; Index < NumCmds; Index++) {
  422. //
  423. // Note : Search the whole table as there might
  424. // be aliases for the same command
  425. //
  426. if (CmdToDisable == Commands[Index].Routine) {
  427. Commands[Index].Hidden = TRUE;
  428. Commands[Index].Enabled = FALSE;
  429. Result = TRUE;
  430. }
  431. }
  432. }
  433. return Result;
  434. }