bt2: move common internal functions to their own files
[babeltrace.git] / src / cli / babeltrace2-cfg-src-auto-disc.c
CommitLineData
73760435
SM
1/*
2 * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation
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#define BT_LOG_TAG "CLI-CFG-SRC-AUTO-DISC"
24#include "logging.h"
25
26#include "babeltrace2-cfg-src-auto-disc.h"
27#include "babeltrace2-plugins.h"
3c729b9a 28#include "babeltrace2-query.h"
73760435
SM
29#include "common/common.h"
30
31/* Finalize and free a `struct auto_source_discovery_result`. */
32
33static
34void auto_source_discovery_result_destroy(struct auto_source_discovery_result *res)
35{
36 if (res) {
37 g_free(res->group);
38 bt_value_put_ref(res->inputs);
39 g_free(res);
40 }
41}
42
43/* Allocate and initialize a `struct auto_source_discovery_result`. */
44
45static
46struct auto_source_discovery_result *auto_source_discovery_result_create(
47 const char *plugin_name, const char *source_cc_name,
48 const char *group)
49{
50 struct auto_source_discovery_result *res;
51
52 res = g_new0(struct auto_source_discovery_result, 1);
53 if (!res) {
54 BT_CLI_LOGE_APPEND_CAUSE(
55 "Failed to allocate a auto_source_discovery_result structure.");
56 goto error;
57 }
58
59 res->plugin_name = plugin_name;
60 res->source_cc_name = source_cc_name;
61 res->group = g_strdup(group);
62 if (group && !res->group) {
63 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate a string.");
64 goto error;
65 }
66
67 res->inputs = bt_value_array_create();
68 if (!res->inputs) {
69 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate an array value.");
70 goto error;
71 }
72
73 goto end;
74error:
75 auto_source_discovery_result_destroy(res);
76
77end:
78 return res;
79}
80
81/* Finalize a `struct auto_source_discovery`. */
82
83void auto_source_discovery_fini(struct auto_source_discovery *auto_disc)
84{
85 if (auto_disc->results) {
86 g_ptr_array_free(auto_disc->results, TRUE);
87 }
88}
89
90/* Initialize an already allocated `struct auto_source_discovery`. */
91
92int auto_source_discovery_init(struct auto_source_discovery *auto_disc)
93{
94 int status;
95
96 auto_disc->results = g_ptr_array_new_with_free_func(
97 (GDestroyNotify) auto_source_discovery_result_destroy);
98
99 if (!auto_disc->results) {
100 goto error;
101 }
102
103 status = 0;
104 goto end;
105
106error:
107 auto_source_discovery_fini(auto_disc);
108 status = -1;
109
110end:
111
112 return status;
113}
114
115/*
116 * Assign `input` to source component class `source_cc_name` of plugin
117 * `plugin_name`, in the group with key `group`.
118 */
119
120static
121int auto_source_discovery_add(struct auto_source_discovery *auto_disc,
122 const char *plugin_name,
123 const char *source_cc_name,
124 const char *group,
125 const char *input)
126{
127 int status;
128 bt_value_array_append_element_status append_status;
129 guint len;
130 guint i;
131 struct auto_source_discovery_result *res = NULL;
132
133 len = auto_disc->results->len;
134 i = len;
135
136 if (group) {
137 for (i = 0; i < len; i++) {
138 res = g_ptr_array_index(auto_disc->results, i);
139
140 if (strcmp(res->plugin_name, plugin_name) != 0) {
141 continue;
142 }
143
144 if (strcmp(res->source_cc_name, source_cc_name) != 0) {
145 continue;
146 }
147
148 if (g_strcmp0(res->group, group) != 0) {
149 continue;
150 }
151
152 break;
153 }
154 }
155
156 if (i == len) {
157 /* Add a new result entry. */
158 res = auto_source_discovery_result_create(plugin_name,
159 source_cc_name, group);
160 if (!res) {
161 goto error;
162 }
163
164 g_ptr_array_add(auto_disc->results, res);
165 }
166
167 append_status = bt_value_array_append_string_element(res->inputs, input);
168 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
169 BT_CLI_LOGE_APPEND_CAUSE("Failed to append a string value.");
170 goto error;
171 }
172
173
174 status = 0;
175 goto end;
176
177error:
178 status = -1;
179
180end:
181 return status;
182}
183
184static
185int convert_weight_value(const bt_value *weight_value, double *weight,
186 const char *plugin_name, const char *source_cc_name,
187 const char *input, const char *input_type)
188{
189 enum bt_value_type weight_value_type;
190 int status;
191
192 weight_value_type = bt_value_get_type(weight_value);
193
194 if (weight_value_type == BT_VALUE_TYPE_REAL) {
195 *weight = bt_value_real_get(weight_value);
196 } else if (weight_value_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
197 /* Accept signed integer as a convenience for "return 0" or "return 1" in Python. */
198 *weight = bt_value_integer_signed_get(weight_value);
199 } else {
1a29b831 200 BT_LOGW("babeltrace.support-info query: unexpected type for weight: "
73760435
SM
201 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
202 "expected-entry-type=%s, actual-entry-type=%s",
203 plugin_name, source_cc_name, input, input_type,
204 bt_common_value_type_string(BT_VALUE_TYPE_REAL),
205 bt_common_value_type_string(bt_value_get_type(weight_value)));
206 goto error;
207 }
208
209 if (*weight < 0.0 || *weight > 1.0) {
1a29b831 210 BT_LOGW("babeltrace.support-info query: weight value is out of range [0.0, 1.0]: "
73760435
SM
211 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
212 "weight=%f",
213 plugin_name, source_cc_name, input, input_type, *weight);
214 goto error;
215 }
216
217 status = 0;
218 goto end;
219
220error:
221 status = -1;
222
223end:
224 return status;
225}
226
227/*
228 * Query all known source components to see if any of them can handle `input`
229 * as the given `type`(arbitrary string, directory or file).
230 *
231 * If `plugin_restrict` is non-NULL, only query source component classes provided
232 * by the plugin with that name.
233 *
234 * If `component_class_restrict` is non-NULL, only query source component classes
235 * with that name.
236 *
237 * Return:
238 *
239 * - > 0 on success, if no source component class has reported that it handles `input`
240 * - 0 on success, if a source component class has reported that it handles `input`
241 * - < 0 on failure (e.g. memory error)
242 */
243static
244int support_info_query_all_sources(const char *input,
3c729b9a 245 const char *input_type, size_t plugin_count,
73760435
SM
246 const char *plugin_restrict,
247 const char *component_class_restrict,
248 enum bt_logging_level log_level,
249 struct auto_source_discovery *auto_disc)
250{
251 bt_value_map_insert_entry_status insert_status;
252 bt_value *query_params = NULL;
253 int status;
254 size_t i_plugins;
255 const struct bt_value *query_result = NULL;
256 struct {
257 const bt_component_class_source *source;
258 const bt_plugin *plugin;
259 const bt_value *group;
260 double weigth;
261 } winner = { NULL, NULL, NULL, 0 };
262
263 query_params = bt_value_map_create();
264 if (!query_params) {
265 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate a map value.");
266 goto error;
267 }
268
269 insert_status = bt_value_map_insert_string_entry(query_params, "input", input);
270 if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
271 BT_CLI_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
272 goto error;
273 }
274
275 insert_status = bt_value_map_insert_string_entry(query_params, "type", input_type);
276 if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
277 BT_CLI_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
278 goto error;
279 }
280
281 for (i_plugins = 0; i_plugins < plugin_count; i_plugins++) {
282 const bt_plugin *plugin;
283 const char *plugin_name;
284 uint64_t source_count;
285 uint64_t i_sources;
286
287 plugin = borrow_loaded_plugin(i_plugins);
288 plugin_name = bt_plugin_get_name(plugin);
289
290 /*
291 * If the search is restricted to a specific plugin, only consider
292 * the plugin with that name.
293 */
294 if (plugin_restrict && strcmp(plugin_restrict, plugin_name) != 0) {
295 continue;
296 }
297
298 source_count = bt_plugin_get_source_component_class_count(plugin);
299
300 for (i_sources = 0; i_sources < source_count; i_sources++) {
301 const bt_component_class_source *source_cc;
302 const bt_component_class *cc;
303 const char *source_cc_name;
304 bt_query_executor_query_status query_status;
305
306 source_cc = bt_plugin_borrow_source_component_class_by_index_const(plugin, i_sources);
307 cc = bt_component_class_source_as_component_class_const(source_cc);
308 source_cc_name = bt_component_class_get_name(cc);
309
310 /*
311 * If the search is restricted to a specific component class, only consider the
312 * component classes with that name.
313 */
314 if (component_class_restrict && strcmp(component_class_restrict, source_cc_name) != 0) {
315 continue;
316 }
317
1a29b831 318 BT_LOGD("babeltrace.support-info query: before: component-class-name=source.%s.%s, input=%s, "
73760435
SM
319 "type=%s", plugin_name, source_cc_name, input, input_type);
320
321 BT_VALUE_PUT_REF_AND_RESET(query_result);
3c729b9a
PP
322 query_status = cli_query(cc, "babeltrace.support-info",
323 query_params, log_level, NULL, &query_result,
324 NULL);
73760435
SM
325
326 if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_OK) {
327 double weight;
328 const bt_value *group_value = NULL;
329 enum bt_value_type query_result_type;
330
331 BT_ASSERT(query_result);
332
333 query_result_type = bt_value_get_type(query_result);
334
335 if (query_result_type == BT_VALUE_TYPE_REAL || query_result_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
336 if (convert_weight_value(query_result, &weight, plugin_name, source_cc_name, input, input_type) != 0) {
337 /* convert_weight_value has already warned. */
338 continue;
339 }
340 } else if (query_result_type == BT_VALUE_TYPE_MAP) {
341 const bt_value *weight_value;
342
343 if (!bt_value_map_has_entry(query_result, "weight")) {
1a29b831 344 BT_LOGW("babeltrace.support-info query: result is missing `weight` entry: "
73760435
SM
345 "component-class-name=source.%s.%s, input=%s, input-type=%s",
346 bt_plugin_get_name(plugin),
347 bt_component_class_get_name(cc), input,
348 input_type);
349 continue;
350 }
351
352 weight_value = bt_value_map_borrow_entry_value_const(query_result, "weight");
353 BT_ASSERT(weight_value);
354
355 if (convert_weight_value(weight_value, &weight, plugin_name, source_cc_name, input, input_type) != 0) {
356 /* convert_weight_value has already warned. */
357 continue;
358 }
359
360 if (bt_value_map_has_entry(query_result, "group")) {
361 enum bt_value_type group_value_type;
362
363 group_value = bt_value_map_borrow_entry_value_const(query_result, "group");
364 BT_ASSERT(group_value);
365
366 group_value_type = bt_value_get_type(group_value);
367
368 if (group_value_type == BT_VALUE_TYPE_NULL) {
369 /* Do as if no value was passed. */
370 group_value = NULL;
371 } else if (bt_value_get_type(group_value) != BT_VALUE_TYPE_STRING) {
1a29b831 372 BT_LOGW("babeltrace.support-info query: unexpected type for entry `group`: "
73760435
SM
373 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
374 "expected-entry-type=%s,%s, actual-entry-type=%s",
375 bt_plugin_get_name(plugin),
376 bt_component_class_get_name(cc), input,
377 input_type,
378 bt_common_value_type_string(BT_VALUE_TYPE_NULL),
379 bt_common_value_type_string(BT_VALUE_TYPE_STRING),
380 bt_common_value_type_string(bt_value_get_type(group_value)));
381 continue;
382 }
383 }
384 } else {
1a29b831 385 BT_LOGW("babeltrace.support-info query: unexpected result type: "
73760435
SM
386 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
387 "expected-types=%s,%s,%s, actual-type=%s",
388 bt_plugin_get_name(plugin),
389 bt_component_class_get_name(cc), input,
390 input_type,
391 bt_common_value_type_string(BT_VALUE_TYPE_REAL),
392 bt_common_value_type_string(BT_VALUE_TYPE_MAP),
393 bt_common_value_type_string(BT_VALUE_TYPE_SIGNED_INTEGER),
394 bt_common_value_type_string(bt_value_get_type(query_result)));
395 continue;
396 }
397
1a29b831 398 BT_LOGD("babeltrace.support-info query: success: component-class-name=source.%s.%s, input=%s, "
73760435
SM
399 "type=%s, weight=%f\n",
400 bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
401 input_type, weight);
402
403 if (weight > winner.weigth) {
404 winner.source = source_cc;
405 winner.plugin = plugin;
406
407 bt_value_put_ref(winner.group);
408 winner.group = group_value;
409 bt_value_get_ref(winner.group);
410
411 winner.weigth = weight;
412 }
413 } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_ERROR) {
1a29b831 414 BT_CLI_LOGE_APPEND_CAUSE("babeltrace.support-info query failed.");
73760435
SM
415 goto error;
416 } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR) {
417 BT_CLI_LOGE_APPEND_CAUSE("Memory error.");
418 goto error;
419 } else {
1a29b831 420 BT_LOGD("babeltrace.support-info query: failure: component-class-name=source.%s.%s, input=%s, "
73760435
SM
421 "type=%s, status=%s\n",
422 bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
423 input_type,
424 bt_common_func_status_string(query_status));
425 }
426 }
427 }
428
429 if (winner.source) {
430 const char *source_name;
431 const char *plugin_name;
432 const char *group;
433
434 source_name = bt_component_class_get_name(
435 bt_component_class_source_as_component_class_const(winner.source));
436 plugin_name = bt_plugin_get_name(winner.plugin);
437 group = winner.group ? bt_value_string_get(winner.group) : NULL;
438
439 BT_LOGI("Input %s is awarded to component class source.%s.%s with weight %f",
440 input, plugin_name, source_name, winner.weigth);
441
442 status = auto_source_discovery_add(auto_disc, plugin_name, source_name, group, input);
443 if (status != 0) {
444 goto error;
445 }
446 } else {
447 BT_LOGI("Input %s (%s) was not recognized by any source component class.",
448 input, input_type);
449 status = 1;
450 }
451
452 goto end;
453
454error:
455 status = -1;
456
457end:
458 bt_value_put_ref(query_result);
459 bt_value_put_ref(query_params);
460 bt_value_put_ref(winner.group);
461
462 return status;
463}
464
465/*
466 * Look for a source component class that recognizes `input` as an arbitrary
467 * string.
468 *
469 * Same return value semantic as `support_info_query_all_sources`.
470 */
471
472static
473int auto_discover_source_for_input_as_string(const char *input,
3c729b9a 474 size_t plugin_count, const char *plugin_restrict,
73760435
SM
475 const char *component_class_restrict,
476 enum bt_logging_level log_level,
477 struct auto_source_discovery *auto_disc)
478{
479 return support_info_query_all_sources(input, "string",
3c729b9a
PP
480 plugin_count, plugin_restrict, component_class_restrict,
481 log_level, auto_disc);
73760435
SM
482}
483
484static
485int auto_discover_source_for_input_as_dir_or_file_rec(GString *input,
3c729b9a 486 size_t plugin_count, const char *plugin_restrict,
73760435
SM
487 const char *component_class_restrict,
488 enum bt_logging_level log_level,
489 struct auto_source_discovery *auto_disc)
490{
491 int status;
492 GError *error = NULL;
493
494 if (g_file_test(input->str, G_FILE_TEST_IS_REGULAR)) {
495 /* It's a file. */
496 status = support_info_query_all_sources(input->str,
3c729b9a 497 "file", plugin_count,
73760435
SM
498 plugin_restrict, component_class_restrict, log_level, auto_disc);
499 } else if (g_file_test(input->str, G_FILE_TEST_IS_DIR)) {
500 GDir *dir;
501 const gchar *dirent;
502 gsize saved_input_len;
503 int dir_status = 1;
504
505 /* It's a directory. */
506 status = support_info_query_all_sources(input->str,
3c729b9a 507 "directory", plugin_count,
73760435
SM
508 plugin_restrict, component_class_restrict, log_level,
509 auto_disc);
510
511 if (status < 0) {
512 /* Fatal error. */
513 goto error;
514 } else if (status == 0) {
515 /*
516 * A component class claimed this input as a directory,
517 * don't recurse.
518 */
519 goto end;
520 }
521
522 dir = g_dir_open(input->str, 0, &error);
523 if (!dir) {
524 const char *fmt = "Failed to open directory %s: %s";
525 BT_LOGW(fmt, input->str, error->message);
526
d847ef86 527 if (error->code == G_FILE_ERROR_ACCES) {
73760435
SM
528 /* This is not a fatal error, we just skip it. */
529 status = 1;
530 goto end;
531 } else {
532 BT_CLI_LOGE_APPEND_CAUSE(fmt, input->str,
533 error->message);
534 goto error;
535 }
536 }
537
538 saved_input_len = input->len;
539
540 do {
541 errno = 0;
542 dirent = g_dir_read_name(dir);
543 if (dirent) {
544 g_string_append_c_inline(input, G_DIR_SEPARATOR);
545 g_string_append(input, dirent);
546
547 status = auto_discover_source_for_input_as_dir_or_file_rec(
3c729b9a 548 input, plugin_count,
73760435
SM
549 plugin_restrict, component_class_restrict,
550 log_level, auto_disc);
551
552 g_string_truncate(input, saved_input_len);
553
554 if (status < 0) {
555 /* Fatal error. */
556 goto error;
557 } else if (status == 0) {
558 dir_status = 0;
559 }
560 } else if (errno != 0) {
561 BT_LOGW_ERRNO("Failed to read directory entry", ": dir=%s", input->str);
562 goto error;
563 }
5084732e 564 } while (dirent);
73760435
SM
565
566 status = dir_status;
567
568 g_dir_close(dir);
569 } else {
570 BT_LOGD("Skipping %s, not a file or directory", input->str);
571 status = 1;
572 }
573
574 goto end;
575
576error:
577 status = -1;
578
579end:
580
581 if (error) {
582 g_error_free(error);
583 }
584
585 return status;
586}
587
588/*
589 * Look for a source component class that recognizes `input` as a directory or
590 * file. If `input` is a directory and is not directly recognized, recurse and
591 * apply the same logic to children nodes.
592 *
593 * Same return value semantic as `support_info_query_all_sources`.
594 */
595
596static
597int auto_discover_source_for_input_as_dir_or_file(const char *input,
3c729b9a 598 size_t plugin_count, const char *plugin_restrict,
73760435
SM
599 const char *component_class_restrict,
600 enum bt_logging_level log_level,
601 struct auto_source_discovery *auto_disc)
602{
603 GString *mutable_input;
604 int status;
605
606 mutable_input = g_string_new(input);
607 if (!mutable_input) {
608 status = -1;
609 goto end;
610 }
611
612 status = auto_discover_source_for_input_as_dir_or_file_rec(
3c729b9a 613 mutable_input, plugin_count, plugin_restrict,
73760435
SM
614 component_class_restrict, log_level, auto_disc);
615
616 g_string_free(mutable_input, TRUE);
617end:
618 return status;
619}
620
621int auto_discover_source_components(
622 const bt_value *plugin_paths,
623 const bt_value *inputs,
624 const char *plugin_restrict,
625 const char *component_class_restrict,
626 enum bt_logging_level log_level,
627 struct auto_source_discovery *auto_disc)
628{
629 uint64_t i_inputs, input_count;
630 int status;
631 size_t plugin_count;
73760435
SM
632
633 input_count = bt_value_array_get_size(inputs);
634
635 status = require_loaded_plugins(plugin_paths);
636 if (status != 0) {
637 goto end;
638 }
639
640 plugin_count = get_loaded_plugins_count();
641
73760435
SM
642 for (i_inputs = 0; i_inputs < input_count; i_inputs++) {
643 const bt_value *input_value;
644 const char *input;
645
646 input_value = bt_value_array_borrow_element_by_index_const(inputs, i_inputs);
647 input = bt_value_string_get(input_value);
3c729b9a 648 status = auto_discover_source_for_input_as_string(input,
73760435
SM
649 plugin_count, plugin_restrict, component_class_restrict,
650 log_level, auto_disc);
651 if (status < 0) {
652 /* Fatal error. */
653 goto end;
654 } else if (status == 0) {
655 /* A component class has claimed this input as an arbitrary string. */
656 continue;
657 }
658
659 status = auto_discover_source_for_input_as_dir_or_file(input,
3c729b9a
PP
660 plugin_count, plugin_restrict, component_class_restrict,
661 log_level, auto_disc);
73760435
SM
662 if (status < 0) {
663 /* Fatal error. */
664 goto end;
665 } else if (status == 0) {
666 /*
667 * This input (or something under it) was recognized.
668 */
669 continue;
670 }
671
672 BT_LOGW("No trace was found based on input `%s`.", input);
673 }
674
675 status = 0;
676end:
73760435
SM
677 return status;
678}
This page took 0.048977 seconds and 4 git commands to generate.