ROS 2 rclcpp + rcl - humble  humble
ROS 2 C++ Client Library with ROS Client Library
rmw_implementation_identifier_check.c
1 // Copyright 2016 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 #ifdef __cplusplus
16 extern "C"
17 {
18 #endif
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "rcl/allocator.h"
25 #include "rcl/error_handling.h"
26 #include "rcutils/env.h"
27 #include "rcutils/logging_macros.h"
28 #include "rcutils/strdup.h"
29 #include "rmw/rmw.h"
30 
31 #include "rcl/types.h"
32 
34 
35 // Extracted this portable method of doing a "shared library constructor" from SO:
36 // http://stackoverflow.com/a/2390626/671658
37 // Initializer/finalizer sample for MSVC and GCC/Clang.
38 // 2010-2016 Joe Lowe. Released into the public domain.
39 #if defined(_MSC_VER)
40  #pragma section(".CRT$XCU", read)
41  #define INITIALIZER2_(f, p) \
42  static void f(void); \
43  __declspec(allocate(".CRT$XCU"))void(*f ## _)(void) = f; \
44  __pragma(comment(linker, "/include:" p #f "_")) \
45  static void f(void)
46  #ifdef _WIN64
47  #define INITIALIZER(f) INITIALIZER2_(f, "")
48  #else
49  #define INITIALIZER(f) INITIALIZER2_(f, "_")
50  #endif
51 #else
52  #define INITIALIZER(f) \
53  static void f(void) __attribute__((constructor)); \
54  static void f(void)
55 #endif
56 
58 {
59  // If the environment variable RMW_IMPLEMENTATION is set, or
60  // the environment variable RCL_ASSERT_RMW_ID_MATCHES is set,
61  // check that the result of `rmw_get_implementation_identifier` matches.
62  rcl_ret_t ret = RCL_RET_OK;
64  char * expected_rmw_impl = NULL;
65  const char * expected_rmw_impl_env = NULL;
66  const char * get_env_error_str = rcutils_get_env(
68  &expected_rmw_impl_env);
69  if (NULL != get_env_error_str) {
70  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
71  "Error getting env var '" RCUTILS_STRINGIFY(RMW_IMPLEMENTATION_ENV_VAR_NAME) "': %s\n",
72  get_env_error_str);
73  return RCL_RET_ERROR;
74  }
75  if (strlen(expected_rmw_impl_env) > 0) {
76  // Copy the environment variable so it doesn't get over-written by the next getenv call.
77  expected_rmw_impl = rcutils_strdup(expected_rmw_impl_env, allocator);
78  if (!expected_rmw_impl) {
79  RCL_SET_ERROR_MSG("allocation failed");
80  return RCL_RET_BAD_ALLOC;
81  }
82  }
83 
84  char * asserted_rmw_impl = NULL;
85  const char * asserted_rmw_impl_env = NULL;
86  get_env_error_str = rcutils_get_env(
87  RCL_ASSERT_RMW_ID_MATCHES_ENV_VAR_NAME, &asserted_rmw_impl_env);
88  if (NULL != get_env_error_str) {
89  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
90  "Error getting env var '"
91  RCUTILS_STRINGIFY(RCL_ASSERT_RMW_ID_MATCHES_ENV_VAR_NAME) "': %s\n",
92  get_env_error_str);
93  ret = RCL_RET_ERROR;
94  goto cleanup;
95  }
96  if (strlen(asserted_rmw_impl_env) > 0) {
97  // Copy the environment variable so it doesn't get over-written by the next getenv call.
98  asserted_rmw_impl = rcutils_strdup(asserted_rmw_impl_env, allocator);
99  if (!asserted_rmw_impl) {
100  RCL_SET_ERROR_MSG("allocation failed");
101  ret = RCL_RET_BAD_ALLOC;
102  goto cleanup;
103  }
104  }
105 
106  // If both environment variables are set, and they do not match, print an error and exit.
107  if (expected_rmw_impl && asserted_rmw_impl && strcmp(expected_rmw_impl, asserted_rmw_impl) != 0) {
108  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
109  "Values of RMW_IMPLEMENTATION ('%s') and RCL_ASSERT_RMW_ID_MATCHES ('%s') environment "
110  "variables do not match, exiting with %d.",
111  expected_rmw_impl, asserted_rmw_impl, RCL_RET_ERROR
112  );
113  ret = RCL_RET_ERROR;
114  goto cleanup;
115  }
116 
117  // Collapse the expected_rmw_impl and asserted_rmw_impl variables so only expected_rmw_impl needs
118  // to be used from now on.
119  if (expected_rmw_impl && asserted_rmw_impl) {
120  // The strings at this point must be equal.
121  // No need for asserted_rmw_impl anymore, free the memory.
122  allocator.deallocate(asserted_rmw_impl, allocator.state);
123  asserted_rmw_impl = NULL;
124  } else {
125  // One or none are set.
126  // If asserted_rmw_impl has contents, move it over to expected_rmw_impl.
127  if (asserted_rmw_impl) {
128  expected_rmw_impl = asserted_rmw_impl;
129  asserted_rmw_impl = NULL;
130  }
131  }
132 
133  // If either environment variable is set, and it does not match, print an error and exit.
134  if (expected_rmw_impl) {
135  const char * actual_rmw_impl_id = rmw_get_implementation_identifier();
136  const rcutils_error_string_t rmw_error_msg = rcl_get_error_string();
137  rcl_reset_error();
138  if (!actual_rmw_impl_id) {
139  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
140  "Error getting RMW implementation identifier / RMW implementation not installed "
141  "(expected identifier of '%s'), with error message '%s', exiting with %d.",
142  expected_rmw_impl,
143  rmw_error_msg.str,
145  );
146  ret = RCL_RET_ERROR;
147  goto cleanup;
148  }
149  if (strcmp(actual_rmw_impl_id, expected_rmw_impl) != 0) {
150  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
151  "Expected RMW implementation identifier of '%s' but instead found '%s', exiting with %d.",
152  expected_rmw_impl,
153  actual_rmw_impl_id,
155  );
157  goto cleanup;
158  }
159  }
160  ret = RCL_RET_OK;
161 // fallthrough
162 cleanup:
163  allocator.deallocate(expected_rmw_impl, allocator.state);
164  allocator.deallocate(asserted_rmw_impl, allocator.state);
165  return ret;
166 }
167 
168 INITIALIZER(initialize) {
170  if (ret != RCL_RET_OK) {
171  RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, "%s\n", rcl_get_error_string().str);
172  exit(ret);
173  }
174 }
175 
176 #ifdef __cplusplus
177 }
178 #endif
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
#define RMW_IMPLEMENTATION_ENV_VAR_NAME
The environment variable name to control which RMW implementation is used.
#define RCL_ASSERT_RMW_ID_MATCHES_ENV_VAR_NAME
RCL_PUBLIC rcl_ret_t rcl_rmw_implementation_identifier_check(void)
Check whether the RMW implementation in use matches what the user requested.
#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_MISMATCHED_RMW_ID
Mismatched rmw identifier return code.
Definition: types.h:44
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:28
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:23