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