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
,
323 struct bt_config_component
*comp
;
325 comp
= find_component_in_array(cfg
->cmd_data
.convert
.sources
, name
);
330 comp
= find_component_in_array(cfg
->cmd_data
.convert
.filters
, name
);
335 comp
= find_component_in_array(cfg
->cmd_data
.convert
.sinks
, name
);
344 static int validate_all_endpoints_exist(struct bt_config
*cfg
, char *error_buf
,
345 size_t error_buf_size
)
350 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
351 struct bt_config_connection
*connection
=
352 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
353 struct bt_config_component
*comp
;
355 comp
= find_component(cfg
, connection
->src_instance_name
->str
);
358 comp
= find_component(cfg
,
359 connection
->dst_instance_name
->str
);
362 snprintf(error_buf
, error_buf_size
,
363 "Invalid connection: cannot find component `%s`:\n %s\n",
364 connection
->dst_instance_name
->str
,
365 connection
->arg
->str
);
376 static int validate_connection_directions(struct bt_config
*cfg
,
377 char *error_buf
, size_t error_buf_size
)
381 struct bt_config_component
*src_comp
= NULL
;
382 struct bt_config_component
*dst_comp
= NULL
;
384 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
385 struct bt_config_connection
*connection
=
386 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
388 src_comp
= find_component(cfg
,
389 connection
->src_instance_name
->str
);
391 dst_comp
= find_component(cfg
,
392 connection
->dst_instance_name
->str
);
395 if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_SOURCE
) {
396 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
397 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
398 snprintf(error_buf
, error_buf_size
,
399 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
400 connection
->src_instance_name
->str
,
401 connection
->arg
->str
);
405 } else if (src_comp
->type
== BT_COMPONENT_CLASS_TYPE_FILTER
) {
406 if (dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_FILTER
&&
407 dst_comp
->type
!= BT_COMPONENT_CLASS_TYPE_SINK
) {
408 snprintf(error_buf
, error_buf_size
,
409 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
410 connection
->src_instance_name
->str
,
411 connection
->arg
->str
);
416 snprintf(error_buf
, error_buf_size
,
417 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
418 connection
->src_instance_name
->str
,
419 connection
->dst_instance_name
->str
,
420 connection
->arg
->str
);
435 static int validate_self_connections(struct bt_config
*cfg
, char *error_buf
,
436 size_t error_buf_size
)
441 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
442 struct bt_config_connection
*connection
=
443 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
445 if (strcmp(connection
->src_instance_name
->str
,
446 connection
->dst_instance_name
->str
) == 0) {
447 snprintf(error_buf
, error_buf_size
,
448 "Invalid connection: component `%s` is connected to itself:\n %s\n",
449 connection
->src_instance_name
->str
,
450 connection
->arg
->str
);
460 static int validate_all_components_connected_in_array(GPtrArray
*comps
,
461 struct bt_value
*connected_components
,
462 char *error_buf
, size_t error_buf_size
)
467 for (i
= 0; i
< comps
->len
; i
++) {
468 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
470 if (!bt_value_map_has_key(connected_components
,
471 comp
->instance_name
->str
)) {
472 snprintf(error_buf
, error_buf_size
,
473 "Component `%s` is not connected\n",
474 comp
->instance_name
->str
);
484 static int validate_all_components_connected(struct bt_config
*cfg
,
485 char *error_buf
, size_t error_buf_size
)
489 struct bt_value
*connected_components
= bt_value_map_create();
491 if (!connected_components
) {
496 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
497 struct bt_config_connection
*connection
=
498 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
500 ret
= bt_value_map_insert(connected_components
,
501 connection
->src_instance_name
->str
, bt_value_null
);
506 ret
= bt_value_map_insert(connected_components
,
507 connection
->dst_instance_name
->str
, bt_value_null
);
513 ret
= validate_all_components_connected_in_array(
514 cfg
->cmd_data
.convert
.sources
, connected_components
,
515 error_buf
, error_buf_size
);
520 ret
= validate_all_components_connected_in_array(
521 cfg
->cmd_data
.convert
.filters
, connected_components
,
522 error_buf
, error_buf_size
);
527 ret
= validate_all_components_connected_in_array(
528 cfg
->cmd_data
.convert
.sinks
, connected_components
,
529 error_buf
, error_buf_size
);
535 bt_put(connected_components
);
539 static int validate_no_duplicate_connection(struct bt_config
*cfg
,
540 char *error_buf
, size_t error_buf_size
)
544 struct bt_value
*flat_connection_names
= bt_value_map_create();
545 GString
*flat_connection_name
= NULL
;
547 if (!flat_connection_names
) {
552 flat_connection_name
= g_string_new(NULL
);
553 if (!flat_connection_name
) {
558 for (i
= 0; i
< cfg
->cmd_data
.convert
.connections
->len
; i
++) {
559 struct bt_config_connection
*connection
=
560 g_ptr_array_index(cfg
->cmd_data
.convert
.connections
, i
);
562 g_string_printf(flat_connection_name
, "%s.%s:%s.%s",
563 connection
->src_instance_name
->str
,
564 connection
->src_port_name
->str
,
565 connection
->dst_instance_name
->str
,
566 connection
->dst_port_name
->str
);
568 if (bt_value_map_has_key(flat_connection_names
,
569 flat_connection_name
->str
)) {
570 snprintf(error_buf
, error_buf_size
,
571 "Duplicate connection:\n %s\n",
572 connection
->arg
->str
);
577 ret
= bt_value_map_insert(flat_connection_names
,
578 flat_connection_name
->str
, bt_value_null
);
585 bt_put(flat_connection_names
);
587 if (flat_connection_name
) {
588 g_string_free(flat_connection_name
, TRUE
);
594 static int validate_connections(struct bt_config
*cfg
, char *error_buf
,
595 size_t error_buf_size
)
599 ret
= validate_all_endpoints_exist(cfg
, error_buf
, error_buf_size
);
604 ret
= validate_connection_directions(cfg
, error_buf
, error_buf_size
);
609 ret
= validate_self_connections(cfg
, error_buf
, error_buf_size
);
614 ret
= validate_all_components_connected(cfg
, error_buf
, error_buf_size
);
619 ret
= validate_no_duplicate_connection(cfg
, error_buf
, error_buf_size
);
628 static bool component_name_exists_in_array(GPtrArray
*comps
, const char *name
)
633 for (i
= 0; i
< comps
->len
; i
++) {
634 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
636 if (comp
->instance_name
->len
> 0) {
637 if (strcmp(comp
->instance_name
->str
, name
) == 0) {
647 static bool component_name_exists_at_all(struct bt_config
*cfg
,
650 return component_name_exists_in_array(cfg
->cmd_data
.convert
.sources
,
651 name
) || component_name_exists_in_array(
652 cfg
->cmd_data
.convert
.filters
, name
) ||
653 component_name_exists_in_array(
654 cfg
->cmd_data
.convert
.sinks
, name
);
657 static int auto_name_component(struct bt_config
*cfg
, const char *prefix
,
658 struct bt_config_component
*comp
)
664 assert(comp
->instance_name
->len
== 0);
665 new_name
= g_string_new(NULL
);
672 g_string_printf(new_name
, "%s-%s.%s-%d", prefix
,
673 comp
->plugin_name
->str
, comp
->component_name
->str
, i
);
675 } while (component_name_exists_at_all(cfg
, new_name
->str
));
677 g_string_assign(comp
->instance_name
, new_name
->str
);
681 g_string_free(new_name
, TRUE
);
687 static int auto_name_components_in_array(struct bt_config
*cfg
,
688 GPtrArray
*comps
, const char *prefix
)
693 for (i
= 0; i
< comps
->len
; i
++) {
694 struct bt_config_component
*comp
= g_ptr_array_index(comps
, i
);
696 if (comp
->instance_name
->len
== 0) {
697 ret
= auto_name_component(cfg
, prefix
, comp
);
708 static int auto_name_components(struct bt_config
*cfg
)
712 ret
= auto_name_components_in_array(cfg
, cfg
->cmd_data
.convert
.sources
,
718 ret
= auto_name_components_in_array(cfg
, cfg
->cmd_data
.convert
.filters
,
724 ret
= auto_name_components_in_array(cfg
, cfg
->cmd_data
.convert
.sinks
,
734 static int auto_connect(struct bt_config
*cfg
)
737 struct bt_config_component
*muxer_cfg_comp
= NULL
;
739 const char *last_filter_comp_name
;
741 /* Make sure all components have a unique instance name */
742 ret
= auto_name_components(cfg
);
747 /* Add an implicit muxer filter */
748 muxer_cfg_comp
= bt_config_component_from_arg(
749 BT_COMPONENT_CLASS_TYPE_FILTER
, "utils.muxer");
750 if (!muxer_cfg_comp
) {
754 auto_name_component(cfg
, "filter", muxer_cfg_comp
);
755 g_ptr_array_add(cfg
->cmd_data
.convert
.filters
, bt_get(muxer_cfg_comp
));
757 /* Connect all sources to this mux */
758 for (i
= 0; i
< cfg
->cmd_data
.convert
.sources
->len
; i
++) {
759 struct bt_config_component
*comp
=
760 g_ptr_array_index(cfg
->cmd_data
.convert
.sources
, i
);
761 struct bt_config_connection
*cfg_connection
=
762 bt_config_connection_create_full(
763 comp
->instance_name
->str
, "",
764 muxer_cfg_comp
->instance_name
->str
, "",
767 if (!cfg_connection
) {
771 g_ptr_array_add(cfg
->cmd_data
.convert
.connections
,
775 /* Connect this mux to the filter components, in order */
776 last_filter_comp_name
= muxer_cfg_comp
->instance_name
->str
;
778 for (i
= 0; i
< cfg
->cmd_data
.convert
.filters
->len
- 1; i
++) {
779 struct bt_config_component
*comp
=
780 g_ptr_array_index(cfg
->cmd_data
.convert
.filters
, i
);
781 struct bt_config_connection
*cfg_connection
;
783 cfg_connection
= bt_config_connection_create_full(
784 last_filter_comp_name
, "",
785 comp
->instance_name
->str
, "",
788 if (!cfg_connection
) {
792 g_ptr_array_add(cfg
->cmd_data
.convert
.connections
,
794 last_filter_comp_name
= comp
->instance_name
->str
;
797 /* Connect the last filter component to all sink components */
798 for (i
= 0; i
< cfg
->cmd_data
.convert
.sinks
->len
; i
++) {
799 struct bt_config_component
*comp
=
800 g_ptr_array_index(cfg
->cmd_data
.convert
.sinks
, i
);
801 struct bt_config_connection
*cfg_connection
=
802 bt_config_connection_create_full(
803 last_filter_comp_name
, "",
804 comp
->instance_name
->str
, "",
807 if (!cfg_connection
) {
811 g_ptr_array_add(cfg
->cmd_data
.convert
.connections
,
821 bt_put(muxer_cfg_comp
);
825 int bt_config_create_connections(struct bt_config
*cfg
,
826 struct bt_value
*connection_args
,
827 char *error_buf
, size_t error_buf_size
)
832 if (bt_value_array_is_empty(connection_args
)) {
833 /* No explicit connections: do automatic connection */
834 ret
= auto_connect(cfg
);
840 if (!all_named(cfg
)) {
841 snprintf(error_buf
, error_buf_size
, "At least one connection (--connect) specified, but not all component\ninstances are named (use --name)\n");
845 for (i
= 0; i
< bt_value_array_size(connection_args
); i
++) {
846 struct bt_value
*arg_value
=
847 bt_value_array_get(connection_args
, i
);
849 struct bt_config_connection
*cfg_connection
;
851 ret
= bt_value_string_get(arg_value
, &arg
);
854 cfg_connection
= cfg_connection_from_arg(arg
);
855 if (!cfg_connection
) {
856 snprintf(error_buf
, error_buf_size
, "Cannot parse --connect option's argument:\n %s\n",
861 g_ptr_array_add(cfg
->cmd_data
.convert
.connections
,
866 ret
= validate_connections(cfg
, error_buf
, error_buf_size
);