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