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-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 void bt_config_connection_destroy(struct bt_config_connection
*connection
)
64 if (connection
->src_instance_name
) {
65 g_string_free(connection
->src_instance_name
, TRUE
);
68 if (connection
->dst_instance_name
) {
69 g_string_free(connection
->dst_instance_name
, TRUE
);
72 if (connection
->src_port_name
) {
73 g_string_free(connection
->src_port_name
, TRUE
);
76 if (connection
->dst_port_name
) {
77 g_string_free(connection
->dst_port_name
, TRUE
);
80 if (connection
->arg
) {
81 g_string_free(connection
->arg
, TRUE
);
87 static struct bt_config_connection
*bt_config_connection_create(const char *arg
)
89 struct bt_config_connection
*cfg_connection
;
91 cfg_connection
= g_new0(struct bt_config_connection
, 1);
92 if (!cfg_connection
) {
96 cfg_connection
->src_instance_name
= g_string_new(NULL
);
97 if (!cfg_connection
->src_instance_name
) {
101 cfg_connection
->dst_instance_name
= g_string_new(NULL
);
102 if (!cfg_connection
->dst_instance_name
) {
106 cfg_connection
->src_port_name
= g_string_new(NULL
);
107 if (!cfg_connection
->src_port_name
) {
111 cfg_connection
->dst_port_name
= g_string_new(NULL
);
112 if (!cfg_connection
->dst_port_name
) {
116 cfg_connection
->arg
= g_string_new(arg
);
117 if (!cfg_connection
->arg
) {
124 g_free(cfg_connection
);
125 cfg_connection
= NULL
;
128 return cfg_connection
;
131 static struct bt_config_connection
*cfg_connection_from_arg(const char *arg
)
133 const char *at
= arg
;
135 struct bt_config_connection
*cfg_conn
= NULL
;
144 if (!bt_common_string_is_printable(arg
)) {
148 cfg_conn
= bt_config_connection_create(arg
);
156 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
157 if (!gs
|| gs
->len
== 0) {
161 g_string_free(cfg_conn
->src_instance_name
, TRUE
);
162 cfg_conn
->src_instance_name
= gs
;
165 if (at
[end_pos
] == ':') {
167 } else if (at
[end_pos
] == '.') {
168 state
= SRC_PORT_NAME
;
176 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
177 if (!gs
|| gs
->len
== 0) {
181 g_string_free(cfg_conn
->dst_instance_name
, TRUE
);
182 cfg_conn
->dst_instance_name
= gs
;
185 if (at
[end_pos
] == '.') {
186 state
= DST_PORT_NAME
;
187 } else if (at
[end_pos
] == '\0') {
196 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
197 if (!gs
|| gs
->len
== 0) {
201 g_string_free(cfg_conn
->src_port_name
, TRUE
);
202 cfg_conn
->src_port_name
= gs
;
205 if (at
[end_pos
] == ':') {
214 gs
= bt_common_string_until(at
, ".:\\", ".:", &end_pos
);
215 if (!gs
|| gs
->len
== 0) {
219 g_string_free(cfg_conn
->dst_port_name
, TRUE
);
220 cfg_conn
->dst_port_name
= gs
;
223 if (at
[end_pos
] == '\0') {
235 bt_config_connection_destroy(cfg_conn
);
240 g_string_free(gs
, TRUE
);
246 static struct bt_config_component
*find_component_in_array(GPtrArray
*comps
,
250 struct bt_config_component
*found_comp
= NULL
;
252 for (i
= 0; i
< comps
->len
; i
++) {
253 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
255 if (strcmp(name
, comp
->instance_name
->str
) == 0) {
256 found_comp
= bt_get(comp
);
265 static struct bt_config_component
*find_component(struct bt_config
*cfg
,
268 struct bt_config_component
*comp
;
270 comp
= find_component_in_array(cfg
->cmd_data
.run
.sources
, name
);
275 comp
= find_component_in_array(cfg
->cmd_data
.run
.filters
, name
);
280 comp
= find_component_in_array(cfg
->cmd_data
.run
.sinks
, name
);
289 static int validate_all_endpoints_exist(struct bt_config
*cfg
, char *error_buf
,
290 size_t error_buf_size
)
295 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
296 struct bt_config_connection
*connection
=
297 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
298 struct bt_config_component
*comp
;
300 comp
= find_component(cfg
, connection
->src_instance_name
->str
);
303 snprintf(error_buf
, error_buf_size
,
304 "Invalid connection: cannot find upstream component `%s`:\n %s\n",
305 connection
->src_instance_name
->str
,
306 connection
->arg
->str
);
311 comp
= find_component(cfg
, connection
->dst_instance_name
->str
);
314 snprintf(error_buf
, error_buf_size
,
315 "Invalid connection: cannot find downstream component `%s`:\n %s\n",
316 connection
->dst_instance_name
->str
,
317 connection
->arg
->str
);
327 static int validate_connection_directions(struct bt_config
*cfg
,
328 char *error_buf
, size_t error_buf_size
)
332 struct bt_config_component
*src_comp
= NULL
;
333 struct bt_config_component
*dst_comp
= NULL
;
335 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
336 struct bt_config_connection
*connection
=
337 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
339 src_comp
= find_component(cfg
,
340 connection
->src_instance_name
->str
);
342 dst_comp
= find_component(cfg
,
343 connection
->dst_instance_name
->str
);
346 if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_SOURCE
) {
347 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
348 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
349 snprintf(error_buf
, error_buf_size
,
350 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
351 connection
->src_instance_name
->str
,
352 connection
->arg
->str
);
356 } else if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_FILTER
) {
357 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
358 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
359 snprintf(error_buf
, error_buf_size
,
360 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
361 connection
->src_instance_name
->str
,
362 connection
->arg
->str
);
367 snprintf(error_buf
, error_buf_size
,
368 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
369 connection
->src_instance_name
->str
,
370 connection
->dst_instance_name
->str
,
371 connection
->arg
->str
);
386 static int validate_no_cycles_rec(struct bt_config
*cfg
, GPtrArray
*path
,
387 char *error_buf
, size_t error_buf_size
)
391 const char *src_comp_name
;
393 assert(path
&& path
->len
> 0);
394 src_comp_name
= g_ptr_array_index(path
, path
->len
- 1);
396 for (conn_i
= 0; conn_i
< cfg
->cmd_data
.run
.connections
->len
; conn_i
++) {
397 struct bt_config_connection
*conn
=
398 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, conn_i
);
400 if (strcmp(conn
->src_instance_name
->str
, src_comp_name
) == 0) {
403 for (path_i
= 0; path_i
< path
->len
; path_i
++) {
404 const char *comp_name
=
405 g_ptr_array_index(path
, path_i
);
407 if (strcmp(comp_name
, conn
->dst_instance_name
->str
) == 0) {
408 snprintf(error_buf
, error_buf_size
,
409 "Invalid connection: connection forms a cycle:\n %s\n",
416 g_ptr_array_add(path
, conn
->dst_instance_name
->str
);
417 ret
= validate_no_cycles_rec(cfg
, path
, error_buf
,
423 g_ptr_array_remove_index(path
, path
->len
- 1);
431 static int validate_no_cycles(struct bt_config
*cfg
, char *error_buf
,
432 size_t error_buf_size
)
438 path
= g_ptr_array_new();
444 g_ptr_array_add(path
, NULL
);
446 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
447 struct bt_config_connection
*conn
=
448 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
450 g_ptr_array_index(path
, 0) = conn
->src_instance_name
->str
;
451 ret
= validate_no_cycles_rec(cfg
, path
,
452 error_buf
, error_buf_size
);
460 g_ptr_array_free(path
, TRUE
);
466 static int validate_all_components_connected_in_array(GPtrArray
*comps
,
467 struct bt_value
*connected_components
,
468 char *error_buf
, size_t error_buf_size
)
473 for (i
= 0; i
< comps
->len
; i
++) {
474 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
476 if (!bt_value_map_has_key(connected_components
,
477 comp
->instance_name
->str
)) {
478 snprintf(error_buf
, error_buf_size
,
479 "Component `%s` is not connected\n",
480 comp
->instance_name
->str
);
490 static int validate_all_components_connected(struct bt_config
*cfg
,
491 char *error_buf
, size_t error_buf_size
)
495 struct bt_value
*connected_components
= bt_value_map_create();
497 if (!connected_components
) {
502 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
503 struct bt_config_connection
*connection
=
504 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
506 ret
= bt_value_map_insert(connected_components
,
507 connection
->src_instance_name
->str
, bt_value_null
);
512 ret
= bt_value_map_insert(connected_components
,
513 connection
->dst_instance_name
->str
, bt_value_null
);
519 ret
= validate_all_components_connected_in_array(
520 cfg
->cmd_data
.run
.sources
, connected_components
,
521 error_buf
, error_buf_size
);
526 ret
= validate_all_components_connected_in_array(
527 cfg
->cmd_data
.run
.filters
, connected_components
,
528 error_buf
, error_buf_size
);
533 ret
= validate_all_components_connected_in_array(
534 cfg
->cmd_data
.run
.sinks
, connected_components
,
535 error_buf
, error_buf_size
);
541 bt_put(connected_components
);
545 static int validate_no_duplicate_connection(struct bt_config
*cfg
,
546 char *error_buf
, size_t error_buf_size
)
550 struct bt_value
*flat_connection_names
= bt_value_map_create();
551 GString
*flat_connection_name
= NULL
;
553 if (!flat_connection_names
) {
558 flat_connection_name
= g_string_new(NULL
);
559 if (!flat_connection_name
) {
564 for (i
= 0; i
< cfg
->cmd_data
.run
.connections
->len
; i
++) {
565 struct bt_config_connection
*connection
=
566 g_ptr_array_index(cfg
->cmd_data
.run
.connections
, i
);
568 g_string_printf(flat_connection_name
, "%s\x01%s\x01%s\x01%s",
569 connection
->src_instance_name
->str
,
570 connection
->src_port_name
->str
,
571 connection
->dst_instance_name
->str
,
572 connection
->dst_port_name
->str
);
574 if (bt_value_map_has_key(flat_connection_names
,
575 flat_connection_name
->str
)) {
576 snprintf(error_buf
, error_buf_size
,
577 "Duplicate connection:\n %s\n",
578 connection
->arg
->str
);
583 ret
= bt_value_map_insert(flat_connection_names
,
584 flat_connection_name
->str
, bt_value_null
);
591 bt_put(flat_connection_names
);
593 if (flat_connection_name
) {
594 g_string_free(flat_connection_name
, TRUE
);
600 static int validate_connections(struct bt_config
*cfg
, char *error_buf
,
601 size_t error_buf_size
)
605 ret
= validate_all_endpoints_exist(cfg
, error_buf
, error_buf_size
);
610 ret
= validate_connection_directions(cfg
, error_buf
, error_buf_size
);
615 ret
= validate_all_components_connected(cfg
, error_buf
, error_buf_size
);
620 ret
= validate_no_duplicate_connection(cfg
, error_buf
, error_buf_size
);
625 ret
= validate_no_cycles(cfg
, error_buf
, error_buf_size
);
634 int bt_config_create_connections(struct bt_config
*cfg
,
635 struct bt_value
*connection_args
,
636 char *error_buf
, size_t error_buf_size
)
641 if (!all_named_and_printable(cfg
)) {
642 snprintf(error_buf
, error_buf_size
,
643 "One or more components are unnamed (use --name) or contain a non-printable character\n");
647 for (i
= 0; i
< bt_value_array_size(connection_args
); i
++) {
648 struct bt_value
*arg_value
=
649 bt_value_array_get(connection_args
, i
);
651 struct bt_config_connection
*cfg_connection
;
653 ret
= bt_value_string_get(arg_value
, &arg
);
656 cfg_connection
= cfg_connection_from_arg(arg
);
657 if (!cfg_connection
) {
658 snprintf(error_buf
, error_buf_size
, "Cannot parse --connect option's argument:\n %s\n",
663 g_ptr_array_add(cfg
->cmd_data
.run
.connections
,
668 ret
= validate_connections(cfg
, error_buf
, error_buf_size
);