lib/graph/iterator.c: add logging
[babeltrace.git] / cli / babeltrace-cfg-cli-args-connect.c
CommitLineData
ebba3338
PP
1/*
2 * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
3 *
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:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
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
20 * SOFTWARE.
21 */
22
23#include <babeltrace/values.h>
db0f160a 24#include <babeltrace/common-internal.h>
ebba3338 25#include "babeltrace-cfg.h"
9009cc24 26#include "babeltrace-cfg-cli-args-connect.h"
ebba3338 27
db0f160a 28static bool all_named_and_printable_in_array(GPtrArray *comps)
ebba3338
PP
29{
30 size_t i;
db0f160a 31 bool all_named_and_printable = true;
ebba3338
PP
32
33 for (i = 0; i < comps->len; i++) {
34 struct bt_config_component *comp = g_ptr_array_index(comps, i);
35
36 if (comp->instance_name->len == 0) {
db0f160a
PP
37 all_named_and_printable = false;
38 goto end;
39 }
40
41 if (!bt_common_string_is_printable(comp->instance_name->str)) {
42 all_named_and_printable = false;
ebba3338
PP
43 goto end;
44 }
45 }
46
47end:
db0f160a 48 return all_named_and_printable;
ebba3338
PP
49}
50
db0f160a 51static bool all_named_and_printable(struct bt_config *cfg)
ebba3338 52{
db0f160a
PP
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);
ebba3338
PP
56}
57
ebba3338
PP
58static struct bt_config_connection *bt_config_connection_create(const char *arg)
59{
60 struct bt_config_connection *cfg_connection;
61
62 cfg_connection = g_new0(struct bt_config_connection, 1);
63 if (!cfg_connection) {
64 goto error;
65 }
66
9009cc24
PP
67 cfg_connection->upstream_comp_name = g_string_new(NULL);
68 if (!cfg_connection->upstream_comp_name) {
ebba3338
PP
69 goto error;
70 }
71
9009cc24
PP
72 cfg_connection->downstream_comp_name = g_string_new(NULL);
73 if (!cfg_connection->downstream_comp_name) {
ebba3338
PP
74 goto error;
75 }
76
9009cc24
PP
77 cfg_connection->upstream_port_glob = g_string_new("*");
78 if (!cfg_connection->upstream_port_glob) {
ebba3338
PP
79 goto error;
80 }
81
9009cc24
PP
82 cfg_connection->downstream_port_glob = g_string_new("*");
83 if (!cfg_connection->downstream_port_glob) {
ebba3338
PP
84 goto error;
85 }
86
87 cfg_connection->arg = g_string_new(arg);
88 if (!cfg_connection->arg) {
89 goto error;
90 }
91
92 goto end;
93
94error:
95 g_free(cfg_connection);
96 cfg_connection = NULL;
97
98end:
99 return cfg_connection;
100}
101
9009cc24
PP
102static bool validate_port_glob(const char *port_glob)
103{
104 bool is_valid = true;
105 const char *ch = port_glob;
106
107 assert(port_glob);
108
109 while (*ch != '\0') {
110 switch (*ch) {
111 case '\\':
112 switch (ch[1]) {
113 case '\0':
114 goto end;
115 default:
116 ch += 2;
117 continue;
118 }
119 case '?':
120 case '[':
121 /*
122 * This is reserved for future use, to support
123 * full globbing patterns. Those characters must
124 * be escaped with `\`.
125 */
126 is_valid = false;
127 goto end;
128 default:
129 ch++;
130 break;
131 }
132 }
133
134end:
135 return is_valid;
136}
137
138static int normalize_glob_pattern(GString *glob_pattern_gs)
139{
140 int ret = 0;
141 char *glob_pattern = strdup(glob_pattern_gs->str);
142
143 if (!glob_pattern) {
144 ret = -1;
145 goto end;
146 }
147
148 bt_common_normalize_star_glob_pattern(glob_pattern);
149 g_string_assign(glob_pattern_gs, glob_pattern);
150 free(glob_pattern);
151
152end:
153 return ret;
154}
155
ebba3338
PP
156static struct bt_config_connection *cfg_connection_from_arg(const char *arg)
157{
db0f160a
PP
158 const char *at = arg;
159 size_t end_pos;
160 struct bt_config_connection *cfg_conn = NULL;
161 GString *gs = NULL;
ebba3338 162 enum {
9009cc24
PP
163 UPSTREAM_NAME,
164 DOWNSTREAM_NAME,
165 UPSTREAM_PORT_GLOB,
166 DOWNSTREAM_PORT_GLOB,
167 } state = UPSTREAM_NAME;
db0f160a
PP
168
169 if (!bt_common_string_is_printable(arg)) {
ebba3338
PP
170 goto error;
171 }
172
db0f160a
PP
173 cfg_conn = bt_config_connection_create(arg);
174 if (!cfg_conn) {
ebba3338
PP
175 goto error;
176 }
177
ebba3338 178 while (true) {
ebba3338 179 switch (state) {
9009cc24 180 case UPSTREAM_NAME:
db0f160a
PP
181 gs = bt_common_string_until(at, ".:\\", ".:", &end_pos);
182 if (!gs || gs->len == 0) {
ebba3338
PP
183 goto error;
184 }
185
9009cc24
PP
186 g_string_free(cfg_conn->upstream_comp_name, TRUE);
187 cfg_conn->upstream_comp_name = gs;
db0f160a 188 gs = NULL;
ebba3338 189
db0f160a 190 if (at[end_pos] == ':') {
9009cc24 191 state = DOWNSTREAM_NAME;
db0f160a 192 } else if (at[end_pos] == '.') {
9009cc24 193 state = UPSTREAM_PORT_GLOB;
db0f160a 194 } else {
ebba3338
PP
195 goto error;
196 }
197
db0f160a 198 at += end_pos + 1;
ebba3338 199 break;
9009cc24 200 case DOWNSTREAM_NAME:
db0f160a
PP
201 gs = bt_common_string_until(at, ".:\\", ".:", &end_pos);
202 if (!gs || gs->len == 0) {
ebba3338
PP
203 goto error;
204 }
205
9009cc24
PP
206 g_string_free(cfg_conn->downstream_comp_name, TRUE);
207 cfg_conn->downstream_comp_name = gs;
db0f160a
PP
208 gs = NULL;
209
210 if (at[end_pos] == '.') {
9009cc24 211 state = DOWNSTREAM_PORT_GLOB;
db0f160a
PP
212 } else if (at[end_pos] == '\0') {
213 goto end;
214 } else {
ebba3338
PP
215 goto error;
216 }
217
db0f160a 218 at += end_pos + 1;
ebba3338 219 break;
9009cc24
PP
220 case UPSTREAM_PORT_GLOB:
221 gs = bt_common_string_until(at, ".:", ".:", &end_pos);
db0f160a 222 if (!gs || gs->len == 0) {
ebba3338
PP
223 goto error;
224 }
225
9009cc24
PP
226 if (!validate_port_glob(gs->str)) {
227 goto error;
228 }
229
230 if (normalize_glob_pattern(gs)) {
231 goto error;
232 }
233
234 g_string_free(cfg_conn->upstream_port_glob, TRUE);
235 cfg_conn->upstream_port_glob = gs;
db0f160a
PP
236 gs = NULL;
237
238 if (at[end_pos] == ':') {
9009cc24 239 state = DOWNSTREAM_NAME;
db0f160a 240 } else {
ebba3338
PP
241 goto error;
242 }
243
db0f160a 244 at += end_pos + 1;
ebba3338 245 break;
9009cc24
PP
246 case DOWNSTREAM_PORT_GLOB:
247 gs = bt_common_string_until(at, ".:", ".:", &end_pos);
db0f160a 248 if (!gs || gs->len == 0) {
ebba3338
PP
249 goto error;
250 }
251
9009cc24
PP
252 if (!validate_port_glob(gs->str)) {
253 goto error;
254 }
255
256 if (normalize_glob_pattern(gs)) {
257 goto error;
258 }
259
260 g_string_free(cfg_conn->downstream_port_glob, TRUE);
261 cfg_conn->downstream_port_glob = gs;
db0f160a
PP
262 gs = NULL;
263
264 if (at[end_pos] == '\0') {
265 goto end;
266 } else {
267 goto error;
268 }
ebba3338
PP
269 break;
270 default:
db0f160a 271 assert(false);
ebba3338
PP
272 }
273 }
274
ebba3338 275error:
db0f160a
PP
276 bt_config_connection_destroy(cfg_conn);
277 cfg_conn = NULL;
ebba3338
PP
278
279end:
db0f160a
PP
280 if (gs) {
281 g_string_free(gs, TRUE);
ebba3338
PP
282 }
283
db0f160a 284 return cfg_conn;
ebba3338
PP
285}
286
287static struct bt_config_component *find_component_in_array(GPtrArray *comps,
288 const char *name)
289{
290 size_t i;
291 struct bt_config_component *found_comp = NULL;
292
293 for (i = 0; i < comps->len; i++) {
294 struct bt_config_component *comp = g_ptr_array_index(comps, i);
295
296 if (strcmp(name, comp->instance_name->str) == 0) {
297 found_comp = bt_get(comp);
298 goto end;
299 }
300 }
301
302end:
303 return found_comp;
304}
305
306static struct bt_config_component *find_component(struct bt_config *cfg,
90de159b 307 const char *name)
ebba3338
PP
308{
309 struct bt_config_component *comp;
310
db0f160a 311 comp = find_component_in_array(cfg->cmd_data.run.sources, name);
ebba3338 312 if (comp) {
ebba3338
PP
313 goto end;
314 }
315
db0f160a 316 comp = find_component_in_array(cfg->cmd_data.run.filters, name);
ebba3338 317 if (comp) {
ebba3338
PP
318 goto end;
319 }
320
db0f160a 321 comp = find_component_in_array(cfg->cmd_data.run.sinks, name);
ebba3338 322 if (comp) {
ebba3338
PP
323 goto end;
324 }
325
326end:
327 return comp;
328}
329
330static int validate_all_endpoints_exist(struct bt_config *cfg, char *error_buf,
331 size_t error_buf_size)
332{
333 size_t i;
334 int ret = 0;
335
db0f160a 336 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
ebba3338 337 struct bt_config_connection *connection =
db0f160a 338 g_ptr_array_index(cfg->cmd_data.run.connections, i);
ebba3338 339 struct bt_config_component *comp;
ebba3338 340
9009cc24 341 comp = find_component(cfg, connection->upstream_comp_name->str);
ebba3338
PP
342 bt_put(comp);
343 if (!comp) {
db0f160a
PP
344 snprintf(error_buf, error_buf_size,
345 "Invalid connection: cannot find upstream component `%s`:\n %s\n",
9009cc24 346 connection->upstream_comp_name->str,
db0f160a
PP
347 connection->arg->str);
348 ret = -1;
349 goto end;
350 }
351
9009cc24 352 comp = find_component(cfg, connection->downstream_comp_name->str);
db0f160a
PP
353 bt_put(comp);
354 if (!comp) {
355 snprintf(error_buf, error_buf_size,
356 "Invalid connection: cannot find downstream component `%s`:\n %s\n",
9009cc24 357 connection->downstream_comp_name->str,
db0f160a
PP
358 connection->arg->str);
359 ret = -1;
360 goto end;
ebba3338
PP
361 }
362 }
363
364end:
365 return ret;
366}
367
368static int validate_connection_directions(struct bt_config *cfg,
369 char *error_buf, size_t error_buf_size)
370{
371 size_t i;
372 int ret = 0;
373 struct bt_config_component *src_comp = NULL;
374 struct bt_config_component *dst_comp = NULL;
375
db0f160a 376 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
ebba3338 377 struct bt_config_connection *connection =
db0f160a 378 g_ptr_array_index(cfg->cmd_data.run.connections, i);
ebba3338
PP
379
380 src_comp = find_component(cfg,
9009cc24 381 connection->upstream_comp_name->str);
ebba3338
PP
382 assert(src_comp);
383 dst_comp = find_component(cfg,
9009cc24 384 connection->downstream_comp_name->str);
ebba3338
PP
385 assert(dst_comp);
386
90de159b
PP
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) {
ebba3338
PP
390 snprintf(error_buf, error_buf_size,
391 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
9009cc24 392 connection->upstream_comp_name->str,
ebba3338
PP
393 connection->arg->str);
394 ret = -1;
395 goto end;
396 }
90de159b
PP
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) {
ebba3338
PP
400 snprintf(error_buf, error_buf_size,
401 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
9009cc24 402 connection->upstream_comp_name->str,
ebba3338
PP
403 connection->arg->str);
404 ret = -1;
405 goto end;
406 }
407 } else {
408 snprintf(error_buf, error_buf_size,
409 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
9009cc24
PP
410 connection->upstream_comp_name->str,
411 connection->downstream_comp_name->str,
ebba3338
PP
412 connection->arg->str);
413 ret = -1;
414 goto end;
415 }
416
417 BT_PUT(src_comp);
418 BT_PUT(dst_comp);
419 }
420
421end:
422 bt_put(src_comp);
423 bt_put(dst_comp);
424 return ret;
425}
426
db0f160a
PP
427static int validate_no_cycles_rec(struct bt_config *cfg, GPtrArray *path,
428 char *error_buf, size_t error_buf_size)
429{
430 int ret = 0;
431 size_t conn_i;
432 const char *src_comp_name;
433
434 assert(path && path->len > 0);
435 src_comp_name = g_ptr_array_index(path, path->len - 1);
436
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);
440
9009cc24 441 if (strcmp(conn->upstream_comp_name->str, src_comp_name) == 0) {
db0f160a
PP
442 size_t path_i;
443
444 for (path_i = 0; path_i < path->len; path_i++) {
445 const char *comp_name =
446 g_ptr_array_index(path, path_i);
447
9009cc24 448 if (strcmp(comp_name, conn->downstream_comp_name->str) == 0) {
db0f160a
PP
449 snprintf(error_buf, error_buf_size,
450 "Invalid connection: connection forms a cycle:\n %s\n",
451 conn->arg->str);
452 ret = -1;
453 goto end;
454 }
455 }
456
9009cc24 457 g_ptr_array_add(path, conn->downstream_comp_name->str);
db0f160a
PP
458 ret = validate_no_cycles_rec(cfg, path, error_buf,
459 error_buf_size);
460 if (ret) {
461 goto end;
462 }
463
464 g_ptr_array_remove_index(path, path->len - 1);
465 }
466 }
467
468end:
469 return ret;
470}
471
472static int validate_no_cycles(struct bt_config *cfg, char *error_buf,
ebba3338
PP
473 size_t error_buf_size)
474{
475 size_t i;
476 int ret = 0;
db0f160a 477 GPtrArray *path;
ebba3338 478
db0f160a
PP
479 path = g_ptr_array_new();
480 if (!path) {
481 ret = -1;
482 goto end;
483 }
ebba3338 484
db0f160a
PP
485 g_ptr_array_add(path, NULL);
486
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);
490
9009cc24 491 g_ptr_array_index(path, 0) = conn->upstream_comp_name->str;
db0f160a
PP
492 ret = validate_no_cycles_rec(cfg, path,
493 error_buf, error_buf_size);
494 if (ret) {
ebba3338
PP
495 goto end;
496 }
497 }
498
499end:
db0f160a
PP
500 if (path) {
501 g_ptr_array_free(path, TRUE);
502 }
503
ebba3338
PP
504 return ret;
505}
506
507static int validate_all_components_connected_in_array(GPtrArray *comps,
508 struct bt_value *connected_components,
509 char *error_buf, size_t error_buf_size)
510{
511 int ret = 0;
512 size_t i;
513
514 for (i = 0; i < comps->len; i++) {
515 struct bt_config_component *comp = g_ptr_array_index(comps, i);
516
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);
522 ret = -1;
523 goto end;
524 }
525 }
526
527end:
528 return ret;
529}
530
531static int validate_all_components_connected(struct bt_config *cfg,
532 char *error_buf, size_t error_buf_size)
533{
534 size_t i;
535 int ret = 0;
536 struct bt_value *connected_components = bt_value_map_create();
537
538 if (!connected_components) {
539 ret = -1;
540 goto end;
541 }
542
db0f160a 543 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
ebba3338 544 struct bt_config_connection *connection =
db0f160a 545 g_ptr_array_index(cfg->cmd_data.run.connections, i);
ebba3338
PP
546
547 ret = bt_value_map_insert(connected_components,
9009cc24 548 connection->upstream_comp_name->str, bt_value_null);
ebba3338
PP
549 if (ret) {
550 goto end;
551 }
552
553 ret = bt_value_map_insert(connected_components,
9009cc24 554 connection->downstream_comp_name->str, bt_value_null);
ebba3338
PP
555 if (ret) {
556 goto end;
557 }
558 }
559
560 ret = validate_all_components_connected_in_array(
db0f160a 561 cfg->cmd_data.run.sources, connected_components,
ebba3338
PP
562 error_buf, error_buf_size);
563 if (ret) {
564 goto end;
565 }
566
567 ret = validate_all_components_connected_in_array(
db0f160a 568 cfg->cmd_data.run.filters, connected_components,
ebba3338
PP
569 error_buf, error_buf_size);
570 if (ret) {
571 goto end;
572 }
573
574 ret = validate_all_components_connected_in_array(
db0f160a 575 cfg->cmd_data.run.sinks, connected_components,
ebba3338
PP
576 error_buf, error_buf_size);
577 if (ret) {
578 goto end;
579 }
580
581end:
582 bt_put(connected_components);
583 return ret;
584}
585
586static int validate_no_duplicate_connection(struct bt_config *cfg,
587 char *error_buf, size_t error_buf_size)
588{
589 size_t i;
590 int ret = 0;
591 struct bt_value *flat_connection_names = bt_value_map_create();
592 GString *flat_connection_name = NULL;
593
594 if (!flat_connection_names) {
595 ret = -1;
596 goto end;
597 }
598
599 flat_connection_name = g_string_new(NULL);
600 if (!flat_connection_name) {
601 ret = -1;
602 goto end;
603 }
604
db0f160a 605 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
ebba3338 606 struct bt_config_connection *connection =
db0f160a 607 g_ptr_array_index(cfg->cmd_data.run.connections, i);
ebba3338 608
db0f160a 609 g_string_printf(flat_connection_name, "%s\x01%s\x01%s\x01%s",
9009cc24
PP
610 connection->upstream_comp_name->str,
611 connection->upstream_port_glob->str,
612 connection->downstream_comp_name->str,
613 connection->downstream_port_glob->str);
ebba3338
PP
614
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);
620 ret = -1;
621 goto end;
622 }
623
624 ret = bt_value_map_insert(flat_connection_names,
625 flat_connection_name->str, bt_value_null);
626 if (ret) {
627 goto end;
628 }
629 }
630
631end:
632 bt_put(flat_connection_names);
633
634 if (flat_connection_name) {
635 g_string_free(flat_connection_name, TRUE);
636 }
637
638 return ret;
639}
640
641static int validate_connections(struct bt_config *cfg, char *error_buf,
642 size_t error_buf_size)
643{
644 int ret;
645
646 ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size);
647 if (ret) {
648 goto end;
649 }
650
651 ret = validate_connection_directions(cfg, error_buf, error_buf_size);
652 if (ret) {
653 goto end;
654 }
655
ebba3338
PP
656 ret = validate_all_components_connected(cfg, error_buf, error_buf_size);
657 if (ret) {
658 goto end;
659 }
660
661 ret = validate_no_duplicate_connection(cfg, error_buf, error_buf_size);
662 if (ret) {
663 goto end;
664 }
665
db0f160a 666 ret = validate_no_cycles(cfg, error_buf, error_buf_size);
ebba3338
PP
667 if (ret) {
668 goto end;
669 }
670
ebba3338 671end:
ebba3338
PP
672 return ret;
673}
674
9009cc24 675int bt_config_cli_args_create_connections(struct bt_config *cfg,
ebba3338
PP
676 struct bt_value *connection_args,
677 char *error_buf, size_t error_buf_size)
678{
679 int ret;
680 size_t i;
681
db0f160a
PP
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");
ebba3338
PP
685 goto error;
686 }
687
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);
691 const char *arg;
692 struct bt_config_connection *cfg_connection;
693
694 ret = bt_value_string_get(arg_value, &arg);
695 BT_PUT(arg_value);
696 assert(ret == 0);
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",
700 arg);
701 goto error;
702 }
703
db0f160a 704 g_ptr_array_add(cfg->cmd_data.run.connections,
ebba3338
PP
705 cfg_connection);
706 }
707
708
709 ret = validate_connections(cfg, error_buf, error_buf_size);
710 if (ret) {
711 goto error;
712 }
713
714 goto end;
715
716error:
717 ret = -1;
718
719end:
720 return ret;
721}
This page took 0.056273 seconds and 4 git commands to generate.