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
24 #include <babeltrace2/babeltrace.h>
25 #include "common/common.h"
26 #include "babeltrace2-cfg.h"
27 #include "babeltrace2-cfg-cli-args-connect.h"
29 static bool all_named_and_printable_in_array(GPtrArray
*comps
)
32 bool all_named_and_printable
= true;
34 for (i
= 0; i
< comps
->len
; i
++) {
35 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
37 if (comp
->instance_name
->len
== 0) {
38 all_named_and_printable
= false;
42 if (!bt_common_string_is_printable(comp
->instance_name
->str
)) {
43 all_named_and_printable
= false;
49 return all_named_and_printable
;
52 static bool all_named_and_printable(struct bt_config
*cfg
)
54 return all_named_and_printable_in_array(cfg
->cmd_data
.run
.sources
) &&
55 all_named_and_printable_in_array(cfg
->cmd_data
.run
.filters
) &&
56 all_named_and_printable_in_array(cfg
->cmd_data
.run
.sinks
);
59 static struct bt_config_connection
*bt_config_connection_create(const char *arg
)
61 struct bt_config_connection
*cfg_connection
;
63 cfg_connection
= g_new0(struct bt_config_connection
, 1);
64 if (!cfg_connection
) {
68 cfg_connection
->upstream_comp_name
= g_string_new(NULL
);
69 if (!cfg_connection
->upstream_comp_name
) {
73 cfg_connection
->downstream_comp_name
= g_string_new(NULL
);
74 if (!cfg_connection
->downstream_comp_name
) {
78 cfg_connection
->upstream_port_glob
= g_string_new("*");
79 if (!cfg_connection
->upstream_port_glob
) {
83 cfg_connection
->downstream_port_glob
= g_string_new("*");
84 if (!cfg_connection
->downstream_port_glob
) {
88 cfg_connection
->arg
= g_string_new(arg
);
89 if (!cfg_connection
->arg
) {
96 g_free(cfg_connection
);
97 cfg_connection
= NULL
;
100 return cfg_connection
;
103 static bool validate_port_glob(const char *port_glob
)
105 bool is_valid
= true;
106 const char *ch
= port_glob
;
108 BT_ASSERT(port_glob
);
110 while (*ch
!= '\0') {
123 * This is reserved for future use, to support
124 * full globbing patterns. Those characters must
125 * be escaped with `\`.
139 static int normalize_glob_pattern(GString
*glob_pattern_gs
)
142 char *glob_pattern
= strdup(glob_pattern_gs
->str
);
149 bt_common_normalize_star_glob_pattern(glob_pattern
);
150 g_string_assign(glob_pattern_gs
, glob_pattern
);
157 static struct bt_config_connection
*cfg_connection_from_arg(const char *arg
)
159 const char *at
= arg
;
161 struct bt_config_connection
*cfg_conn
= NULL
;
167 DOWNSTREAM_PORT_GLOB
,
168 } state
= UPSTREAM_NAME
;
170 if (!bt_common_string_is_printable(arg
)) {
174 cfg_conn
= bt_config_connection_create(arg
);
182 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
183 if (!gs
|| gs
->len
== 0) {
187 g_string_free(cfg_conn
->upstream_comp_name
, TRUE
);
188 cfg_conn
->upstream_comp_name
= gs
;
191 if (at
[end_pos
] == ':') {
192 state
= DOWNSTREAM_NAME
;
193 } else if (at
[end_pos
] == '.') {
194 state
= UPSTREAM_PORT_GLOB
;
201 case DOWNSTREAM_NAME
:
202 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
203 if (!gs
|| gs
->len
== 0) {
207 g_string_free(cfg_conn
->downstream_comp_name
, TRUE
);
208 cfg_conn
->downstream_comp_name
= gs
;
211 if (at
[end_pos
] == '.') {
212 state
= DOWNSTREAM_PORT_GLOB
;
213 } else if (at
[end_pos
] == '\0') {
221 case UPSTREAM_PORT_GLOB
:
222 gs
= bt_common_string_until(at
, ".:", ".:", &end_pos
);
223 if (!gs
|| gs
->len
== 0) {
227 if (!validate_port_glob(gs
->str
)) {
231 if (normalize_glob_pattern(gs
)) {
235 g_string_free(cfg_conn
->upstream_port_glob
, TRUE
);
236 cfg_conn
->upstream_port_glob
= gs
;
239 if (at
[end_pos
] == ':') {
240 state
= DOWNSTREAM_NAME
;
247 case DOWNSTREAM_PORT_GLOB
:
248 gs
= bt_common_string_until(at
, ".:", ".:", &end_pos
);
249 if (!gs
|| gs
->len
== 0) {
253 if (!validate_port_glob(gs
->str
)) {
257 if (normalize_glob_pattern(gs
)) {
261 g_string_free(cfg_conn
->downstream_port_glob
, TRUE
);
262 cfg_conn
->downstream_port_glob
= gs
;
265 if (at
[end_pos
] == '\0') {
277 bt_config_connection_destroy(cfg_conn
);
282 g_string_free(gs
, TRUE
);
288 static struct bt_config_component
*find_component_in_array(GPtrArray
*comps
,
292 struct bt_config_component
*found_comp
= NULL
;
294 for (i
= 0; i
< comps
->len
; i
++) {
295 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
297 if (strcmp(name
, comp
->instance_name
->str
) == 0) {
299 bt_object_get_ref(found_comp
);
308 static struct bt_config_component
*find_component(struct bt_config
*cfg
,
311 struct bt_config_component
*comp
;
313 comp
= find_component_in_array(cfg
->cmd_data
.run
.sources
, name
);
318 comp
= find_component_in_array(cfg
->cmd_data
.run
.filters
, name
);
323 comp
= find_component_in_array(cfg
->cmd_data
.run
.sinks
, name
);
332 static int validate_all_endpoints_exist(struct bt_config
*cfg
, char *error_buf
,
333 size_t error_buf_size
)
338 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
339 struct bt_config_connection
*connection
=
340 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
341 struct bt_config_component
*comp
;
343 comp
= find_component(cfg
, connection
->upstream_comp_name
->str
);
344 bt_object_put_ref(comp
);
346 snprintf(error_buf
, error_buf_size
,
347 "Invalid connection: cannot find upstream component `%s`:\n %s\n",
348 connection
->upstream_comp_name
->str
,
349 connection
->arg
->str
);
354 comp
= find_component(cfg
, connection
->downstream_comp_name
->str
);
355 bt_object_put_ref(comp
);
357 snprintf(error_buf
, error_buf_size
,
358 "Invalid connection: cannot find downstream component `%s`:\n %s\n",
359 connection
->downstream_comp_name
->str
,
360 connection
->arg
->str
);
370 static int validate_connection_directions(struct bt_config
*cfg
,
371 char *error_buf
, size_t error_buf_size
)
375 struct bt_config_component
*src_comp
= NULL
;
376 struct bt_config_component
*dst_comp
= NULL
;
378 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
379 struct bt_config_connection
*connection
=
380 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
382 src_comp
= find_component(cfg
,
383 connection
->upstream_comp_name
->str
);
385 dst_comp
= find_component(cfg
,
386 connection
->downstream_comp_name
->str
);
389 if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_SOURCE
) {
390 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
391 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
392 snprintf(error_buf
, error_buf_size
,
393 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
394 connection
->upstream_comp_name
->str
,
395 connection
->arg
->str
);
399 } else if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_FILTER
) {
400 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
401 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
402 snprintf(error_buf
, error_buf_size
,
403 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
404 connection
->upstream_comp_name
->str
,
405 connection
->arg
->str
);
410 snprintf(error_buf
, error_buf_size
,
411 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
412 connection
->upstream_comp_name
->str
,
413 connection
->downstream_comp_name
->str
,
414 connection
->arg
->str
);
419 BT_OBJECT_PUT_REF_AND_RESET(src_comp
);
420 BT_OBJECT_PUT_REF_AND_RESET(dst_comp
);
424 bt_object_put_ref(src_comp
);
425 bt_object_put_ref(dst_comp
);
429 static int validate_no_cycles_rec(struct bt_config
*cfg
, GPtrArray
*path
,
430 char *error_buf
, size_t error_buf_size
)
434 const char *src_comp_name
;
436 BT_ASSERT(path
&& path
->len
> 0);
437 src_comp_name
= g_ptr_array_index(path
, path
->len
- 1);
439 for (conn_i
= 0; conn_i
< cfg
->cmd_data
.run
.connections
->len
; conn_i
++) {
440 struct bt_config_connection
*conn
=
441 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, conn_i
);
443 if (strcmp(conn
->upstream_comp_name
->str
, src_comp_name
) == 0) {
446 for (path_i
= 0; path_i
< path
->len
; path_i
++) {
447 const char *comp_name
=
448 g_ptr_array_index(path
, path_i
);
450 if (strcmp(comp_name
, conn
->downstream_comp_name
->str
) == 0) {
451 snprintf(error_buf
, error_buf_size
,
452 "Invalid connection: connection forms a cycle:\n %s\n",
459 g_ptr_array_add(path
, conn
->downstream_comp_name
->str
);
460 ret
= validate_no_cycles_rec(cfg
, path
, error_buf
,
466 g_ptr_array_remove_index(path
, path
->len
- 1);
474 static int validate_no_cycles(struct bt_config
*cfg
, char *error_buf
,
475 size_t error_buf_size
)
481 path
= g_ptr_array_new();
487 g_ptr_array_add(path
, NULL
);
489 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
490 struct bt_config_connection
*conn
=
491 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
493 g_ptr_array_index(path
, 0) = conn
->upstream_comp_name
->str
;
494 ret
= validate_no_cycles_rec(cfg
, path
,
495 error_buf
, error_buf_size
);
503 g_ptr_array_free(path
, TRUE
);
509 static int validate_all_components_connected_in_array(GPtrArray
*comps
,
510 const bt_value
*connected_components
,
511 char *error_buf
, size_t error_buf_size
)
516 for (i
= 0; i
< comps
->len
; i
++) {
517 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
519 if (!bt_value_map_has_entry(connected_components
,
520 comp
->instance_name
->str
)) {
521 snprintf(error_buf
, error_buf_size
,
522 "Component `%s` is not connected\n",
523 comp
->instance_name
->str
);
533 static int validate_all_components_connected(struct bt_config
*cfg
,
534 char *error_buf
, size_t error_buf_size
)
538 bt_value
*connected_components
= bt_value_map_create();
540 if (!connected_components
) {
545 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
546 struct bt_config_connection
*connection
=
547 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
549 ret
= bt_value_map_insert_entry(connected_components
,
550 connection
->upstream_comp_name
->str
, bt_value_null
);
555 ret
= bt_value_map_insert_entry(connected_components
,
556 connection
->downstream_comp_name
->str
, bt_value_null
);
562 ret
= validate_all_components_connected_in_array(
563 cfg
->cmd_data
.run
.sources
,
564 connected_components
,
565 error_buf
, error_buf_size
);
570 ret
= validate_all_components_connected_in_array(
571 cfg
->cmd_data
.run
.filters
,
572 connected_components
,
573 error_buf
, error_buf_size
);
578 ret
= validate_all_components_connected_in_array(
579 cfg
->cmd_data
.run
.sinks
,
580 connected_components
,
581 error_buf
, error_buf_size
);
587 bt_value_put_ref(connected_components
);
591 static int validate_no_duplicate_connection(struct bt_config
*cfg
,
592 char *error_buf
, size_t error_buf_size
)
596 bt_value
*flat_connection_names
=
597 bt_value_map_create();
598 GString
*flat_connection_name
= NULL
;
600 if (!flat_connection_names
) {
605 flat_connection_name
= g_string_new(NULL
);
606 if (!flat_connection_name
) {
611 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
612 struct bt_config_connection
*connection
=
613 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
615 g_string_printf(flat_connection_name
, "%s\x01%s\x01%s\x01%s",
616 connection
->upstream_comp_name
->str
,
617 connection
->upstream_port_glob
->str
,
618 connection
->downstream_comp_name
->str
,
619 connection
->downstream_port_glob
->str
);
621 if (bt_value_map_has_entry(flat_connection_names
,
622 flat_connection_name
->str
)) {
623 snprintf(error_buf
, error_buf_size
,
624 "Duplicate connection:\n %s\n",
625 connection
->arg
->str
);
630 ret
= bt_value_map_insert_entry(flat_connection_names
,
631 flat_connection_name
->str
, bt_value_null
);
638 bt_value_put_ref(flat_connection_names
);
640 if (flat_connection_name
) {
641 g_string_free(flat_connection_name
, TRUE
);
647 static int validate_connections(struct bt_config
*cfg
, char *error_buf
,
648 size_t error_buf_size
)
652 ret
= validate_all_endpoints_exist(cfg
, error_buf
, error_buf_size
);
657 ret
= validate_connection_directions(cfg
, error_buf
, error_buf_size
);
662 ret
= validate_all_components_connected(cfg
, error_buf
, error_buf_size
);
667 ret
= validate_no_duplicate_connection(cfg
, error_buf
, error_buf_size
);
672 ret
= validate_no_cycles(cfg
, error_buf
, error_buf_size
);
681 int bt_config_cli_args_create_connections(struct bt_config
*cfg
,
682 const bt_value
*connection_args
,
683 char *error_buf
, size_t error_buf_size
)
688 if (!all_named_and_printable(cfg
)) {
689 snprintf(error_buf
, error_buf_size
,
690 "One or more components are unnamed (use --name) or contain a non-printable character\n");
694 for (i
= 0; i
< bt_value_array_get_size(connection_args
); i
++) {
695 const bt_value
*arg_value
=
696 bt_value_array_borrow_element_by_index_const(
699 struct bt_config_connection
*cfg_connection
;
701 arg
= bt_value_string_get(arg_value
);
702 cfg_connection
= cfg_connection_from_arg(arg
);
703 if (!cfg_connection
) {
704 snprintf(error_buf
, error_buf_size
, "Cannot parse --connect option's argument:\n %s\n",
709 g_ptr_array_add(cfg
->cmd_data
.run
.connections
,
714 ret
= validate_connections(cfg
, error_buf
, error_buf_size
);