RTE RBC 2oo2 Reference Application 0.1.0 build 0006685
SIL 4 2oo2 Dual-Site Fault-Tolerant ERTMS Radio Block Centre Reference Architecture
Loading...
Searching...
No Matches
app_main_common.c
Go to the documentation of this file.
1/* _POSIX_C_SOURCE 200809L: nanosleep() below is POSIX.1-2001, not base
2 * ISO C99; must be defined before any header is included. */
3#define _POSIX_C_SOURCE 200809L
4
9#include "app_main_common.h"
10
11#include <stdio.h>
12#include <stdlib.h> /* getenv */
13#include <time.h>
14
15#include "rte/app/appmanager/rte_appmanager.h"
16#include "rte/redundancy/checksum/rte_checksum.h"
17#include "rte/oal/log/rte_log.h"
18#include "rte/oal/platform/rte_platform.h"
19#include "rte/posix_osadapter/rte_posix_osadapter.h"
20#include "rte/oal/reboot/rte_reboot.h"
21#include "rte/utils/safestate/rte_safestate.h"
22#include "rte/redundancy/watchdog/rte_watchdog.h"
23
24#include "common_config.h"
25
27
29
31
33
55static void reboot_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file,
56 int32_t line, const char *message);
71static void safe_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file,
72 int32_t line, const char *message);
80static void degraded_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file,
81 int32_t line, const char *message);
82
83
85rte_status_t app_main_common_init(int argc, char **argv)
86{
87 rte_status_t status;
88
89 /* Unbuffered: this app's stdout is diagnostic console output from
90 * several threads/processes racing in real time - buffering would
91 * make the interleaving misleading (or, worse, lose output entirely
92 * if a process is ever killed by a signal instead of returning from
93 * main()). Not a hot path, so the extra syscalls are fine here. */
94 (void)setvbuf(stdout, NULL, _IONBF, 0);
95
96 status = rte_posix_osadapter_register_all();
97 if (status != RTE_STATUS_OK)
98 {
99 /* The one deliberate exception to "every module uses rte_log":
100 * this call is what registers the log OSAdapter in the first
101 * place (rte_osadapter_log_register() is one of the registrations
102 * inside it) - if it failed, rte_log_write() cannot be trusted
103 * to reach anywhere, so this stays a raw fprintf(). Every other
104 * failure path in this file runs after this call has already
105 * succeeded and is a real rte_log_write() call. */
106 (void)fprintf(stderr, "rte_posix_osadapter_register_all() failed: %s\n", rte_status_to_string(status));
107 return status;
108 }
109
110 /* Configure real-time platform capabilities (mlockall, SCHED_FIFO on a
111 * PREEMPT_RT host) through the framework's OAL, not the OSAdapter
112 * directly - the concrete behavior is whatever
113 * rte_posix_osadapter_register_all() above registered (ADR-035).
114 * Best-effort: a (void)-cast NOT_SUPPORTED/partial result is expected
115 * on an unprivileged or non-RT host. */
116 (void)rte_platform_realtime_init(80U);
117
118 /* Optional diagnostic log verbosity: RTE_RBC_LOG_LEVEL=DEBUG|INFO|
119 * WARNING|ERROR raises rte_log's threshold for this process. Purely
120 * non-safety (rte_log, ADR-001 section 4); an unset or unrecognised
121 * value leaves the framework default. The C/Gateway role additionally
122 * accepts a runtime change and fans it to A and B (gateway_c_logctl.c),
123 * so the whole RBC pair can be re-levelled from one place. */
124 {
125 const char *lvl_env = getenv("RTE_RBC_LOG_LEVEL");
126 rte_log_level_t lvl;
127
128 if ((lvl_env != NULL) && (rte_log_level_from_string(lvl_env, &lvl) == RTE_STATUS_OK))
129 {
130 rte_log_write_event(RTE_LOG_LEVEL_INFO, "-", 0U, "MAIN", "-", "LOG_LEVEL",
131 "set from RTE_RBC_LOG_LEVEL", lvl_env);
132 rte_log_set_level(lvl);
133 }
134 }
135
136 /* Registered as early as possible (right after the OSAdapter that will
137 * use it is registered), so any later REBOOT re-execs with this same
138 * argv and this process resumes as the SAME role/site rather than
139 * losing that to a placeholder command line. See
140 * rte_posix_osadapter.h's own doc. */
141 rte_posix_osadapter_reboot_set_argv(argc, argv);
142
143 /* Required once before any rte_checksum_crc64() call (ab_gp_channel.c's
144 * A<->B peer-link integrity check). Harmless for roles that never call
145 * into rte_checksum - initializing unconditionally here keeps every
146 * main() simple and matches how OSAdapter registration above is
147 * likewise done once regardless of role. */
148 status = rte_checksum_crc64_init(RTE_EXAMPLE_CRC64_POLYNOMIAL);
149 if (status != RTE_STATUS_OK)
150 {
151 char msg[64];
152
153 (void)snprintf(msg, sizeof(msg), "rte_checksum_crc64_init() failed: %s", rte_status_to_string(status));
154 rte_log_write(RTE_LOG_LEVEL_ERROR, "MAIN", msg);
155 return status;
156 }
157
158 /* Idempotent (safe even though every one of this example's processes
159 * calls it independently - each process gets its own in-process
160 * watchdog pool, there is no cross-process sharing here). Harmless for
161 * roles that don't arm a watchdog of their own. */
162 status = rte_watchdog_manager_initialize();
163 if (status != RTE_STATUS_OK)
164 {
165 char msg[64];
166
167 (void)snprintf(msg, sizeof(msg), "rte_watchdog_manager_initialize() failed: %s",
168 rte_status_to_string(status));
169 rte_log_write(RTE_LOG_LEVEL_ERROR, "MAIN", msg);
170 return status;
171 }
172
173 /* rte_posix_osadapter_register_all() above already registered the log
174 * OSAdapter (rte_osadapter_log_register() inside it) - rte_log_write()/
175 * rte_log_write_event() are live from that point on, not from this
176 * call: the POSIX OSAdapter's own osadapter_init() is a pure no-op and
177 * osadapter_write() is stateless (see safeAPIBackendPosix's
178 * rte_posix_osadapter_log.c), so nothing here actually depends on
179 * rte_log_init() having run first. This call remains best-effort
180 * (REQ-OAL-LOG-001: a no-op if somehow no OSAdapter were registered) -
181 * every module in this application uses rte_log exclusively now,
182 * there is no parallel printf trail left to fall back on. */
183 (void)rte_log_init();
184
185 /* Non-fatal if this ever returns non-OK: on this project's only actual
186 * target (a real POSIX host, via src/posix_osadapter/) it always
187 * succeeds, but failing to install a convenience stop handler should
188 * not itself prevent the demo from running - it would just mean
189 * Ctrl+C/SIGTERM falls back to the OS default (which for A/B/C's
190 * now-infinite loops is still a clean process exit, just not a
191 * graceful rte_appmanager shutdown). */
192 status = rte_appmanager_install_default_signal_handlers();
193 if (status != RTE_STATUS_OK)
194 {
195 char msg[96];
196
197 (void)snprintf(msg, sizeof(msg), "rte_appmanager_install_default_signal_handlers(): %s - continuing anyway",
198 rte_status_to_string(status));
199 rte_log_write(RTE_LOG_LEVEL_WARNING, "MAIN", msg);
200 }
201
202 /* Only ab_gp_channel_crosscompare.c/ab_gp_channel_negotiate.c (A/B roles)
203 * ever call RTE_SAFESTATE_LEVEL_REBOOT in this demo, but registering
204 * the handler unconditionally here is harmless for C and keeps this
205 * the one place every role's REBOOT reaction is wired up, same
206 * rationale as the signal handler install above. */
207 status = rte_safestate_register_handler(RTE_SAFESTATE_LEVEL_REBOOT, reboot_safestate_handler);
208 if (status != RTE_STATUS_OK)
209 {
210 char msg[96];
211
212 (void)snprintf(msg, sizeof(msg), "rte_safestate_register_handler(REBOOT): %s - continuing anyway",
213 rte_status_to_string(status));
214 rte_log_write(RTE_LOG_LEVEL_WARNING, "MAIN", msg);
215 }
216
217 status = rte_safestate_register_handler(RTE_SAFESTATE_LEVEL_DEGRADED, degraded_safestate_handler);
218 if (status != RTE_STATUS_OK)
219 {
220 char msg[96];
221
222 (void)snprintf(msg, sizeof(msg), "rte_safestate_register_handler(DEGRADED): %s - continuing anyway",
223 rte_status_to_string(status));
224 rte_log_write(RTE_LOG_LEVEL_WARNING, "MAIN", msg);
225 }
226
227 /* See safe_safestate_handler's own doc: registering this is what
228 * turns a SAFE-level halt from a silent, undiagnosable freeze into
229 * one that at least logs why before it stops responding. */
230 status = rte_safestate_register_handler(RTE_SAFESTATE_LEVEL_SAFE, safe_safestate_handler);
231 if (status != RTE_STATUS_OK)
232 {
233 char msg[96];
234
235 (void)snprintf(msg, sizeof(msg), "rte_safestate_register_handler(SAFE): %s - continuing anyway",
236 rte_status_to_string(status));
237 rte_log_write(RTE_LOG_LEVEL_WARNING, "MAIN", msg);
238 }
239
240 return RTE_STATUS_OK;
241}
242
243
244/****Local functions ****/
245
246static void reboot_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file,
247 int32_t line, const char *message)
248{
249 char extra[32];
250
251 (void)level;
252 (void)file;
253 (void)line;
254 (void)message;
255 (void)snprintf(extra, sizeof(extra), "DelayMs=%u", (unsigned int)RTE_EXAMPLE_REBOOT_DELAY_MS);
256 /* No site identifier is available in this handler's scope (fixed
257 * rte_safestate handler signature, no user_ctx - registered once in
258 * app_main_common_init() before/independent of this process's own
259 * site/role) - "" per rte_log_write_event()'s own doc on a caller
260 * with no site concept reachable here. */
261 rte_log_write_event(RTE_LOG_LEVEL_WARNING, "", 0U, "REBOOT", "-", "REBOOT",
262 "FAULTED - simulating hardware reboot", extra);
263 {
264 struct timespec ts;
265
266 ts.tv_sec = (time_t)(RTE_EXAMPLE_REBOOT_DELAY_MS / 1000U);
267 ts.tv_nsec = (long)((RTE_EXAMPLE_REBOOT_DELAY_MS % 1000U) * 1000000L);
268 (void)nanosleep(&ts, NULL);
269 }
270 /* Info text literally contains "REBOOT requested" - depended on by
271 * safeAPIRBC2oo2TestEnv's own Robot fault-injection suite
272 * (ContainerSupportFunctions.extended_partition_container_and_verify_self_reboot(),
273 * which greps container logs for that exact substring to confirm the
274 * channel-down->REBOOT escalation actually fired) - do not reword
275 * this Info text without updating that check too. */
276 (void)snprintf(extra, sizeof(extra), "Reason=%u", (unsigned int)reason);
277 rte_log_write_event(RTE_LOG_LEVEL_WARNING, "", 0U, "REBOOT", "-", "REBOOT",
278 "REBOOT requested - re-execing process image", extra);
279 (void)rte_reboot_request((uint16_t)reason);
280}
281
282static void safe_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file,
283 int32_t line, const char *message)
284{
285 char extra[160];
286
287 (void)level;
288 (void)snprintf(extra, sizeof(extra), "Reason=%u File=%s Line=%d", (unsigned int)reason,
289 (file != NULL) ? file : "?", (int)line);
290 rte_log_write_event(RTE_LOG_LEVEL_ERROR, "", 0U, "SAFESTATE", "-", "SAFE_HALT",
291 (message != NULL) ? message : "entering SAFE state", extra);
292}
293
294static void degraded_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file,
295 int32_t line, const char *message)
296{
297 char extra[160];
298 const rte_log_level_t log_level =
299 (reason == RTE_SAFESTATE_REASON_SETUP_AFTER_INIT) ? RTE_LOG_LEVEL_ERROR : RTE_LOG_LEVEL_WARNING;
300
301 (void)level;
302 (void)snprintf(extra, sizeof(extra), "Reason=%u File=%s Line=%d", (unsigned int)reason,
303 (file != NULL) ? file : "?", (int)line);
304 rte_log_write_event(log_level, "", 0U, "SAFESTATE", "-", "DEGRADED",
305 (message != NULL) ? message : "DEGRADED reported", extra);
306}
static void degraded_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file, int32_t line, const char *message)
static void reboot_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file, int32_t line, const char *message)
rte_status_t app_main_common_init(int argc, char **argv)
Runs this process's one-time runtime setup, common to every role: unbuffered stdout,...
static void safe_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file, int32_t line, const char *message)
Process-wide runtime setup shared by every one of this example's binaries (main.c for A/B,...
#define RTE_EXAMPLE_REBOOT_DELAY_MS
#define RTE_EXAMPLE_CRC64_POLYNOMIAL