/** @file function declarations for shell environment functions. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Shell.h" #define INIT_NAME_BUFFER_SIZE 128 #define INIT_DATA_BUFFER_SIZE 1024 // // The list is used to cache the environment variables. // ENV_VAR_LIST gShellEnvVarList; /** Reports whether an environment variable is Volatile or Non-Volatile. @param EnvVarName The name of the environment variable in question @param Volatile Return TRUE if the environment variable is volatile @retval EFI_SUCCESS The volatile attribute is returned successfully @retval others Some errors happened. **/ EFI_STATUS IsVolatileEnv ( IN CONST CHAR16 *EnvVarName, OUT BOOLEAN *Volatile ) { EFI_STATUS Status; UINTN Size; VOID *Buffer; UINT32 Attribs; ASSERT (Volatile != NULL); Size = 0; Buffer = NULL; // // get the variable // Status = gRT->GetVariable ( (CHAR16 *)EnvVarName, &gShellVariableGuid, &Attribs, &Size, Buffer ); if (Status == EFI_BUFFER_TOO_SMALL) { Buffer = AllocateZeroPool (Size); if (Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } Status = gRT->GetVariable ( (CHAR16 *)EnvVarName, &gShellVariableGuid, &Attribs, &Size, Buffer ); FreePool (Buffer); } // // not found means volatile // if (Status == EFI_NOT_FOUND) { *Volatile = TRUE; return EFI_SUCCESS; } if (EFI_ERROR (Status)) { return Status; } // // check for the Non Volatile bit // *Volatile = !(BOOLEAN)((Attribs & EFI_VARIABLE_NON_VOLATILE) == EFI_VARIABLE_NON_VOLATILE); return EFI_SUCCESS; } /** free function for ENV_VAR_LIST objects. @param[in] List The pointer to pointer to list. **/ VOID FreeEnvironmentVariableList ( IN LIST_ENTRY *List ) { ENV_VAR_LIST *Node; ASSERT (List != NULL); if (List == NULL) { return; } for ( Node = (ENV_VAR_LIST *)GetFirstNode (List) ; !IsListEmpty (List) ; Node = (ENV_VAR_LIST *)GetFirstNode (List) ) { ASSERT (Node != NULL); RemoveEntryList (&Node->Link); if (Node->Key != NULL) { FreePool (Node->Key); } if (Node->Val != NULL) { FreePool (Node->Val); } FreePool (Node); } } /** Creates a list of all Shell-Guid-based environment variables. @param[in, out] ListHead The pointer to pointer to LIST ENTRY object for storing this list. @retval EFI_SUCCESS the list was created successfully. **/ EFI_STATUS GetEnvironmentVariableList ( IN OUT LIST_ENTRY *ListHead ) { CHAR16 *VariableName; UINTN NameSize; UINTN NameBufferSize; EFI_STATUS Status; EFI_GUID Guid; UINTN ValSize; UINTN ValBufferSize; ENV_VAR_LIST *VarList; if (ListHead == NULL) { return (EFI_INVALID_PARAMETER); } Status = EFI_SUCCESS; ValBufferSize = INIT_DATA_BUFFER_SIZE; NameBufferSize = INIT_NAME_BUFFER_SIZE; VariableName = AllocateZeroPool (NameBufferSize); if (VariableName == NULL) { return (EFI_OUT_OF_RESOURCES); } *VariableName = CHAR_NULL; while (!EFI_ERROR (Status)) { NameSize = NameBufferSize; Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid); if (Status == EFI_NOT_FOUND) { Status = EFI_SUCCESS; break; } else if (Status == EFI_BUFFER_TOO_SMALL) { NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2; SHELL_FREE_NON_NULL (VariableName); VariableName = AllocateZeroPool (NameBufferSize); if (VariableName == NULL) { Status = EFI_OUT_OF_RESOURCES; break; } NameSize = NameBufferSize; Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid); } if (!EFI_ERROR (Status) && CompareGuid (&Guid, &gShellVariableGuid)) { VarList = AllocateZeroPool (sizeof (ENV_VAR_LIST)); if (VarList == NULL) { Status = EFI_OUT_OF_RESOURCES; } else { ValSize = ValBufferSize; // // We need another CHAR16 to save '\0' in VarList->Val. // VarList->Val = AllocateZeroPool (ValSize + sizeof (CHAR16)); if (VarList->Val == NULL) { SHELL_FREE_NON_NULL (VarList); Status = EFI_OUT_OF_RESOURCES; break; } Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (VariableName, &VarList->Atts, &ValSize, VarList->Val); if (Status == EFI_BUFFER_TOO_SMALL) { ValBufferSize = ValSize > ValBufferSize * 2 ? ValSize : ValBufferSize * 2; SHELL_FREE_NON_NULL (VarList->Val); // // We need another CHAR16 to save '\0' in VarList->Val. // VarList->Val = AllocateZeroPool (ValBufferSize + sizeof (CHAR16)); if (VarList->Val == NULL) { SHELL_FREE_NON_NULL (VarList); Status = EFI_OUT_OF_RESOURCES; break; } ValSize = ValBufferSize; Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (VariableName, &VarList->Atts, &ValSize, VarList->Val); } if (!EFI_ERROR (Status)) { VarList->Key = AllocateCopyPool (StrSize (VariableName), VariableName); if (VarList->Key == NULL) { SHELL_FREE_NON_NULL (VarList->Val); SHELL_FREE_NON_NULL (VarList); Status = EFI_OUT_OF_RESOURCES; } else { InsertTailList (ListHead, &VarList->Link); } } else { SHELL_FREE_NON_NULL (VarList->Val); SHELL_FREE_NON_NULL (VarList); } } // if (VarList == NULL) ... else ... } // compare guid } // while SHELL_FREE_NON_NULL (VariableName); if (EFI_ERROR (Status)) { FreeEnvironmentVariableList (ListHead); } return (Status); } /** Sets a list of all Shell-Guid-based environment variables. this will also eliminate all existing shell environment variables (even if they are not on the list). This function will also deallocate the memory from List. @param[in] ListHead The pointer to LIST_ENTRY from GetShellEnvVarList(). @retval EFI_SUCCESS the list was Set successfully. **/ EFI_STATUS SetEnvironmentVariableList ( IN LIST_ENTRY *ListHead ) { ENV_VAR_LIST VarList; ENV_VAR_LIST *Node; EFI_STATUS Status; UINTN Size; InitializeListHead (&VarList.Link); // // Delete all the current environment variables // Status = GetEnvironmentVariableList (&VarList.Link); ASSERT_EFI_ERROR (Status); for ( Node = (ENV_VAR_LIST *)GetFirstNode (&VarList.Link) ; !IsNull (&VarList.Link, &Node->Link) ; Node = (ENV_VAR_LIST *)GetNextNode (&VarList.Link, &Node->Link) ) { if (Node->Key != NULL) { Status = SHELL_DELETE_ENVIRONMENT_VARIABLE (Node->Key); } ASSERT_EFI_ERROR (Status); } FreeEnvironmentVariableList (&VarList.Link); // // set all the variables from the list // for ( Node = (ENV_VAR_LIST *)GetFirstNode (ListHead) ; !IsNull (ListHead, &Node->Link) ; Node = (ENV_VAR_LIST *)GetNextNode (ListHead, &Node->Link) ) { Size = StrSize (Node->Val) - sizeof (CHAR16); if (Node->Atts & EFI_VARIABLE_NON_VOLATILE) { Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV (Node->Key, Size, Node->Val); } else { Status = SHELL_SET_ENVIRONMENT_VARIABLE_V (Node->Key, Size, Node->Val); } ASSERT_EFI_ERROR (Status); } FreeEnvironmentVariableList (ListHead); return (Status); } /** sets a list of all Shell-Guid-based environment variables. @param Environment Points to a NULL-terminated array of environment variables with the format 'x=y', where x is the environment variable name and y is the value. @retval EFI_SUCCESS The command executed successfully. @retval EFI_INVALID_PARAMETER The parameter is invalid. @retval EFI_OUT_OF_RESOURCES Out of resources. @sa SetEnvironmentVariableList **/ EFI_STATUS SetEnvironmentVariables ( IN CONST CHAR16 **Environment ) { CONST CHAR16 *CurrentString; UINTN CurrentCount; ENV_VAR_LIST *VarList; ENV_VAR_LIST *Node; VarList = NULL; if (Environment == NULL) { return (EFI_INVALID_PARAMETER); } // // Build a list identical to the ones used for get/set list functions above // for ( CurrentCount = 0 ; ; CurrentCount++ ) { CurrentString = Environment[CurrentCount]; if (CurrentString == NULL) { break; } ASSERT (StrStr (CurrentString, L"=") != NULL); Node = AllocateZeroPool (sizeof (ENV_VAR_LIST)); if (Node == NULL) { SetEnvironmentVariableList (&VarList->Link); return (EFI_OUT_OF_RESOURCES); } Node->Key = AllocateZeroPool ((StrStr (CurrentString, L"=") - CurrentString + 1) * sizeof (CHAR16)); if (Node->Key == NULL) { SHELL_FREE_NON_NULL (Node); SetEnvironmentVariableList (&VarList->Link); return (EFI_OUT_OF_RESOURCES); } // // Copy the string into the Key, leaving the last character allocated as NULL to terminate // StrnCpyS ( Node->Key, StrStr (CurrentString, L"=") - CurrentString + 1, CurrentString, StrStr (CurrentString, L"=") - CurrentString ); // // ValueSize = TotalSize - already removed size - size for '=' + size for terminator (the last 2 items cancel each other) // Node->Val = AllocateCopyPool (StrSize (CurrentString) - StrSize (Node->Key), CurrentString + StrLen (Node->Key) + 1); if (Node->Val == NULL) { SHELL_FREE_NON_NULL (Node->Key); SHELL_FREE_NON_NULL (Node); SetEnvironmentVariableList (&VarList->Link); return (EFI_OUT_OF_RESOURCES); } Node->Atts = EFI_VARIABLE_BOOTSERVICE_ACCESS; if (VarList == NULL) { VarList = AllocateZeroPool (sizeof (ENV_VAR_LIST)); if (VarList == NULL) { SHELL_FREE_NON_NULL (Node->Key); SHELL_FREE_NON_NULL (Node->Val); SHELL_FREE_NON_NULL (Node); return (EFI_OUT_OF_RESOURCES); } InitializeListHead (&VarList->Link); } InsertTailList (&VarList->Link, &Node->Link); } // for loop // // set this new list as the set of all environment variables. // this function also frees the memory and deletes all pre-existing // shell-guid based environment variables. // return (SetEnvironmentVariableList (&VarList->Link)); } /** Find an environment variable in the gShellEnvVarList. @param Key The name of the environment variable. @param Value The value of the environment variable, the buffer shoule be freed by the caller. @param ValueSize The size in bytes of the environment variable including the tailing CHAR_NELL. @param Atts The attributes of the variable. @retval EFI_SUCCESS The command executed successfully. @retval EFI_NOT_FOUND The environment variable is not found in gShellEnvVarList. **/ EFI_STATUS ShellFindEnvVarInList ( IN CONST CHAR16 *Key, OUT CHAR16 **Value, OUT UINTN *ValueSize, OUT UINT32 *Atts OPTIONAL ) { ENV_VAR_LIST *Node; if ((Key == NULL) || (Value == NULL) || (ValueSize == NULL)) { return SHELL_INVALID_PARAMETER; } for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link) ; !IsNull (&gShellEnvVarList.Link, &Node->Link) ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link) ) { if ((Node->Key != NULL) && (StrCmp (Key, Node->Key) == 0)) { *Value = AllocateCopyPool (StrSize (Node->Val), Node->Val); *ValueSize = StrSize (Node->Val); if (Atts != NULL) { *Atts = Node->Atts; } return EFI_SUCCESS; } } return EFI_NOT_FOUND; } /** Add an environment variable into gShellEnvVarList. @param Key The name of the environment variable. @param Value The value of environment variable. @param ValueSize The size in bytes of the environment variable including the tailing CHAR_NULL @param Atts The attributes of the variable. @retval EFI_SUCCESS The environment variable was added to list successfully. @retval others Some errors happened. **/ EFI_STATUS ShellAddEnvVarToList ( IN CONST CHAR16 *Key, IN CONST CHAR16 *Value, IN UINTN ValueSize, IN UINT32 Atts ) { ENV_VAR_LIST *Node; CHAR16 *LocalKey; CHAR16 *LocalValue; if ((Key == NULL) || (Value == NULL) || (ValueSize == 0)) { return EFI_INVALID_PARAMETER; } LocalValue = AllocateCopyPool (ValueSize, Value); if (LocalValue == NULL) { return EFI_OUT_OF_RESOURCES; } // // Update the variable value if it exists in gShellEnvVarList. // for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link) ; !IsNull (&gShellEnvVarList.Link, &Node->Link) ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link) ) { if ((Node->Key != NULL) && (StrCmp (Key, Node->Key) == 0)) { Node->Atts = Atts; SHELL_FREE_NON_NULL (Node->Val); Node->Val = LocalValue; return EFI_SUCCESS; } } // // If the environment variable key doesn't exist in list just insert // a new node. // LocalKey = AllocateCopyPool (StrSize (Key), Key); if (LocalKey == NULL) { FreePool (LocalValue); return EFI_OUT_OF_RESOURCES; } Node = (ENV_VAR_LIST *)AllocateZeroPool (sizeof (ENV_VAR_LIST)); if (Node == NULL) { FreePool (LocalKey); FreePool (LocalValue); return EFI_OUT_OF_RESOURCES; } Node->Key = LocalKey; Node->Val = LocalValue; Node->Atts = Atts; InsertTailList (&gShellEnvVarList.Link, &Node->Link); return EFI_SUCCESS; } /** Remove a specified environment variable in gShellEnvVarList. @param Key The name of the environment variable. @retval EFI_SUCCESS The command executed successfully. @retval EFI_NOT_FOUND The environment variable is not found in gShellEnvVarList. **/ EFI_STATUS ShellRemvoeEnvVarFromList ( IN CONST CHAR16 *Key ) { ENV_VAR_LIST *Node; if (Key == NULL) { return EFI_INVALID_PARAMETER; } for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link) ; !IsNull (&gShellEnvVarList.Link, &Node->Link) ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link) ) { if ((Node->Key != NULL) && (StrCmp (Key, Node->Key) == 0)) { SHELL_FREE_NON_NULL (Node->Key); SHELL_FREE_NON_NULL (Node->Val); RemoveEntryList (&Node->Link); SHELL_FREE_NON_NULL (Node); return EFI_SUCCESS; } } return EFI_NOT_FOUND; } /** Initialize the gShellEnvVarList and cache all Shell-Guid-based environment variables. **/ EFI_STATUS ShellInitEnvVarList ( VOID ) { EFI_STATUS Status; InitializeListHead (&gShellEnvVarList.Link); Status = GetEnvironmentVariableList (&gShellEnvVarList.Link); return Status; } /** Destructe the gShellEnvVarList. **/ VOID ShellFreeEnvVarList ( VOID ) { FreeEnvironmentVariableList (&gShellEnvVarList.Link); InitializeListHead (&gShellEnvVarList.Link); return; }