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