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