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