Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1bb953e
Get discovery preferences from the environment
gbiggs Aug 2, 2022
8ecb6b3
Support specification of discovery range and static peers
gbiggs Aug 7, 2022
6d4f0e6
Add some debug helpers
gbiggs Sep 7, 2022
f557eea
Cleanup the LOCALHOST_ONLY deprecation a bit.
clalancette Oct 7, 2022
02015ff
Rewrite parsing of static peers.
clalancette Oct 7, 2022
4529112
Fix some silly bugs.
clalancette Oct 13, 2022
8b99c1f
Use names instead of integers for discovery range env vars
mxgrey Feb 6, 2023
0d117f6
Add support for dynamic allocation
arjo129 Mar 2, 2023
137ea61
Add a warning if ROS_AUTOMATIC_DISCOVERY_OFF is set and STATIC_PEERS are
arjo129 Mar 3, 2023
0bbaabd
Update to latest rmw API
mxgrey Mar 9, 2023
5225a54
Uncrustify
mxgrey Mar 9, 2023
f7d4487
Merge branch 'rolling' into gbiggs/discovery-peers-specification
sloretz Mar 24, 2023
7deb711
Update for rmw_discovery_options_t changes
sloretz Mar 28, 2023
911932f
Address feedback: use strncmp
arjo129 Mar 28, 2023
df48726
Address feedback: Log levels
arjo129 Mar 28, 2023
ac342ad
Address feedback: remove TODO
arjo129 Mar 28, 2023
ffd5000
Address feedback: rename function
arjo129 Mar 28, 2023
c3f1c77
ddress feedback: Docstring
arjo129 Mar 28, 2023
81fa1f0
Address feedback: comment
arjo129 Mar 28, 2023
211d4a6
Add `RCL_RET_ERROR` if fini fails.
arjo129 Mar 28, 2023
18c8800
`snprintf`->`rcutils_snprintf`
arjo129 Mar 28, 2023
185d5ae
Rename tests
arjo129 Mar 28, 2023
06efd9d
rcl_get_discovery_automatic_range -> rcl_get_automatic_discovery_rang…
sloretz Mar 28, 2023
1945a0c
Annotate tests
arjo129 Mar 29, 2023
4260e6f
More comments
arjo129 Mar 29, 2023
d3bb058
Style
arjo129 Mar 29, 2023
fe50db2
Style
arjo129 Mar 29, 2023
b644644
Uncrustify
arjo129 Mar 29, 2023
32e6cce
Constness and warning
arjo129 Mar 29, 2023
cd2eeb8
address TODO about IP address validation in the static peers
wjwwood Mar 29, 2023
5b568ca
NOT_SET and SYSTEM_DEFAULT values
sloretz Mar 29, 2023
a610f99
refactor discovery options to handle env vars better and simplify str…
wjwwood Mar 29, 2023
4c4248f
fixup docs
wjwwood Mar 29, 2023
12e55f0
(re)improve the discovery range debug message
wjwwood Mar 29, 2023
4101c90
Set discovery options to NOT_SET to detect user changse
sloretz Mar 31, 2023
2cb6d41
lint
sloretz Mar 31, 2023
90ba86d
Merge branch 'rolling' into gbiggs/discovery-peers-specification
sloretz Mar 31, 2023
07ad46f
strncpy_s on windows
sloretz Mar 31, 2023
bf024b8
Merge branch 'rolling' into gbiggs/discovery-peers-specification
sloretz Apr 6, 2023
ee00c56
Change default range to SUBNET, and allow configuring it at build time
sloretz Apr 6, 2023
e5e8877
Merge branch 'rolling' into gbiggs/discovery-peers-specification
sloretz Apr 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions rcl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ set(${PROJECT_NAME}_sources
src/rcl/client.c
src/rcl/common.c
src/rcl/context.c
src/rcl/discovery_options.c
src/rcl/domain_id.c
src/rcl/event.c
src/rcl/expand_topic_name.c
Expand Down Expand Up @@ -98,6 +99,12 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
yaml
)

# Allow configuring the default discovery range
if(DEFINED RCL_DEFAULT_DISCOVERY_RANGE)
target_compile_definitions(${PROJECT_NAME} PRIVATE
"RCL_DEFAULT_DISCOVERY_RANGE=${RCL_DEFAULT_DISCOVERY_RANGE}")
endif()

# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(${PROJECT_NAME} PRIVATE "RCL_BUILDING_DLL")
Expand Down
109 changes: 109 additions & 0 deletions rcl/include/rcl/discovery_options.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2022 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.

/// @file

#ifndef RCL__DISCOVERY_OPTIONS_H_
#define RCL__DISCOVERY_OPTIONS_H_

#ifdef __cplusplus
extern "C"
{
#endif

#include "rcl/types.h"
#include "rcl/visibility_control.h"

#include "rcutils/allocator.h"

#include "rmw/discovery_options.h"

/// Determine how the user wishes to discover other ROS nodes automatically.
/**
* Use the ROS_AUTOMATIC_DISCOVERY_RANGE environment variable to determine how
* far automatic discovery should be allowed to propagate:
*
* - not at all (no automatic discovery),
* - the local machine only,
* - or the subnet (or as far beyond the local system as the middleware can)
*
* When the subnet is specified the automatic discovery mechanism used by the
* rmw implementation dictates how far discovery can propagate on the network,
* e.g. for multicast-based discovery this will be the local subnet, hence the
* name.
*
* The option indicated by the environment variable will be stored in the
* automatic_discovery_range field of the given discovery_options struct.
*
* If the environment variable isn't set, then the default discovery range
* will be the value of the preprocessor definition
* `RCL_DEFAULT_DISCOVERY_RANGE`.
* If the definition is undefined, then the default will be SUBNET.
* It is intended that the default will be LOCALHOST in future versions of ROS.
*
* \param[out] discovery_options Must not be NULL.
* \return #RCL_RET_INVALID_ARGUMENT if an argument is invalid, or
* \return #RCL_RET_ERROR if an unexpected error happened, or
* \return #RCL_RET_OK.
*/
RCL_PUBLIC
rcl_ret_t
rcl_get_automatic_discovery_range(rmw_discovery_options_t * discovery_options);

/// Convert the automatic discovery range value to a string for easy printing.
/**
* \param[in] automatic_discovery_range range enum to stringify
* \return string version of enum, or NULL if not recognized
*/
RCL_PUBLIC
const char *
rcl_automatic_discovery_range_to_string(rmw_automatic_discovery_range_t automatic_discovery_range);

/// Determine how the user wishes to discover other ROS nodes via statically-configured peers.
/**
* Use the ROS_STATIC_PEERS environment variable to determine which hosts
* the user wants to communicate with, in addition to localhost.
*
* Values for the static peers are not validated beyond basic string checks,
* e.g. avoiding empty strings, etc.
* Any validation of IP addresses or hostnames is left up to the
* rmw implementation, and therefore what is and is not acceptable in these
* fields is dependent on it.
*
* The general expectation, however, is that IP addresses and hostnames are
* acceptable.
*
* The static peers are split by ';' and returned as a list of fixed length
* c-strings in the static_peers and static_peers_count fields of the given
* discovery_options struct.
* Each peer may only be RMW_DISCOVERY_OPTIONS_STATIC_PEERS_MAX_LENGTH long,
* and if it is longer it will be skipped and a warning log message will be
* produced.
*
* \param[out] discovery_options Must not be NULL.
* \return #RCL_RET_INVALID_ARGUMENT if an argument is invalid, or
* \return #RCL_RET_ERROR if an unexpected error happened, or
* \return #RCL_RET_OK.
*/
RCL_PUBLIC
rcl_ret_t
rcl_get_discovery_static_peers(
rmw_discovery_options_t * discovery_options,
rcutils_allocator_t * allocator);

#ifdef __cplusplus
}
#endif

#endif // RCL__DISCOVERY_OPTIONS_H_
182 changes: 182 additions & 0 deletions rcl/src/rcl/discovery_options.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright 2022 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 "rcl/discovery_options.h"

#include <stdlib.h>
#include <string.h>

#include "rcutils/allocator.h"
#include "rcutils/env.h"
#include "rcutils/logging_macros.h"
#include "rcutils/snprintf.h"
#include "rcutils/split.h"
#include "rcutils/types/string_array.h"

#include "rcl/error_handling.h"
#include "rcl/types.h"

#include "rmw/error_handling.h"

#include "./common.h"

static const char * const RCL_STATIC_PEERS_ENV_VAR = "ROS_STATIC_PEERS";
static const char * const RCL_AUTOMATIC_DISCOVERY_RANGE_ENV_VAR = "ROS_AUTOMATIC_DISCOVERY_RANGE";

#define GET_RMW_DISCOVERY_RANGE(x) \
_GET_DEFAULT_DISCOVERY_RANGE(x)
#define _GET_DEFAULT_DISCOVERY_RANGE(x) \
RMW_AUTOMATIC_DISCOVERY_RANGE_ ## x

rcl_ret_t
rcl_get_automatic_discovery_range(rmw_discovery_options_t * discovery_options)
{
const char * ros_automatic_discovery_range_env_val = NULL;
const char * get_env_error_str = NULL;

RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
RCL_CHECK_ARGUMENT_FOR_NULL(discovery_options, RCL_RET_INVALID_ARGUMENT);

get_env_error_str = rcutils_get_env(
RCL_AUTOMATIC_DISCOVERY_RANGE_ENV_VAR,
&ros_automatic_discovery_range_env_val);
if (NULL != get_env_error_str) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Error getting env var '%s': %s", RCL_AUTOMATIC_DISCOVERY_RANGE_ENV_VAR,
get_env_error_str);
return RCL_RET_ERROR;
}
if (strcmp(ros_automatic_discovery_range_env_val, "") == 0) {
#ifdef RCL_DEFAULT_DISCOVERY_RANGE
discovery_options->automatic_discovery_range =
GET_RMW_DISCOVERY_RANGE(RCL_DEFAULT_DISCOVERY_RANGE);
#else
discovery_options->automatic_discovery_range = RMW_AUTOMATIC_DISCOVERY_RANGE_SUBNET;
#endif
} else if (strcmp(ros_automatic_discovery_range_env_val, "OFF") == 0) {
discovery_options->automatic_discovery_range = RMW_AUTOMATIC_DISCOVERY_RANGE_OFF;
} else if (strcmp(ros_automatic_discovery_range_env_val, "LOCALHOST") == 0) {
discovery_options->automatic_discovery_range = RMW_AUTOMATIC_DISCOVERY_RANGE_LOCALHOST;
} else if (strcmp(ros_automatic_discovery_range_env_val, "SUBNET") == 0) {
discovery_options->automatic_discovery_range = RMW_AUTOMATIC_DISCOVERY_RANGE_SUBNET;
} else if (strcmp(ros_automatic_discovery_range_env_val, "SYSTEM_DEFAULT") == 0) {
discovery_options->automatic_discovery_range = RMW_AUTOMATIC_DISCOVERY_RANGE_SYSTEM_DEFAULT;
} else {
RCUTILS_LOG_WARN_NAMED(
ROS_PACKAGE_NAME,
"Invalid value '%s' specified for '%s', assuming localhost only",
ros_automatic_discovery_range_env_val,
RCL_AUTOMATIC_DISCOVERY_RANGE_ENV_VAR);

discovery_options->automatic_discovery_range = RMW_AUTOMATIC_DISCOVERY_RANGE_LOCALHOST;
}

return RCL_RET_OK;
}

RCL_PUBLIC
const char *
rcl_automatic_discovery_range_to_string(rmw_automatic_discovery_range_t automatic_discovery_range)
{
switch (automatic_discovery_range) {
case RMW_AUTOMATIC_DISCOVERY_RANGE_NOT_SET:
return "RMW_AUTOMATIC_DISCOVERY_RANGE_NOT_SET";
case RMW_AUTOMATIC_DISCOVERY_RANGE_OFF:
return "RMW_AUTOMATIC_DISCOVERY_RANGE_OFF";
case RMW_AUTOMATIC_DISCOVERY_RANGE_LOCALHOST:
return "RMW_AUTOMATIC_DISCOVERY_RANGE_LOCALHOST";
case RMW_AUTOMATIC_DISCOVERY_RANGE_SUBNET:
return "RMW_AUTOMATIC_DISCOVERY_RANGE_SUBNET";
case RMW_AUTOMATIC_DISCOVERY_RANGE_SYSTEM_DEFAULT:
return "RMW_AUTOMATIC_DISCOVERY_RANGE_SYSTEM_DEFAULT";
default:
return NULL;
}
}

rcl_ret_t
rcl_get_discovery_static_peers(
rmw_discovery_options_t * discovery_options,
rcutils_allocator_t * allocator)
{
const char * ros_peers_env_val = NULL;
const char * get_env_error_str = NULL;

RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
RCL_CHECK_ARGUMENT_FOR_NULL(discovery_options, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(allocator, RCL_RET_INVALID_ARGUMENT);

get_env_error_str = rcutils_get_env(RCL_STATIC_PEERS_ENV_VAR, &ros_peers_env_val);
if (NULL != get_env_error_str) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Error getting environment variable '%s': %s",
RCL_STATIC_PEERS_ENV_VAR, get_env_error_str);
return RCL_RET_ERROR;
}

// The value of the env var should be at least "", even when not set.
if (NULL == ros_peers_env_val) {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Environment variable value unexpectedly NULL when checking '%s'",
RCL_STATIC_PEERS_ENV_VAR);
return RCL_RET_ERROR;
}

rcutils_string_array_t array = rcutils_get_zero_initialized_string_array();
rcutils_ret_t split_ret = rcutils_split(ros_peers_env_val, ';', *allocator, &array);
if (RCUTILS_RET_OK != split_ret) {
RCL_SET_ERROR_MSG(rcutils_get_error_string().str);
return RCL_RET_ERROR;
}

rmw_ret_t rmw_ret = rmw_discovery_options_init(discovery_options, array.size, allocator);
if (RMW_RET_OK != rmw_ret) {
RCL_SET_ERROR_MSG(rmw_get_error_string().str);
return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
}

for (size_t i = 0; i < array.size; ++i) {
if (strlen(array.data[i]) > (RMW_DISCOVERY_OPTIONS_STATIC_PEERS_MAX_LENGTH - 1)) {
RCUTILS_LOG_WARN_NAMED(
ROS_PACKAGE_NAME,
"Static peer %s specified to '%s' is too long (maximum of %d); skipping",
array.data[i], RCL_STATIC_PEERS_ENV_VAR,
RMW_DISCOVERY_OPTIONS_STATIC_PEERS_MAX_LENGTH - 1);
continue;
}
#ifdef _WIN32
strncpy_s(
discovery_options->static_peers[i].peer_address,
RMW_DISCOVERY_OPTIONS_STATIC_PEERS_MAX_LENGTH,
array.data[i],
RMW_DISCOVERY_OPTIONS_STATIC_PEERS_MAX_LENGTH);
#else
strncpy(
discovery_options->static_peers[i].peer_address,
array.data[i],
RMW_DISCOVERY_OPTIONS_STATIC_PEERS_MAX_LENGTH);
discovery_options->static_peers[i].peer_address[
RMW_DISCOVERY_OPTIONS_STATIC_PEERS_MAX_LENGTH - 1] = '\0';
#endif
}

if (RCUTILS_RET_OK != rcutils_string_array_fini(&array)) {
RCL_SET_ERROR_MSG(rcutils_get_error_string().str);
return RCL_RET_ERROR;
}

return RCL_RET_OK;
}
Loading