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