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
23 #include <babeltrace/values.h>
24 #include <babeltrace/common-internal.h>
25 #include "babeltrace-cfg.h"
26 #include "babeltrace-cfg-cli-args-connect.h"
28 static bool all_named_and_printable_in_array(GPtrArray
*comps
)
31 bool all_named_and_printable
= true;
33 for (i
= 0; i
< comps
->len
; i
++) {
34 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
36 if (comp
->instance_name
->len
== 0) {
37 all_named_and_printable
= false;
41 if (!bt_common_string_is_printable(comp
->instance_name
->str
)) {
42 all_named_and_printable
= false;
48 return all_named_and_printable
;
51 static bool all_named_and_printable(struct bt_config
*cfg
)
53 return all_named_and_printable_in_array(cfg
->cmd_data
.run
.sources
) &&
54 all_named_and_printable_in_array(cfg
->cmd_data
.run
.filters
) &&
55 all_named_and_printable_in_array(cfg
->cmd_data
.run
.sinks
);
58 static struct bt_config_connection
*bt_config_connection_create(const char *arg
)
60 struct bt_config_connection
*cfg_connection
;
62 cfg_connection
= g_new0(struct bt_config_connection
, 1);
63 if (!cfg_connection
) {
67 cfg_connection
->upstream_comp_name
= g_string_new(NULL
);
68 if (!cfg_connection
->upstream_comp_name
) {
72 cfg_connection
->downstream_comp_name
= g_string_new(NULL
);
73 if (!cfg_connection
->downstream_comp_name
) {
77 cfg_connection
->upstream_port_glob
= g_string_new("*");
78 if (!cfg_connection
->upstream_port_glob
) {
82 cfg_connection
->downstream_port_glob
= g_string_new("*");
83 if (!cfg_connection
->downstream_port_glob
) {
87 cfg_connection
->arg
= g_string_new(arg
);
88 if (!cfg_connection
->arg
) {
95 g_free(cfg_connection
);
96 cfg_connection
= NULL
;
99 return cfg_connection
;
102 static bool validate_port_glob(const char *port_glob
)
104 bool is_valid
= true;
105 const char *ch
= port_glob
;
109 while (*ch
!= '\0') {
122 * This is reserved for future use, to support
123 * full globbing patterns. Those characters must
124 * be escaped with `\`.
138 static int normalize_glob_pattern(GString
*glob_pattern_gs
)
141 char *glob_pattern
= strdup(glob_pattern_gs
->str
);
148 bt_common_normalize_star_glob_pattern(glob_pattern
);
149 g_string_assign(glob_pattern_gs
, glob_pattern
);
156 static struct bt_config_connection
*cfg_connection_from_arg(const char *arg
)
158 const char *at
= arg
;
160 struct bt_config_connection
*cfg_conn
= NULL
;
166 DOWNSTREAM_PORT_GLOB
,
167 } state
= UPSTREAM_NAME
;
169 if (!bt_common_string_is_printable(arg
)) {
173 cfg_conn
= bt_config_connection_create(arg
);
181 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
182 if (!gs
|| gs
->len
== 0) {
186 g_string_free(cfg_conn
->upstream_comp_name
, TRUE
);
187 cfg_conn
->upstream_comp_name
= gs
;
190 if (at
[end_pos
] == ':') {
191 state
= DOWNSTREAM_NAME
;
192 } else if (at
[end_pos
] == '.') {
193 state
= UPSTREAM_PORT_GLOB
;
200 case DOWNSTREAM_NAME
:
201 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
202 if (!gs
|| gs
->len
== 0) {
206 g_string_free(cfg_conn
->downstream_comp_name
, TRUE
);
207 cfg_conn
->downstream_comp_name
= gs
;
210 if (at
[end_pos
] == '.') {
211 state
= DOWNSTREAM_PORT_GLOB
;
212 } else if (at
[end_pos
] == '\0') {
220 case UPSTREAM_PORT_GLOB
:
221 gs
= bt_common_string_until(at
, ".:", ".:", &end_pos
);
222 if (!gs
|| gs
->len
== 0) {
226 if (!validate_port_glob(gs
->str
)) {
230 if (normalize_glob_pattern(gs
)) {
234 g_string_free(cfg_conn
->upstream_port_glob
, TRUE
);
235 cfg_conn
->upstream_port_glob
= gs
;
238 if (at
[end_pos
] == ':') {
239 state
= DOWNSTREAM_NAME
;
246 case DOWNSTREAM_PORT_GLOB
:
247 gs
= bt_common_string_until(at
, ".:", ".:", &end_pos
);
248 if (!gs
|| gs
->len
== 0) {
252 if (!validate_port_glob(gs
->str
)) {
256 if (normalize_glob_pattern(gs
)) {
260 g_string_free(cfg_conn
->downstream_port_glob
, TRUE
);
261 cfg_conn
->downstream_port_glob
= gs
;
264 if (at
[end_pos
] == '\0') {
276 bt_config_connection_destroy(cfg_conn
);
281 g_string_free(gs
, TRUE
);
287 static struct bt_config_component
*find_component_in_array(GPtrArray
*comps
,
291 struct bt_config_component
*found_comp
= NULL
;
293 for (i
= 0; i
< comps
->len
; i
++) {
294 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
296 if (strcmp(name
, comp
->instance_name
->str
) == 0) {
297 found_comp
= bt_get(comp
);
306 static struct bt_config_component
*find_component(struct bt_config
*cfg
,
309 struct bt_config_component
*comp
;
311 comp
= find_component_in_array(cfg
->cmd_data
.run
.sources
, name
);
316 comp
= find_component_in_array(cfg
->cmd_data
.run
.filters
, name
);
321 comp
= find_component_in_array(cfg
->cmd_data
.run
.sinks
, name
);
330 static int validate_all_endpoints_exist(struct bt_config
*cfg
, char *error_buf
,
331 size_t error_buf_size
)
336 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
337 struct bt_config_connection
*connection
=
338 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
339 struct bt_config_component
*comp
;
341 comp
= find_component(cfg
, connection
->upstream_comp_name
->str
);
344 snprintf(error_buf
, error_buf_size
,
345 "Invalid connection: cannot find upstream component `%s`:\n %s\n",
346 connection
->upstream_comp_name
->str
,
347 connection
->arg
->str
);
352 comp
= find_component(cfg
, connection
->downstream_comp_name
->str
);
355 snprintf(error_buf
, error_buf_size
,
356 "Invalid connection: cannot find downstream component `%s`:\n %s\n",
357 connection
->downstream_comp_name
->str
,
358 connection
->arg
->str
);
368 static int validate_connection_directions(struct bt_config
*cfg
,
369 char *error_buf
, size_t error_buf_size
)
373 struct bt_config_component
*src_comp
= NULL
;
374 struct bt_config_component
*dst_comp
= NULL
;
376 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
377 struct bt_config_connection
*connection
=
378 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
380 src_comp
= find_component(cfg
,
381 connection
->upstream_comp_name
->str
);
383 dst_comp
= find_component(cfg
,
384 connection
->downstream_comp_name
->str
);
387 if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_SOURCE
) {
388 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
389 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
390 snprintf(error_buf
, error_buf_size
,
391 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
392 connection
->upstream_comp_name
->str
,
393 connection
->arg
->str
);
397 } else if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_FILTER
) {
398 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
399 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
400 snprintf(error_buf
, error_buf_size
,
401 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
402 connection
->upstream_comp_name
->str
,
403 connection
->arg
->str
);
408 snprintf(error_buf
, error_buf_size
,
409 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
410 connection
->upstream_comp_name
->str
,
411 connection
->downstream_comp_name
->str
,
412 connection
->arg
->str
);
427 static int validate_no_cycles_rec(struct bt_config
*cfg
, GPtrArray
*path
,
428 char *error_buf
, size_t error_buf_size
)
432 const char *src_comp_name
;
434 assert(path
&& path
->len
> 0);
435 src_comp_name
= g_ptr_array_index(path
, path
->len
- 1);
437 for (conn_i
= 0; conn_i
< cfg
->cmd_data
.run
.connections
->len
; conn_i
++) {
438 struct bt_config_connection
*conn
=
439 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, conn_i
);
441 if (strcmp(conn
->upstream_comp_name
->str
, src_comp_name
) == 0) {
444 for (path_i
= 0; path_i
< path
->len
; path_i
++) {
445 const char *comp_name
=
446 g_ptr_array_index(path
, path_i
);
448 if (strcmp(comp_name
, conn
->downstream_comp_name
->str
) == 0) {
449 snprintf(error_buf
, error_buf_size
,
450 "Invalid connection: connection forms a cycle:\n %s\n",
457 g_ptr_array_add(path
, conn
->downstream_comp_name
->str
);
458 ret
= validate_no_cycles_rec(cfg
, path
, error_buf
,
464 g_ptr_array_remove_index(path
, path
->len
- 1);
472 static int validate_no_cycles(struct bt_config
*cfg
, char *error_buf
,
473 size_t error_buf_size
)
479 path
= g_ptr_array_new();
485 g_ptr_array_add(path
, NULL
);
487 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
488 struct bt_config_connection
*conn
=
489 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
491 g_ptr_array_index(path
, 0) = conn
->upstream_comp_name
->str
;
492 ret
= validate_no_cycles_rec(cfg
, path
,
493 error_buf
, error_buf_size
);
501 g_ptr_array_free(path
, TRUE
);
507 static int validate_all_components_connected_in_array(GPtrArray
*comps
,
508 struct bt_value
*connected_components
,
509 char *error_buf
, size_t error_buf_size
)
514 for (i
= 0; i
< comps
->len
; i
++) {
515 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
517 if (!bt_value_map_has_key(connected_components
,
518 comp
->instance_name
->str
)) {
519 snprintf(error_buf
, error_buf_size
,
520 "Component `%s` is not connected\n",
521 comp
->instance_name
->str
);
531 static int validate_all_components_connected(struct bt_config
*cfg
,
532 char *error_buf
, size_t error_buf_size
)
536 struct bt_value
*connected_components
= bt_value_map_create();
538 if (!connected_components
) {
543 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
544 struct bt_config_connection
*connection
=
545 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
547 ret
= bt_value_map_insert(connected_components
,
548 connection
->upstream_comp_name
->str
, bt_value_null
);
553 ret
= bt_value_map_insert(connected_components
,
554 connection
->downstream_comp_name
->str
, bt_value_null
);
560 ret
= validate_all_components_connected_in_array(
561 cfg
->cmd_data
.run
.sources
, connected_components
,
562 error_buf
, error_buf_size
);
567 ret
= validate_all_components_connected_in_array(
568 cfg
->cmd_data
.run
.filters
, connected_components
,
569 error_buf
, error_buf_size
);
574 ret
= validate_all_components_connected_in_array(
575 cfg
->cmd_data
.run
.sinks
, connected_components
,
576 error_buf
, error_buf_size
);
582 bt_put(connected_components
);
586 static int validate_no_duplicate_connection(struct bt_config
*cfg
,
587 char *error_buf
, size_t error_buf_size
)
591 struct bt_value
*flat_connection_names
= bt_value_map_create();
592 GString
*flat_connection_name
= NULL
;
594 if (!flat_connection_names
) {
599 flat_connection_name
= g_string_new(NULL
);
600 if (!flat_connection_name
) {
605 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
606 struct bt_config_connection
*connection
=
607 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
609 g_string_printf(flat_connection_name
, "%s\x01%s\x01%s\x01%s",
610 connection
->upstream_comp_name
->str
,
611 connection
->upstream_port_glob
->str
,
612 connection
->downstream_comp_name
->str
,
613 connection
->downstream_port_glob
->str
);
615 if (bt_value_map_has_key(flat_connection_names
,
616 flat_connection_name
->str
)) {
617 snprintf(error_buf
, error_buf_size
,
618 "Duplicate connection:\n %s\n",
619 connection
->arg
->str
);
624 ret
= bt_value_map_insert(flat_connection_names
,
625 flat_connection_name
->str
, bt_value_null
);
632 bt_put(flat_connection_names
);
634 if (flat_connection_name
) {
635 g_string_free(flat_connection_name
, TRUE
);
641 static int validate_connections(struct bt_config
*cfg
, char *error_buf
,
642 size_t error_buf_size
)
646 ret
= validate_all_endpoints_exist(cfg
, error_buf
, error_buf_size
);
651 ret
= validate_connection_directions(cfg
, error_buf
, error_buf_size
);
656 ret
= validate_all_components_connected(cfg
, error_buf
, error_buf_size
);
661 ret
= validate_no_duplicate_connection(cfg
, error_buf
, error_buf_size
);
666 ret
= validate_no_cycles(cfg
, error_buf
, error_buf_size
);
675 int bt_config_cli_args_create_connections(struct bt_config
*cfg
,
676 struct bt_value
*connection_args
,
677 char *error_buf
, size_t error_buf_size
)
682 if (!all_named_and_printable(cfg
)) {
683 snprintf(error_buf
, error_buf_size
,
684 "One or more components are unnamed (use --name) or contain a non-printable character\n");
688 for (i
= 0; i
< bt_value_array_size(connection_args
); i
++) {
689 struct bt_value
*arg_value
=
690 bt_value_array_get(connection_args
, i
);
692 struct bt_config_connection
*cfg_connection
;
694 ret
= bt_value_string_get(arg_value
, &arg
);
697 cfg_connection
= cfg_connection_from_arg(arg
);
698 if (!cfg_connection
) {
699 snprintf(error_buf
, error_buf_size
, "Cannot parse --connect option's argument:\n %s\n",
704 g_ptr_array_add(cfg
->cmd_data
.run
.connections
,
709 ret
= validate_connections(cfg
, error_buf
, error_buf_size
);