Copy the packet_header in the plugins
[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/common-internal.h>
26 #include "babeltrace-cfg.h"
27 #include "babeltrace-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 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 = bt_get(comp);
299 goto end;
300 }
301 }
302
303 end:
304 return found_comp;
305 }
306
307 static struct bt_config_component *find_component(struct bt_config *cfg,
308 const char *name)
309 {
310 struct bt_config_component *comp;
311
312 comp = find_component_in_array(cfg->cmd_data.run.sources, name);
313 if (comp) {
314 goto end;
315 }
316
317 comp = find_component_in_array(cfg->cmd_data.run.filters, name);
318 if (comp) {
319 goto end;
320 }
321
322 comp = find_component_in_array(cfg->cmd_data.run.sinks, name);
323 if (comp) {
324 goto end;
325 }
326
327 end:
328 return comp;
329 }
330
331 static int validate_all_endpoints_exist(struct bt_config *cfg, char *error_buf,
332 size_t error_buf_size)
333 {
334 size_t i;
335 int ret = 0;
336
337 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
338 struct bt_config_connection *connection =
339 g_ptr_array_index(cfg->cmd_data.run.connections, i);
340 struct bt_config_component *comp;
341
342 comp = find_component(cfg, connection->upstream_comp_name->str);
343 bt_put(comp);
344 if (!comp) {
345 snprintf(error_buf, error_buf_size,
346 "Invalid connection: cannot find upstream component `%s`:\n %s\n",
347 connection->upstream_comp_name->str,
348 connection->arg->str);
349 ret = -1;
350 goto end;
351 }
352
353 comp = find_component(cfg, connection->downstream_comp_name->str);
354 bt_put(comp);
355 if (!comp) {
356 snprintf(error_buf, error_buf_size,
357 "Invalid connection: cannot find downstream component `%s`:\n %s\n",
358 connection->downstream_comp_name->str,
359 connection->arg->str);
360 ret = -1;
361 goto end;
362 }
363 }
364
365 end:
366 return ret;
367 }
368
369 static int validate_connection_directions(struct bt_config *cfg,
370 char *error_buf, size_t error_buf_size)
371 {
372 size_t i;
373 int ret = 0;
374 struct bt_config_component *src_comp = NULL;
375 struct bt_config_component *dst_comp = NULL;
376
377 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
378 struct bt_config_connection *connection =
379 g_ptr_array_index(cfg->cmd_data.run.connections, i);
380
381 src_comp = find_component(cfg,
382 connection->upstream_comp_name->str);
383 assert(src_comp);
384 dst_comp = find_component(cfg,
385 connection->downstream_comp_name->str);
386 assert(dst_comp);
387
388 if (src_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) {
389 if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER &&
390 dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) {
391 snprintf(error_buf, error_buf_size,
392 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
393 connection->upstream_comp_name->str,
394 connection->arg->str);
395 ret = -1;
396 goto end;
397 }
398 } else if (src_comp->type == BT_COMPONENT_CLASS_TYPE_FILTER) {
399 if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER &&
400 dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) {
401 snprintf(error_buf, error_buf_size,
402 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
403 connection->upstream_comp_name->str,
404 connection->arg->str);
405 ret = -1;
406 goto end;
407 }
408 } else {
409 snprintf(error_buf, error_buf_size,
410 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
411 connection->upstream_comp_name->str,
412 connection->downstream_comp_name->str,
413 connection->arg->str);
414 ret = -1;
415 goto end;
416 }
417
418 BT_PUT(src_comp);
419 BT_PUT(dst_comp);
420 }
421
422 end:
423 bt_put(src_comp);
424 bt_put(dst_comp);
425 return ret;
426 }
427
428 static int validate_no_cycles_rec(struct bt_config *cfg, GPtrArray *path,
429 char *error_buf, size_t error_buf_size)
430 {
431 int ret = 0;
432 size_t conn_i;
433 const char *src_comp_name;
434
435 assert(path && path->len > 0);
436 src_comp_name = g_ptr_array_index(path, path->len - 1);
437
438 for (conn_i = 0; conn_i < cfg->cmd_data.run.connections->len; conn_i++) {
439 struct bt_config_connection *conn =
440 g_ptr_array_index(cfg->cmd_data.run.connections, conn_i);
441
442 if (strcmp(conn->upstream_comp_name->str, src_comp_name) == 0) {
443 size_t path_i;
444
445 for (path_i = 0; path_i < path->len; path_i++) {
446 const char *comp_name =
447 g_ptr_array_index(path, path_i);
448
449 if (strcmp(comp_name, conn->downstream_comp_name->str) == 0) {
450 snprintf(error_buf, error_buf_size,
451 "Invalid connection: connection forms a cycle:\n %s\n",
452 conn->arg->str);
453 ret = -1;
454 goto end;
455 }
456 }
457
458 g_ptr_array_add(path, conn->downstream_comp_name->str);
459 ret = validate_no_cycles_rec(cfg, path, error_buf,
460 error_buf_size);
461 if (ret) {
462 goto end;
463 }
464
465 g_ptr_array_remove_index(path, path->len - 1);
466 }
467 }
468
469 end:
470 return ret;
471 }
472
473 static int validate_no_cycles(struct bt_config *cfg, char *error_buf,
474 size_t error_buf_size)
475 {
476 size_t i;
477 int ret = 0;
478 GPtrArray *path;
479
480 path = g_ptr_array_new();
481 if (!path) {
482 ret = -1;
483 goto end;
484 }
485
486 g_ptr_array_add(path, NULL);
487
488 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
489 struct bt_config_connection *conn =
490 g_ptr_array_index(cfg->cmd_data.run.connections, i);
491
492 g_ptr_array_index(path, 0) = conn->upstream_comp_name->str;
493 ret = validate_no_cycles_rec(cfg, path,
494 error_buf, error_buf_size);
495 if (ret) {
496 goto end;
497 }
498 }
499
500 end:
501 if (path) {
502 g_ptr_array_free(path, TRUE);
503 }
504
505 return ret;
506 }
507
508 static int validate_all_components_connected_in_array(GPtrArray *comps,
509 struct bt_value *connected_components,
510 char *error_buf, size_t error_buf_size)
511 {
512 int ret = 0;
513 size_t i;
514
515 for (i = 0; i < comps->len; i++) {
516 struct bt_config_component *comp = g_ptr_array_index(comps, i);
517
518 if (!bt_value_map_has_key(connected_components,
519 comp->instance_name->str)) {
520 snprintf(error_buf, error_buf_size,
521 "Component `%s` is not connected\n",
522 comp->instance_name->str);
523 ret = -1;
524 goto end;
525 }
526 }
527
528 end:
529 return ret;
530 }
531
532 static int validate_all_components_connected(struct bt_config *cfg,
533 char *error_buf, size_t error_buf_size)
534 {
535 size_t i;
536 int ret = 0;
537 struct bt_value *connected_components = bt_value_map_create();
538
539 if (!connected_components) {
540 ret = -1;
541 goto end;
542 }
543
544 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
545 struct bt_config_connection *connection =
546 g_ptr_array_index(cfg->cmd_data.run.connections, i);
547
548 ret = bt_value_map_insert(connected_components,
549 connection->upstream_comp_name->str, bt_value_null);
550 if (ret) {
551 goto end;
552 }
553
554 ret = bt_value_map_insert(connected_components,
555 connection->downstream_comp_name->str, bt_value_null);
556 if (ret) {
557 goto end;
558 }
559 }
560
561 ret = validate_all_components_connected_in_array(
562 cfg->cmd_data.run.sources, connected_components,
563 error_buf, error_buf_size);
564 if (ret) {
565 goto end;
566 }
567
568 ret = validate_all_components_connected_in_array(
569 cfg->cmd_data.run.filters, connected_components,
570 error_buf, error_buf_size);
571 if (ret) {
572 goto end;
573 }
574
575 ret = validate_all_components_connected_in_array(
576 cfg->cmd_data.run.sinks, connected_components,
577 error_buf, error_buf_size);
578 if (ret) {
579 goto end;
580 }
581
582 end:
583 bt_put(connected_components);
584 return ret;
585 }
586
587 static int validate_no_duplicate_connection(struct bt_config *cfg,
588 char *error_buf, size_t error_buf_size)
589 {
590 size_t i;
591 int ret = 0;
592 struct bt_value *flat_connection_names = bt_value_map_create();
593 GString *flat_connection_name = NULL;
594
595 if (!flat_connection_names) {
596 ret = -1;
597 goto end;
598 }
599
600 flat_connection_name = g_string_new(NULL);
601 if (!flat_connection_name) {
602 ret = -1;
603 goto end;
604 }
605
606 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
607 struct bt_config_connection *connection =
608 g_ptr_array_index(cfg->cmd_data.run.connections, i);
609
610 g_string_printf(flat_connection_name, "%s\x01%s\x01%s\x01%s",
611 connection->upstream_comp_name->str,
612 connection->upstream_port_glob->str,
613 connection->downstream_comp_name->str,
614 connection->downstream_port_glob->str);
615
616 if (bt_value_map_has_key(flat_connection_names,
617 flat_connection_name->str)) {
618 snprintf(error_buf, error_buf_size,
619 "Duplicate connection:\n %s\n",
620 connection->arg->str);
621 ret = -1;
622 goto end;
623 }
624
625 ret = bt_value_map_insert(flat_connection_names,
626 flat_connection_name->str, bt_value_null);
627 if (ret) {
628 goto end;
629 }
630 }
631
632 end:
633 bt_put(flat_connection_names);
634
635 if (flat_connection_name) {
636 g_string_free(flat_connection_name, TRUE);
637 }
638
639 return ret;
640 }
641
642 static int validate_connections(struct bt_config *cfg, char *error_buf,
643 size_t error_buf_size)
644 {
645 int ret;
646
647 ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size);
648 if (ret) {
649 goto end;
650 }
651
652 ret = validate_connection_directions(cfg, error_buf, error_buf_size);
653 if (ret) {
654 goto end;
655 }
656
657 ret = validate_all_components_connected(cfg, error_buf, error_buf_size);
658 if (ret) {
659 goto end;
660 }
661
662 ret = validate_no_duplicate_connection(cfg, error_buf, error_buf_size);
663 if (ret) {
664 goto end;
665 }
666
667 ret = validate_no_cycles(cfg, error_buf, error_buf_size);
668 if (ret) {
669 goto end;
670 }
671
672 end:
673 return ret;
674 }
675
676 int bt_config_cli_args_create_connections(struct bt_config *cfg,
677 struct bt_value *connection_args,
678 char *error_buf, size_t error_buf_size)
679 {
680 int ret;
681 size_t i;
682
683 if (!all_named_and_printable(cfg)) {
684 snprintf(error_buf, error_buf_size,
685 "One or more components are unnamed (use --name) or contain a non-printable character\n");
686 goto error;
687 }
688
689 for (i = 0; i < bt_value_array_size(connection_args); i++) {
690 struct bt_value *arg_value =
691 bt_value_array_get(connection_args, i);
692 const char *arg;
693 struct bt_config_connection *cfg_connection;
694
695 ret = bt_value_string_get(arg_value, &arg);
696 BT_PUT(arg_value);
697 assert(ret == 0);
698 cfg_connection = cfg_connection_from_arg(arg);
699 if (!cfg_connection) {
700 snprintf(error_buf, error_buf_size, "Cannot parse --connect option's argument:\n %s\n",
701 arg);
702 goto error;
703 }
704
705 g_ptr_array_add(cfg->cmd_data.run.connections,
706 cfg_connection);
707 }
708
709
710 ret = validate_connections(cfg, error_buf, error_buf_size);
711 if (ret) {
712 goto error;
713 }
714
715 goto end;
716
717 error:
718 ret = -1;
719
720 end:
721 return ret;
722 }
This page took 0.044868 seconds and 4 git commands to generate.