ROS 2 rclcpp + rcl - humble  humble
ROS 2 C++ Client Library with ROS Client Library
remap.c
1 // Copyright 2018 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "rcl/remap.h"
16 
17 #include "./arguments_impl.h"
18 #include "./remap_impl.h"
19 #include "rcl/error_handling.h"
20 #include "rcl/expand_topic_name.h"
21 #include "rcutils/allocator.h"
22 #include "rcutils/macros.h"
23 #include "rcutils/strdup.h"
24 #include "rcutils/types/string_map.h"
25 
26 #ifdef __cplusplus
27 extern "C"
28 {
29 #endif
30 
33 {
34  static rcl_remap_t default_rule = {
35  .impl = NULL
36  };
37  return default_rule;
38 }
39 
42  const rcl_remap_t * rule,
43  rcl_remap_t * rule_out)
44 {
45  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
46  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
47 
48  RCL_CHECK_ARGUMENT_FOR_NULL(rule, RCL_RET_INVALID_ARGUMENT);
49  RCL_CHECK_ARGUMENT_FOR_NULL(rule_out, RCL_RET_INVALID_ARGUMENT);
50  RCL_CHECK_ARGUMENT_FOR_NULL(rule->impl, RCL_RET_INVALID_ARGUMENT);
51 
52  if (NULL != rule_out->impl) {
53  RCL_SET_ERROR_MSG("rule_out must be zero initialized");
55  }
56 
57  rcl_allocator_t allocator = rule->impl->allocator;
58 
59  rule_out->impl = allocator.allocate(sizeof(rcl_remap_impl_t), allocator.state);
60  if (NULL == rule_out->impl) {
61  return RCL_RET_BAD_ALLOC;
62  }
63 
64  rule_out->impl->allocator = allocator;
65 
66  // Zero so it's safe to call rcl_remap_fini() if an error occurs while copying.
67  rule_out->impl->type = RCL_UNKNOWN_REMAP;
68  rule_out->impl->node_name = NULL;
69  rule_out->impl->match = NULL;
70  rule_out->impl->replacement = NULL;
71 
72  rule_out->impl->type = rule->impl->type;
73  if (NULL != rule->impl->node_name) {
74  rule_out->impl->node_name = rcutils_strdup(rule->impl->node_name, allocator);
75  if (NULL == rule_out->impl->node_name) {
76  goto fail;
77  }
78  }
79  if (NULL != rule->impl->match) {
80  rule_out->impl->match = rcutils_strdup(rule->impl->match, allocator);
81  if (NULL == rule_out->impl->match) {
82  goto fail;
83  }
84  }
85  if (NULL != rule->impl->replacement) {
86  rule_out->impl->replacement = rcutils_strdup(
87  rule->impl->replacement, allocator);
88  if (NULL == rule_out->impl->replacement) {
89  goto fail;
90  }
91  }
92  return RCL_RET_OK;
93 fail:
94  if (RCL_RET_OK != rcl_remap_fini(rule_out)) {
95  RCL_SET_ERROR_MSG("Error while finalizing remap rule due to another error");
96  }
97  return RCL_RET_BAD_ALLOC;
98 }
99 
102 static
103 rcl_ret_t
104 rcl_remap_first_match(
105  rcl_remap_t * remap_rules,
106  int num_rules,
107  rcl_remap_type_t type_bitmask,
108  const char * name,
109  const char * node_name,
110  const char * node_namespace,
111  const rcutils_string_map_t * substitutions,
112  rcutils_allocator_t allocator,
113  rcl_remap_t ** output_rule)
114 {
115  *output_rule = NULL;
116  for (int i = 0; i < num_rules; ++i) {
117  rcl_remap_t * rule = &(remap_rules[i]);
118  if (!(rule->impl->type & type_bitmask)) {
119  // Not the type of remap rule we're looking fore
120  continue;
121  }
122  if (rule->impl->node_name != NULL && 0 != strcmp(rule->impl->node_name, node_name)) {
123  // Rule has a node name prefix and the supplied node name didn't match
124  continue;
125  }
126  bool matched = false;
127  if (rule->impl->type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
128  // topic and service rules need the match side to be expanded to a FQN
129  char * expanded_match = NULL;
131  rule->impl->match, node_name, node_namespace,
132  substitutions, allocator, &expanded_match);
133  if (RCL_RET_OK != ret) {
134  rcl_reset_error();
135  if (
137  RCL_RET_NODE_INVALID_NAME == ret ||
138  RCL_RET_BAD_ALLOC == ret)
139  {
140  // these are probably going to happen again. Stop processing rules
141  return ret;
142  }
143  continue;
144  }
145  if (NULL != name) {
146  // this check is to satisfy clang-tidy – name is always not null when type_bitmask is
147  // RCL_TOPIC_REMAP or RCL_SERVICE_REMAP. That is guaranteed because rcl_remap_first_match
148  // and rcl_remap_name are not public.
149  matched = (0 == strcmp(expanded_match, name));
150  }
151  allocator.deallocate(expanded_match, allocator.state);
152  } else {
153  // nodename and namespace replacement apply if the type and node name prefix checks passed
154  matched = true;
155  }
156  if (matched) {
157  *output_rule = rule;
158  break;
159  }
160  }
161  return RCL_RET_OK;
162 }
163 
165 RCL_LOCAL
166 rcl_ret_t
167 rcl_remap_name(
168  const rcl_arguments_t * local_arguments,
169  const rcl_arguments_t * global_arguments,
170  rcl_remap_type_t type_bitmask,
171  const char * name,
172  const char * node_name,
173  const char * node_namespace,
174  const rcutils_string_map_t * substitutions,
175  rcl_allocator_t allocator,
176  char ** output_name)
177 {
178  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
179  RCL_CHECK_ARGUMENT_FOR_NULL(output_name, RCL_RET_INVALID_ARGUMENT);
180  if (NULL != local_arguments && NULL == local_arguments->impl) {
181  local_arguments = NULL;
182  }
183  if (NULL != global_arguments && NULL == global_arguments->impl) {
184  global_arguments = NULL;
185  }
186  if (NULL == local_arguments && NULL == global_arguments) {
187  RCL_SET_ERROR_MSG("local_arguments invalid and not using global arguments");
189  }
190 
191  *output_name = NULL;
192  rcl_remap_t * rule = NULL;
193 
194  // Look at local rules first
195  if (NULL != local_arguments) {
196  rcl_ret_t ret = rcl_remap_first_match(
197  local_arguments->impl->remap_rules, local_arguments->impl->num_remap_rules, type_bitmask,
198  name, node_name, node_namespace, substitutions, allocator, &rule);
199  if (ret != RCL_RET_OK) {
200  return ret;
201  }
202  }
203  // Check global rules if no local rule matched
204  if (NULL == rule && NULL != global_arguments) {
205  rcl_ret_t ret = rcl_remap_first_match(
206  global_arguments->impl->remap_rules, global_arguments->impl->num_remap_rules, type_bitmask,
207  name, node_name, node_namespace, substitutions, allocator, &rule);
208  if (ret != RCL_RET_OK) {
209  return ret;
210  }
211  }
212  // Do the remapping
213  if (NULL != rule) {
214  if (rule->impl->type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
215  // topic and service rules need the replacement to be expanded to a FQN
217  rule->impl->replacement, node_name, node_namespace, substitutions, allocator, output_name);
218  if (RCL_RET_OK != ret) {
219  return ret;
220  }
221  } else {
222  // nodename and namespace rules don't need replacment expanded
223  *output_name = rcutils_strdup(rule->impl->replacement, allocator);
224  }
225  if (NULL == *output_name) {
226  RCL_SET_ERROR_MSG("Failed to set output");
227  return RCL_RET_ERROR;
228  }
229  }
230  return RCL_RET_OK;
231 }
232 
233 rcl_ret_t
235  const rcl_arguments_t * local_arguments,
236  const rcl_arguments_t * global_arguments,
237  const char * topic_name,
238  const char * node_name,
239  const char * node_namespace,
240  rcl_allocator_t allocator,
241  char ** output_name)
242 {
243  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
244  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
245 
246  rcutils_string_map_t substitutions = rcutils_get_zero_initialized_string_map();
247  rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions, 0, allocator);
248  rcl_ret_t ret = RCL_RET_ERROR;
249  if (RCUTILS_RET_OK == rcutils_ret) {
250  ret = rcl_get_default_topic_name_substitutions(&substitutions);
251  if (RCL_RET_OK == ret) {
252  ret = rcl_remap_name(
253  local_arguments, global_arguments, RCL_TOPIC_REMAP, topic_name, node_name,
254  node_namespace, &substitutions, allocator, output_name);
255  }
256  }
257  if (RCUTILS_RET_OK != rcutils_string_map_fini(&substitutions)) {
258  return RCL_RET_ERROR;
259  }
260  return ret;
261 }
262 
263 rcl_ret_t
265  const rcl_arguments_t * local_arguments,
266  const rcl_arguments_t * global_arguments,
267  const char * service_name,
268  const char * node_name,
269  const char * node_namespace,
270  rcl_allocator_t allocator,
271  char ** output_name)
272 {
273  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
274  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
275 
276  rcutils_string_map_t substitutions = rcutils_get_zero_initialized_string_map();
277  rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions, 0, allocator);
278  rcl_ret_t ret = RCL_RET_ERROR;
279  if (rcutils_ret == RCUTILS_RET_OK) {
280  ret = rcl_get_default_topic_name_substitutions(&substitutions);
281  if (ret == RCL_RET_OK) {
282  ret = rcl_remap_name(
283  local_arguments, global_arguments, RCL_SERVICE_REMAP, service_name, node_name,
284  node_namespace, &substitutions, allocator, output_name);
285  }
286  }
287  if (RCUTILS_RET_OK != rcutils_string_map_fini(&substitutions)) {
288  return RCL_RET_ERROR;
289  }
290  return ret;
291 }
292 
293 rcl_ret_t
295  const rcl_arguments_t * local_arguments,
296  const rcl_arguments_t * global_arguments,
297  const char * node_name,
298  rcl_allocator_t allocator,
299  char ** output_name)
300 {
301  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
302  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID_NAME);
303  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
304  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
305 
306  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
307  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
308  return rcl_remap_name(
309  local_arguments, global_arguments, RCL_NODENAME_REMAP, NULL, node_name, NULL, NULL,
310  allocator, output_name);
311 }
312 
313 rcl_ret_t
315  const rcl_arguments_t * local_arguments,
316  const rcl_arguments_t * global_arguments,
317  const char * node_name,
318  rcl_allocator_t allocator,
319  char ** output_namespace)
320 {
321  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
322  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID_NAMESPACE);
323  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
324  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
325 
326  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
327  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
328  return rcl_remap_name(
329  local_arguments, global_arguments, RCL_NAMESPACE_REMAP, NULL, node_name, NULL, NULL,
330  allocator, output_namespace);
331 }
332 
333 rcl_ret_t
335  rcl_remap_t * rule)
336 {
337  RCL_CHECK_ARGUMENT_FOR_NULL(rule, RCL_RET_INVALID_ARGUMENT);
338  if (rule->impl) {
339  rcl_ret_t ret = RCL_RET_OK;
340  if (NULL != rule->impl->node_name) {
341  rule->impl->allocator.deallocate(
342  rule->impl->node_name, rule->impl->allocator.state);
343  rule->impl->node_name = NULL;
344  }
345  if (NULL != rule->impl->match) {
346  rule->impl->allocator.deallocate(
347  rule->impl->match, rule->impl->allocator.state);
348  rule->impl->match = NULL;
349  }
350  if (NULL != rule->impl->replacement) {
351  rule->impl->allocator.deallocate(
352  rule->impl->replacement, rule->impl->allocator.state);
353  rule->impl->replacement = NULL;
354  }
355  rule->impl->allocator.deallocate(rule->impl, rule->impl->allocator.state);
356  rule->impl = NULL;
357  return ret;
358  }
359  RCL_SET_ERROR_MSG("rcl_remap_t finalized twice");
360  return RCL_RET_ERROR;
361 }
362 
363 #ifdef __cplusplus
364 }
365 #endif
#define RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, msg, fail_statement)
Check that the given allocator is initialized, or fail with a message.
Definition: allocator.h:56
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_expand_topic_name(const char *input_topic_name, const char *node_name, const char *node_namespace, const rcutils_string_map_t *substitutions, rcl_allocator_t allocator, char **output_topic_name)
Expand a given topic name into a fully-qualified topic name.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_default_topic_name_substitutions(rcutils_string_map_t *string_map)
Fill a given string map with the default substitution pairs.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_node_namespace(const rcl_arguments_t *local_arguments, const rcl_arguments_t *global_arguments, const char *node_name, rcl_allocator_t allocator, char **output_namespace)
Remap a namespace based on given rules.
Definition: remap.c:314
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_fini(rcl_remap_t *remap)
Reclaim resources held inside rcl_remap_t structure.
Definition: remap.c:334
RCL_PUBLIC RCL_WARN_UNUSED rcl_remap_t rcl_get_zero_initialized_remap(void)
Return a rcl_remap_t struct with members initialized to NULL.
Definition: remap.c:32
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_service_name(const rcl_arguments_t *local_arguments, const rcl_arguments_t *global_arguments, const char *service_name, const char *node_name, const char *node_namespace, rcl_allocator_t allocator, char **output_name)
Remap a service name based on given rules.
Definition: remap.c:264
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_copy(const rcl_remap_t *rule, rcl_remap_t *rule_out)
Copy one remap structure into another.
Definition: remap.c:41
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_topic_name(const rcl_arguments_t *local_arguments, const rcl_arguments_t *global_arguments, const char *topic_name, const char *node_name, const char *node_namespace, rcl_allocator_t allocator, char **output_name)
Remap a topic name based on given rules.
Definition: remap.c:234
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_node_name(const rcl_arguments_t *local_arguments, const rcl_arguments_t *global_arguments, const char *node_name, rcl_allocator_t allocator, char **output_name)
Remap a node name based on given rules.
Definition: remap.c:294
rcl_remap_t * remap_rules
Array of rules for name remapping.
int num_remap_rules
Length of remap_rules.
Hold output of parsing command line arguments.
Definition: arguments.h:36
rcl_arguments_impl_t * impl
Private implementation pointer.
Definition: arguments.h:38
char * replacement
Replacement portion of a rule.
Definition: remap_impl.h:48
char * node_name
A node name that this rule is limited to, or NULL if it applies to any node.
Definition: remap_impl.h:44
rcl_allocator_t allocator
Allocator used to allocate objects in this struct.
Definition: remap_impl.h:51
char * match
Match portion of a rule, or NULL if node name or namespace replacement.
Definition: remap_impl.h:46
rcl_remap_type_t type
Bitmask indicating what type of rule this is.
Definition: remap_impl.h:42
Hold remapping rules.
Definition: remap.h:35
rcl_remap_impl_t * impl
Private implementation pointer.
Definition: remap.h:37
#define RCL_RET_NODE_INVALID_NAMESPACE
Invalid node namespace return code.
Definition: types.h:60
#define RCL_RET_OK
Success return code.
Definition: types.h:26
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
Definition: types.h:32
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:34
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:28
#define RCL_RET_NODE_INVALID_NAME
Invalid node name return code.
Definition: types.h:58
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:23