Skip to content

Commit

Permalink
Add new API to set envar while specifying overwrite (#473)
Browse files Browse the repository at this point in the history
* Add rcutils_set_env_overwrite api

Signed-off-by: Yadunund <yadunund@intrinsic.ai>

* Add some tests

Signed-off-by: Yadunund <yadunund@intrinsic.ai>

---------

Signed-off-by: Yadunund <yadunund@intrinsic.ai>
  • Loading branch information
Yadunund authored Sep 16, 2024
1 parent 7fdf30c commit ea3675f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 12 deletions.
30 changes: 30 additions & 0 deletions include/rcutils/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,36 @@ RCUTILS_WARN_UNUSED
bool
rcutils_set_env(const char * env_name, const char * env_value);

/// Set or un-set a process-scoped environment variable while specifying overwrite behavior.
/**
* This function modifies the environment variables for the current process by
* copying given string values into the process' global environment variable
* store.
*
* \par Thread Safety:
* This function is not thread-safe. Take care not to modify the environment variables while
* another thread might be reading or writing environment variables.
*
* \par Platform Consistency:
* The behavior when setting a variable to an empty string (`""`) differs
* between platforms. On Windows, the variable is un-set (as if \p env_value was
* `NULL`), while on other platforms the variable is set to an empty string as
* expected.
*
* \param[in] env_name Name of the environment variable to modify.
* \param[in] env_value Value to set the environment variable to, or `NULL` to
* un-set.
* \param[in] overwrite If true, the environemnt variable value will not be overwritten
* if previously set.
* \return `true` if success, or
* \return `false` if env_name is invalid or NULL, or
* \return `false` on failure.
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
bool
rcutils_set_env_overwrite(const char * env_name, const char * env_value, bool overwrite);

/// Retrieve the value of the given environment variable if it exists, or "".
/** The c-string which is returned in the env_value output parameter is only
* valid until the next time this function is called, because it is a direct
Expand Down
31 changes: 19 additions & 12 deletions src/env.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,41 @@ extern "C"

bool
rcutils_set_env(const char * env_name, const char * env_value)
{
return rcutils_set_env_overwrite(env_name, env_value, true);
}

bool
rcutils_set_env_overwrite(const char * env_name, const char * env_value, bool overwrite)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(false);

RCUTILS_CHECK_FOR_NULL_WITH_MSG(
env_name, "env_name is null", return false);

if ((int)overwrite == 0 && getenv(env_name) != NULL) {
return true;
}

int set_ret;
#ifdef _WIN32
if (NULL == env_value) {
env_value = "";
}
if (0 != _putenv_s(env_name, env_value)) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("_putenv_s failed: %d", errno);
return false;
}
set_ret = _putenv_s(env_name, env_value);
#else
if (NULL == env_value) {
if (0 != unsetenv(env_name)) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("unsetenv failed: %d", errno);
return false;
}
set_ret = unsetenv(env_name);
} else {
if (0 != setenv(env_name, env_value, 1)) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("setenv failed: %d", errno);
return false;
}
set_ret = setenv(env_name, env_value, (int) overwrite);
}
#endif

if (set_ret != 0) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("setting environment variable failed: %d", errno);
return false;
}

return true;
}

Expand Down
26 changes: 26 additions & 0 deletions test/test_env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,32 @@ TEST(TestEnv, test_get_env) {
EXPECT_STREQ("", env);
}

/* Tests rcutils_get_env.
*
* Expected environment variables must be set by the calling code:
*
* - EMPTY_TEST=
* - NORMAL_TEST=foo
*
* These are set in the call to `ament_add_gtest()` in the `CMakeLists.txt`.
*/
TEST(TestEnv, test_set_env_overwrite) {
const char * env;
const char * ret;

// Do not overwrite environment variable if preset if overwrite is set false.
EXPECT_TRUE(rcutils_set_env_overwrite("NORMAL_TEST", "NewEnvValue", false));
ret = rcutils_get_env("NORMAL_TEST", &env);
EXPECT_TRUE(NULL == ret);
EXPECT_STREQ("foo", env);

// Overwrite environment variable if present if overwrite is set true.
EXPECT_TRUE(rcutils_set_env_overwrite("NORMAL_TEST", "NewEnvValue", true));
ret = rcutils_get_env("NORMAL_TEST", &env);
EXPECT_TRUE(NULL == ret);
EXPECT_STREQ("NewEnvValue", env);
}

TEST(TestEnv, test_get_home) {
EXPECT_STRNE(NULL, rcutils_get_home_dir());
const char * home = NULL;
Expand Down

0 comments on commit ea3675f

Please sign in to comment.