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