7e1657e0aeff7c7a95d140109956acb5621533a4
[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 <stdlib.h>
24 #include <babeltrace2/babeltrace.h>
25 #include "common/common.h"
26 #include "babeltrace2-cfg.h"
27 #include "babeltrace2-cfg-cli-args-connect.h"
28
29 static bool all_named_and_printable_in_array(GPtrArray *comps)
30 {
31 size_t i;
32 bool all_named_and_printable = true;
33
34 for (i = 0; i < comps->len; i++) {
35 struct bt_config_component *comp = g_ptr_array_index(comps, i);
36
37 if (comp->instance_name->len == 0) {
38 all_named_and_printable = false;
39 goto end;
40 }
41
42 if (!bt_common_string_is_printable(comp->instance_name->str)) {
43 all_named_and_printable = false;
44 goto end;
45 }
46 }
47
48 end:
49 return all_named_and_printable;
50 }
51
52 static bool all_named_and_printable(struct bt_config *cfg)
53 {
54 return all_named_and_printable_in_array(cfg->cmd_data.run.sources) &&
55 all_named_and_printable_in_array(cfg->cmd_data.run.filters) &&
56 all_named_and_printable_in_array(cfg->cmd_data.run.sinks);
57 }
58
59 static struct bt_config_connection *bt_config_connection_create(const char *arg)
60 {
61 struct bt_config_connection *cfg_connection;
62
63 cfg_connection = g_new0(struct bt_config_connection, 1);
64 if (!cfg_connection) {
65 goto error;
66 }
67
68 cfg_connection->upstream_comp_name = g_string_new(NULL);
69 if (!cfg_connection->upstream_comp_name) {
70 goto error;
71 }
72
73 cfg_connection->downstream_comp_name = g_string_new(NULL);
74 if (!cfg_connection->downstream_comp_name) {
75 goto error;
76 }
77
78 cfg_connection->upstream_port_glob = g_string_new("*");
79 if (!cfg_connection->upstream_port_glob) {
80 goto error;
81 }
82
83 cfg_connection->downstream_port_glob = g_string_new("*");
84 if (!cfg_connection->downstream_port_glob) {
85 goto error;
86 }
87
88 cfg_connection->arg = g_string_new(arg);
89 if (!cfg_connection->arg) {
90 goto error;
91 }
92
93 goto end;
94
95 error:
96 g_free(cfg_connection);
97 cfg_connection = NULL;
98
99 end:
100 return cfg_connection;
101 }
102
103 static bool validate_port_glob(const char *port_glob)
104 {
105 bool is_valid = true;
106 const char *ch = port_glob;
107
108 BT_ASSERT(port_glob);
109
110 while (*ch != '\0') {
111 switch (*ch) {
112 case '\\':
113 switch (ch[1]) {
114 case '\0':
115 goto end;
116 default:
117 ch += 2;
118 continue;
119 }
120 case '?':
121 case '[':
122 /*
123 * This is reserved for future use, to support
124 * full globbing patterns. Those characters must
125 * be escaped with `\`.
126 */
127 is_valid = false;
128 goto end;
129 default:
130 ch++;
131 break;
132 }
133 }
134
135 end:
136 return is_valid;
137 }
138
139 static int normalize_glob_pattern(GString *glob_pattern_gs)
140 {
141 int ret = 0;
142 char *glob_pattern = strdup(glob_pattern_gs->str);
143
144 if (!glob_pattern) {
145 ret = -1;
146 goto end;
147 }
148
149 bt_common_normalize_star_glob_pattern(glob_pattern);
150 g_string_assign(glob_pattern_gs, glob_pattern);
151 free(glob_pattern);
152
153 end:
154 return ret;
155 }
156
157 static struct bt_config_connection *cfg_connection_from_arg(const char *arg)
158 {
159 const char *at = arg;
160 size_t end_pos;
161 struct bt_config_connection *cfg_conn = NULL;
162 GString *gs = NULL;
163 enum {
164 UPSTREAM_NAME,
165 DOWNSTREAM_NAME,
166 UPSTREAM_PORT_GLOB,
167 DOWNSTREAM_PORT_GLOB,
168 } state = UPSTREAM_NAME;
169
170 if (!bt_common_string_is_printable(arg)) {
171 goto error;
172 }
173
174 cfg_conn = bt_config_connection_create(arg);
175 if (!cfg_conn) {
176 goto error;
177 }
178
179 while (true) {
180 switch (state) {
181 case UPSTREAM_NAME:
182 gs = bt_common_string_until(at, ".:\\", ".:", &end_pos);
183 if (!gs || gs->len == 0) {
184 goto error;
185 }
186
187 g_string_free(cfg_conn->upstream_comp_name, TRUE);
188 cfg_conn->upstream_comp_name = gs;
189 gs = NULL;
190
191 if (at[end_pos] == ':') {
192 state = DOWNSTREAM_NAME;
193 } else if (at[end_pos] == '.') {
194 state = UPSTREAM_PORT_GLOB;
195 } else {
196 goto error;
197 }
198
199 at += end_pos + 1;
200 break;
201 case DOWNSTREAM_NAME:
202 gs = bt_common_string_until(at, ".:\\", ".:", &end_pos);
203 if (!gs || gs->len == 0) {
204 goto error;
205 }
206
207 g_string_free(cfg_conn->downstream_comp_name, TRUE);
208 cfg_conn->downstream_comp_name = gs;
209 gs = NULL;
210
211 if (at[end_pos] == '.') {
212 state = DOWNSTREAM_PORT_GLOB;
213 } else if (at[end_pos] == '\0') {
214 goto end;
215 } else {
216 goto error;
217 }
218
219 at += end_pos + 1;
220 break;
221 case UPSTREAM_PORT_GLOB:
222 gs = bt_common_string_until(at, ".:", ".:", &end_pos);
223 if (!gs || gs->len == 0) {
224 goto error;
225 }
226
227 if (!validate_port_glob(gs->str)) {
228 goto error;
229 }
230
231 if (normalize_glob_pattern(gs)) {
232 goto error;
233 }
234
235 g_string_free(cfg_conn->upstream_port_glob, TRUE);
236 cfg_conn->upstream_port_glob = gs;
237 gs = NULL;
238
239 if (at[end_pos] == ':') {
240 state = DOWNSTREAM_NAME;
241 } else {
242 goto error;
243 }
244
245 at += end_pos + 1;
246 break;
247 case DOWNSTREAM_PORT_GLOB:
248 gs = bt_common_string_until(at, ".:", ".:", &end_pos);
249 if (!gs || gs->len == 0) {
250 goto error;
251 }
252
253 if (!validate_port_glob(gs->str)) {
254 goto error;
255 }
256
257 if (normalize_glob_pattern(gs)) {
258 goto error;
259 }
260
261 g_string_free(cfg_conn->downstream_port_glob, TRUE);
262 cfg_conn->downstream_port_glob = gs;
263 gs = NULL;
264
265 if (at[end_pos] == '\0') {
266 goto end;
267 } else {
268 goto error;
269 }
270 break;
271 default:
272 abort();
273 }
274 }
275
276 error:
277 bt_config_connection_destroy(cfg_conn);
278 cfg_conn = NULL;
279
280 end:
281 if (gs) {
282 g_string_free(gs, TRUE);
283 }
284
285 return cfg_conn;
286 }
287
288 static struct bt_config_component *find_component_in_array(GPtrArray *comps,
289 const char *name)
290 {
291 size_t i;
292 struct bt_config_component *found_comp = NULL;
293
294 for (i = 0; i < comps->len; i++) {
295 struct bt_config_component *comp = g_ptr_array_index(comps, i);
296
297 if (strcmp(name, comp->instance_name->str) == 0) {
298 found_comp = comp;
299 bt_object_get_ref(found_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 const 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 bt_value *connected_components = bt_value_map_create();
539
540 if (!connected_components) {
541 ret = -1;
542 goto end;
543 }
544
545 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
546 struct bt_config_connection *connection =
547 g_ptr_array_index(cfg->cmd_data.run.connections, i);
548
549 ret = bt_value_map_insert_entry(connected_components,
550 connection->upstream_comp_name->str, bt_value_null);
551 if (ret) {
552 goto end;
553 }
554
555 ret = bt_value_map_insert_entry(connected_components,
556 connection->downstream_comp_name->str, bt_value_null);
557 if (ret) {
558 goto end;
559 }
560 }
561
562 ret = validate_all_components_connected_in_array(
563 cfg->cmd_data.run.sources,
564 connected_components,
565 error_buf, error_buf_size);
566 if (ret) {
567 goto end;
568 }
569
570 ret = validate_all_components_connected_in_array(
571 cfg->cmd_data.run.filters,
572 connected_components,
573 error_buf, error_buf_size);
574 if (ret) {
575 goto end;
576 }
577
578 ret = validate_all_components_connected_in_array(
579 cfg->cmd_data.run.sinks,
580 connected_components,
581 error_buf, error_buf_size);
582 if (ret) {
583 goto end;
584 }
585
586 end:
587 bt_value_put_ref(connected_components);
588 return ret;
589 }
590
591 static int validate_no_duplicate_connection(struct bt_config *cfg,
592 char *error_buf, size_t error_buf_size)
593 {
594 size_t i;
595 int ret = 0;
596 bt_value *flat_connection_names =
597 bt_value_map_create();
598 GString *flat_connection_name = NULL;
599
600 if (!flat_connection_names) {
601 ret = -1;
602 goto end;
603 }
604
605 flat_connection_name = g_string_new(NULL);
606 if (!flat_connection_name) {
607 ret = -1;
608 goto end;
609 }
610
611 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
612 struct bt_config_connection *connection =
613 g_ptr_array_index(cfg->cmd_data.run.connections, i);
614
615 g_string_printf(flat_connection_name, "%s\x01%s\x01%s\x01%s",
616 connection->upstream_comp_name->str,
617 connection->upstream_port_glob->str,
618 connection->downstream_comp_name->str,
619 connection->downstream_port_glob->str);
620
621 if (bt_value_map_has_entry(flat_connection_names,
622 flat_connection_name->str)) {
623 snprintf(error_buf, error_buf_size,
624 "Duplicate connection:\n %s\n",
625 connection->arg->str);
626 ret = -1;
627 goto end;
628 }
629
630 ret = bt_value_map_insert_entry(flat_connection_names,
631 flat_connection_name->str, bt_value_null);
632 if (ret) {
633 goto end;
634 }
635 }
636
637 end:
638 bt_value_put_ref(flat_connection_names);
639
640 if (flat_connection_name) {
641 g_string_free(flat_connection_name, TRUE);
642 }
643
644 return ret;
645 }
646
647 static int validate_connections(struct bt_config *cfg, char *error_buf,
648 size_t error_buf_size)
649 {
650 int ret;
651
652 ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size);
653 if (ret) {
654 goto end;
655 }
656
657 ret = validate_connection_directions(cfg, error_buf, error_buf_size);
658 if (ret) {
659 goto end;
660 }
661
662 ret = validate_all_components_connected(cfg, error_buf, error_buf_size);
663 if (ret) {
664 goto end;
665 }
666
667 ret = validate_no_duplicate_connection(cfg, error_buf, error_buf_size);
668 if (ret) {
669 goto end;
670 }
671
672 ret = validate_no_cycles(cfg, error_buf, error_buf_size);
673 if (ret) {
674 goto end;
675 }
676
677 end:
678 return ret;
679 }
680
681 int bt_config_cli_args_create_connections(struct bt_config *cfg,
682 const bt_value *connection_args,
683 char *error_buf, size_t error_buf_size)
684 {
685 int ret;
686 size_t i;
687
688 if (!all_named_and_printable(cfg)) {
689 snprintf(error_buf, error_buf_size,
690 "One or more components are unnamed (use --name) or contain a non-printable character\n");
691 goto error;
692 }
693
694 for (i = 0; i < bt_value_array_get_size(connection_args); i++) {
695 const bt_value *arg_value =
696 bt_value_array_borrow_element_by_index_const(
697 connection_args, i);
698 const char *arg;
699 struct bt_config_connection *cfg_connection;
700
701 arg = bt_value_string_get(arg_value);
702 cfg_connection = cfg_connection_from_arg(arg);
703 if (!cfg_connection) {
704 snprintf(error_buf, error_buf_size, "Cannot parse --connect option's argument:\n %s\n",
705 arg);
706 goto error;
707 }
708
709 g_ptr_array_add(cfg->cmd_data.run.connections,
710 cfg_connection);
711 }
712
713
714 ret = validate_connections(cfg, error_buf, error_buf_size);
715 if (ret) {
716 goto error;
717 }
718
719 goto end;
720
721 error:
722 ret = -1;
723
724 end:
725 return ret;
726 }
This page took 0.043748 seconds and 3 git commands to generate.