2 * SPDX-License-Identifier: MIT
4 * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
11 #include <babeltrace2/babeltrace.h>
12 #include "common/common.h"
13 #include "babeltrace2-cfg.h"
14 #include "babeltrace2-cfg-cli-args-connect.h"
16 static bool all_named_and_printable_in_array(GPtrArray
*comps
)
19 bool all_named_and_printable
= true;
21 for (i
= 0; i
< comps
->len
; i
++) {
22 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
24 if (comp
->instance_name
->len
== 0) {
25 all_named_and_printable
= false;
29 if (!bt_common_string_is_printable(comp
->instance_name
->str
)) {
30 all_named_and_printable
= false;
36 return all_named_and_printable
;
39 static bool all_named_and_printable(struct bt_config
*cfg
)
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
);
46 static struct bt_config_connection
*bt_config_connection_create(const char *arg
)
48 struct bt_config_connection
*cfg_connection
;
50 cfg_connection
= g_new0(struct bt_config_connection
, 1);
51 if (!cfg_connection
) {
55 cfg_connection
->upstream_comp_name
= g_string_new(NULL
);
56 if (!cfg_connection
->upstream_comp_name
) {
60 cfg_connection
->downstream_comp_name
= g_string_new(NULL
);
61 if (!cfg_connection
->downstream_comp_name
) {
65 cfg_connection
->upstream_port_glob
= g_string_new("*");
66 if (!cfg_connection
->upstream_port_glob
) {
70 cfg_connection
->downstream_port_glob
= g_string_new("*");
71 if (!cfg_connection
->downstream_port_glob
) {
75 cfg_connection
->arg
= g_string_new(arg
);
76 if (!cfg_connection
->arg
) {
83 g_free(cfg_connection
);
84 cfg_connection
= NULL
;
87 return cfg_connection
;
90 static bool validate_port_glob(const char *port_glob
)
93 const char *ch
= port_glob
;
110 * This is reserved for future use, to support
111 * full globbing patterns. Those characters must
112 * be escaped with `\`.
126 static int normalize_glob_pattern(GString
*glob_pattern_gs
)
129 char *glob_pattern
= strdup(glob_pattern_gs
->str
);
136 bt_common_normalize_star_glob_pattern(glob_pattern
);
137 g_string_assign(glob_pattern_gs
, glob_pattern
);
144 static struct bt_config_connection
*cfg_connection_from_arg(const char *arg
)
146 const char *at
= arg
;
148 struct bt_config_connection
*cfg_conn
= NULL
;
154 DOWNSTREAM_PORT_GLOB
,
155 } state
= UPSTREAM_NAME
;
157 if (!bt_common_string_is_printable(arg
)) {
161 cfg_conn
= bt_config_connection_create(arg
);
169 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
170 if (!gs
|| gs
->len
== 0) {
174 g_string_free(cfg_conn
->upstream_comp_name
, TRUE
);
175 cfg_conn
->upstream_comp_name
= gs
;
178 if (at
[end_pos
] == ':') {
179 state
= DOWNSTREAM_NAME
;
180 } else if (at
[end_pos
] == '.') {
181 state
= UPSTREAM_PORT_GLOB
;
188 case DOWNSTREAM_NAME
:
189 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
190 if (!gs
|| gs
->len
== 0) {
194 g_string_free(cfg_conn
->downstream_comp_name
, TRUE
);
195 cfg_conn
->downstream_comp_name
= gs
;
198 if (at
[end_pos
] == '.') {
199 state
= DOWNSTREAM_PORT_GLOB
;
200 } else if (at
[end_pos
] == '\0') {
208 case UPSTREAM_PORT_GLOB
:
209 gs
= bt_common_string_until(at
, ".:", ".:", &end_pos
);
210 if (!gs
|| gs
->len
== 0) {
214 if (!validate_port_glob(gs
->str
)) {
218 if (normalize_glob_pattern(gs
)) {
222 g_string_free(cfg_conn
->upstream_port_glob
, TRUE
);
223 cfg_conn
->upstream_port_glob
= gs
;
226 if (at
[end_pos
] == ':') {
227 state
= DOWNSTREAM_NAME
;
234 case DOWNSTREAM_PORT_GLOB
:
235 gs
= bt_common_string_until(at
, ".:", ".:", &end_pos
);
236 if (!gs
|| gs
->len
== 0) {
240 if (!validate_port_glob(gs
->str
)) {
244 if (normalize_glob_pattern(gs
)) {
248 g_string_free(cfg_conn
->downstream_port_glob
, TRUE
);
249 cfg_conn
->downstream_port_glob
= gs
;
252 if (at
[end_pos
] == '\0') {
264 bt_config_connection_destroy(cfg_conn
);
269 g_string_free(gs
, TRUE
);
275 static struct bt_config_component
*find_component_in_array(GPtrArray
*comps
,
279 struct bt_config_component
*found_comp
= NULL
;
281 for (i
= 0; i
< comps
->len
; i
++) {
282 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
284 if (strcmp(name
, comp
->instance_name
->str
) == 0) {
286 bt_object_get_ref(found_comp
);
295 static struct bt_config_component
*find_component(struct bt_config
*cfg
,
298 struct bt_config_component
*comp
;
300 comp
= find_component_in_array(cfg
->cmd_data
.run
.sources
, name
);
305 comp
= find_component_in_array(cfg
->cmd_data
.run
.filters
, name
);
310 comp
= find_component_in_array(cfg
->cmd_data
.run
.sinks
, name
);
319 static int validate_all_endpoints_exist(struct bt_config
*cfg
, char *error_buf
,
320 size_t error_buf_size
)
325 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
326 struct bt_config_connection
*connection
=
327 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
328 struct bt_config_component
*comp
;
330 comp
= find_component(cfg
, connection
->upstream_comp_name
->str
);
331 bt_object_put_ref(comp
);
333 snprintf(error_buf
, error_buf_size
,
334 "Invalid connection: cannot find upstream component `%s`:\n %s\n",
335 connection
->upstream_comp_name
->str
,
336 connection
->arg
->str
);
341 comp
= find_component(cfg
, connection
->downstream_comp_name
->str
);
342 bt_object_put_ref(comp
);
344 snprintf(error_buf
, error_buf_size
,
345 "Invalid connection: cannot find downstream component `%s`:\n %s\n",
346 connection
->downstream_comp_name
->str
,
347 connection
->arg
->str
);
357 static int validate_connection_directions(struct bt_config
*cfg
,
358 char *error_buf
, size_t error_buf_size
)
362 struct bt_config_component
*src_comp
= NULL
;
363 struct bt_config_component
*dst_comp
= NULL
;
365 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
366 struct bt_config_connection
*connection
=
367 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
369 src_comp
= find_component(cfg
,
370 connection
->upstream_comp_name
->str
);
372 dst_comp
= find_component(cfg
,
373 connection
->downstream_comp_name
->str
);
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
) {
379 snprintf(error_buf
, error_buf_size
,
380 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
381 connection
->upstream_comp_name
->str
,
382 connection
->arg
->str
);
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
) {
389 snprintf(error_buf
, error_buf_size
,
390 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
391 connection
->upstream_comp_name
->str
,
392 connection
->arg
->str
);
397 snprintf(error_buf
, error_buf_size
,
398 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
399 connection
->upstream_comp_name
->str
,
400 connection
->downstream_comp_name
->str
,
401 connection
->arg
->str
);
406 BT_OBJECT_PUT_REF_AND_RESET(src_comp
);
407 BT_OBJECT_PUT_REF_AND_RESET(dst_comp
);
411 bt_object_put_ref(src_comp
);
412 bt_object_put_ref(dst_comp
);
416 static int validate_no_cycles_rec(struct bt_config
*cfg
, GPtrArray
*path
,
417 char *error_buf
, size_t error_buf_size
)
421 const char *src_comp_name
;
423 BT_ASSERT(path
&& path
->len
> 0);
424 src_comp_name
= g_ptr_array_index(path
, path
->len
- 1);
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
);
430 if (strcmp(conn
->upstream_comp_name
->str
, src_comp_name
) == 0) {
433 for (path_i
= 0; path_i
< path
->len
; path_i
++) {
434 const char *comp_name
=
435 g_ptr_array_index(path
, path_i
);
437 if (strcmp(comp_name
, conn
->downstream_comp_name
->str
) == 0) {
438 snprintf(error_buf
, error_buf_size
,
439 "Invalid connection: connection forms a cycle:\n %s\n",
446 g_ptr_array_add(path
, conn
->downstream_comp_name
->str
);
447 ret
= validate_no_cycles_rec(cfg
, path
, error_buf
,
453 g_ptr_array_remove_index(path
, path
->len
- 1);
461 static int validate_no_cycles(struct bt_config
*cfg
, char *error_buf
,
462 size_t error_buf_size
)
468 path
= g_ptr_array_new();
474 g_ptr_array_add(path
, NULL
);
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
);
480 g_ptr_array_index(path
, 0) = conn
->upstream_comp_name
->str
;
481 ret
= validate_no_cycles_rec(cfg
, path
,
482 error_buf
, error_buf_size
);
490 g_ptr_array_free(path
, TRUE
);
496 static int validate_all_components_connected_in_array(GPtrArray
*comps
,
497 const bt_value
*connected_components
,
498 char *error_buf
, size_t error_buf_size
)
503 for (i
= 0; i
< comps
->len
; i
++) {
504 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
506 if (!bt_value_map_has_entry(connected_components
,
507 comp
->instance_name
->str
)) {
508 snprintf(error_buf
, error_buf_size
,
509 "Component `%s` is not connected\n",
510 comp
->instance_name
->str
);
520 static int validate_all_components_connected(struct bt_config
*cfg
,
521 char *error_buf
, size_t error_buf_size
)
525 bt_value
*connected_components
= bt_value_map_create();
527 if (!connected_components
) {
532 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
533 struct bt_config_connection
*connection
=
534 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
536 ret
= bt_value_map_insert_entry(connected_components
,
537 connection
->upstream_comp_name
->str
, bt_value_null
);
542 ret
= bt_value_map_insert_entry(connected_components
,
543 connection
->downstream_comp_name
->str
, bt_value_null
);
549 ret
= validate_all_components_connected_in_array(
550 cfg
->cmd_data
.run
.sources
,
551 connected_components
,
552 error_buf
, error_buf_size
);
557 ret
= validate_all_components_connected_in_array(
558 cfg
->cmd_data
.run
.filters
,
559 connected_components
,
560 error_buf
, error_buf_size
);
565 ret
= validate_all_components_connected_in_array(
566 cfg
->cmd_data
.run
.sinks
,
567 connected_components
,
568 error_buf
, error_buf_size
);
574 bt_value_put_ref(connected_components
);
578 static int validate_no_duplicate_connection(struct bt_config
*cfg
,
579 char *error_buf
, size_t error_buf_size
)
583 bt_value
*flat_connection_names
=
584 bt_value_map_create();
585 GString
*flat_connection_name
= NULL
;
587 if (!flat_connection_names
) {
592 flat_connection_name
= g_string_new(NULL
);
593 if (!flat_connection_name
) {
598 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
599 struct bt_config_connection
*connection
=
600 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
602 g_string_printf(flat_connection_name
, "%s\x01%s\x01%s\x01%s",
603 connection
->upstream_comp_name
->str
,
604 connection
->upstream_port_glob
->str
,
605 connection
->downstream_comp_name
->str
,
606 connection
->downstream_port_glob
->str
);
608 if (bt_value_map_has_entry(flat_connection_names
,
609 flat_connection_name
->str
)) {
610 snprintf(error_buf
, error_buf_size
,
611 "Duplicate connection:\n %s\n",
612 connection
->arg
->str
);
617 ret
= bt_value_map_insert_entry(flat_connection_names
,
618 flat_connection_name
->str
, bt_value_null
);
625 bt_value_put_ref(flat_connection_names
);
627 if (flat_connection_name
) {
628 g_string_free(flat_connection_name
, TRUE
);
634 static int validate_connections(struct bt_config
*cfg
, char *error_buf
,
635 size_t error_buf_size
)
639 ret
= validate_all_endpoints_exist(cfg
, error_buf
, error_buf_size
);
644 ret
= validate_connection_directions(cfg
, error_buf
, error_buf_size
);
649 ret
= validate_all_components_connected(cfg
, error_buf
, error_buf_size
);
654 ret
= validate_no_duplicate_connection(cfg
, error_buf
, error_buf_size
);
659 ret
= validate_no_cycles(cfg
, error_buf
, error_buf_size
);
668 int bt_config_cli_args_create_connections(struct bt_config
*cfg
,
669 const bt_value
*connection_args
,
670 char *error_buf
, size_t error_buf_size
)
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");
681 for (i
= 0; i
< bt_value_array_get_length(connection_args
); i
++) {
682 const bt_value
*arg_value
=
683 bt_value_array_borrow_element_by_index_const(
686 struct bt_config_connection
*cfg_connection
;
688 arg
= bt_value_string_get(arg_value
);
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",
696 g_ptr_array_add(cfg
->cmd_data
.run
.connections
,
701 ret
= validate_connections(cfg
, error_buf
, error_buf_size
);