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
gateway_c_config.c
Go to the documentation of this file.
1
5
6#include "gateway_c_config.h"
7
8#include <ctype.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include "rte/oal/memory/rte_mem_util.h"
16
17#define MAX_CONFIG_FILE_SIZE 8192U
18
19static const char *skip_whitespace(const char *p)
20{
21 if (p == NULL)
22 {
23 return NULL;
24 }
25 while ((*p != '\0') && (isspace((unsigned char)*p) != 0))
26 {
27 p++;
28 }
29 return p;
30}
31
32static const char *extract_string(const char *p, char *out, size_t out_size)
33{
34 size_t idx = 0U;
35
36 p = skip_whitespace(p);
37 if (*p != '"')
38 {
39 return NULL;
40 }
41 p++;
42
43 while ((*p != '\0') && (*p != '"'))
44 {
45 if (idx + 1U < out_size)
46 {
47 out[idx++] = *p;
48 }
49 p++;
50 }
51
52 if (*p != '"')
53 {
54 return NULL;
55 }
56 out[idx] = '\0';
57 return p + 1;
58}
59
60static const char *extract_uint32(const char *p, uint32_t *out_val)
61{
62 char *endptr = NULL;
63 unsigned long val;
64
65 p = skip_whitespace(p);
66 val = strtoul(p, &endptr, 10);
67 if (endptr == p)
68 {
69 return NULL;
70 }
71 *out_val = (uint32_t)val;
72 return endptr;
73}
74
87static const char *extract_uint32_checked(const char *p, uint32_t *out_val)
88{
89 const char *q = skip_whitespace(p);
90 const char *start = q;
91 const char *result = NULL;
92 uint32_t value = 0U;
93 bool overflow = false;
94
95 while ((!overflow) && (*q >= '0') && (*q <= '9'))
96 {
97 const uint32_t digit = (uint32_t)((uint8_t)*q) - (uint32_t)((uint8_t)'0');
98
99 if (value > ((UINT32_MAX - digit) / 10U))
100 {
101 overflow = true;
102 }
103 else
104 {
105 value = (value * 10U) + digit;
106 q++;
107 }
108 }
109 if ((!overflow) && (q != start))
110 {
111 const char *after = skip_whitespace(q);
112
113 if ((*after == ',') || (*after == '}'))
114 {
115 *out_val = value;
116 result = q;
117 }
118 }
119 return result;
120}
121
123{
124 if (out_cfg == NULL)
125 {
126 return RTE_STATUS_INVALID_PARAM;
127 }
128
129 rte_mem_set(out_cfg, 0, sizeof(*out_cfg));
130 out_cfg->site = site;
135
136 return RTE_STATUS_OK;
137}
138
140static const char *skip_separator(const char *p)
141{
142 p = skip_whitespace(p);
143 if ((p != NULL) && (*p == ','))
144 {
145 p++;
146 }
147 return p;
148}
149
151static const char *extract_into(const char *p, char *dest, size_t dest_size)
152{
153 char text[64];
154 const char *next = extract_string(p, text, sizeof(text));
155
156 if ((next != NULL) && (dest != NULL))
157 {
158 (void)snprintf(dest, dest_size, "%.*s", (int)(dest_size - 1U), text); /* explicit truncation */
159 }
160 return next;
161}
162
164static const char *extract_number_into(const char *p, uint32_t *field)
165{
166 uint32_t value = 0U;
167 const char *next = extract_uint32(p, &value);
168
169 if ((next != NULL) && (field != NULL))
170 {
171 *field = value;
172 }
173 return next;
174}
175
177static const char *extract_buffer_size_into(const char *p, uint32_t *field)
178{
179 uint32_t value = 0U;
180 const char *next = extract_uint32_checked(p, &value);
181
182 if ((next != NULL) && (field != NULL))
183 {
184 *field = value;
185 }
186 return next;
187}
188
196static const char *parse_channel_prop(const char *p, const char *key, gateway_c_config_channel_t *ch,
197 bool allow_identity)
198{
199 uint32_t number = 0U;
200 char text[64];
201
202 if (allow_identity && (strcmp(key, "id") == 0))
203 {
204 return extract_number_into(p, (ch != NULL) ? &ch->id : NULL);
205 }
206 if (allow_identity && (strcmp(key, "name") == 0))
207 {
208 return extract_into(p, (ch != NULL) ? ch->name : NULL, sizeof(ch->name));
209 }
210 if (strcmp(key, "role") == 0)
211 {
212 const char *next = extract_string(p, text, 16U);
213
214 if ((next != NULL) && (ch != NULL))
215 {
216 ch->role = ((strcmp(text, "CONNECT") == 0) || (strcmp(text, "connect") == 0))
217 ? RTE_NETLINK_ROLE_CONNECT
218 : RTE_NETLINK_ROLE_LISTEN;
219 }
220 return next;
221 }
222 if (strcmp(key, "port") == 0)
223 {
224 const char *next = extract_uint32(p, &number);
225
226 if ((next != NULL) && (ch != NULL))
227 {
228 ch->port = (uint16_t)number;
229 }
230 return next;
231 }
232 if (strcmp(key, "host") == 0)
233 {
234 return extract_into(p, (ch != NULL) ? ch->host : NULL, sizeof(ch->host));
235 }
236 if (strcmp(key, "message_size") == 0)
237 {
238 return extract_number_into(p, (ch != NULL) ? &ch->message_size : NULL);
239 }
240 if (strcmp(key, "connect_timeout_ms") == 0)
241 {
242 return extract_number_into(p, (ch != NULL) ? &ch->connect_timeout_ms : NULL);
243 }
244 /* REQ-OAL-NETLINK-015: optional socket buffer sizes, 0 = OS default. A rejected value fails the load. */
245 if (strcmp(key, "rcvbuf_bytes") == 0)
246 {
247 return extract_buffer_size_into(p, (ch != NULL) ? &ch->rcvbuf_bytes : NULL);
248 }
249 if (strcmp(key, "sndbuf_bytes") == 0)
250 {
251 return extract_buffer_size_into(p, (ch != NULL) ? &ch->sndbuf_bytes : NULL);
252 }
253
254 /* Unknown property: consume a string or a number, ignore it. */
255 if (*p == '"')
256 {
257 return extract_string(p, text, sizeof(text));
258 }
259 return extract_uint32(p, &number);
260}
261
267static const char *parse_channel_object(const char *p, gateway_c_config_t *cfg, const char *fixed_name)
268{
270
272 {
273 ch = &cfg->channels[cfg->channel_count++];
274 rte_mem_set(ch, 0, sizeof(*ch));
275 if (fixed_name != NULL)
276 {
277 (void)snprintf(ch->name, sizeof(ch->name), "%.*s", (int)(sizeof(ch->name) - 1U), fixed_name);
278 }
279 ch->is_configured = true;
281 }
282
283 p++; /* Skip '{' */
284 while ((*p != '\0') && (*p != '}'))
285 {
286 char prop_key[32];
287
288 p = skip_whitespace(p);
289 if (*p == '}')
290 {
291 break;
292 }
293 p = extract_string(p, prop_key, sizeof(prop_key));
294 if (p == NULL)
295 {
296 return NULL;
297 }
298 p = skip_whitespace(p);
299 if (*p == ':')
300 {
301 p++;
302 }
303 p = skip_whitespace(p);
304 p = parse_channel_prop(p, prop_key, ch, fixed_name == NULL);
305 if (p == NULL)
306 {
307 return NULL;
308 }
309 p = skip_separator(p);
310 }
311 if (*p == '}')
312 {
313 p++;
314 }
315 return p;
316}
317
319static const char *parse_channels_array(const char *p, gateway_c_config_t *cfg)
320{
321 p++; /* Skip '[' */
322 while ((*p != '\0') && (*p != ']'))
323 {
324 p = skip_whitespace(p);
325 if (*p == ']')
326 {
327 break;
328 }
329 if (*p == '{')
330 {
331 p = parse_channel_object(p, cfg, NULL);
332 if (p == NULL)
333 {
334 return NULL;
335 }
336 }
337 else
338 {
339 p++; /* Stray character: skip so the scan always advances */
340 }
341 p = skip_separator(p);
342 }
343 if (*p == ']')
344 {
345 p++;
346 }
347 return p;
348}
349
351static const char *parse_channels_object(const char *p, gateway_c_config_t *cfg)
352{
353 p++; /* Skip '{' */
354 while ((*p != '\0') && (*p != '}'))
355 {
356 char ch_name[64];
357
358 p = skip_whitespace(p);
359 if (*p == '}')
360 {
361 break;
362 }
363 p = extract_string(p, ch_name, sizeof(ch_name));
364 if (p == NULL)
365 {
366 return NULL;
367 }
368 p = skip_whitespace(p);
369 if (*p == ':')
370 {
371 p++;
372 }
373 p = skip_whitespace(p);
374 if (*p == '{')
375 {
376 p = parse_channel_object(p, cfg, ch_name);
377 if (p == NULL)
378 {
379 return NULL;
380 }
381 }
382 p = skip_separator(p);
383 }
384 if (*p == '}')
385 {
386 p++;
387 }
388 return p;
389}
390
392static const char *parse_top_level_value(const char *p, const char *key, gateway_c_config_t *cfg)
393{
394 if (strcmp(key, "site") == 0)
395 {
396 char site_str[32];
397 const char *next = extract_string(p, site_str, sizeof(site_str));
398
399 if (next != NULL)
400 {
401 (void)site_config_parse(site_str, &cfg->site);
402 }
403 return next;
404 }
405 if (strcmp(key, "report_period_ms") == 0)
406 {
407 return extract_uint32(p, &cfg->timers.report_period_ms);
408 }
409 if (strcmp(key, "link_timeout_ms") == 0)
410 {
411 return extract_uint32(p, &cfg->timers.link_timeout_ms);
412 }
413 if (strcmp(key, "connect_timeout_ms") == 0)
414 {
416 }
417 if (strcmp(key, "reconnect_attempt_timeout_ms") == 0)
418 {
420 }
421 if (strcmp(key, "channels") == 0)
422 {
423 if (*p == '[')
424 {
425 return parse_channels_array(p, cfg);
426 }
427 if (*p == '{')
428 {
429 return parse_channels_object(p, cfg);
430 }
431 }
432 return p; /* Unknown key or container: the scan below walks into it */
433}
434
435rte_status_t gateway_c_config_load_file(const char *filepath, gateway_c_config_t *out_cfg)
436{
437 FILE *fp;
438 char buffer[MAX_CONFIG_FILE_SIZE];
439 size_t bytes_read;
440 const char *p;
441
442 if ((filepath == NULL) || (out_cfg == NULL))
443 {
444 return RTE_STATUS_INVALID_PARAM;
445 }
446
447 fp = fopen(filepath, "r");
448 if (fp == NULL)
449 {
450 return RTE_STATUS_RESOURCE_EXHAUSTED;
451 }
452
453 bytes_read = fread(buffer, 1, sizeof(buffer) - 1U, fp);
454 (void)fclose(fp);
455 buffer[bytes_read] = '\0';
456
457 /* Flat scan: any "key": value pair is dispatched wherever it appears, so the nested "timers" object
458 * needs no handling of its own. A malformed value for a known key is an error, never a crash. */
459 p = buffer;
460 while (*p != '\0')
461 {
462 char key[64];
463
464 if (*p != '"')
465 {
466 p++;
467 continue;
468 }
469 p = extract_string(p, key, sizeof(key));
470 if (p == NULL)
471 {
472 break; /* Unterminated string: truncated file, keep what was parsed */
473 }
474 p = skip_whitespace(p);
475 if (*p != ':')
476 {
477 continue;
478 }
479 p++; /* Skip ':' */
480 p = skip_whitespace(p);
481 p = parse_top_level_value(p, key, out_cfg);
482 if (p == NULL)
483 {
484 return RTE_STATUS_INVALID_PARAM;
485 }
486 }
487
488 return RTE_STATUS_OK;
489}
490
491rte_status_t gateway_c_config_find_channel(const gateway_c_config_t *cfg, const char *channel_name,
492 rte_netlink_config_t *out_netlink_cfg)
493{
494 uint8_t i;
495
496 if ((cfg == NULL) || (channel_name == NULL) || (out_netlink_cfg == NULL))
497 {
498 return RTE_STATUS_INVALID_PARAM;
499 }
500
501 for (i = 0U; i < cfg->channel_count; i++)
502 {
503 const gateway_c_config_channel_t *ch = &cfg->channels[i];
504 if (ch->is_configured && (strcmp(ch->name, channel_name) == 0))
505 {
506 rte_mem_set(out_netlink_cfg, 0, sizeof(*out_netlink_cfg));
507 out_netlink_cfg->role = ch->role;
508 out_netlink_cfg->host = (ch->role == RTE_NETLINK_ROLE_CONNECT && ch->host[0] != '\0') ? ch->host : NULL;
509 out_netlink_cfg->port = ch->port;
510 out_netlink_cfg->message_size = (ch->message_size > 0U) ? ch->message_size : RBC_ENVELOPE_WIRE_SIZE;
511 out_netlink_cfg->connect_timeout_ms = (ch->connect_timeout_ms > 0U) ? ch->connect_timeout_ms
513 out_netlink_cfg->rcvbuf_bytes = ch->rcvbuf_bytes;
514 out_netlink_cfg->sndbuf_bytes = ch->sndbuf_bytes;
515 return RTE_STATUS_OK;
516 }
517 }
518
519 return RTE_STATUS_RESOURCE_EXHAUSTED;
520}
521
522rte_status_t gateway_c_config_find_channel_by_id(const gateway_c_config_t *cfg, uint32_t channel_id,
523 rte_netlink_config_t *out_netlink_cfg)
524{
525 uint8_t i;
526
527 if ((cfg == NULL) || (channel_id == 0U) || (out_netlink_cfg == NULL))
528 {
529 return RTE_STATUS_INVALID_PARAM;
530 }
531
532 for (i = 0U; i < cfg->channel_count; i++)
533 {
534 const gateway_c_config_channel_t *ch = &cfg->channels[i];
535 if (ch->is_configured && (ch->id == channel_id))
536 {
537 rte_mem_set(out_netlink_cfg, 0, sizeof(*out_netlink_cfg));
538 out_netlink_cfg->role = ch->role;
539 out_netlink_cfg->host = (ch->role == RTE_NETLINK_ROLE_CONNECT && ch->host[0] != '\0') ? ch->host : NULL;
540 out_netlink_cfg->port = ch->port;
541 out_netlink_cfg->message_size = (ch->message_size > 0U) ? ch->message_size : RBC_ENVELOPE_WIRE_SIZE;
542 out_netlink_cfg->connect_timeout_ms = (ch->connect_timeout_ms > 0U) ? ch->connect_timeout_ms
544 out_netlink_cfg->rcvbuf_bytes = ch->rcvbuf_bytes;
545 out_netlink_cfg->sndbuf_bytes = ch->sndbuf_bytes;
546 return RTE_STATUS_OK;
547 }
548 }
549
550 return RTE_STATUS_RESOURCE_EXHAUSTED;
551}
552
#define RTE_EXAMPLE_C_REPORT_PERIOD_MS
#define RTE_EXAMPLE_RECONNECT_ATTEMPT_TIMEOUT_MS
#define RBC_ENVELOPE_WIRE_SIZE
#define RTE_EXAMPLE_LINK_TIMEOUT_MS
#define RTE_EXAMPLE_CONNECT_TIMEOUT_MS
rte_status_t gateway_c_config_find_channel(const gateway_c_config_t *cfg, const char *channel_name, rte_netlink_config_t *out_netlink_cfg)
static const char * parse_channels_array(const char *p, gateway_c_config_t *cfg)
static const char * extract_string(const char *p, char *out, size_t out_size)
static const char * extract_buffer_size_into(const char *p, uint32_t *field)
rte_status_t gateway_c_config_init_defaults(rte_example_site_t site, gateway_c_config_t *out_cfg)
static const char * skip_separator(const char *p)
#define MAX_CONFIG_FILE_SIZE
static const char * skip_whitespace(const char *p)
static const char * extract_number_into(const char *p, uint32_t *field)
static const char * extract_into(const char *p, char *dest, size_t dest_size)
static const char * parse_channels_object(const char *p, gateway_c_config_t *cfg)
static const char * parse_channel_prop(const char *p, const char *key, gateway_c_config_channel_t *ch, bool allow_identity)
Parses the value of one channel property into ch.
static const char * extract_uint32_checked(const char *p, uint32_t *out_val)
Checked unsigned decimal parse, used for the socket buffer keys (REQ-OAL-NETLINK-015).
static const char * parse_top_level_value(const char *p, const char *key, gateway_c_config_t *cfg)
static const char * extract_uint32(const char *p, uint32_t *out_val)
rte_status_t gateway_c_config_find_channel_by_id(const gateway_c_config_t *cfg, uint32_t channel_id, rte_netlink_config_t *out_netlink_cfg)
static const char * parse_channel_object(const char *p, gateway_c_config_t *cfg, const char *fixed_name)
Parses one channel object; p points at its '{'.
rte_status_t gateway_c_config_load_file(const char *filepath, gateway_c_config_t *out_cfg)
Configuration data structures and parser for Role C Gateway (JSON-based).
#define GATEWAY_C_MAX_CHANNELS
The RBC Train/IL/CTC scenario's shared wire envelope (ADR-029) - one fixed-size struct/frame reused f...
bool site_config_parse(const char *arg, rte_example_site_t *out_site)
Parses a site name argument ("WEST"/"EAST", case-insensitive).
Definition site_config.c:33
rte_example_site_t
Definition site_config.h:20
gateway_c_config_timers_t timers
rte_example_site_t site
gateway_c_config_channel_t channels[GATEWAY_C_MAX_CHANNELS]