2 * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 #include <babeltrace2/babeltrace.h>
28 #include "common/common.h"
29 #include "babeltrace2-cfg.h"
30 #include "babeltrace2-cfg-cli-args-connect.h"
32 static bool all_named_and_printable_in_array(GPtrArray
*comps
)
35 bool all_named_and_printable
= true;
37 for (i
= 0; i
< comps
->len
; i
++) {
38 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
40 if (comp
->instance_name
->len
== 0) {
41 all_named_and_printable
= false;
45 if (!bt_common_string_is_printable(comp
->instance_name
->str
)) {
46 all_named_and_printable
= false;
52 return all_named_and_printable
;
55 static bool all_named_and_printable(struct bt_config
*cfg
)
57 return all_named_and_printable_in_array(cfg
->cmd_data
.run
.sources
) &&
58 all_named_and_printable_in_array(cfg
->cmd_data
.run
.filters
) &&
59 all_named_and_printable_in_array(cfg
->cmd_data
.run
.sinks
);
62 static struct bt_config_connection
*bt_config_connection_create(const char *arg
)
64 struct bt_config_connection
*cfg_connection
;
66 cfg_connection
= g_new0(struct bt_config_connection
, 1);
67 if (!cfg_connection
) {
71 cfg_connection
->upstream_comp_name
= g_string_new(NULL
);
72 if (!cfg_connection
->upstream_comp_name
) {
76 cfg_connection
->downstream_comp_name
= g_string_new(NULL
);
77 if (!cfg_connection
->downstream_comp_name
) {
81 cfg_connection
->upstream_port_glob
= g_string_new("*");
82 if (!cfg_connection
->upstream_port_glob
) {
86 cfg_connection
->downstream_port_glob
= g_string_new("*");
87 if (!cfg_connection
->downstream_port_glob
) {
91 cfg_connection
->arg
= g_string_new(arg
);
92 if (!cfg_connection
->arg
) {
99 g_free(cfg_connection
);
100 cfg_connection
= NULL
;
103 return cfg_connection
;
106 static bool validate_port_glob(const char *port_glob
)
108 bool is_valid
= true;
109 const char *ch
= port_glob
;
111 BT_ASSERT(port_glob
);
113 while (*ch
!= '\0') {
126 * This is reserved for future use, to support
127 * full globbing patterns. Those characters must
128 * be escaped with `\`.
142 static int normalize_glob_pattern(GString
*glob_pattern_gs
)
145 char *glob_pattern
= strdup(glob_pattern_gs
->str
);
152 bt_common_normalize_star_glob_pattern(glob_pattern
);
153 g_string_assign(glob_pattern_gs
, glob_pattern
);
160 static struct bt_config_connection
*cfg_connection_from_arg(const char *arg
)
162 const char *at
= arg
;
164 struct bt_config_connection
*cfg_conn
= NULL
;
170 DOWNSTREAM_PORT_GLOB
,
171 } state
= UPSTREAM_NAME
;
173 if (!bt_common_string_is_printable(arg
)) {
177 cfg_conn
= bt_config_connection_create(arg
);
185 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
186 if (!gs
|| gs
->len
== 0) {
190 g_string_free(cfg_conn
->upstream_comp_name
, TRUE
);
191 cfg_conn
->upstream_comp_name
= gs
;
194 if (at
[end_pos
] == ':') {
195 state
= DOWNSTREAM_NAME
;
196 } else if (at
[end_pos
] == '.') {
197 state
= UPSTREAM_PORT_GLOB
;
204 case DOWNSTREAM_NAME
:
205 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
206 if (!gs
|| gs
->len
== 0) {
210 g_string_free(cfg_conn
->downstream_comp_name
, TRUE
);
211 cfg_conn
->downstream_comp_name
= gs
;
214 if (at
[end_pos
] == '.') {
215 state
= DOWNSTREAM_PORT_GLOB
;
216 } else if (at
[end_pos
] == '\0') {
224 case UPSTREAM_PORT_GLOB
:
225 gs
= bt_common_string_until(at
, ".:", ".:", &end_pos
);
226 if (!gs
|| gs
->len
== 0) {
230 if (!validate_port_glob(gs
->str
)) {
234 if (normalize_glob_pattern(gs
)) {
238 g_string_free(cfg_conn
->upstream_port_glob
, TRUE
);
239 cfg_conn
->upstream_port_glob
= gs
;
242 if (at
[end_pos
] == ':') {
243 state
= DOWNSTREAM_NAME
;
250 case DOWNSTREAM_PORT_GLOB
:
251 gs
= bt_common_string_until(at
, ".:", ".:", &end_pos
);
252 if (!gs
|| gs
->len
== 0) {
256 if (!validate_port_glob(gs
->str
)) {
260 if (normalize_glob_pattern(gs
)) {
264 g_string_free(cfg_conn
->downstream_port_glob
, TRUE
);
265 cfg_conn
->downstream_port_glob
= gs
;
268 if (at
[end_pos
] == '\0') {
280 bt_config_connection_destroy(cfg_conn
);
285 g_string_free(gs
, TRUE
);
291 static struct bt_config_component
*find_component_in_array(GPtrArray
*comps
,
295 struct bt_config_component
*found_comp
= NULL
;
297 for (i
= 0; i
< comps
->len
; i
++) {
298 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
300 if (strcmp(name
, comp
->instance_name
->str
) == 0) {
302 bt_object_get_ref(found_comp
);
311 static struct bt_config_component
*find_component(struct bt_config
*cfg
,
314 struct bt_config_component
*comp
;
316 comp
= find_component_in_array(cfg
->cmd_data
.run
.sources
, name
);
321 comp
= find_component_in_array(cfg
->cmd_data
.run
.filters
, name
);
326 comp
= find_component_in_array(cfg
->cmd_data
.run
.sinks
, name
);
335 static int validate_all_endpoints_exist(struct bt_config
*cfg
, char *error_buf
,
336 size_t error_buf_size
)
341 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
342 struct bt_config_connection
*connection
=
343 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
344 struct bt_config_component
*comp
;
346 comp
= find_component(cfg
, connection
->upstream_comp_name
->str
);
347 bt_object_put_ref(comp
);
349 snprintf(error_buf
, error_buf_size
,
350 "Invalid connection: cannot find upstream component `%s`:\n %s\n",
351 connection
->upstream_comp_name
->str
,
352 connection
->arg
->str
);
357 comp
= find_component(cfg
, connection
->downstream_comp_name
->str
);
358 bt_object_put_ref(comp
);
360 snprintf(error_buf
, error_buf_size
,
361 "Invalid connection: cannot find downstream component `%s`:\n %s\n",
362 connection
->downstream_comp_name
->str
,
363 connection
->arg
->str
);
373 static int validate_connection_directions(struct bt_config
*cfg
,
374 char *error_buf
, size_t error_buf_size
)
378 struct bt_config_component
*src_comp
= NULL
;
379 struct bt_config_component
*dst_comp
= NULL
;
381 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
382 struct bt_config_connection
*connection
=
383 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
385 src_comp
= find_component(cfg
,
386 connection
->upstream_comp_name
->str
);
388 dst_comp
= find_component(cfg
,
389 connection
->downstream_comp_name
->str
);
392 if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_SOURCE
) {
393 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
394 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
395 snprintf(error_buf
, error_buf_size
,
396 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
397 connection
->upstream_comp_name
->str
,
398 connection
->arg
->str
);
402 } else if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_FILTER
) {
403 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
404 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
405 snprintf(error_buf
, error_buf_size
,
406 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
407 connection
->upstream_comp_name
->str
,
408 connection
->arg
->str
);
413 snprintf(error_buf
, error_buf_size
,
414 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
415 connection
->upstream_comp_name
->str
,
416 connection
->downstream_comp_name
->str
,
417 connection
->arg
->str
);
422 BT_OBJECT_PUT_REF_AND_RESET(src_comp
);
423 BT_OBJECT_PUT_REF_AND_RESET(dst_comp
);
427 bt_object_put_ref(src_comp
);
428 bt_object_put_ref(dst_comp
);
432 static int validate_no_cycles_rec(struct bt_config
*cfg
, GPtrArray
*path
,
433 char *error_buf
, size_t error_buf_size
)
437 const char *src_comp_name
;
439 BT_ASSERT(path
&& path
->len
> 0);
440 src_comp_name
= g_ptr_array_index(path
, path
->len
- 1);
442 for (conn_i
= 0; conn_i
< cfg
->cmd_data
.run
.connections
->len
; conn_i
++) {
443 struct bt_config_connection
*conn
=
444 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, conn_i
);
446 if (strcmp(conn
->upstream_comp_name
->str
, src_comp_name
) == 0) {
449 for (path_i
= 0; path_i
< path
->len
; path_i
++) {
450 const char *comp_name
=
451 g_ptr_array_index(path
, path_i
);
453 if (strcmp(comp_name
, conn
->downstream_comp_name
->str
) == 0) {
454 snprintf(error_buf
, error_buf_size
,
455 "Invalid connection: connection forms a cycle:\n %s\n",
462 g_ptr_array_add(path
, conn
->downstream_comp_name
->str
);
463 ret
= validate_no_cycles_rec(cfg
, path
, error_buf
,
469 g_ptr_array_remove_index(path
, path
->len
- 1);
477 static int validate_no_cycles(struct bt_config
*cfg
, char *error_buf
,
478 size_t error_buf_size
)
484 path
= g_ptr_array_new();
490 g_ptr_array_add(path
, NULL
);
492 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
493 struct bt_config_connection
*conn
=
494 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
496 g_ptr_array_index(path
, 0) = conn
->upstream_comp_name
->str
;
497 ret
= validate_no_cycles_rec(cfg
, path
,
498 error_buf
, error_buf_size
);
506 g_ptr_array_free(path
, TRUE
);
512 static int validate_all_components_connected_in_array(GPtrArray
*comps
,
513 const bt_value
*connected_components
,
514 char *error_buf
, size_t error_buf_size
)
519 for (i
= 0; i
< comps
->len
; i
++) {
520 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
522 if (!bt_value_map_has_entry(connected_components
,
523 comp
->instance_name
->str
)) {
524 snprintf(error_buf
, error_buf_size
,
525 "Component `%s` is not connected\n",
526 comp
->instance_name
->str
);
536 static int validate_all_components_connected(struct bt_config
*cfg
,
537 char *error_buf
, size_t error_buf_size
)
541 bt_value
*connected_components
= bt_value_map_create();
543 if (!connected_components
) {
548 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
549 struct bt_config_connection
*connection
=
550 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
552 ret
= bt_value_map_insert_entry(connected_components
,
553 connection
->upstream_comp_name
->str
, bt_value_null
);
558 ret
= bt_value_map_insert_entry(connected_components
,
559 connection
->downstream_comp_name
->str
, bt_value_null
);
565 ret
= validate_all_components_connected_in_array(
566 cfg
->cmd_data
.run
.sources
,
567 connected_components
,
568 error_buf
, error_buf_size
);
573 ret
= validate_all_components_connected_in_array(
574 cfg
->cmd_data
.run
.filters
,
575 connected_components
,
576 error_buf
, error_buf_size
);
581 ret
= validate_all_components_connected_in_array(
582 cfg
->cmd_data
.run
.sinks
,
583 connected_components
,
584 error_buf
, error_buf_size
);
590 bt_value_put_ref(connected_components
);
594 static int validate_no_duplicate_connection(struct bt_config
*cfg
,
595 char *error_buf
, size_t error_buf_size
)
599 bt_value
*flat_connection_names
=
600 bt_value_map_create();
601 GString
*flat_connection_name
= NULL
;
603 if (!flat_connection_names
) {
608 flat_connection_name
= g_string_new(NULL
);
609 if (!flat_connection_name
) {
614 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
615 struct bt_config_connection
*connection
=
616 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
618 g_string_printf(flat_connection_name
, "%s\x01%s\x01%s\x01%s",
619 connection
->upstream_comp_name
->str
,
620 connection
->upstream_port_glob
->str
,
621 connection
->downstream_comp_name
->str
,
622 connection
->downstream_port_glob
->str
);
624 if (bt_value_map_has_entry(flat_connection_names
,
625 flat_connection_name
->str
)) {
626 snprintf(error_buf
, error_buf_size
,
627 "Duplicate connection:\n %s\n",
628 connection
->arg
->str
);
633 ret
= bt_value_map_insert_entry(flat_connection_names
,
634 flat_connection_name
->str
, bt_value_null
);
641 bt_value_put_ref(flat_connection_names
);
643 if (flat_connection_name
) {
644 g_string_free(flat_connection_name
, TRUE
);
650 static int validate_connections(struct bt_config
*cfg
, char *error_buf
,
651 size_t error_buf_size
)
655 ret
= validate_all_endpoints_exist(cfg
, error_buf
, error_buf_size
);
660 ret
= validate_connection_directions(cfg
, error_buf
, error_buf_size
);
665 ret
= validate_all_components_connected(cfg
, error_buf
, error_buf_size
);
670 ret
= validate_no_duplicate_connection(cfg
, error_buf
, error_buf_size
);
675 ret
= validate_no_cycles(cfg
, error_buf
, error_buf_size
);
684 int bt_config_cli_args_create_connections(struct bt_config
*cfg
,
685 const bt_value
*connection_args
,
686 char *error_buf
, size_t error_buf_size
)
691 if (!all_named_and_printable(cfg
)) {
692 snprintf(error_buf
, error_buf_size
,
693 "One or more components are unnamed (use --name) or contain a non-printable character\n");
697 for (i
= 0; i
< bt_value_array_get_length(connection_args
); i
++) {
698 const bt_value
*arg_value
=
699 bt_value_array_borrow_element_by_index_const(
702 struct bt_config_connection
*cfg_connection
;
704 arg
= bt_value_string_get(arg_value
);
705 cfg_connection
= cfg_connection_from_arg(arg
);
706 if (!cfg_connection
) {
707 snprintf(error_buf
, error_buf_size
, "Cannot parse --connect option's argument:\n %s\n",
712 g_ptr_array_add(cfg
->cmd_data
.run
.connections
,
717 ret
= validate_connections(cfg
, error_buf
, error_buf_size
);