Move to kernel style SPDX license identifiers
[babeltrace.git] / src / cli / babeltrace2-cfg-cli-args-connect.c
CommitLineData
ebba3338 1/*
0235b0db 2 * SPDX-License-Identifier: MIT
ebba3338 3 *
0235b0db 4 * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
ebba3338
PP
5 */
6
c4f23e30 7#include <stdbool.h>
91d81473 8#include <stdio.h>
0fbb9a9f 9#include <stdlib.h>
91d81473 10#include <string.h>
3fadfbc0 11#include <babeltrace2/babeltrace.h>
578e048b 12#include "common/common.h"
ec2c5e50
MJ
13#include "babeltrace2-cfg.h"
14#include "babeltrace2-cfg-cli-args-connect.h"
ebba3338 15
db0f160a 16static bool all_named_and_printable_in_array(GPtrArray *comps)
ebba3338
PP
17{
18 size_t i;
db0f160a 19 bool all_named_and_printable = true;
ebba3338
PP
20
21 for (i = 0; i < comps->len; i++) {
22 struct bt_config_component *comp = g_ptr_array_index(comps, i);
23
24 if (comp->instance_name->len == 0) {
db0f160a
PP
25 all_named_and_printable = false;
26 goto end;
27 }
28
29 if (!bt_common_string_is_printable(comp->instance_name->str)) {
30 all_named_and_printable = false;
ebba3338
PP
31 goto end;
32 }
33 }
34
35end:
db0f160a 36 return all_named_and_printable;
ebba3338
PP
37}
38
db0f160a 39static bool all_named_and_printable(struct bt_config *cfg)
ebba3338 40{
db0f160a
PP
41 return all_named_and_printable_in_array(cfg->cmd_data.run.sources) &&
42 all_named_and_printable_in_array(cfg->cmd_data.run.filters) &&
43 all_named_and_printable_in_array(cfg->cmd_data.run.sinks);
ebba3338
PP
44}
45
ebba3338
PP
46static struct bt_config_connection *bt_config_connection_create(const char *arg)
47{
48 struct bt_config_connection *cfg_connection;
49
50 cfg_connection = g_new0(struct bt_config_connection, 1);
51 if (!cfg_connection) {
52 goto error;
53 }
54
9009cc24
PP
55 cfg_connection->upstream_comp_name = g_string_new(NULL);
56 if (!cfg_connection->upstream_comp_name) {
ebba3338
PP
57 goto error;
58 }
59
9009cc24
PP
60 cfg_connection->downstream_comp_name = g_string_new(NULL);
61 if (!cfg_connection->downstream_comp_name) {
ebba3338
PP
62 goto error;
63 }
64
9009cc24
PP
65 cfg_connection->upstream_port_glob = g_string_new("*");
66 if (!cfg_connection->upstream_port_glob) {
ebba3338
PP
67 goto error;
68 }
69
9009cc24
PP
70 cfg_connection->downstream_port_glob = g_string_new("*");
71 if (!cfg_connection->downstream_port_glob) {
ebba3338
PP
72 goto error;
73 }
74
75 cfg_connection->arg = g_string_new(arg);
76 if (!cfg_connection->arg) {
77 goto error;
78 }
79
80 goto end;
81
82error:
83 g_free(cfg_connection);
84 cfg_connection = NULL;
85
86end:
87 return cfg_connection;
88}
89
9009cc24
PP
90static bool validate_port_glob(const char *port_glob)
91{
92 bool is_valid = true;
93 const char *ch = port_glob;
94
f6ccaed9 95 BT_ASSERT(port_glob);
9009cc24
PP
96
97 while (*ch != '\0') {
98 switch (*ch) {
99 case '\\':
100 switch (ch[1]) {
101 case '\0':
102 goto end;
103 default:
104 ch += 2;
105 continue;
106 }
107 case '?':
108 case '[':
109 /*
110 * This is reserved for future use, to support
111 * full globbing patterns. Those characters must
112 * be escaped with `\`.
113 */
114 is_valid = false;
115 goto end;
116 default:
117 ch++;
118 break;
119 }
120 }
121
122end:
123 return is_valid;
124}
125
126static int normalize_glob_pattern(GString *glob_pattern_gs)
127{
128 int ret = 0;
129 char *glob_pattern = strdup(glob_pattern_gs->str);
130
131 if (!glob_pattern) {
132 ret = -1;
133 goto end;
134 }
135
136 bt_common_normalize_star_glob_pattern(glob_pattern);
137 g_string_assign(glob_pattern_gs, glob_pattern);
138 free(glob_pattern);
139
140end:
141 return ret;
142}
143
ebba3338
PP
144static struct bt_config_connection *cfg_connection_from_arg(const char *arg)
145{
db0f160a
PP
146 const char *at = arg;
147 size_t end_pos;
148 struct bt_config_connection *cfg_conn = NULL;
149 GString *gs = NULL;
ebba3338 150 enum {
9009cc24
PP
151 UPSTREAM_NAME,
152 DOWNSTREAM_NAME,
153 UPSTREAM_PORT_GLOB,
154 DOWNSTREAM_PORT_GLOB,
155 } state = UPSTREAM_NAME;
db0f160a
PP
156
157 if (!bt_common_string_is_printable(arg)) {
ebba3338
PP
158 goto error;
159 }
160
db0f160a
PP
161 cfg_conn = bt_config_connection_create(arg);
162 if (!cfg_conn) {
ebba3338
PP
163 goto error;
164 }
165
ebba3338 166 while (true) {
ebba3338 167 switch (state) {
9009cc24 168 case UPSTREAM_NAME:
db0f160a
PP
169 gs = bt_common_string_until(at, ".:\\", ".:", &end_pos);
170 if (!gs || gs->len == 0) {
ebba3338
PP
171 goto error;
172 }
173
9009cc24
PP
174 g_string_free(cfg_conn->upstream_comp_name, TRUE);
175 cfg_conn->upstream_comp_name = gs;
db0f160a 176 gs = NULL;
ebba3338 177
db0f160a 178 if (at[end_pos] == ':') {
9009cc24 179 state = DOWNSTREAM_NAME;
db0f160a 180 } else if (at[end_pos] == '.') {
9009cc24 181 state = UPSTREAM_PORT_GLOB;
db0f160a 182 } else {
ebba3338
PP
183 goto error;
184 }
185
db0f160a 186 at += end_pos + 1;
ebba3338 187 break;
9009cc24 188 case DOWNSTREAM_NAME:
db0f160a
PP
189 gs = bt_common_string_until(at, ".:\\", ".:", &end_pos);
190 if (!gs || gs->len == 0) {
ebba3338
PP
191 goto error;
192 }
193
9009cc24
PP
194 g_string_free(cfg_conn->downstream_comp_name, TRUE);
195 cfg_conn->downstream_comp_name = gs;
db0f160a
PP
196 gs = NULL;
197
198 if (at[end_pos] == '.') {
9009cc24 199 state = DOWNSTREAM_PORT_GLOB;
db0f160a
PP
200 } else if (at[end_pos] == '\0') {
201 goto end;
202 } else {
ebba3338
PP
203 goto error;
204 }
205
db0f160a 206 at += end_pos + 1;
ebba3338 207 break;
9009cc24
PP
208 case UPSTREAM_PORT_GLOB:
209 gs = bt_common_string_until(at, ".:", ".:", &end_pos);
db0f160a 210 if (!gs || gs->len == 0) {
ebba3338
PP
211 goto error;
212 }
213
9009cc24
PP
214 if (!validate_port_glob(gs->str)) {
215 goto error;
216 }
217
218 if (normalize_glob_pattern(gs)) {
219 goto error;
220 }
221
222 g_string_free(cfg_conn->upstream_port_glob, TRUE);
223 cfg_conn->upstream_port_glob = gs;
db0f160a
PP
224 gs = NULL;
225
226 if (at[end_pos] == ':') {
9009cc24 227 state = DOWNSTREAM_NAME;
db0f160a 228 } else {
ebba3338
PP
229 goto error;
230 }
231
db0f160a 232 at += end_pos + 1;
ebba3338 233 break;
9009cc24
PP
234 case DOWNSTREAM_PORT_GLOB:
235 gs = bt_common_string_until(at, ".:", ".:", &end_pos);
db0f160a 236 if (!gs || gs->len == 0) {
ebba3338
PP
237 goto error;
238 }
239
9009cc24
PP
240 if (!validate_port_glob(gs->str)) {
241 goto error;
242 }
243
244 if (normalize_glob_pattern(gs)) {
245 goto error;
246 }
247
248 g_string_free(cfg_conn->downstream_port_glob, TRUE);
249 cfg_conn->downstream_port_glob = gs;
db0f160a
PP
250 gs = NULL;
251
252 if (at[end_pos] == '\0') {
253 goto end;
254 } else {
255 goto error;
256 }
ebba3338
PP
257 break;
258 default:
498e7994 259 bt_common_abort();
ebba3338
PP
260 }
261 }
262
ebba3338 263error:
db0f160a
PP
264 bt_config_connection_destroy(cfg_conn);
265 cfg_conn = NULL;
ebba3338
PP
266
267end:
db0f160a
PP
268 if (gs) {
269 g_string_free(gs, TRUE);
ebba3338
PP
270 }
271
db0f160a 272 return cfg_conn;
ebba3338
PP
273}
274
275static struct bt_config_component *find_component_in_array(GPtrArray *comps,
276 const char *name)
277{
278 size_t i;
279 struct bt_config_component *found_comp = NULL;
280
281 for (i = 0; i < comps->len; i++) {
282 struct bt_config_component *comp = g_ptr_array_index(comps, i);
283
284 if (strcmp(name, comp->instance_name->str) == 0) {
398454ed
PP
285 found_comp = comp;
286 bt_object_get_ref(found_comp);
ebba3338
PP
287 goto end;
288 }
289 }
290
291end:
292 return found_comp;
293}
294
295static struct bt_config_component *find_component(struct bt_config *cfg,
90de159b 296 const char *name)
ebba3338
PP
297{
298 struct bt_config_component *comp;
299
db0f160a 300 comp = find_component_in_array(cfg->cmd_data.run.sources, name);
ebba3338 301 if (comp) {
ebba3338
PP
302 goto end;
303 }
304
db0f160a 305 comp = find_component_in_array(cfg->cmd_data.run.filters, name);
ebba3338 306 if (comp) {
ebba3338
PP
307 goto end;
308 }
309
db0f160a 310 comp = find_component_in_array(cfg->cmd_data.run.sinks, name);
ebba3338 311 if (comp) {
ebba3338
PP
312 goto end;
313 }
314
315end:
316 return comp;
317}
318
319static int validate_all_endpoints_exist(struct bt_config *cfg, char *error_buf,
320 size_t error_buf_size)
321{
322 size_t i;
323 int ret = 0;
324
db0f160a 325 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
ebba3338 326 struct bt_config_connection *connection =
db0f160a 327 g_ptr_array_index(cfg->cmd_data.run.connections, i);
ebba3338 328 struct bt_config_component *comp;
ebba3338 329
9009cc24 330 comp = find_component(cfg, connection->upstream_comp_name->str);
65300d60 331 bt_object_put_ref(comp);
ebba3338 332 if (!comp) {
db0f160a
PP
333 snprintf(error_buf, error_buf_size,
334 "Invalid connection: cannot find upstream component `%s`:\n %s\n",
9009cc24 335 connection->upstream_comp_name->str,
db0f160a
PP
336 connection->arg->str);
337 ret = -1;
338 goto end;
339 }
340
9009cc24 341 comp = find_component(cfg, connection->downstream_comp_name->str);
65300d60 342 bt_object_put_ref(comp);
db0f160a
PP
343 if (!comp) {
344 snprintf(error_buf, error_buf_size,
345 "Invalid connection: cannot find downstream component `%s`:\n %s\n",
9009cc24 346 connection->downstream_comp_name->str,
db0f160a
PP
347 connection->arg->str);
348 ret = -1;
349 goto end;
ebba3338
PP
350 }
351 }
352
353end:
354 return ret;
355}
356
357static int validate_connection_directions(struct bt_config *cfg,
358 char *error_buf, size_t error_buf_size)
359{
360 size_t i;
361 int ret = 0;
362 struct bt_config_component *src_comp = NULL;
363 struct bt_config_component *dst_comp = NULL;
364
db0f160a 365 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
ebba3338 366 struct bt_config_connection *connection =
db0f160a 367 g_ptr_array_index(cfg->cmd_data.run.connections, i);
ebba3338
PP
368
369 src_comp = find_component(cfg,
9009cc24 370 connection->upstream_comp_name->str);
f6ccaed9 371 BT_ASSERT(src_comp);
ebba3338 372 dst_comp = find_component(cfg,
9009cc24 373 connection->downstream_comp_name->str);
f6ccaed9 374 BT_ASSERT(dst_comp);
ebba3338 375
90de159b
PP
376 if (src_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) {
377 if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER &&
378 dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) {
ebba3338
PP
379 snprintf(error_buf, error_buf_size,
380 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
9009cc24 381 connection->upstream_comp_name->str,
ebba3338
PP
382 connection->arg->str);
383 ret = -1;
384 goto end;
385 }
90de159b
PP
386 } else if (src_comp->type == BT_COMPONENT_CLASS_TYPE_FILTER) {
387 if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER &&
388 dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) {
ebba3338
PP
389 snprintf(error_buf, error_buf_size,
390 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
9009cc24 391 connection->upstream_comp_name->str,
ebba3338
PP
392 connection->arg->str);
393 ret = -1;
394 goto end;
395 }
396 } else {
397 snprintf(error_buf, error_buf_size,
398 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
9009cc24
PP
399 connection->upstream_comp_name->str,
400 connection->downstream_comp_name->str,
ebba3338
PP
401 connection->arg->str);
402 ret = -1;
403 goto end;
404 }
405
65300d60
PP
406 BT_OBJECT_PUT_REF_AND_RESET(src_comp);
407 BT_OBJECT_PUT_REF_AND_RESET(dst_comp);
ebba3338
PP
408 }
409
410end:
65300d60
PP
411 bt_object_put_ref(src_comp);
412 bt_object_put_ref(dst_comp);
ebba3338
PP
413 return ret;
414}
415
db0f160a
PP
416static int validate_no_cycles_rec(struct bt_config *cfg, GPtrArray *path,
417 char *error_buf, size_t error_buf_size)
418{
419 int ret = 0;
420 size_t conn_i;
421 const char *src_comp_name;
422
f6ccaed9 423 BT_ASSERT(path && path->len > 0);
db0f160a
PP
424 src_comp_name = g_ptr_array_index(path, path->len - 1);
425
426 for (conn_i = 0; conn_i < cfg->cmd_data.run.connections->len; conn_i++) {
427 struct bt_config_connection *conn =
428 g_ptr_array_index(cfg->cmd_data.run.connections, conn_i);
429
9009cc24 430 if (strcmp(conn->upstream_comp_name->str, src_comp_name) == 0) {
db0f160a
PP
431 size_t path_i;
432
433 for (path_i = 0; path_i < path->len; path_i++) {
434 const char *comp_name =
435 g_ptr_array_index(path, path_i);
436
9009cc24 437 if (strcmp(comp_name, conn->downstream_comp_name->str) == 0) {
db0f160a
PP
438 snprintf(error_buf, error_buf_size,
439 "Invalid connection: connection forms a cycle:\n %s\n",
440 conn->arg->str);
441 ret = -1;
442 goto end;
443 }
444 }
445
9009cc24 446 g_ptr_array_add(path, conn->downstream_comp_name->str);
db0f160a
PP
447 ret = validate_no_cycles_rec(cfg, path, error_buf,
448 error_buf_size);
449 if (ret) {
450 goto end;
451 }
452
453 g_ptr_array_remove_index(path, path->len - 1);
454 }
455 }
456
457end:
458 return ret;
459}
460
461static int validate_no_cycles(struct bt_config *cfg, char *error_buf,
ebba3338
PP
462 size_t error_buf_size)
463{
464 size_t i;
465 int ret = 0;
db0f160a 466 GPtrArray *path;
ebba3338 467
db0f160a
PP
468 path = g_ptr_array_new();
469 if (!path) {
470 ret = -1;
471 goto end;
472 }
ebba3338 473
db0f160a
PP
474 g_ptr_array_add(path, NULL);
475
476 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
477 struct bt_config_connection *conn =
478 g_ptr_array_index(cfg->cmd_data.run.connections, i);
479
9009cc24 480 g_ptr_array_index(path, 0) = conn->upstream_comp_name->str;
db0f160a
PP
481 ret = validate_no_cycles_rec(cfg, path,
482 error_buf, error_buf_size);
483 if (ret) {
ebba3338
PP
484 goto end;
485 }
486 }
487
488end:
db0f160a
PP
489 if (path) {
490 g_ptr_array_free(path, TRUE);
491 }
492
ebba3338
PP
493 return ret;
494}
495
496static int validate_all_components_connected_in_array(GPtrArray *comps,
b19ff26f 497 const bt_value *connected_components,
ebba3338
PP
498 char *error_buf, size_t error_buf_size)
499{
500 int ret = 0;
501 size_t i;
502
503 for (i = 0; i < comps->len; i++) {
504 struct bt_config_component *comp = g_ptr_array_index(comps, i);
505
07208d85 506 if (!bt_value_map_has_entry(connected_components,
ebba3338
PP
507 comp->instance_name->str)) {
508 snprintf(error_buf, error_buf_size,
509 "Component `%s` is not connected\n",
510 comp->instance_name->str);
511 ret = -1;
512 goto end;
513 }
514 }
515
516end:
517 return ret;
518}
519
520static int validate_all_components_connected(struct bt_config *cfg,
521 char *error_buf, size_t error_buf_size)
522{
523 size_t i;
524 int ret = 0;
b19ff26f 525 bt_value *connected_components = bt_value_map_create();
ebba3338
PP
526
527 if (!connected_components) {
528 ret = -1;
529 goto end;
530 }
531
db0f160a 532 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
ebba3338 533 struct bt_config_connection *connection =
db0f160a 534 g_ptr_array_index(cfg->cmd_data.run.connections, i);
ebba3338 535
05e21286 536 ret = bt_value_map_insert_entry(connected_components,
9009cc24 537 connection->upstream_comp_name->str, bt_value_null);
ebba3338
PP
538 if (ret) {
539 goto end;
540 }
541
05e21286 542 ret = bt_value_map_insert_entry(connected_components,
9009cc24 543 connection->downstream_comp_name->str, bt_value_null);
ebba3338
PP
544 if (ret) {
545 goto end;
546 }
547 }
548
549 ret = validate_all_components_connected_in_array(
da91b29a 550 cfg->cmd_data.run.sources,
05e21286 551 connected_components,
ebba3338
PP
552 error_buf, error_buf_size);
553 if (ret) {
554 goto end;
555 }
556
557 ret = validate_all_components_connected_in_array(
da91b29a 558 cfg->cmd_data.run.filters,
05e21286 559 connected_components,
ebba3338
PP
560 error_buf, error_buf_size);
561 if (ret) {
562 goto end;
563 }
564
565 ret = validate_all_components_connected_in_array(
da91b29a 566 cfg->cmd_data.run.sinks,
05e21286 567 connected_components,
ebba3338
PP
568 error_buf, error_buf_size);
569 if (ret) {
570 goto end;
571 }
572
573end:
c5b9b441 574 bt_value_put_ref(connected_components);
ebba3338
PP
575 return ret;
576}
577
578static int validate_no_duplicate_connection(struct bt_config *cfg,
579 char *error_buf, size_t error_buf_size)
580{
581 size_t i;
582 int ret = 0;
b19ff26f 583 bt_value *flat_connection_names =
05e21286 584 bt_value_map_create();
ebba3338
PP
585 GString *flat_connection_name = NULL;
586
587 if (!flat_connection_names) {
588 ret = -1;
589 goto end;
590 }
591
592 flat_connection_name = g_string_new(NULL);
593 if (!flat_connection_name) {
594 ret = -1;
595 goto end;
596 }
597
db0f160a 598 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
ebba3338 599 struct bt_config_connection *connection =
db0f160a 600 g_ptr_array_index(cfg->cmd_data.run.connections, i);
ebba3338 601
db0f160a 602 g_string_printf(flat_connection_name, "%s\x01%s\x01%s\x01%s",
9009cc24
PP
603 connection->upstream_comp_name->str,
604 connection->upstream_port_glob->str,
605 connection->downstream_comp_name->str,
606 connection->downstream_port_glob->str);
ebba3338 607
05e21286
PP
608 if (bt_value_map_has_entry(flat_connection_names,
609 flat_connection_name->str)) {
ebba3338
PP
610 snprintf(error_buf, error_buf_size,
611 "Duplicate connection:\n %s\n",
612 connection->arg->str);
613 ret = -1;
614 goto end;
615 }
616
05e21286 617 ret = bt_value_map_insert_entry(flat_connection_names,
ebba3338
PP
618 flat_connection_name->str, bt_value_null);
619 if (ret) {
620 goto end;
621 }
622 }
623
624end:
c5b9b441 625 bt_value_put_ref(flat_connection_names);
ebba3338
PP
626
627 if (flat_connection_name) {
628 g_string_free(flat_connection_name, TRUE);
629 }
630
631 return ret;
632}
633
634static int validate_connections(struct bt_config *cfg, char *error_buf,
635 size_t error_buf_size)
636{
637 int ret;
638
639 ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size);
640 if (ret) {
641 goto end;
642 }
643
644 ret = validate_connection_directions(cfg, error_buf, error_buf_size);
645 if (ret) {
646 goto end;
647 }
648
ebba3338
PP
649 ret = validate_all_components_connected(cfg, error_buf, error_buf_size);
650 if (ret) {
651 goto end;
652 }
653
654 ret = validate_no_duplicate_connection(cfg, error_buf, error_buf_size);
655 if (ret) {
656 goto end;
657 }
658
db0f160a 659 ret = validate_no_cycles(cfg, error_buf, error_buf_size);
ebba3338
PP
660 if (ret) {
661 goto end;
662 }
663
ebba3338 664end:
ebba3338
PP
665 return ret;
666}
667
9009cc24 668int bt_config_cli_args_create_connections(struct bt_config *cfg,
b19ff26f 669 const bt_value *connection_args,
ebba3338
PP
670 char *error_buf, size_t error_buf_size)
671{
672 int ret;
f80e9ec1 673 uint64_t i;
ebba3338 674
db0f160a
PP
675 if (!all_named_and_printable(cfg)) {
676 snprintf(error_buf, error_buf_size,
677 "One or more components are unnamed (use --name) or contain a non-printable character\n");
ebba3338
PP
678 goto error;
679 }
680
393729a6 681 for (i = 0; i < bt_value_array_get_length(connection_args); i++) {
b19ff26f 682 const bt_value *arg_value =
05e21286 683 bt_value_array_borrow_element_by_index_const(
07208d85 684 connection_args, i);
ebba3338
PP
685 const char *arg;
686 struct bt_config_connection *cfg_connection;
687
601b0d3c 688 arg = bt_value_string_get(arg_value);
ebba3338
PP
689 cfg_connection = cfg_connection_from_arg(arg);
690 if (!cfg_connection) {
691 snprintf(error_buf, error_buf_size, "Cannot parse --connect option's argument:\n %s\n",
692 arg);
693 goto error;
694 }
695
db0f160a 696 g_ptr_array_add(cfg->cmd_data.run.connections,
ebba3338
PP
697 cfg_connection);
698 }
699
700
701 ret = validate_connections(cfg, error_buf, error_buf_size);
702 if (ret) {
703 goto error;
704 }
705
706 goto end;
707
708error:
709 ret = -1;
710
711end:
712 return ret;
713}
This page took 0.090503 seconds and 4 git commands to generate.