UefiShellCommandLib.c revision d7bb8de9b956efbf2ec51dc2d613b361a08e6712
1/** @file 2 Provides interface to shell internal functions for shell commands. 3 4 Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13**/ 14 15#include "UefiShellCommandLib.h" 16 17// STATIC local variables 18STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY mCommandList; 19STATIC SCRIPT_FILE_LIST mScriptList; 20STATIC ALIAS_LIST mAliasList; 21STATIC BOOLEAN mEchoState; 22STATIC BOOLEAN mExitRequested; 23STATIC UINT64 mExitCode; 24STATIC BOOLEAN mExitScript; 25STATIC CHAR16 *mProfileList; 26STATIC UINTN mProfileListSize; 27STATIC UINTN mFsMaxCount = 0; 28STATIC UINTN mBlkMaxCount = 0; 29STATIC BUFFER_LIST mFileHandleList; 30 31// global variables required by library class. 32EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL; 33EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *gDevPathToText = NULL; 34SHELL_MAP_LIST gShellMapList; 35SHELL_MAP_LIST *gShellCurDir = NULL; 36 37CONST CHAR16* SupportLevel[] = { 38 L"Minimal", 39 L"Scripting", 40 L"Basic", 41 L"Interactive" 42}; 43 44/** 45 Function to make sure that the global protocol pointers are valid. 46 must be called after constructor before accessing the pointers. 47**/ 48EFI_STATUS 49EFIAPI 50CommandInit( 51 VOID 52 ) 53{ 54 EFI_STATUS Status; 55 if (gUnicodeCollation == NULL) { 56 Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation); 57 if (EFI_ERROR(Status)) { 58 return (EFI_DEVICE_ERROR); 59 } 60 } 61 if (gDevPathToText == NULL) { 62 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID**)&gDevPathToText); 63 if (EFI_ERROR(Status)) { 64 return (EFI_DEVICE_ERROR); 65 } 66 } 67 return (EFI_SUCCESS); 68} 69 70/** 71 Constructor for the Shell Command library. 72 73 Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell. 74 75 @param ImageHandle the image handle of the process 76 @param SystemTable the EFI System Table pointer 77 78 @retval EFI_SUCCESS the initialization was complete sucessfully 79**/ 80RETURN_STATUS 81EFIAPI 82ShellCommandLibConstructor ( 83 IN EFI_HANDLE ImageHandle, 84 IN EFI_SYSTEM_TABLE *SystemTable 85 ) 86{ 87 EFI_STATUS Status; 88 InitializeListHead(&gShellMapList.Link); 89 InitializeListHead(&mCommandList.Link); 90 InitializeListHead(&mAliasList.Link); 91 InitializeListHead(&mScriptList.Link); 92 InitializeListHead(&mFileHandleList.Link); 93 mEchoState = TRUE; 94 95 mExitRequested = FALSE; 96 mExitScript = FALSE; 97 mProfileListSize = 0; 98 mProfileList = NULL; 99 100 if (gUnicodeCollation == NULL) { 101 Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation); 102 if (EFI_ERROR(Status)) { 103 return (EFI_DEVICE_ERROR); 104 } 105 } 106 107 return (RETURN_SUCCESS); 108} 109 110/** 111 Destructor for the library. free any resources. 112 113 @param ImageHandle the image handle of the process 114 @param SystemTable the EFI System Table pointer 115 116 @retval RETURN_SUCCESS this function always returns success 117**/ 118RETURN_STATUS 119EFIAPI 120ShellCommandLibDestructor ( 121 IN EFI_HANDLE ImageHandle, 122 IN EFI_SYSTEM_TABLE *SystemTable 123 ) 124{ 125 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 126 ALIAS_LIST *Node2; 127 SCRIPT_FILE_LIST *Node3; 128 SHELL_MAP_LIST *MapNode; 129 // 130 // enumerate throught the list and free all the memory 131 // 132 while (!IsListEmpty (&mCommandList.Link)) { 133 Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link); 134 RemoveEntryList(&Node->Link); 135 SHELL_FREE_NON_NULL(Node->CommandString); 136 FreePool(Node); 137 DEBUG_CODE(Node = NULL;); 138 } 139 140 // 141 // enumerate through the alias list and free all memory 142 // 143 while (!IsListEmpty (&mAliasList.Link)) { 144 Node2 = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link); 145 RemoveEntryList(&Node2->Link); 146 SHELL_FREE_NON_NULL(Node2->CommandString); 147 SHELL_FREE_NON_NULL(Node2->Alias); 148 SHELL_FREE_NON_NULL(Node2); 149 DEBUG_CODE(Node2 = NULL;); 150 } 151 152 // 153 // enumerate throught the list and free all the memory 154 // 155 while (!IsListEmpty (&mScriptList.Link)) { 156 Node3 = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link); 157 RemoveEntryList(&Node3->Link); 158 DeleteScriptFileStruct(Node3->Data); 159 FreePool(Node3); 160 } 161 162 // 163 // enumerate throught the mappings list and free all the memory 164 // 165 if (!IsListEmpty(&gShellMapList.Link)) { 166 for (MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) 167 ; !IsListEmpty (&gShellMapList.Link) 168 ; MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) 169 ){ 170 ASSERT(MapNode != NULL); 171 RemoveEntryList(&MapNode->Link); 172 SHELL_FREE_NON_NULL(MapNode->DevicePath); 173 SHELL_FREE_NON_NULL(MapNode->MapName); 174 SHELL_FREE_NON_NULL(MapNode->CurrentDirectoryPath); 175 FreePool(MapNode); 176 } 177 } 178 if (!IsListEmpty(&mFileHandleList.Link)){ 179 FreeBufferList(&mFileHandleList); 180 } 181 182 if (mProfileList != NULL) { 183 FreePool(mProfileList); 184 } 185 186 gUnicodeCollation = NULL; 187 gDevPathToText = NULL; 188 gShellCurDir = NULL; 189 190 return (RETURN_SUCCESS); 191} 192 193/** 194 Checks if a command is already on the list. 195 196 @param[in] CommandString The command string to check for on the list. 197**/ 198BOOLEAN 199EFIAPI 200ShellCommandIsCommandOnList ( 201 IN CONST CHAR16 *CommandString 202 ) 203{ 204 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 205 206 // 207 // assert for NULL parameter 208 // 209 ASSERT(CommandString != NULL); 210 211 // 212 // check for the command 213 // 214 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link) 215 ; !IsNull(&mCommandList.Link, &Node->Link) 216 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link) 217 ){ 218 ASSERT(Node->CommandString != NULL); 219 if (gUnicodeCollation->StriColl( 220 gUnicodeCollation, 221 (CHAR16*)CommandString, 222 Node->CommandString) == 0 223 ){ 224 return (TRUE); 225 } 226 } 227 return (FALSE); 228} 229 230/** 231 Get the help text for a command. 232 233 @param[in] CommandString The command name. 234 235 @retval NULL No help text was found. 236 @return String of help text. Caller reuiqred to free. 237**/ 238CHAR16* 239EFIAPI 240ShellCommandGetCommandHelp ( 241 IN CONST CHAR16 *CommandString 242 ) 243{ 244 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 245 246 // 247 // assert for NULL parameter 248 // 249 ASSERT(CommandString != NULL); 250 251 // 252 // check for the command 253 // 254 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link) 255 ; !IsNull(&mCommandList.Link, &Node->Link) 256 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link) 257 ){ 258 ASSERT(Node->CommandString != NULL); 259 if (gUnicodeCollation->StriColl( 260 gUnicodeCollation, 261 (CHAR16*)CommandString, 262 Node->CommandString) == 0 263 ){ 264 return (HiiGetString(Node->HiiHandle, Node->ManFormatHelp, NULL)); 265 } 266 } 267 return (NULL); 268} 269 270/** 271 Registers handlers of type SHELL_RUN_COMMAND and 272 SHELL_GET_MAN_FILENAME for each shell command. 273 274 If the ShellSupportLevel is greater than the value of the 275 PcdShellSupportLevel then return RETURN_UNSUPPORTED. 276 277 Registers the handlers specified by GetHelpInfoHandler and CommandHandler 278 with the command specified by CommandString. If the command named by 279 CommandString has already been registered, then return 280 RETURN_ALREADY_STARTED. 281 282 If there are not enough resources available to register the handlers then 283 RETURN_OUT_OF_RESOURCES is returned. 284 285 If CommandString is NULL, then ASSERT(). 286 If GetHelpInfoHandler is NULL, then ASSERT(). 287 If CommandHandler is NULL, then ASSERT(). 288 If ProfileName is NULL, then ASSERT(). 289 290 @param[in] CommandString Pointer to the command name. This is the 291 name to look for on the command line in 292 the shell. 293 @param[in] CommandHandler Pointer to a function that runs the 294 specified command. 295 @param[in] GetManFileName Pointer to a function that provides man 296 filename. 297 @param[in] ShellMinSupportLevel minimum Shell Support Level which has this 298 function. 299 @param[in] ProfileName profile name to require for support of this 300 function. 301 @param[in] CanAffectLE indicates whether this command's return value 302 can change the LASTERROR environment variable. 303 @param[in] HiiHandle Handle of this command's HII entry. 304 @param[in] ManFormatHelp HII locator for the help text. 305 306 @retval RETURN_SUCCESS The handlers were registered. 307 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to 308 register the shell command. 309 @retval RETURN_UNSUPPORTED the ShellMinSupportLevel was higher than the 310 currently allowed support level. 311 @retval RETURN_ALREADY_STARTED The CommandString represents a command that 312 is already registered. Only 1 handler set for 313 a given command is allowed. 314 @sa SHELL_GET_MAN_FILENAME 315 @sa SHELL_RUN_COMMAND 316**/ 317RETURN_STATUS 318EFIAPI 319ShellCommandRegisterCommandName ( 320 IN CONST CHAR16 *CommandString, 321 IN SHELL_RUN_COMMAND CommandHandler, 322 IN SHELL_GET_MAN_FILENAME GetManFileName, 323 IN UINT32 ShellMinSupportLevel, 324 IN CONST CHAR16 *ProfileName, 325 IN CONST BOOLEAN CanAffectLE, 326 IN CONST EFI_HANDLE HiiHandle, 327 IN CONST EFI_STRING_ID ManFormatHelp 328 ) 329{ 330 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 331 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command; 332 SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand; 333 INTN LexicalMatchValue; 334 335 // 336 // Initialize local variables. 337 // 338 Command = NULL; 339 PrevCommand = NULL; 340 LexicalMatchValue = 0; 341 342 // 343 // ASSERTs for NULL parameters 344 // 345 ASSERT(CommandString != NULL); 346 ASSERT(GetManFileName != NULL); 347 ASSERT(CommandHandler != NULL); 348 ASSERT(ProfileName != NULL); 349 350 // 351 // check for shell support level 352 // 353 if (PcdGet8(PcdShellSupportLevel) < ShellMinSupportLevel) { 354 return (RETURN_UNSUPPORTED); 355 } 356 357 // 358 // check for already on the list 359 // 360 if (ShellCommandIsCommandOnList(CommandString)) { 361 return (RETURN_ALREADY_STARTED); 362 } 363 364 // 365 // allocate memory for new struct 366 // 367 Node = AllocateZeroPool(sizeof(SHELL_COMMAND_INTERNAL_LIST_ENTRY)); 368 ASSERT(Node != NULL); 369 Node->CommandString = AllocateZeroPool(StrSize(CommandString)); 370 ASSERT(Node->CommandString != NULL); 371 372 // 373 // populate the new struct 374 // 375 StrCpy(Node->CommandString, CommandString); 376 377 Node->GetManFileName = GetManFileName; 378 Node->CommandHandler = CommandHandler; 379 Node->LastError = CanAffectLE; 380 Node->HiiHandle = HiiHandle; 381 Node->ManFormatHelp = ManFormatHelp; 382 383 if ( StrLen(ProfileName)>0 384 && ((mProfileList != NULL 385 && StrStr(mProfileList, ProfileName) == NULL) || mProfileList == NULL) 386 ){ 387 ASSERT((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL)); 388 if (mProfileList == NULL) { 389 // 390 // If this is the first make a leading ';' 391 // 392 StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0); 393 } 394 StrnCatGrow(&mProfileList, &mProfileListSize, ProfileName, 0); 395 StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0); 396 } 397 398 // 399 // Insert a new entry on top of the list 400 // 401 InsertHeadList (&mCommandList.Link, &Node->Link); 402 403 // 404 // Move a new registered command to its sorted ordered location in the list 405 // 406 for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link), 407 PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link) 408 ; !IsNull (&mCommandList.Link, &Command->Link) 409 ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link)) { 410 411 // 412 // Get Lexical Comparison Value between PrevCommand and Command list entry 413 // 414 LexicalMatchValue = gUnicodeCollation->StriColl ( 415 gUnicodeCollation, 416 PrevCommand->CommandString, 417 Command->CommandString 418 ); 419 420 // 421 // Swap PrevCommand and Command list entry if PrevCommand list entry 422 // is alphabetically greater than Command list entry 423 // 424 if (LexicalMatchValue > 0){ 425 Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *) SwapListEntries (&PrevCommand->Link, &Command->Link); 426 } else if (LexicalMatchValue < 0) { 427 // 428 // PrevCommand entry is lexically lower than Command entry 429 // 430 break; 431 } 432 } 433 434 return (RETURN_SUCCESS); 435} 436 437/** 438 Function to get the current Profile string. 439 440 @retval NULL There are no installed profiles. 441 @return A semi-colon delimited list of profiles. 442**/ 443CONST CHAR16 * 444EFIAPI 445ShellCommandGetProfileList ( 446 VOID 447 ) 448{ 449 return (mProfileList); 450} 451 452/** 453 Checks if a command string has been registered for CommandString and if so it runs 454 the previously registered handler for that command with the command line. 455 456 If CommandString is NULL, then ASSERT(). 457 458 If Sections is specified, then each section name listed will be compared in a casesensitive 459 manner, to the section names described in Appendix B UEFI Shell 2.0 spec. If the section exists, 460 it will be appended to the returned help text. If the section does not exist, no 461 information will be returned. If Sections is NULL, then all help text information 462 available will be returned. 463 464 @param[in] CommandString Pointer to the command name. This is the name 465 found on the command line in the shell. 466 @param[in, out] RetVal Pointer to the return vaule from the command handler. 467 468 @param[in, out] CanAffectLE indicates whether this command's return value 469 needs to be placed into LASTERROR environment variable. 470 471 @retval RETURN_SUCCESS The handler was run. 472 @retval RETURN_NOT_FOUND The CommandString did not match a registered 473 command name. 474 @sa SHELL_RUN_COMMAND 475**/ 476RETURN_STATUS 477EFIAPI 478ShellCommandRunCommandHandler ( 479 IN CONST CHAR16 *CommandString, 480 IN OUT SHELL_STATUS *RetVal, 481 IN OUT BOOLEAN *CanAffectLE OPTIONAL 482 ) 483{ 484 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 485 486 // 487 // assert for NULL parameters 488 // 489 ASSERT(CommandString != NULL); 490 491 // 492 // check for the command 493 // 494 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link) 495 ; !IsNull(&mCommandList.Link, &Node->Link) 496 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link) 497 ){ 498 ASSERT(Node->CommandString != NULL); 499 if (gUnicodeCollation->StriColl( 500 gUnicodeCollation, 501 (CHAR16*)CommandString, 502 Node->CommandString) == 0 503 ){ 504 if (CanAffectLE != NULL) { 505 *CanAffectLE = Node->LastError; 506 } 507 if (RetVal != NULL) { 508 *RetVal = Node->CommandHandler(NULL, gST); 509 } else { 510 Node->CommandHandler(NULL, gST); 511 } 512 return (RETURN_SUCCESS); 513 } 514 } 515 return (RETURN_NOT_FOUND); 516} 517 518/** 519 Checks if a command string has been registered for CommandString and if so it 520 returns the MAN filename specified for that command. 521 522 If CommandString is NULL, then ASSERT(). 523 524 @param[in] CommandString Pointer to the command name. This is the name 525 found on the command line in the shell.\ 526 527 @retval NULL the commandString was not a registered command. 528 @return other the name of the MAN file. 529 @sa SHELL_GET_MAN_FILENAME 530**/ 531CONST CHAR16* 532EFIAPI 533ShellCommandGetManFileNameHandler ( 534 IN CONST CHAR16 *CommandString 535 ) 536{ 537 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 538 539 // 540 // assert for NULL parameters 541 // 542 ASSERT(CommandString != NULL); 543 544 // 545 // check for the command 546 // 547 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link) 548 ; !IsNull(&mCommandList.Link, &Node->Link) 549 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link) 550 ){ 551 ASSERT(Node->CommandString != NULL); 552 if (gUnicodeCollation->StriColl( 553 gUnicodeCollation, 554 (CHAR16*)CommandString, 555 Node->CommandString) == 0 556 ){ 557 return (Node->GetManFileName()); 558 } 559 } 560 return (NULL); 561} 562 563/** 564 Get the list of all available shell internal commands. This is a linked list 565 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked 566 list functions. do not modify the values. 567 568 @param[in] Sort TRUE to alphabetically sort the values first. FALSE otherwise. 569 570 @return a Linked list of all available shell commands. 571**/ 572CONST COMMAND_LIST* 573EFIAPI 574ShellCommandGetCommandList ( 575 IN CONST BOOLEAN Sort 576 ) 577{ 578// if (!Sort) { 579// return ((COMMAND_LIST*)(&mCommandList)); 580// } 581 return ((COMMAND_LIST*)(&mCommandList)); 582} 583 584/** 585 Registers aliases to be set as part of the initialization of the shell application. 586 587 If Command is NULL, then ASSERT(). 588 If Alias is NULL, then ASSERT(). 589 590 @param[in] Command Pointer to the Command 591 @param[in] Alias Pointer to Alias 592 593 @retval RETURN_SUCCESS The handlers were registered. 594 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to 595 register the shell command. 596**/ 597RETURN_STATUS 598EFIAPI 599ShellCommandRegisterAlias ( 600 IN CONST CHAR16 *Command, 601 IN CONST CHAR16 *Alias 602 ) 603{ 604 ALIAS_LIST *Node; 605 606 // 607 // Asserts for NULL 608 // 609 ASSERT(Command != NULL); 610 ASSERT(Alias != NULL); 611 612 // 613 // allocate memory for new struct 614 // 615 Node = AllocateZeroPool(sizeof(ALIAS_LIST)); 616 ASSERT(Node != NULL); 617 Node->CommandString = AllocateZeroPool(StrSize(Command)); 618 Node->Alias = AllocateZeroPool(StrSize(Alias)); 619 ASSERT(Node->CommandString != NULL); 620 ASSERT(Node->Alias != NULL); 621 622 // 623 // populate the new struct 624 // 625 StrCpy(Node->CommandString, Command); 626 StrCpy(Node->Alias , Alias ); 627 628 // 629 // add the new struct to the list 630 // 631 InsertTailList (&mAliasList.Link, &Node->Link); 632 633 return (RETURN_SUCCESS); 634} 635 636/** 637 Get the list of all shell alias commands. This is a linked list 638 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked 639 list functions. do not modify the values. 640 641 @return a Linked list of all requested shell alias'. 642**/ 643CONST ALIAS_LIST* 644EFIAPI 645ShellCommandGetInitAliasList ( 646 VOID 647 ) 648{ 649 return (&mAliasList); 650} 651 652/** 653 Determine if a given alias is on the list of built in alias'. 654 655 @param[in] Alias The alias to test for 656 657 @retval TRUE The alias is a built in alias 658 @retval FALSE The alias is not a built in alias 659**/ 660BOOLEAN 661EFIAPI 662ShellCommandIsOnAliasList( 663 IN CONST CHAR16 *Alias 664 ) 665{ 666 ALIAS_LIST *Node; 667 668 // 669 // assert for NULL parameter 670 // 671 ASSERT(Alias != NULL); 672 673 // 674 // check for the Alias 675 // 676 for ( Node = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link) 677 ; !IsNull(&mAliasList.Link, &Node->Link) 678 ; Node = (ALIAS_LIST *)GetNextNode(&mAliasList.Link, &Node->Link) 679 ){ 680 ASSERT(Node->CommandString != NULL); 681 ASSERT(Node->Alias != NULL); 682 if (gUnicodeCollation->StriColl( 683 gUnicodeCollation, 684 (CHAR16*)Alias, 685 Node->CommandString) == 0 686 ){ 687 return (TRUE); 688 } 689 if (gUnicodeCollation->StriColl( 690 gUnicodeCollation, 691 (CHAR16*)Alias, 692 Node->Alias) == 0 693 ){ 694 return (TRUE); 695 } 696 } 697 return (FALSE); 698} 699 700/** 701 Function to determine current state of ECHO. Echo determins if lines from scripts 702 and ECHO commands are enabled. 703 704 @retval TRUE Echo is currently enabled 705 @retval FALSE Echo is currently disabled 706**/ 707BOOLEAN 708EFIAPI 709ShellCommandGetEchoState( 710 VOID 711 ) 712{ 713 return (mEchoState); 714} 715 716/** 717 Function to set current state of ECHO. Echo determins if lines from scripts 718 and ECHO commands are enabled. 719 720 If State is TRUE, Echo will be enabled. 721 If State is FALSE, Echo will be disabled. 722 723 @param[in] State How to set echo. 724**/ 725VOID 726EFIAPI 727ShellCommandSetEchoState( 728 IN BOOLEAN State 729 ) 730{ 731 mEchoState = State; 732} 733 734/** 735 Indicate that the current shell or script should exit. 736 737 @param[in] ScriptOnly TRUE if exiting a script; FALSE otherwise. 738 @param[in] ErrorCode The 64 bit error code to return. 739**/ 740VOID 741EFIAPI 742ShellCommandRegisterExit ( 743 IN BOOLEAN ScriptOnly, 744 IN CONST UINT64 ErrorCode 745 ) 746{ 747 mExitRequested = (BOOLEAN)(!mExitRequested); 748 if (mExitRequested) { 749 mExitScript = ScriptOnly; 750 } else { 751 mExitScript = FALSE; 752 } 753 mExitCode = ErrorCode; 754} 755 756/** 757 Retrieve the Exit indicator. 758 759 @retval TRUE Exit was indicated. 760 @retval FALSE Exis was not indicated. 761**/ 762BOOLEAN 763EFIAPI 764ShellCommandGetExit ( 765 VOID 766 ) 767{ 768 return (mExitRequested); 769} 770 771/** 772 Retrieve the Exit code. 773 774 If ShellCommandGetExit returns FALSE than the return from this is undefined. 775 776 @return the value passed into RegisterExit. 777**/ 778UINT64 779EFIAPI 780ShellCommandGetExitCode ( 781 VOID 782 ) 783{ 784 return (mExitCode); 785} 786/** 787 Retrieve the Exit script indicator. 788 789 If ShellCommandGetExit returns FALSE than the return from this is undefined. 790 791 @retval TRUE ScriptOnly was indicated. 792 @retval FALSE ScriptOnly was not indicated. 793**/ 794BOOLEAN 795EFIAPI 796ShellCommandGetScriptExit ( 797 VOID 798 ) 799{ 800 return (mExitScript); 801} 802 803/** 804 Function to cleanup all memory from a SCRIPT_FILE structure. 805 806 @param[in] Script The pointer to the structure to cleanup. 807**/ 808VOID 809EFIAPI 810DeleteScriptFileStruct ( 811 IN SCRIPT_FILE *Script 812 ) 813{ 814 UINT8 LoopVar; 815 816 if (Script == NULL) { 817 return; 818 } 819 820 for (LoopVar = 0 ; LoopVar < Script->Argc ; LoopVar++) { 821 SHELL_FREE_NON_NULL(Script->Argv[LoopVar]); 822 } 823 if (Script->Argv != NULL) { 824 SHELL_FREE_NON_NULL(Script->Argv); 825 } 826 Script->CurrentCommand = NULL; 827 while (!IsListEmpty (&Script->CommandList)) { 828 Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&Script->CommandList); 829 if (Script->CurrentCommand != NULL) { 830 RemoveEntryList(&Script->CurrentCommand->Link); 831 if (Script->CurrentCommand->Cl != NULL) { 832 SHELL_FREE_NON_NULL(Script->CurrentCommand->Cl); 833 } 834 if (Script->CurrentCommand->Data != NULL) { 835 SHELL_FREE_NON_NULL(Script->CurrentCommand->Data); 836 } 837 SHELL_FREE_NON_NULL(Script->CurrentCommand); 838 } 839 } 840 SHELL_FREE_NON_NULL(Script->ScriptName); 841 SHELL_FREE_NON_NULL(Script); 842} 843 844/** 845 Function to return a pointer to the currently running script file object. 846 847 @retval NULL A script file is not currently running. 848 @return A pointer to the current script file object. 849**/ 850SCRIPT_FILE* 851EFIAPI 852ShellCommandGetCurrentScriptFile ( 853 VOID 854 ) 855{ 856 SCRIPT_FILE_LIST *List; 857 if (IsListEmpty (&mScriptList.Link)) { 858 return (NULL); 859 } 860 List = ((SCRIPT_FILE_LIST*)GetFirstNode(&mScriptList.Link)); 861 return (List->Data); 862} 863 864/** 865 Function to set a new script as the currently running one. 866 867 This function will correctly stack and unstack nested scripts. 868 869 @param[in] Script Pointer to new script information structure. if NULL 870 will remove and de-allocate the top-most Script structure. 871 872 @return A pointer to the current running script file after this 873 change. NULL if removing the final script. 874**/ 875SCRIPT_FILE* 876EFIAPI 877ShellCommandSetNewScript ( 878 IN SCRIPT_FILE *Script OPTIONAL 879 ) 880{ 881 SCRIPT_FILE_LIST *Node; 882 if (Script == NULL) { 883 if (IsListEmpty (&mScriptList.Link)) { 884 return (NULL); 885 } 886 Node = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link); 887 RemoveEntryList(&Node->Link); 888 DeleteScriptFileStruct(Node->Data); 889 FreePool(Node); 890 } else { 891 Node = AllocateZeroPool(sizeof(SCRIPT_FILE_LIST)); 892 if (Node == NULL) { 893 return (NULL); 894 } 895 Node->Data = Script; 896 InsertHeadList(&mScriptList.Link, &Node->Link); 897 } 898 return (ShellCommandGetCurrentScriptFile()); 899} 900 901/** 902 Function to generate the next default mapping name. 903 904 If the return value is not NULL then it must be callee freed. 905 906 @param Type What kind of mapping name to make. 907 908 @retval NULL a memory allocation failed. 909 @return a new map name string 910**/ 911CHAR16* 912EFIAPI 913ShellCommandCreateNewMappingName( 914 IN CONST SHELL_MAPPING_TYPE Type 915 ) 916{ 917 CHAR16 *String; 918 ASSERT(Type < MappingTypeMax); 919 920 String = NULL; 921 922 String = AllocateZeroPool(PcdGet8(PcdShellMapNameLength) * sizeof(String[0])); 923 UnicodeSPrint( 924 String, 925 PcdGet8(PcdShellMapNameLength) * sizeof(String[0]), 926 Type == MappingTypeFileSystem?L"FS%d:":L"BLK%d:", 927 Type == MappingTypeFileSystem?mFsMaxCount++:mBlkMaxCount++); 928 929 return (String); 930} 931 932/** 933 Function to add a map node to the list of map items and update the "path" environment variable (optionally). 934 935 If Path is TRUE (during initialization only), the path environment variable will also be updated to include 936 default paths on the new map name... 937 938 Path should be FALSE when this function is called from the protocol SetMap function. 939 940 @param[in] Name The human readable mapped name. 941 @param[in] DevicePath The Device Path for this map. 942 @param[in] Flags The Flags attribute for this map item. 943 @param[in] Path TRUE to update path, FALSE to skip this step (should only be TRUE during initialization). 944 945 @retval EFI_SUCCESS The addition was sucessful. 946 @retval EFI_OUT_OF_RESOURCES A memory allocation failed. 947 @retval EFI_INVALID_PARAMETER A parameter was invalid. 948**/ 949EFI_STATUS 950EFIAPI 951ShellCommandAddMapItemAndUpdatePath( 952 IN CONST CHAR16 *Name, 953 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, 954 IN CONST UINT64 Flags, 955 IN CONST BOOLEAN Path 956 ) 957{ 958 EFI_STATUS Status; 959 SHELL_MAP_LIST *MapListNode; 960 CONST CHAR16 *OriginalPath; 961 CHAR16 *NewPath; 962 UINTN NewPathSize; 963 964 NewPathSize = 0; 965 NewPath = NULL; 966 OriginalPath = NULL; 967 Status = EFI_SUCCESS; 968 969 MapListNode = AllocateZeroPool(sizeof(SHELL_MAP_LIST)); 970 if (MapListNode == NULL) { 971 Status = EFI_OUT_OF_RESOURCES; 972 } else { 973 MapListNode->Flags = Flags; 974 MapListNode->MapName = AllocateZeroPool(StrSize(Name)); 975 MapListNode->DevicePath = DuplicateDevicePath(DevicePath); 976 if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)){ 977 Status = EFI_OUT_OF_RESOURCES; 978 } else { 979 StrCpy(MapListNode->MapName, Name); 980 InsertTailList(&gShellMapList.Link, &MapListNode->Link); 981 } 982 } 983 if (EFI_ERROR(Status)) { 984 if (MapListNode != NULL) { 985 if (MapListNode->DevicePath != NULL) { 986 FreePool(MapListNode->DevicePath); 987 } 988 if (MapListNode->MapName != NULL) { 989 FreePool(MapListNode->MapName); 990 } 991 FreePool(MapListNode); 992 } 993 } else if (Path) { 994 // 995 // Since there was no error and Path was TRUE 996 // Now add the correct path for that mapping 997 // 998 OriginalPath = gEfiShellProtocol->GetEnv(L"path"); 999 ASSERT((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL)); 1000 if (OriginalPath != NULL) { 1001 StrnCatGrow(&NewPath, &NewPathSize, OriginalPath, 0); 1002 } else { 1003 StrnCatGrow(&NewPath, &NewPathSize, L".\\", 0); 1004 } 1005 StrnCatGrow(&NewPath, &NewPathSize, L";", 0); 1006 StrnCatGrow(&NewPath, &NewPathSize, Name, 0); 1007 StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0); 1008 StrnCatGrow(&NewPath, &NewPathSize, Name, 0); 1009 StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0); 1010 StrnCatGrow(&NewPath, &NewPathSize, Name, 0); 1011 StrnCatGrow(&NewPath, &NewPathSize, L"\\", 0); 1012 1013 Status = gEfiShellProtocol->SetEnv(L"path", NewPath, TRUE); 1014 ASSERT_EFI_ERROR(Status); 1015 FreePool(NewPath); 1016 } 1017 return (Status); 1018} 1019 1020/** 1021 Creates the default map names for each device path in the system with 1022 a protocol depending on the Type. 1023 1024 Creates the consistent map names for each device path in the system with 1025 a protocol depending on the Type. 1026 1027 Note: This will reset all mappings in the system("map -r"). 1028 1029 Also sets up the default path environment variable if Type is FileSystem. 1030 1031 @retval EFI_SUCCESS All map names were created sucessfully. 1032 @retval EFI_NOT_FOUND No protocols were found in the system. 1033 @return Error returned from gBS->LocateHandle(). 1034 1035 @sa LocateHandle 1036**/ 1037EFI_STATUS 1038EFIAPI 1039ShellCommandCreateInitialMappingsAndPaths( 1040 VOID 1041 ) 1042{ 1043 EFI_STATUS Status; 1044 EFI_HANDLE *HandleList; 1045 UINTN Count; 1046 EFI_DEVICE_PATH_PROTOCOL **DevicePathList; 1047 CHAR16 *NewDefaultName; 1048 CHAR16 *NewConsistName; 1049 EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable; 1050 SHELL_MAP_LIST *MapListNode; 1051 1052 HandleList = NULL; 1053 1054 // 1055 // Reset the static members back to zero 1056 // 1057 mFsMaxCount = 0; 1058 mBlkMaxCount = 0; 1059 1060 gEfiShellProtocol->SetEnv(L"path", L"", TRUE); 1061 1062 // 1063 // First empty out the existing list. 1064 // 1065 if (!IsListEmpty(&gShellMapList.Link)) { 1066 for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) 1067 ; !IsListEmpty(&gShellMapList.Link) 1068 ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) 1069 ){ 1070 RemoveEntryList(&MapListNode->Link); 1071 FreePool(MapListNode); 1072 } // for loop 1073 } 1074 1075 // 1076 // Find each handle with Simple File System 1077 // 1078 HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid); 1079 if (HandleList != NULL) { 1080 // 1081 // Do a count of the handles 1082 // 1083 for (Count = 0 ; HandleList[Count] != NULL ; Count++); 1084 1085 // 1086 // Get all Device Paths 1087 // 1088 DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count); 1089 ASSERT(DevicePathList != NULL); 1090 1091 for (Count = 0 ; HandleList[Count] != NULL ; Count++) { 1092 DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]); 1093 } 1094 1095 // 1096 // Sort all DevicePaths 1097 // 1098 PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare); 1099 1100 ShellCommandConsistMappingInitialize(&ConsistMappingTable); 1101 // 1102 // Assign new Mappings to all... 1103 // 1104 for (Count = 0 ; HandleList[Count] != NULL ; Count++) { 1105 // 1106 // Get default name first 1107 // 1108 NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem); 1109 ASSERT(NewDefaultName != NULL); 1110 Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, TRUE); 1111 ASSERT_EFI_ERROR(Status); 1112 FreePool(NewDefaultName); 1113 1114 // 1115 // Now do consistent name 1116 // 1117 NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable); 1118 if (NewConsistName != NULL) { 1119 Status = ShellCommandAddMapItemAndUpdatePath(NewConsistName, DevicePathList[Count], 0, FALSE); 1120 ASSERT_EFI_ERROR(Status); 1121 FreePool(NewConsistName); 1122 } 1123 } 1124 1125 ShellCommandConsistMappingUnInitialize(ConsistMappingTable); 1126 1127 SHELL_FREE_NON_NULL(HandleList); 1128 SHELL_FREE_NON_NULL(DevicePathList); 1129 1130 HandleList = NULL; 1131 } else { 1132 Count = (UINTN)-1; 1133 } 1134 1135 // 1136 // Find each handle with Block Io 1137 // 1138 HandleList = GetHandleListByProtocol(&gEfiBlockIoProtocolGuid); 1139 if (HandleList != NULL) { 1140 for (Count = 0 ; HandleList[Count] != NULL ; Count++); 1141 1142 // 1143 // Get all Device Paths 1144 // 1145 DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count); 1146 ASSERT(DevicePathList != NULL); 1147 1148 for (Count = 0 ; HandleList[Count] != NULL ; Count++) { 1149 DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]); 1150 } 1151 1152 // 1153 // Sort all DevicePaths 1154 // 1155 PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare); 1156 1157 // 1158 // Assign new Mappings to all... 1159 // 1160 for (Count = 0 ; HandleList[Count] != NULL ; Count++) { 1161 // 1162 // Get default name first 1163 // 1164 NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeBlockIo); 1165 ASSERT(NewDefaultName != NULL); 1166 Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, FALSE); 1167 ASSERT_EFI_ERROR(Status); 1168 FreePool(NewDefaultName); 1169 } 1170 1171 SHELL_FREE_NON_NULL(HandleList); 1172 SHELL_FREE_NON_NULL(DevicePathList); 1173 } else if (Count == (UINTN)-1) { 1174 return (EFI_NOT_FOUND); 1175 } 1176 1177 return (EFI_SUCCESS); 1178} 1179 1180/** 1181 Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*. 1182 1183 @param[in] Handle The SHELL_FILE_HANDLE to convert. 1184 1185 @return a EFI_FILE_PROTOCOL* representing the same file. 1186**/ 1187EFI_FILE_PROTOCOL* 1188EFIAPI 1189ConvertShellHandleToEfiFileProtocol( 1190 IN CONST SHELL_FILE_HANDLE Handle 1191 ) 1192{ 1193 return ((EFI_FILE_PROTOCOL*)(Handle)); 1194} 1195 1196/** 1197 Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE. 1198 1199 @param[in] Handle The pointer to EFI_FILE_PROTOCOL to convert. 1200 @param[in] Path The path to the file for verification. 1201 1202 @return A SHELL_FILE_HANDLE representing the same file. 1203 @retval NULL There was not enough memory. 1204**/ 1205SHELL_FILE_HANDLE 1206EFIAPI 1207ConvertEfiFileProtocolToShellHandle( 1208 IN CONST EFI_FILE_PROTOCOL *Handle, 1209 IN CONST CHAR16 *Path 1210 ) 1211{ 1212 SHELL_COMMAND_FILE_HANDLE *Buffer; 1213 BUFFER_LIST *NewNode; 1214 1215 if (Path != NULL) { 1216 Buffer = AllocateZeroPool(sizeof(SHELL_COMMAND_FILE_HANDLE)); 1217 if (Buffer == NULL) { 1218 return (NULL); 1219 } 1220 NewNode = AllocateZeroPool(sizeof(BUFFER_LIST)); 1221 if (NewNode == NULL) { 1222 return (NULL); 1223 } 1224 Buffer->FileHandle = (EFI_FILE_PROTOCOL*)Handle; 1225 Buffer->Path = StrnCatGrow(&Buffer->Path, NULL, Path, 0); 1226 if (Buffer->Path == NULL) { 1227 return (NULL); 1228 } 1229 NewNode->Buffer = Buffer; 1230 1231 InsertHeadList(&mFileHandleList.Link, &NewNode->Link); 1232 } 1233 return ((SHELL_FILE_HANDLE)(Handle)); 1234} 1235 1236/** 1237 Find the path that was logged with the specified SHELL_FILE_HANDLE. 1238 1239 @param[in] Handle The SHELL_FILE_HANDLE to query on. 1240 1241 @return A pointer to the path for the file. 1242**/ 1243CONST CHAR16* 1244EFIAPI 1245ShellFileHandleGetPath( 1246 IN CONST SHELL_FILE_HANDLE Handle 1247 ) 1248{ 1249 BUFFER_LIST *Node; 1250 1251 for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link) 1252 ; !IsNull(&mFileHandleList.Link, &Node->Link) 1253 ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link) 1254 ){ 1255 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){ 1256 return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path); 1257 } 1258 } 1259 return (NULL); 1260} 1261 1262/** 1263 Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES. 1264 1265 @param[in] Handle The SHELL_FILE_HANDLE to remove. 1266 1267 @retval TRUE The item was removed. 1268 @retval FALSE The item was not found. 1269**/ 1270BOOLEAN 1271EFIAPI 1272ShellFileHandleRemove( 1273 IN CONST SHELL_FILE_HANDLE Handle 1274 ) 1275{ 1276 BUFFER_LIST *Node; 1277 1278 for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link) 1279 ; !IsNull(&mFileHandleList.Link, &Node->Link) 1280 ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link) 1281 ){ 1282 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){ 1283 RemoveEntryList(&Node->Link); 1284 SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path); 1285 SHELL_FREE_NON_NULL(Node->Buffer); 1286 SHELL_FREE_NON_NULL(Node); 1287 return (TRUE); 1288 } 1289 } 1290 return (FALSE); 1291} 1292 1293/** 1294 Function to determine if a SHELL_FILE_HANDLE is at the end of the file. 1295 1296 This will NOT work on directories. 1297 1298 If Handle is NULL, then ASSERT. 1299 1300 @param[in] Handle the file handle 1301 1302 @retval TRUE the position is at the end of the file 1303 @retval FALSE the position is not at the end of the file 1304**/ 1305BOOLEAN 1306EFIAPI 1307ShellFileHandleEof( 1308 IN SHELL_FILE_HANDLE Handle 1309 ) 1310{ 1311 EFI_FILE_INFO *Info; 1312 UINT64 Pos; 1313 BOOLEAN RetVal; 1314 1315 // 1316 // ASSERT if Handle is NULL 1317 // 1318 ASSERT(Handle != NULL); 1319 1320 gEfiShellProtocol->GetFilePosition(Handle, &Pos); 1321 Info = gEfiShellProtocol->GetFileInfo (Handle); 1322 ASSERT(Info != NULL); 1323 gEfiShellProtocol->SetFilePosition(Handle, Pos); 1324 1325 if (Info == NULL) { 1326 return (FALSE); 1327 } 1328 1329 if (Pos == Info->FileSize) { 1330 RetVal = TRUE; 1331 } else { 1332 RetVal = FALSE; 1333 } 1334 1335 FreePool (Info); 1336 1337 return (RetVal); 1338} 1339 1340/** 1341 Frees any BUFFER_LIST defined type. 1342 1343 @param[in] List The BUFFER_LIST object to free. 1344**/ 1345VOID 1346EFIAPI 1347FreeBufferList ( 1348 IN BUFFER_LIST *List 1349 ) 1350{ 1351 BUFFER_LIST *BufferListEntry; 1352 1353 if (List == NULL){ 1354 return; 1355 } 1356 // 1357 // enumerate through the buffer list and free all memory 1358 // 1359 for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link) 1360 ; !IsListEmpty (&List->Link) 1361 ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link) 1362 ){ 1363 RemoveEntryList(&BufferListEntry->Link); 1364 ASSERT(BufferListEntry->Buffer != NULL); 1365 if (BufferListEntry->Buffer != NULL) { 1366 FreePool(BufferListEntry->Buffer); 1367 } 1368 FreePool(BufferListEntry); 1369 } 1370} 1371 1372