-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow configuring logging directory through environment variables (#53)
Signed-off-by: Christophe Bedard <[email protected]>
- Loading branch information
1 parent
37bb732
commit e2456ba
Showing
9 changed files
with
380 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// 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 <rcutils/allocator.h> | ||
#include <rcutils/error_handling.h> | ||
#include <rcutils/filesystem.h> | ||
#include <rcutils/format_string.h> | ||
#include <rcutils/get_env.h> | ||
#include <rcutils/strdup.h> | ||
|
||
#include "rcl_logging_interface/rcl_logging_interface.h" | ||
|
||
rcl_logging_ret_t | ||
rcl_logging_get_logging_directory(rcutils_allocator_t allocator, char ** directory) | ||
{ | ||
if (NULL == directory) { | ||
RCUTILS_SET_ERROR_MSG("directory argument must not be null"); | ||
return RCL_LOGGING_RET_INVALID_ARGUMENT; | ||
} | ||
if (NULL != *directory) { | ||
RCUTILS_SET_ERROR_MSG("directory argument must point to null"); | ||
return RCL_LOGGING_RET_INVALID_ARGUMENT; | ||
} | ||
|
||
const char * log_dir_env; | ||
const char * err = rcutils_get_env("ROS_LOG_DIR", &log_dir_env); | ||
if (NULL != err) { | ||
RCUTILS_SET_ERROR_MSG("rcutils_get_env failed"); | ||
return RCL_LOGGING_RET_ERROR; | ||
} | ||
if ('\0' != *log_dir_env) { | ||
*directory = rcutils_strdup(log_dir_env, allocator); | ||
if (NULL == *directory) { | ||
RCUTILS_SET_ERROR_MSG("rcutils_strdup failed"); | ||
return RCL_LOGGING_RET_ERROR; | ||
} | ||
} else { | ||
const char * ros_home_dir_env; | ||
err = rcutils_get_env("ROS_HOME", &ros_home_dir_env); | ||
if (NULL != err) { | ||
RCUTILS_SET_ERROR_MSG("rcutils_get_env failed"); | ||
return RCL_LOGGING_RET_ERROR; | ||
} | ||
char * ros_home_dir; | ||
if ('\0' == *ros_home_dir_env) { | ||
ros_home_dir = rcutils_join_path("~", ".ros", allocator); | ||
if (NULL == ros_home_dir) { | ||
RCUTILS_SET_ERROR_MSG("rcutils_join_path failed"); | ||
return RCL_LOGGING_RET_ERROR; | ||
} | ||
} else { | ||
ros_home_dir = rcutils_strdup(ros_home_dir_env, allocator); | ||
if (NULL == ros_home_dir) { | ||
RCUTILS_SET_ERROR_MSG("rcutils_strdup failed"); | ||
return RCL_LOGGING_RET_ERROR; | ||
} | ||
} | ||
*directory = rcutils_join_path(ros_home_dir, "log", allocator); | ||
allocator.deallocate(ros_home_dir, allocator.state); | ||
if (NULL == *directory) { | ||
RCUTILS_SET_ERROR_MSG("rcutils_join_path failed"); | ||
return RCL_LOGGING_RET_ERROR; | ||
} | ||
} | ||
|
||
// Expand home directory | ||
if ('~' == (*directory)[0]) { | ||
const char * homedir = rcutils_get_home_dir(); | ||
if (NULL == homedir) { | ||
allocator.deallocate(*directory, allocator.state); | ||
RCUTILS_SET_ERROR_MSG("failed to get the home directory"); | ||
return RCL_LOGGING_RET_ERROR; | ||
} | ||
char * directory_not_expanded = *directory; | ||
*directory = rcutils_format_string_limit( | ||
allocator, | ||
strlen(homedir) + strlen(directory_not_expanded), | ||
"%s%s", | ||
homedir, | ||
directory_not_expanded + 1); | ||
allocator.deallocate(directory_not_expanded, allocator.state); | ||
if (NULL == *directory) { | ||
RCUTILS_SET_ERROR_MSG("rcutils_format_string failed"); | ||
return RCL_LOGGING_RET_ERROR; | ||
} | ||
} | ||
|
||
char * directory_maybe_not_native = *directory; | ||
*directory = rcutils_to_native_path(directory_maybe_not_native, allocator); | ||
allocator.deallocate(directory_maybe_not_native, allocator.state); | ||
if (NULL == *directory) { | ||
RCUTILS_SET_ERROR_MSG("rcutils_to_native_path failed"); | ||
return RCL_LOGGING_RET_ERROR; | ||
} | ||
return RCL_LOGGING_RET_OK; | ||
} |
183 changes: 183 additions & 0 deletions
183
rcl_logging_interface/test/test_get_logging_directory.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
// 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 <rcpputils/filesystem_helper.hpp> | ||
#include <rcpputils/get_env.hpp> | ||
#include <rcutils/allocator.h> | ||
#include <rcutils/env.h> | ||
#include <rcutils/error_handling.h> | ||
|
||
#include <iostream> | ||
#include <string> | ||
|
||
#include "gtest/gtest.h" | ||
#include "rcl_logging_interface/rcl_logging_interface.h" | ||
|
||
// This is a helper class that resets an environment | ||
// variable when leaving scope | ||
class RestoreEnvVar | ||
{ | ||
public: | ||
explicit RestoreEnvVar(const std::string & name) | ||
: name_(name), | ||
value_(rcpputils::get_env_var(name.c_str())) | ||
{ | ||
} | ||
|
||
~RestoreEnvVar() | ||
{ | ||
if (!rcutils_set_env(name_.c_str(), value_.c_str())) { | ||
std::cerr << "Failed to restore value of environment variable: " << name_ << std::endl; | ||
} | ||
} | ||
|
||
private: | ||
const std::string name_; | ||
const std::string value_; | ||
}; | ||
|
||
TEST(test_logging_directory, directory) | ||
{ | ||
RestoreEnvVar home_var("HOME"); | ||
RestoreEnvVar userprofile_var("USERPROFILE"); | ||
ASSERT_EQ(true, rcutils_set_env("HOME", nullptr)); | ||
ASSERT_EQ(true, rcutils_set_env("USERPROFILE", nullptr)); | ||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", nullptr)); | ||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr)); | ||
|
||
rcutils_allocator_t allocator = rcutils_get_default_allocator(); | ||
|
||
// Invalid argument if given a nullptr | ||
EXPECT_EQ( | ||
RCL_LOGGING_RET_INVALID_ARGUMENT, rcl_logging_get_logging_directory(allocator, nullptr)); | ||
EXPECT_TRUE(rcutils_error_is_set()); | ||
rcutils_reset_error(); | ||
// Invalid argument if the C string is not nullptr | ||
char * could_leak = const_cast<char *>("/could/be/leaked"); | ||
EXPECT_EQ( | ||
RCL_LOGGING_RET_INVALID_ARGUMENT, rcl_logging_get_logging_directory(allocator, &could_leak)); | ||
EXPECT_TRUE(rcutils_error_is_set()); | ||
rcutils_reset_error(); | ||
|
||
// Fails without any relevant env vars at all (HOME included) | ||
char * directory = nullptr; | ||
EXPECT_EQ(RCL_LOGGING_RET_ERROR, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_TRUE(rcutils_error_is_set()); | ||
rcutils_reset_error(); | ||
directory = nullptr; | ||
|
||
// Default case without ROS_LOG_DIR or ROS_HOME being set (but with HOME) | ||
rcpputils::fs::path fake_home("/fake_home_dir"); | ||
ASSERT_EQ(true, rcutils_set_env("HOME", fake_home.string().c_str())); | ||
ASSERT_EQ(true, rcutils_set_env("USERPROFILE", fake_home.string().c_str())); | ||
rcpputils::fs::path default_dir = fake_home / ".ros" / "log"; | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, default_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
|
||
// Use $ROS_LOG_DIR if it is set | ||
const char * my_log_dir_raw = "/my/ros_log_dir"; | ||
rcpputils::fs::path my_log_dir(my_log_dir_raw); | ||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", my_log_dir.string().c_str())); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, my_log_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
// Make sure it converts path separators when necessary | ||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", my_log_dir_raw)); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, my_log_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
// Setting ROS_HOME won't change anything since ROS_LOG_DIR is used first | ||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", "/this/wont/be/used")); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, my_log_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr)); | ||
// Empty is considered unset | ||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "")); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, default_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
// Make sure '~' is expanded to the home directory | ||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "~/logdir")); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
rcpputils::fs::path fake_log_dir = fake_home / "logdir"; | ||
EXPECT_STREQ(directory, fake_log_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
// But it should only be expanded if it's at the beginning | ||
rcpputils::fs::path prefixed_fake_log_dir("/prefix/~/logdir"); | ||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", prefixed_fake_log_dir.string().c_str())); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, prefixed_fake_log_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "~")); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, fake_home.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
rcpputils::fs::path home_trailing_slash(fake_home.string() + "/"); | ||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "~/")); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, home_trailing_slash.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
|
||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", nullptr)); | ||
|
||
// Without ROS_LOG_DIR, use $ROS_HOME/log | ||
rcpputils::fs::path fake_ros_home = fake_home / ".fakeroshome"; | ||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", fake_ros_home.string().c_str())); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
rcpputils::fs::path fake_ros_home_log_dir = fake_ros_home / "log"; | ||
EXPECT_STREQ(directory, fake_ros_home_log_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
// Make sure it converts path separators when necessary | ||
const char * my_ros_home_raw = "/my/ros/home"; | ||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", my_ros_home_raw)); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
rcpputils::fs::path my_ros_home_log_dir = rcpputils::fs::path(my_ros_home_raw) / "log"; | ||
EXPECT_STREQ(directory, my_ros_home_log_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
// Empty is considered unset | ||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", "")); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, default_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
// Make sure '~' is expanded to the home directory | ||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", "~/.fakeroshome")); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, fake_ros_home_log_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
// But it should only be expanded if it's at the beginning | ||
rcpputils::fs::path prefixed_fake_ros_home("/prefix/~/.fakeroshome"); | ||
rcpputils::fs::path prefixed_fake_ros_home_log_dir = prefixed_fake_ros_home / "log"; | ||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", prefixed_fake_ros_home.string().c_str())); | ||
EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); | ||
EXPECT_STREQ(directory, prefixed_fake_ros_home_log_dir.string().c_str()); | ||
allocator.deallocate(directory, allocator.state); | ||
directory = nullptr; | ||
|
||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr)); | ||
} |
Oops, something went wrong.