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-cfg.h"
25 #include "babeltrace-cfg-connect.h"
27 static bool all_named_in_array(GPtrArray
*comps
)
30 bool all_named
= true;
32 for (i
= 0; i
< comps
->len
; i
++) {
33 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
35 if (comp
->instance_name
->len
== 0) {
45 static bool all_named(struct bt_config
*cfg
)
47 return all_named_in_array(cfg
->cmd_data
.convert
.sources
) &&
48 all_named_in_array(cfg
->cmd_data
.convert
.filters
) &&
49 all_named_in_array(cfg
->cmd_data
.convert
.sinks
);
52 void bt_config_connection_destroy(struct bt_config_connection
*connection
)
58 if (connection
->src_instance_name
) {
59 g_string_free(connection
->src_instance_name
, TRUE
);
62 if (connection
->dst_instance_name
) {
63 g_string_free(connection
->dst_instance_name
, TRUE
);
66 if (connection
->src_port_name
) {
67 g_string_free(connection
->src_port_name
, TRUE
);
70 if (connection
->dst_port_name
) {
71 g_string_free(connection
->dst_port_name
, TRUE
);
74 if (connection
->arg
) {
75 g_string_free(connection
->arg
, TRUE
);
81 static struct bt_config_connection
*bt_config_connection_create(const char *arg
)
83 struct bt_config_connection
*cfg_connection
;
85 cfg_connection
= g_new0(struct bt_config_connection
, 1);
86 if (!cfg_connection
) {
90 cfg_connection
->src_instance_name
= g_string_new(NULL
);
91 if (!cfg_connection
->src_instance_name
) {
95 cfg_connection
->dst_instance_name
= g_string_new(NULL
);
96 if (!cfg_connection
->dst_instance_name
) {
100 cfg_connection
->src_port_name
= g_string_new(NULL
);
101 if (!cfg_connection
->src_port_name
) {
105 cfg_connection
->dst_port_name
= g_string_new(NULL
);
106 if (!cfg_connection
->dst_port_name
) {
110 cfg_connection
->arg
= g_string_new(arg
);
111 if (!cfg_connection
->arg
) {
118 g_free(cfg_connection
);
119 cfg_connection
= NULL
;
122 return cfg_connection
;
125 static struct bt_config_connection
*bt_config_connection_create_full(
126 const char *src_instance_name
, const char *src_port_name
,
127 const char *dst_instance_name
, const char *dst_port_name
,
130 struct bt_config_connection
*cfg_connection
=
131 bt_config_connection_create(arg
);
133 if (!cfg_connection
) {
137 g_string_assign(cfg_connection
->src_instance_name
, src_instance_name
);
138 g_string_assign(cfg_connection
->dst_instance_name
, dst_instance_name
);
139 g_string_assign(cfg_connection
->src_port_name
, src_port_name
);
140 g_string_assign(cfg_connection
->dst_port_name
, dst_port_name
);
143 return cfg_connection
;
146 static GScanner
*create_connection_arg_scanner(void)
148 GScannerConfig scanner_config
= {
149 .cset_skip_characters
= " \t\n",
150 .cset_identifier_first
= G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS
"_-",
151 .cset_identifier_nth
= G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS
"_-",
152 .case_sensitive
= TRUE
,
153 .cpair_comment_single
= NULL
,
154 .skip_comment_multi
= TRUE
,
155 .skip_comment_single
= TRUE
,
156 .scan_comment_multi
= FALSE
,
157 .scan_identifier
= TRUE
,
158 .scan_identifier_1char
= TRUE
,
159 .scan_identifier_NULL
= FALSE
,
160 .scan_symbols
= FALSE
,
161 .symbol_2_token
= FALSE
,
162 .scope_0_fallback
= FALSE
,
163 .scan_binary
= FALSE
,
167 .scan_hex_dollar
= FALSE
,
168 .numbers_2_int
= FALSE
,
169 .int_2_float
= FALSE
,
170 .store_int64
= FALSE
,
171 .scan_string_sq
= FALSE
,
172 .scan_string_dq
= FALSE
,
173 .identifier_2_string
= FALSE
,
174 .char_2_token
= TRUE
,
177 return g_scanner_new(&scanner_config
);
180 static struct bt_config_connection
*cfg_connection_from_arg(const char *arg
)
182 struct bt_config_connection
*connection
= NULL
;
183 GScanner
*scanner
= NULL
;
193 } state
= EXPECTING_SRC
;
195 connection
= bt_config_connection_create(arg
);
200 scanner
= create_connection_arg_scanner();
205 g_scanner_input_text(scanner
, arg
, strlen(arg
));
208 GTokenType token_type
= g_scanner_get_next_token(scanner
);
210 if (token_type
== G_TOKEN_EOF
) {
216 if (token_type
!= G_TOKEN_IDENTIFIER
) {
220 g_string_assign(connection
->src_instance_name
,
221 scanner
->value
.v_identifier
);
222 state
= EXPECTING_SRC_DOT
;
224 case EXPECTING_SRC_DOT
:
225 if (token_type
== ':') {
226 state
= EXPECTING_DST
;
230 if (token_type
!= '.') {
234 state
= EXPECTING_SRC_PORT
;
236 case EXPECTING_SRC_PORT
:
237 if (token_type
!= G_TOKEN_IDENTIFIER
) {
241 g_string_assign(connection
->src_port_name
,
242 scanner
->value
.v_identifier
);
243 state
= EXPECTING_COLON
;
245 case EXPECTING_COLON
:
246 if (token_type
!= ':') {
250 state
= EXPECTING_DST
;
253 if (token_type
!= G_TOKEN_IDENTIFIER
) {
257 g_string_assign(connection
->dst_instance_name
,
258 scanner
->value
.v_identifier
);
259 state
= EXPECTING_DST_DOT
;
261 case EXPECTING_DST_DOT
:
262 if (token_type
!= '.') {
266 state
= EXPECTING_DST_PORT
;
268 case EXPECTING_DST_PORT
:
269 if (token_type
!= G_TOKEN_IDENTIFIER
) {
273 g_string_assign(connection
->dst_port_name
,
274 scanner
->value
.v_identifier
);
283 if (state
!= EXPECTING_DST_DOT
&& state
!= DONE
) {
290 bt_config_connection_destroy(connection
);
295 g_scanner_destroy(scanner
);
301 static struct bt_config_component
*find_component_in_array(GPtrArray
*comps
,
305 struct bt_config_component
*found_comp
= NULL
;
307 for (i
= 0; i
< comps
->len
; i
++) {
308 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
310 if (strcmp(name
, comp
->instance_name
->str
) == 0) {
311 found_comp
= bt_get(comp
);
320 static struct bt_config_component
*find_component(struct bt_config
*cfg
,
321 const char *name
, enum bt_component_class_type
*type
)
323 struct bt_config_component
*comp
;
325 comp
= find_component_in_array(cfg
->cmd_data
.convert
.sources
, name
);
327 *type
= BT_COMPONENT_CLASS_TYPE_SOURCE
;
331 comp
= find_component_in_array(cfg
->cmd_data
.convert
.filters
, name
);
333 *type
= BT_COMPONENT_CLASS_TYPE_FILTER
;
337 comp
= find_component_in_array(cfg
->cmd_data
.convert
.sinks
, name
);
339 *type
= BT_COMPONENT_CLASS_TYPE_SINK
;
347 static int validate_all_endpoints_exist(struct bt_config
*cfg
, char *error_buf
,
348 size_t error_buf_size
)
353 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
354 struct bt_config_connection
*connection
=
355 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
356 struct bt_config_component
*comp
;
357 enum bt_component_class_type type
;
359 comp
= find_component(cfg
, connection
->src_instance_name
->str
,
363 comp
= find_component(cfg
,
364 connection
->dst_instance_name
->str
, &type
);
367 snprintf(error_buf
, error_buf_size
,
368 "Invalid connection: cannot find component `%s`:\n %s\n",
369 connection
->dst_instance_name
->str
,
370 connection
->arg
->str
);
381 static int validate_connection_directions(struct bt_config
*cfg
,
382 char *error_buf
, size_t error_buf_size
)
386 struct bt_config_component
*src_comp
= NULL
;
387 struct bt_config_component
*dst_comp
= NULL
;
389 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
390 struct bt_config_connection
*connection
=
391 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
392 enum bt_component_class_type src_type
;
393 enum bt_component_class_type dst_type
;
395 src_comp
= find_component(cfg
,
396 connection
->src_instance_name
->str
, &src_type
);
398 dst_comp
= find_component(cfg
,
399 connection
->dst_instance_name
->str
, &dst_type
);
402 if (src_type
== BT_COMPONENT_CLASS_TYPE_SOURCE
) {
403 if (dst_type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
404 dst_type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
405 snprintf(error_buf
, error_buf_size
,
406 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
407 connection
->src_instance_name
->str
,
408 connection
->arg
->str
);
412 } else if (src_type
== BT_COMPONENT_CLASS_TYPE_FILTER
) {
413 if (dst_type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
414 dst_type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
415 snprintf(error_buf
, error_buf_size
,
416 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
417 connection
->src_instance_name
->str
,
418 connection
->arg
->str
);
423 snprintf(error_buf
, error_buf_size
,
424 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
425 connection
->src_instance_name
->str
,
426 connection
->dst_instance_name
->str
,
427 connection
->arg
->str
);
442 static int validate_self_connections(struct bt_config
*cfg
, char *error_buf
,
443 size_t error_buf_size
)
448 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
449 struct bt_config_connection
*connection
=
450 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
452 if (strcmp(connection
->src_instance_name
->str
,
453 connection
->dst_instance_name
->str
) == 0) {
454 snprintf(error_buf
, error_buf_size
,
455 "Invalid connection: component `%s` is connected to itself:\n %s\n",
456 connection
->src_instance_name
->str
,
457 connection
->arg
->str
);
467 static int validate_all_components_connected_in_array(GPtrArray
*comps
,
468 struct bt_value
*connected_components
,
469 char *error_buf
, size_t error_buf_size
)
474 for (i
= 0; i
< comps
->len
; i
++) {
475 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
477 if (!bt_value_map_has_key(connected_components
,
478 comp
->instance_name
->str
)) {
479 snprintf(error_buf
, error_buf_size
,
480 "Component `%s` is not connected\n",
481 comp
->instance_name
->str
);
491 static int validate_all_components_connected(struct bt_config
*cfg
,
492 char *error_buf
, size_t error_buf_size
)
496 struct bt_value
*connected_components
= bt_value_map_create();
498 if (!connected_components
) {
503 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
504 struct bt_config_connection
*connection
=
505 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
507 ret
= bt_value_map_insert(connected_components
,
508 connection
->src_instance_name
->str
, bt_value_null
);
513 ret
= bt_value_map_insert(connected_components
,
514 connection
->dst_instance_name
->str
, bt_value_null
);
520 ret
= validate_all_components_connected_in_array(
521 cfg
->cmd_data
.convert
.sources
, connected_components
,
522 error_buf
, error_buf_size
);
527 ret
= validate_all_components_connected_in_array(
528 cfg
->cmd_data
.convert
.filters
, connected_components
,
529 error_buf
, error_buf_size
);
534 ret
= validate_all_components_connected_in_array(
535 cfg
->cmd_data
.convert
.sinks
, connected_components
,
536 error_buf
, error_buf_size
);
542 bt_put(connected_components
);
546 static int validate_no_duplicate_connection(struct bt_config
*cfg
,
547 char *error_buf
, size_t error_buf_size
)
551 struct bt_value
*flat_connection_names
= bt_value_map_create();
552 GString
*flat_connection_name
= NULL
;
554 if (!flat_connection_names
) {
559 flat_connection_name
= g_string_new(NULL
);
560 if (!flat_connection_name
) {
565 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
566 struct bt_config_connection
*connection
=
567 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
569 g_string_printf(flat_connection_name
, "%s.%s:%s.%s",
570 connection
->src_instance_name
->str
,
571 connection
->src_port_name
->str
,
572 connection
->dst_instance_name
->str
,
573 connection
->dst_port_name
->str
);
575 if (bt_value_map_has_key(flat_connection_names
,
576 flat_connection_name
->str
)) {
577 snprintf(error_buf
, error_buf_size
,
578 "Duplicate connection:\n %s\n",
579 connection
->arg
->str
);
584 ret
= bt_value_map_insert(flat_connection_names
,
585 flat_connection_name
->str
, bt_value_null
);
592 bt_put(flat_connection_names
);
594 if (flat_connection_name
) {
595 g_string_free(flat_connection_name
, TRUE
);
601 static int validate_connections(struct bt_config
*cfg
, char *error_buf
,
602 size_t error_buf_size
)
606 ret
= validate_all_endpoints_exist(cfg
, error_buf
, error_buf_size
);
611 ret
= validate_connection_directions(cfg
, error_buf
, error_buf_size
);
616 ret
= validate_self_connections(cfg
, error_buf
, error_buf_size
);
621 ret
= validate_all_components_connected(cfg
, error_buf
, error_buf_size
);
626 ret
= validate_no_duplicate_connection(cfg
, error_buf
, error_buf_size
);
635 static bool component_name_exists_in_array(GPtrArray
*comps
, const char *name
)
640 for (i
= 0; i
< comps
->len
; i
++) {
641 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
643 if (comp
->instance_name
->len
> 0) {
644 if (strcmp(comp
->instance_name
->str
, name
) == 0) {
654 static bool component_name_exists_at_all(struct bt_config
*cfg
,
657 return component_name_exists_in_array(cfg
->cmd_data
.convert
.sources
,
658 name
) || component_name_exists_in_array(
659 cfg
->cmd_data
.convert
.filters
, name
) ||
660 component_name_exists_in_array(
661 cfg
->cmd_data
.convert
.sinks
, name
);
664 static int auto_name_component(struct bt_config
*cfg
, const char *prefix
,
665 struct bt_config_component
*comp
)
671 assert(comp
->instance_name
->len
== 0);
672 new_name
= g_string_new(NULL
);
679 g_string_printf(new_name
, "%s-%s.%s-%d", prefix
,
680 comp
->plugin_name
->str
, comp
->component_name
->str
, i
);
682 } while (component_name_exists_at_all(cfg
, new_name
->str
));
684 g_string_assign(comp
->instance_name
, new_name
->str
);
688 g_string_free(new_name
, TRUE
);
694 static int auto_name_components_in_array(struct bt_config
*cfg
,
695 GPtrArray
*comps
, const char *prefix
)
700 for (i
= 0; i
< comps
->len
; i
++) {
701 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
703 if (comp
->instance_name
->len
== 0) {
704 ret
= auto_name_component(cfg
, prefix
, comp
);
715 static int auto_name_components(struct bt_config
*cfg
)
719 ret
= auto_name_components_in_array(cfg
, cfg
->cmd_data
.convert
.sources
,
725 ret
= auto_name_components_in_array(cfg
, cfg
->cmd_data
.convert
.filters
,
731 ret
= auto_name_components_in_array(cfg
, cfg
->cmd_data
.convert
.sinks
,
741 static int auto_connect(struct bt_config
*cfg
)
744 struct bt_config_component
*muxer_cfg_comp
= NULL
;
746 const char *last_filter_comp_name
;
748 /* Make sure all components have a unique instance name */
749 ret
= auto_name_components(cfg
);
754 /* Add an implicit muxer filter */
755 muxer_cfg_comp
= bt_config_component_from_arg("utils.muxer");
756 if (!muxer_cfg_comp
) {
760 auto_name_component(cfg
, "filter", muxer_cfg_comp
);
761 g_ptr_array_add(cfg
->cmd_data
.convert
.filters
, bt_get(muxer_cfg_comp
));
763 /* Connect all sources to this mux */
764 for (i
= 0; i
< cfg
->cmd_data
.convert
.sources
->len
; i
++) {
765 struct bt_config_component
*comp
=
766 g_ptr_array_index(cfg
->cmd_data
.convert
.sources
, i
);
767 struct bt_config_connection
*cfg_connection
=
768 bt_config_connection_create_full(
769 comp
->instance_name
->str
, "",
770 muxer_cfg_comp
->instance_name
->str
, "",
773 if (!cfg_connection
) {
777 g_ptr_array_add(cfg
->cmd_data
.convert
.connections
,
781 /* Connect this mux to the filter components, in order */
782 last_filter_comp_name
= muxer_cfg_comp
->instance_name
->str
;
784 for (i
= 0; i
< cfg
->cmd_data
.convert
.filters
->len
- 1; i
++) {
785 struct bt_config_component
*comp
=
786 g_ptr_array_index(cfg
->cmd_data
.convert
.filters
, i
);
787 struct bt_config_connection
*cfg_connection
;
789 cfg_connection
= bt_config_connection_create_full(
790 last_filter_comp_name
, "",
791 comp
->instance_name
->str
, "",
794 if (!cfg_connection
) {
798 g_ptr_array_add(cfg
->cmd_data
.convert
.connections
,
800 last_filter_comp_name
= comp
->instance_name
->str
;
803 /* Connect the last filter component to all sink components */
804 for (i
= 0; i
< cfg
->cmd_data
.convert
.sinks
->len
; i
++) {
805 struct bt_config_component
*comp
=
806 g_ptr_array_index(cfg
->cmd_data
.convert
.sinks
, i
);
807 struct bt_config_connection
*cfg_connection
=
808 bt_config_connection_create_full(
809 last_filter_comp_name
, "",
810 comp
->instance_name
->str
, "",
813 if (!cfg_connection
) {
817 g_ptr_array_add(cfg
->cmd_data
.convert
.connections
,
827 bt_put(muxer_cfg_comp
);
831 int bt_config_create_connections(struct bt_config
*cfg
,
832 struct bt_value
*connection_args
,
833 char *error_buf
, size_t error_buf_size
)
838 if (bt_value_array_is_empty(connection_args
)) {
839 /* No explicit connections: do automatic connection */
840 ret
= auto_connect(cfg
);
846 if (!all_named(cfg
)) {
847 snprintf(error_buf
, error_buf_size
, "At least one connection (--connect) specified, but not all component\ninstances are named (use --name)\n");
851 for (i
= 0; i
< bt_value_array_size(connection_args
); i
++) {
852 struct bt_value
*arg_value
=
853 bt_value_array_get(connection_args
, i
);
855 struct bt_config_connection
*cfg_connection
;
857 ret
= bt_value_string_get(arg_value
, &arg
);
860 cfg_connection
= cfg_connection_from_arg(arg
);
861 if (!cfg_connection
) {
862 snprintf(error_buf
, error_buf_size
, "Cannot parse --connect option's argument:\n %s\n",
867 g_ptr_array_add(cfg
->cmd_data
.convert
.connections
,
872 ret
= validate_connections(cfg
, error_buf
, error_buf_size
);