/* * Copyright (c) 2022 Lawrence Livermore National Laboratory * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Peter D. Barnes, Jr. */ #include "environment-variable.h" #include "ns3/string.h" #include // std::getenv #include // strlen #include // clog #include // Global functions setenv, unsetenv /** * \file * \ingroup core-environ * Class EnvironmentVariable implementation. */ #ifdef __WIN32__ #include /** * Windows implementation of the POSIX function `setenv()` * * \param [in] var_name The environment variable to set. * Must not be a null-pointer, and must not contain `=`. * \param [in] new_value The new value to set \p var_name to. * Must not by a null pointer or empty. * \param [in] change_flag Must be non-zero to actually change the environment. * \returns 0 if successful, -1 if failed. */ int setenv(const char* var_name, const char* new_value, int change_flag) { std::string variable{var_name}; std::string value{new_value}; // In case arguments are null pointers, return invalid error // Windows does not accept empty environment variables if (variable.empty() || value.empty()) { errno = EINVAL; return -1; } // Posix does not accept '=', so impose that here if (variable.find('=') != std::string::npos) { errno = EINVAL; return -1; } // Change flag equals to zero preserves a pre-existing value if (change_flag == 0) { char* old_value = std::getenv(var_name); if (old_value != nullptr) { return 0; } } // Write new value for the environment variable return _putenv_s(var_name, new_value); } /** * Windows implementation of the POSIX function `unsetenv()` * \param [in] var_name The environment variable to unset and remove from the environment. * \returns 0 if successful, -1 if failed. */ int unsetenv(const char* var_name) { return _putenv_s(var_name, ""); } #endif // __WIN32__ namespace ns3 { /** * \ingroup core-environ * * \def NS_LOCAL_LOG(msg) * File-local logging macro for environment-variable.cc * Our usual Logging doesn't work here because these functions * get called during static initialization of Logging itself. * \param msg The message stream to log * * \def NS_LOCAL_ASSERT(cond, msg) * File-local assert macro for environment-variable.cc * Our usual assert doesn't work here because these functions * get called during static initialization of Logging itself. * \param cond The condition which is asserted to be \c true * \param msg The message stream to log */ #if 0 #define NS_LOCAL_LOG(msg) \ std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): " << msg << std::endl #define NS_LOCAL_ASSERT(cond, msg) \ do \ { \ if (!(cond)) \ { \ NS_LOCAL_LOG("assert failed. cond=\"" << #cond << "\", " << msg); \ } \ } while (false) #else #define NS_LOCAL_LOG(msg) #define NS_LOCAL_ASSERT(cond, msg) #endif /* static */ EnvironmentVariable::DictionaryList& EnvironmentVariable::Instance() { static DictionaryList instance; return instance; } /* static */ void EnvironmentVariable::Clear() { Instance().clear(); } /* static */ std::shared_ptr EnvironmentVariable::GetDictionary(const std::string& envvar, const std::string& delim /* ";" */) { NS_LOCAL_LOG(envvar << ", " << delim); std::shared_ptr dict; auto loc = Instance().find(envvar); if (loc != Instance().end()) { NS_LOCAL_LOG("found envvar in cache"); dict = loc->second; } else { NS_LOCAL_LOG("envvar not in cache, checking environment"); dict = std::make_shared(envvar, delim); Instance().insert({envvar, dict}); } return dict; } /* static */ EnvironmentVariable::KeyFoundType EnvironmentVariable::Get(const std::string& envvar, const std::string& key /* "" */, const std::string& delim /* ";" */) { auto dict = GetDictionary(envvar, delim); return dict->Get(key); } /* static */ bool EnvironmentVariable::Set(const std::string& variable, const std::string& value) { int fail = setenv(variable.c_str(), value.c_str(), 1); return !fail; } /* static */ bool EnvironmentVariable::Unset(const std::string& variable) { int fail = unsetenv(variable.c_str()); return !fail; } EnvironmentVariable::KeyFoundType EnvironmentVariable::Dictionary::Get(const std::string& key) const { NS_LOCAL_LOG(key); if (!m_exists) { return {false, ""}; } if (key.empty()) { // Empty key is request for entire value return {true, m_variable}; } auto loc = m_dict.find(key); if (loc != m_dict.end()) { NS_LOCAL_LOG("found key in dictionary"); NS_LOCAL_LOG("found: key '" << key << "', value: '" << loc->second << "'"); return {true, loc->second}; } // key not found return {false, ""}; } EnvironmentVariable::Dictionary::Dictionary(const std::string& envvar, const std::string& delim /* "=" */) { NS_LOCAL_LOG(envvar << ", " << delim); const char* envCstr = std::getenv(envvar.c_str()); // Returns null pointer if envvar doesn't exist if (!envCstr) { m_exists = false; return; } // So it exists m_exists = true; m_variable = envCstr; NS_LOCAL_LOG("found envvar in environment with value '" << m_variable << "'"); // ...but might be empty if (m_variable.empty()) { return; } StringVector keyvals = SplitString(m_variable, delim); NS_LOCAL_ASSERT(keyvals.empty(), "Unexpected empty keyvals from non-empty m_variable"); for (const auto& keyval : keyvals) { if (keyval.empty()) { continue; } std::size_t equals = keyval.find_first_of('='); std::string key{keyval, 0, equals}; std::string value; if (equals < keyval.size() - 1) { value = keyval.substr(equals + 1, keyval.size()); } NS_LOCAL_LOG("found key '" << key << "' with value '" << value << "'"); m_dict.insert({key, value}); } } EnvironmentVariable::Dictionary::KeyValueStore EnvironmentVariable::Dictionary::GetStore() const { return m_dict; } } // namespace ns3