Commit | Line | Data |
---|---|---|
ebba3338 PP |
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> | |
db0f160a | 24 | #include <babeltrace/common-internal.h> |
ebba3338 | 25 | #include "babeltrace-cfg.h" |
9009cc24 | 26 | #include "babeltrace-cfg-cli-args-connect.h" |
ebba3338 | 27 | |
db0f160a | 28 | static bool all_named_and_printable_in_array(GPtrArray *comps) |
ebba3338 PP |
29 | { |
30 | size_t i; | |
db0f160a | 31 | bool all_named_and_printable = true; |
ebba3338 PP |
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) { | |
db0f160a PP |
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; | |
ebba3338 PP |
43 | goto end; |
44 | } | |
45 | } | |
46 | ||
47 | end: | |
db0f160a | 48 | return all_named_and_printable; |
ebba3338 PP |
49 | } |
50 | ||
db0f160a | 51 | static bool all_named_and_printable(struct bt_config *cfg) |
ebba3338 | 52 | { |
db0f160a PP |
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); | |
ebba3338 PP |
56 | } |
57 | ||
ebba3338 PP |
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 | ||
9009cc24 PP |
67 | cfg_connection->upstream_comp_name = g_string_new(NULL); |
68 | if (!cfg_connection->upstream_comp_name) { | |
ebba3338 PP |
69 | goto error; |
70 | } | |
71 | ||
9009cc24 PP |
72 | cfg_connection->downstream_comp_name = g_string_new(NULL); |
73 | if (!cfg_connection->downstream_comp_name) { | |
ebba3338 PP |
74 | goto error; |
75 | } | |
76 | ||
9009cc24 PP |
77 | cfg_connection->upstream_port_glob = g_string_new("*"); |
78 | if (!cfg_connection->upstream_port_glob) { | |
ebba3338 PP |
79 | goto error; |
80 | } | |
81 | ||
9009cc24 PP |
82 | cfg_connection->downstream_port_glob = g_string_new("*"); |
83 | if (!cfg_connection->downstream_port_glob) { | |
ebba3338 PP |
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 | ||
9009cc24 PP |
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 | ||
ebba3338 PP |
156 | static struct bt_config_connection *cfg_connection_from_arg(const char *arg) |
157 | { | |
db0f160a PP |
158 | const char *at = arg; |
159 | size_t end_pos; | |
160 | struct bt_config_connection *cfg_conn = NULL; | |
161 | GString *gs = NULL; | |
ebba3338 | 162 | enum { |
9009cc24 PP |
163 | UPSTREAM_NAME, |
164 | DOWNSTREAM_NAME, | |
165 | UPSTREAM_PORT_GLOB, | |
166 | DOWNSTREAM_PORT_GLOB, | |
167 | } state = UPSTREAM_NAME; | |
db0f160a PP |
168 | |
169 | if (!bt_common_string_is_printable(arg)) { | |
ebba3338 PP |
170 | goto error; |
171 | } | |
172 | ||
db0f160a PP |
173 | cfg_conn = bt_config_connection_create(arg); |
174 | if (!cfg_conn) { | |
ebba3338 PP |
175 | goto error; |
176 | } | |
177 | ||
ebba3338 | 178 | while (true) { |
ebba3338 | 179 | switch (state) { |
9009cc24 | 180 | case UPSTREAM_NAME: |
db0f160a PP |
181 | gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); |
182 | if (!gs || gs->len == 0) { | |
ebba3338 PP |
183 | goto error; |
184 | } | |
185 | ||
9009cc24 PP |
186 | g_string_free(cfg_conn->upstream_comp_name, TRUE); |
187 | cfg_conn->upstream_comp_name = gs; | |
db0f160a | 188 | gs = NULL; |
ebba3338 | 189 | |
db0f160a | 190 | if (at[end_pos] == ':') { |
9009cc24 | 191 | state = DOWNSTREAM_NAME; |
db0f160a | 192 | } else if (at[end_pos] == '.') { |
9009cc24 | 193 | state = UPSTREAM_PORT_GLOB; |
db0f160a | 194 | } else { |
ebba3338 PP |
195 | goto error; |
196 | } | |
197 | ||
db0f160a | 198 | at += end_pos + 1; |
ebba3338 | 199 | break; |
9009cc24 | 200 | case DOWNSTREAM_NAME: |
db0f160a PP |
201 | gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); |
202 | if (!gs || gs->len == 0) { | |
ebba3338 PP |
203 | goto error; |
204 | } | |
205 | ||
9009cc24 PP |
206 | g_string_free(cfg_conn->downstream_comp_name, TRUE); |
207 | cfg_conn->downstream_comp_name = gs; | |
db0f160a PP |
208 | gs = NULL; |
209 | ||
210 | if (at[end_pos] == '.') { | |
9009cc24 | 211 | state = DOWNSTREAM_PORT_GLOB; |
db0f160a PP |
212 | } else if (at[end_pos] == '\0') { |
213 | goto end; | |
214 | } else { | |
ebba3338 PP |
215 | goto error; |
216 | } | |
217 | ||
db0f160a | 218 | at += end_pos + 1; |
ebba3338 | 219 | break; |
9009cc24 PP |
220 | case UPSTREAM_PORT_GLOB: |
221 | gs = bt_common_string_until(at, ".:", ".:", &end_pos); | |
db0f160a | 222 | if (!gs || gs->len == 0) { |
ebba3338 PP |
223 | goto error; |
224 | } | |
225 | ||
9009cc24 PP |
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; | |
db0f160a PP |
236 | gs = NULL; |
237 | ||
238 | if (at[end_pos] == ':') { | |
9009cc24 | 239 | state = DOWNSTREAM_NAME; |
db0f160a | 240 | } else { |
ebba3338 PP |
241 | goto error; |
242 | } | |
243 | ||
db0f160a | 244 | at += end_pos + 1; |
ebba3338 | 245 | break; |
9009cc24 PP |
246 | case DOWNSTREAM_PORT_GLOB: |
247 | gs = bt_common_string_until(at, ".:", ".:", &end_pos); | |
db0f160a | 248 | if (!gs || gs->len == 0) { |
ebba3338 PP |
249 | goto error; |
250 | } | |
251 | ||
9009cc24 PP |
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; | |
db0f160a PP |
262 | gs = NULL; |
263 | ||
264 | if (at[end_pos] == '\0') { | |
265 | goto end; | |
266 | } else { | |
267 | goto error; | |
268 | } | |
ebba3338 PP |
269 | break; |
270 | default: | |
db0f160a | 271 | assert(false); |
ebba3338 PP |
272 | } |
273 | } | |
274 | ||
ebba3338 | 275 | error: |
db0f160a PP |
276 | bt_config_connection_destroy(cfg_conn); |
277 | cfg_conn = NULL; | |
ebba3338 PP |
278 | |
279 | end: | |
db0f160a PP |
280 | if (gs) { |
281 | g_string_free(gs, TRUE); | |
ebba3338 PP |
282 | } |
283 | ||
db0f160a | 284 | return cfg_conn; |
ebba3338 PP |
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, | |
90de159b | 307 | const char *name) |
ebba3338 PP |
308 | { |
309 | struct bt_config_component *comp; | |
310 | ||
db0f160a | 311 | comp = find_component_in_array(cfg->cmd_data.run.sources, name); |
ebba3338 | 312 | if (comp) { |
ebba3338 PP |
313 | goto end; |
314 | } | |
315 | ||
db0f160a | 316 | comp = find_component_in_array(cfg->cmd_data.run.filters, name); |
ebba3338 | 317 | if (comp) { |
ebba3338 PP |
318 | goto end; |
319 | } | |
320 | ||
db0f160a | 321 | comp = find_component_in_array(cfg->cmd_data.run.sinks, name); |
ebba3338 | 322 | if (comp) { |
ebba3338 PP |
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 | ||
db0f160a | 336 | for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { |
ebba3338 | 337 | struct bt_config_connection *connection = |
db0f160a | 338 | g_ptr_array_index(cfg->cmd_data.run.connections, i); |
ebba3338 | 339 | struct bt_config_component *comp; |
ebba3338 | 340 | |
9009cc24 | 341 | comp = find_component(cfg, connection->upstream_comp_name->str); |
ebba3338 PP |
342 | bt_put(comp); |
343 | if (!comp) { | |
db0f160a PP |
344 | snprintf(error_buf, error_buf_size, |
345 | "Invalid connection: cannot find upstream component `%s`:\n %s\n", | |
9009cc24 | 346 | connection->upstream_comp_name->str, |
db0f160a PP |
347 | connection->arg->str); |
348 | ret = -1; | |
349 | goto end; | |
350 | } | |
351 | ||
9009cc24 | 352 | comp = find_component(cfg, connection->downstream_comp_name->str); |
db0f160a PP |
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", | |
9009cc24 | 357 | connection->downstream_comp_name->str, |
db0f160a PP |
358 | connection->arg->str); |
359 | ret = -1; | |
360 | goto end; | |
ebba3338 PP |
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 | ||
db0f160a | 376 | for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { |
ebba3338 | 377 | struct bt_config_connection *connection = |
db0f160a | 378 | g_ptr_array_index(cfg->cmd_data.run.connections, i); |
ebba3338 PP |
379 | |
380 | src_comp = find_component(cfg, | |
9009cc24 | 381 | connection->upstream_comp_name->str); |
ebba3338 PP |
382 | assert(src_comp); |
383 | dst_comp = find_component(cfg, | |
9009cc24 | 384 | connection->downstream_comp_name->str); |
ebba3338 PP |
385 | assert(dst_comp); |
386 | ||
90de159b PP |
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) { | |
ebba3338 PP |
390 | snprintf(error_buf, error_buf_size, |
391 | "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n", | |
9009cc24 | 392 | connection->upstream_comp_name->str, |
ebba3338 PP |
393 | connection->arg->str); |
394 | ret = -1; | |
395 | goto end; | |
396 | } | |
90de159b PP |
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) { | |
ebba3338 PP |
400 | snprintf(error_buf, error_buf_size, |
401 | "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n", | |
9009cc24 | 402 | connection->upstream_comp_name->str, |
ebba3338 PP |
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", | |
9009cc24 PP |
410 | connection->upstream_comp_name->str, |
411 | connection->downstream_comp_name->str, | |
ebba3338 PP |
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 | ||
db0f160a PP |
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 | ||
9009cc24 | 441 | if (strcmp(conn->upstream_comp_name->str, src_comp_name) == 0) { |
db0f160a PP |
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 | ||
9009cc24 | 448 | if (strcmp(comp_name, conn->downstream_comp_name->str) == 0) { |
db0f160a PP |
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 | ||
9009cc24 | 457 | g_ptr_array_add(path, conn->downstream_comp_name->str); |
db0f160a PP |
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, | |
ebba3338 PP |
473 | size_t error_buf_size) |
474 | { | |
475 | size_t i; | |
476 | int ret = 0; | |
db0f160a | 477 | GPtrArray *path; |
ebba3338 | 478 | |
db0f160a PP |
479 | path = g_ptr_array_new(); |
480 | if (!path) { | |
481 | ret = -1; | |
482 | goto end; | |
483 | } | |
ebba3338 | 484 | |
db0f160a PP |
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 | ||
9009cc24 | 491 | g_ptr_array_index(path, 0) = conn->upstream_comp_name->str; |
db0f160a PP |
492 | ret = validate_no_cycles_rec(cfg, path, |
493 | error_buf, error_buf_size); | |
494 | if (ret) { | |
ebba3338 PP |
495 | goto end; |
496 | } | |
497 | } | |
498 | ||
499 | end: | |
db0f160a PP |
500 | if (path) { |
501 | g_ptr_array_free(path, TRUE); | |
502 | } | |
503 | ||
ebba3338 PP |
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 | ||
db0f160a | 543 | for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { |
ebba3338 | 544 | struct bt_config_connection *connection = |
db0f160a | 545 | g_ptr_array_index(cfg->cmd_data.run.connections, i); |
ebba3338 PP |
546 | |
547 | ret = bt_value_map_insert(connected_components, | |
9009cc24 | 548 | connection->upstream_comp_name->str, bt_value_null); |
ebba3338 PP |
549 | if (ret) { |
550 | goto end; | |
551 | } | |
552 | ||
553 | ret = bt_value_map_insert(connected_components, | |
9009cc24 | 554 | connection->downstream_comp_name->str, bt_value_null); |
ebba3338 PP |
555 | if (ret) { |
556 | goto end; | |
557 | } | |
558 | } | |
559 | ||
560 | ret = validate_all_components_connected_in_array( | |
db0f160a | 561 | cfg->cmd_data.run.sources, connected_components, |
ebba3338 PP |
562 | error_buf, error_buf_size); |
563 | if (ret) { | |
564 | goto end; | |
565 | } | |
566 | ||
567 | ret = validate_all_components_connected_in_array( | |
db0f160a | 568 | cfg->cmd_data.run.filters, connected_components, |
ebba3338 PP |
569 | error_buf, error_buf_size); |
570 | if (ret) { | |
571 | goto end; | |
572 | } | |
573 | ||
574 | ret = validate_all_components_connected_in_array( | |
db0f160a | 575 | cfg->cmd_data.run.sinks, connected_components, |
ebba3338 PP |
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 | ||
db0f160a | 605 | for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { |
ebba3338 | 606 | struct bt_config_connection *connection = |
db0f160a | 607 | g_ptr_array_index(cfg->cmd_data.run.connections, i); |
ebba3338 | 608 | |
db0f160a | 609 | g_string_printf(flat_connection_name, "%s\x01%s\x01%s\x01%s", |
9009cc24 PP |
610 | connection->upstream_comp_name->str, |
611 | connection->upstream_port_glob->str, | |
612 | connection->downstream_comp_name->str, | |
613 | connection->downstream_port_glob->str); | |
ebba3338 PP |
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 | ||
ebba3338 PP |
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 | ||
db0f160a | 666 | ret = validate_no_cycles(cfg, error_buf, error_buf_size); |
ebba3338 PP |
667 | if (ret) { |
668 | goto end; | |
669 | } | |
670 | ||
ebba3338 | 671 | end: |
ebba3338 PP |
672 | return ret; |
673 | } | |
674 | ||
9009cc24 | 675 | int bt_config_cli_args_create_connections(struct bt_config *cfg, |
ebba3338 PP |
676 | struct bt_value *connection_args, |
677 | char *error_buf, size_t error_buf_size) | |
678 | { | |
679 | int ret; | |
680 | size_t i; | |
681 | ||
db0f160a PP |
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"); | |
ebba3338 PP |
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 | ||
db0f160a | 704 | g_ptr_array_add(cfg->cmd_data.run.connections, |
ebba3338 PP |
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 | } |