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