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