lib: bt_object_{get,put}_ref(): accept a `const` parameter
[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 = comp;
300 bt_object_get_ref(found_comp);
301 goto end;
302 }
303 }
304
305 end:
306 return found_comp;
307 }
308
309 static struct bt_config_component *find_component(struct bt_config *cfg,
310 const char *name)
311 {
312 struct bt_config_component *comp;
313
314 comp = find_component_in_array(cfg->cmd_data.run.sources, name);
315 if (comp) {
316 goto end;
317 }
318
319 comp = find_component_in_array(cfg->cmd_data.run.filters, name);
320 if (comp) {
321 goto end;
322 }
323
324 comp = find_component_in_array(cfg->cmd_data.run.sinks, name);
325 if (comp) {
326 goto end;
327 }
328
329 end:
330 return comp;
331 }
332
333 static int validate_all_endpoints_exist(struct bt_config *cfg, char *error_buf,
334 size_t error_buf_size)
335 {
336 size_t i;
337 int ret = 0;
338
339 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
340 struct bt_config_connection *connection =
341 g_ptr_array_index(cfg->cmd_data.run.connections, i);
342 struct bt_config_component *comp;
343
344 comp = find_component(cfg, connection->upstream_comp_name->str);
345 bt_object_put_ref(comp);
346 if (!comp) {
347 snprintf(error_buf, error_buf_size,
348 "Invalid connection: cannot find upstream component `%s`:\n %s\n",
349 connection->upstream_comp_name->str,
350 connection->arg->str);
351 ret = -1;
352 goto end;
353 }
354
355 comp = find_component(cfg, connection->downstream_comp_name->str);
356 bt_object_put_ref(comp);
357 if (!comp) {
358 snprintf(error_buf, error_buf_size,
359 "Invalid connection: cannot find downstream component `%s`:\n %s\n",
360 connection->downstream_comp_name->str,
361 connection->arg->str);
362 ret = -1;
363 goto end;
364 }
365 }
366
367 end:
368 return ret;
369 }
370
371 static int validate_connection_directions(struct bt_config *cfg,
372 char *error_buf, size_t error_buf_size)
373 {
374 size_t i;
375 int ret = 0;
376 struct bt_config_component *src_comp = NULL;
377 struct bt_config_component *dst_comp = NULL;
378
379 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
380 struct bt_config_connection *connection =
381 g_ptr_array_index(cfg->cmd_data.run.connections, i);
382
383 src_comp = find_component(cfg,
384 connection->upstream_comp_name->str);
385 BT_ASSERT(src_comp);
386 dst_comp = find_component(cfg,
387 connection->downstream_comp_name->str);
388 BT_ASSERT(dst_comp);
389
390 if (src_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) {
391 if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER &&
392 dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) {
393 snprintf(error_buf, error_buf_size,
394 "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n",
395 connection->upstream_comp_name->str,
396 connection->arg->str);
397 ret = -1;
398 goto end;
399 }
400 } else if (src_comp->type == BT_COMPONENT_CLASS_TYPE_FILTER) {
401 if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER &&
402 dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) {
403 snprintf(error_buf, error_buf_size,
404 "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n",
405 connection->upstream_comp_name->str,
406 connection->arg->str);
407 ret = -1;
408 goto end;
409 }
410 } else {
411 snprintf(error_buf, error_buf_size,
412 "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n",
413 connection->upstream_comp_name->str,
414 connection->downstream_comp_name->str,
415 connection->arg->str);
416 ret = -1;
417 goto end;
418 }
419
420 BT_OBJECT_PUT_REF_AND_RESET(src_comp);
421 BT_OBJECT_PUT_REF_AND_RESET(dst_comp);
422 }
423
424 end:
425 bt_object_put_ref(src_comp);
426 bt_object_put_ref(dst_comp);
427 return ret;
428 }
429
430 static int validate_no_cycles_rec(struct bt_config *cfg, GPtrArray *path,
431 char *error_buf, size_t error_buf_size)
432 {
433 int ret = 0;
434 size_t conn_i;
435 const char *src_comp_name;
436
437 BT_ASSERT(path && path->len > 0);
438 src_comp_name = g_ptr_array_index(path, path->len - 1);
439
440 for (conn_i = 0; conn_i < cfg->cmd_data.run.connections->len; conn_i++) {
441 struct bt_config_connection *conn =
442 g_ptr_array_index(cfg->cmd_data.run.connections, conn_i);
443
444 if (strcmp(conn->upstream_comp_name->str, src_comp_name) == 0) {
445 size_t path_i;
446
447 for (path_i = 0; path_i < path->len; path_i++) {
448 const char *comp_name =
449 g_ptr_array_index(path, path_i);
450
451 if (strcmp(comp_name, conn->downstream_comp_name->str) == 0) {
452 snprintf(error_buf, error_buf_size,
453 "Invalid connection: connection forms a cycle:\n %s\n",
454 conn->arg->str);
455 ret = -1;
456 goto end;
457 }
458 }
459
460 g_ptr_array_add(path, conn->downstream_comp_name->str);
461 ret = validate_no_cycles_rec(cfg, path, error_buf,
462 error_buf_size);
463 if (ret) {
464 goto end;
465 }
466
467 g_ptr_array_remove_index(path, path->len - 1);
468 }
469 }
470
471 end:
472 return ret;
473 }
474
475 static int validate_no_cycles(struct bt_config *cfg, char *error_buf,
476 size_t error_buf_size)
477 {
478 size_t i;
479 int ret = 0;
480 GPtrArray *path;
481
482 path = g_ptr_array_new();
483 if (!path) {
484 ret = -1;
485 goto end;
486 }
487
488 g_ptr_array_add(path, NULL);
489
490 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
491 struct bt_config_connection *conn =
492 g_ptr_array_index(cfg->cmd_data.run.connections, i);
493
494 g_ptr_array_index(path, 0) = conn->upstream_comp_name->str;
495 ret = validate_no_cycles_rec(cfg, path,
496 error_buf, error_buf_size);
497 if (ret) {
498 goto end;
499 }
500 }
501
502 end:
503 if (path) {
504 g_ptr_array_free(path, TRUE);
505 }
506
507 return ret;
508 }
509
510 static int validate_all_components_connected_in_array(GPtrArray *comps,
511 struct bt_value *connected_components,
512 char *error_buf, size_t error_buf_size)
513 {
514 int ret = 0;
515 size_t i;
516
517 for (i = 0; i < comps->len; i++) {
518 struct bt_config_component *comp = g_ptr_array_index(comps, i);
519
520 if (!bt_value_map_has_entry(connected_components,
521 comp->instance_name->str)) {
522 snprintf(error_buf, error_buf_size,
523 "Component `%s` is not connected\n",
524 comp->instance_name->str);
525 ret = -1;
526 goto end;
527 }
528 }
529
530 end:
531 return ret;
532 }
533
534 static int validate_all_components_connected(struct bt_config *cfg,
535 char *error_buf, size_t error_buf_size)
536 {
537 size_t i;
538 int ret = 0;
539 struct bt_private_value *connected_components =
540 bt_private_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_private_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_private_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 bt_private_value_as_value(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 bt_private_value_as_value(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 bt_private_value_as_value(connected_components),
583 error_buf, error_buf_size);
584 if (ret) {
585 goto end;
586 }
587
588 end:
589 bt_object_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 struct bt_private_value *flat_connection_names =
599 bt_private_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(bt_private_value_as_value(
624 flat_connection_names),
625 flat_connection_name->str)) {
626 snprintf(error_buf, error_buf_size,
627 "Duplicate connection:\n %s\n",
628 connection->arg->str);
629 ret = -1;
630 goto end;
631 }
632
633 ret = bt_private_value_map_insert_entry(flat_connection_names,
634 flat_connection_name->str, bt_value_null);
635 if (ret) {
636 goto end;
637 }
638 }
639
640 end:
641 bt_object_put_ref(flat_connection_names);
642
643 if (flat_connection_name) {
644 g_string_free(flat_connection_name, TRUE);
645 }
646
647 return ret;
648 }
649
650 static int validate_connections(struct bt_config *cfg, char *error_buf,
651 size_t error_buf_size)
652 {
653 int ret;
654
655 ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size);
656 if (ret) {
657 goto end;
658 }
659
660 ret = validate_connection_directions(cfg, error_buf, error_buf_size);
661 if (ret) {
662 goto end;
663 }
664
665 ret = validate_all_components_connected(cfg, error_buf, error_buf_size);
666 if (ret) {
667 goto end;
668 }
669
670 ret = validate_no_duplicate_connection(cfg, error_buf, error_buf_size);
671 if (ret) {
672 goto end;
673 }
674
675 ret = validate_no_cycles(cfg, error_buf, error_buf_size);
676 if (ret) {
677 goto end;
678 }
679
680 end:
681 return ret;
682 }
683
684 int bt_config_cli_args_create_connections(struct bt_config *cfg,
685 struct bt_value *connection_args,
686 char *error_buf, size_t error_buf_size)
687 {
688 int ret;
689 size_t i;
690
691 if (!all_named_and_printable(cfg)) {
692 snprintf(error_buf, error_buf_size,
693 "One or more components are unnamed (use --name) or contain a non-printable character\n");
694 goto error;
695 }
696
697 for (i = 0; i < bt_value_array_get_size(connection_args); i++) {
698 struct bt_value *arg_value =
699 bt_value_array_borrow_element_by_index(
700 connection_args, i);
701 const char *arg;
702 struct bt_config_connection *cfg_connection;
703
704 arg = bt_value_string_get(arg_value);
705 cfg_connection = cfg_connection_from_arg(arg);
706 if (!cfg_connection) {
707 snprintf(error_buf, error_buf_size, "Cannot parse --connect option's argument:\n %s\n",
708 arg);
709 goto error;
710 }
711
712 g_ptr_array_add(cfg->cmd_data.run.connections,
713 cfg_connection);
714 }
715
716
717 ret = validate_connections(cfg, error_buf, error_buf_size);
718 if (ret) {
719 goto error;
720 }
721
722 goto end;
723
724 error:
725 ret = -1;
726
727 end:
728 return ret;
729 }
This page took 0.045719 seconds and 4 git commands to generate.