Skip to content

Commit

Permalink
Add rcutils_set_env function (ros2#250)
Browse files Browse the repository at this point in the history
This change adds a simple function for setting or un-setting an
environment variable for the process.

The existing environment variable functions in rcutils are under a
header called `get_env.h`. It might be good to deprecate that header and
move the functionality to the more generically-named `env.h`.

Signed-off-by: Scott K Logan <logans@cottsay.net>
  • Loading branch information
cottsay committed May 20, 2020
1 parent 54da1c2 commit e5dd0bd
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 1 deletion.
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ set(rcutils_sources
src/array_list.c
src/char_array.c
src/cmdline_parser.c
src/env.c
src/error_handling.c
src/filesystem.c
src/find.c
Expand Down Expand Up @@ -291,6 +292,13 @@ if(BUILD_TESTING)
target_link_libraries(test_get_env ${PROJECT_NAME})
endif()

rcutils_custom_add_gtest(test_env test/test_env.cpp
APPEND_LIBRARY_DIRS "$<TARGET_FILE_DIR:${PROJECT_NAME}>"
)
if(TARGET test_env)
target_link_libraries(test_env ${PROJECT_NAME})
endif()

rcutils_custom_add_gtest(test_filesystem
test/test_filesystem.cpp
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ The API is a combination of parts:
- A convenient string formatting function, which takes a custom allocator:
- rcutils_format_string()
- rcutils/format_string.h
- A function to get an environment variable's value:
- Functions for interfacing with process environment variables:
- rcutils_get_env()
- rcutils_get_home_dir()
- rcutils_set_env()
- rcutils/env.h
- rcutils/get_env.h
- Extensible logging macros:
- Some examples (not exhaustive):
Expand Down
64 changes: 64 additions & 0 deletions include/rcutils/env.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCUTILS__ENV_H_
#define RCUTILS__ENV_H_

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdbool.h>

#include "rcutils/macros.h"
#include "rcutils/visibility_control.h"

// TODO(cottsay): Deprecate get_env.h and eventually merge it here
#include "rcutils/get_env.h"

/// Set or un-set a process-scoped environment variable.
/**
* 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, particularly when called concurrently with
* ::rcutils_get_env. 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.
* \return `True` if success
* \return `False` if env_name is invalid or NULL
* \return `False` on failure
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
bool
rcutils_set_env(const char * env_name, const char * env_value);

#ifdef __cplusplus
}
#endif

#endif // RCUTILS__ENV_H_
61 changes: 61 additions & 0 deletions src/env.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifdef __cplusplus
extern "C"
{
#endif

#include <errno.h>
#include <stdlib.h>

#include "rcutils/env.h"
#include "rcutils/error_handling.h"

// TODO(cottsay): Move the stuff in get_env.c in here

bool
rcutils_set_env(const char * env_name, const char * env_value)
{
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
env_name, "env_name is null", return false);

#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;
}
#else
if (NULL == env_value) {
if (0 != unsetenv(env_name)) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("unsetenv failed: %d", errno);
return false;
}
} else {
if (0 != setenv(env_name, env_value, 1)) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("setenv failed: %d", errno);
return false;
}
}
#endif

return true;
}

#ifdef __cplusplus
}
#endif
57 changes: 57 additions & 0 deletions test/test_env.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gtest/gtest.h>

#include <string>

#include "rcutils/env.h"
#include "rcutils/error_handling.h"
#include "rcutils/get_env.h"

TEST(TestEnv, test_set_env) {
const char * res = nullptr;

// Invalid cases
EXPECT_FALSE(rcutils_set_env(nullptr, nullptr));
rcutils_reset_error();
EXPECT_FALSE(rcutils_set_env("=INVALID_ENV_VAR=", nullptr));
rcutils_reset_error();
EXPECT_FALSE(rcutils_set_env("=INVALID_ENV_VAR=", "InvalidEnvValue"));
rcutils_reset_error();

// Ensure we're starting clean
ASSERT_EQ(nullptr, rcutils_get_env("NEW_ENV_VAR", &res));
ASSERT_STREQ("", res);

// Simple set
ASSERT_TRUE(rcutils_set_env("NEW_ENV_VAR", "NewEnvValue"));
ASSERT_STREQ(nullptr, rcutils_get_env("NEW_ENV_VAR", &res));
EXPECT_STREQ(res, "NewEnvValue");

// Re-set
ASSERT_TRUE(rcutils_set_env("NEW_ENV_VAR", "DifferentEnvValue"));
ASSERT_STREQ(nullptr, rcutils_get_env("NEW_ENV_VAR", &res));
EXPECT_STREQ(res, "DifferentEnvValue");

// Un-set
ASSERT_TRUE(rcutils_set_env("NEW_ENV_VAR", nullptr));
ASSERT_EQ(nullptr, rcutils_get_env("NEW_ENV_VAR", &res));
EXPECT_STREQ("", res);

// Un-set again
ASSERT_TRUE(rcutils_set_env("NEW_ENV_VAR", nullptr));
ASSERT_EQ(nullptr, rcutils_get_env("NEW_ENV_VAR", &res));
EXPECT_STREQ("", res);
}

0 comments on commit e5dd0bd

Please sign in to comment.