autodisc: make it possible to interrupt auto source discovery
[babeltrace.git] / src / autodisc / autodisc.c
CommitLineData
a1040187
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"
77a5caaf
SM
24#define BT_LOG_OUTPUT_LEVEL log_level
25#include "logging/log.h"
a1040187 26
77a5caaf 27#include "autodisc.h"
a1040187
SM
28#include "common/common.h"
29
77a5caaf
SM
30#define BT_AUTODISC_LOG_AND_APPEND(_lvl, _fmt, ...) \
31 do { \
32 BT_LOG_WRITE(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__); \
33 (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( \
34 "Source auto-discovery", _fmt, ##__VA_ARGS__); \
35 } while (0)
36
37#define BT_AUTODISC_LOGE_APPEND_CAUSE(_fmt, ...) \
38 BT_AUTODISC_LOG_AND_APPEND(BT_LOG_ERROR, _fmt, ##__VA_ARGS__)
39
3f8644ec
SM
40/*
41 * Define a status enum for inside the auto source discovery code,
42 * as we don't want to return `NO_MATCH` to the caller.
43 */
44typedef enum auto_source_discovery_internal_status {
45 AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_OK = AUTO_SOURCE_DISCOVERY_STATUS_OK,
46 AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_ERROR = AUTO_SOURCE_DISCOVERY_STATUS_ERROR,
47 AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_MEMORY_ERROR = AUTO_SOURCE_DISCOVERY_STATUS_MEMORY_ERROR,
48 AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_INTERRUPTED = AUTO_SOURCE_DISCOVERY_STATUS_INTERRUPTED,
49 AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_NO_MATCH = __BT_FUNC_STATUS_NO_MATCH,
50} auto_source_discovery_internal_status;
51
a1040187
SM
52/* Finalize and free a `struct auto_source_discovery_result`. */
53
54static
55void auto_source_discovery_result_destroy(struct auto_source_discovery_result *res)
56{
57 if (res) {
58 g_free(res->group);
59 bt_value_put_ref(res->inputs);
821b04aa 60 bt_value_put_ref(res->original_input_indices);
a1040187
SM
61 g_free(res);
62 }
63}
64
65/* Allocate and initialize a `struct auto_source_discovery_result`. */
66
67static
68struct auto_source_discovery_result *auto_source_discovery_result_create(
69 const char *plugin_name, const char *source_cc_name,
77a5caaf 70 const char *group, bt_logging_level log_level)
a1040187
SM
71{
72 struct auto_source_discovery_result *res;
73
74 res = g_new0(struct auto_source_discovery_result, 1);
75 if (!res) {
77a5caaf 76 BT_AUTODISC_LOGE_APPEND_CAUSE(
a1040187
SM
77 "Failed to allocate a auto_source_discovery_result structure.");
78 goto error;
79 }
80
81 res->plugin_name = plugin_name;
82 res->source_cc_name = source_cc_name;
83 res->group = g_strdup(group);
84 if (group && !res->group) {
77a5caaf 85 BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to allocate a string.");
a1040187
SM
86 goto error;
87 }
88
89 res->inputs = bt_value_array_create();
90 if (!res->inputs) {
77a5caaf 91 BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to allocate an array value.");
a1040187
SM
92 goto error;
93 }
94
459f81ca
SM
95 res->original_input_indices = bt_value_array_create();
96 if (!res->original_input_indices) {
77a5caaf 97 BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to allocate an array value.");
459f81ca
SM
98 goto error;
99 }
100
a1040187
SM
101 goto end;
102error:
103 auto_source_discovery_result_destroy(res);
cd0fb218 104 res = NULL;
a1040187
SM
105
106end:
107 return res;
108}
109
110/* Finalize a `struct auto_source_discovery`. */
111
112void auto_source_discovery_fini(struct auto_source_discovery *auto_disc)
113{
114 if (auto_disc->results) {
115 g_ptr_array_free(auto_disc->results, TRUE);
116 }
117}
118
119/* Initialize an already allocated `struct auto_source_discovery`. */
120
121int auto_source_discovery_init(struct auto_source_discovery *auto_disc)
122{
123 int status;
124
125 auto_disc->results = g_ptr_array_new_with_free_func(
126 (GDestroyNotify) auto_source_discovery_result_destroy);
127
128 if (!auto_disc->results) {
129 goto error;
130 }
131
132 status = 0;
133 goto end;
134
135error:
136 auto_source_discovery_fini(auto_disc);
137 status = -1;
138
139end:
140
141 return status;
142}
143
459f81ca
SM
144static
145const bt_value *borrow_array_value_last_element_const(const bt_value *array)
146{
1270d0e9 147 uint64_t last_index = bt_value_array_get_length(array) - 1;
459f81ca
SM
148
149 return bt_value_array_borrow_element_by_index_const(array, last_index);
150}
151
a1040187
SM
152/*
153 * Assign `input` to source component class `source_cc_name` of plugin
154 * `plugin_name`, in the group with key `group`.
155 */
156
157static
3f8644ec
SM
158auto_source_discovery_internal_status auto_source_discovery_add(
159 struct auto_source_discovery *auto_disc,
a1040187
SM
160 const char *plugin_name,
161 const char *source_cc_name,
162 const char *group,
459f81ca 163 const char *input,
77a5caaf
SM
164 uint64_t original_input_index,
165 bt_logging_level log_level)
a1040187 166{
3f8644ec 167 auto_source_discovery_internal_status status;
a1040187
SM
168 bt_value_array_append_element_status append_status;
169 guint len;
170 guint i;
171 struct auto_source_discovery_result *res = NULL;
459f81ca 172 bool append_index;
a1040187
SM
173
174 len = auto_disc->results->len;
175 i = len;
176
177 if (group) {
178 for (i = 0; i < len; i++) {
179 res = g_ptr_array_index(auto_disc->results, i);
180
181 if (strcmp(res->plugin_name, plugin_name) != 0) {
182 continue;
183 }
184
185 if (strcmp(res->source_cc_name, source_cc_name) != 0) {
186 continue;
187 }
188
189 if (g_strcmp0(res->group, group) != 0) {
190 continue;
191 }
192
193 break;
194 }
195 }
196
197 if (i == len) {
198 /* Add a new result entry. */
199 res = auto_source_discovery_result_create(plugin_name,
77a5caaf 200 source_cc_name, group, log_level);
a1040187
SM
201 if (!res) {
202 goto error;
203 }
204
205 g_ptr_array_add(auto_disc->results, res);
206 }
207
208 append_status = bt_value_array_append_string_element(res->inputs, input);
209 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
77a5caaf 210 BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to append a string value.");
a1040187
SM
211 goto error;
212 }
213
459f81ca
SM
214 /*
215 * Append `original_input_index` to `original_input_indices` if not
216 * there already. We process the `inputs` array in order, so if it is
217 * present, it has to be the last element.
218 */
219 if (bt_value_array_is_empty(res->original_input_indices)) {
220 append_index = true;
221 } else {
222 const bt_value *last_index_value;
223 uint64_t last_index;
224
225 last_index_value =
226 borrow_array_value_last_element_const(res->original_input_indices);
227 last_index = bt_value_integer_unsigned_get(last_index_value);
228
229 BT_ASSERT(last_index <= original_input_index);
230
231 append_index = (last_index != original_input_index);
232 }
233
234 if (append_index) {
235 append_status = bt_value_array_append_unsigned_integer_element(
236 res->original_input_indices, original_input_index);
237
238 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
77a5caaf 239 BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to append an unsigned integer value.");
459f81ca
SM
240 goto error;
241 }
242 }
a1040187 243
3f8644ec 244 status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_OK;
a1040187
SM
245 goto end;
246
247error:
3f8644ec 248 status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_ERROR;
a1040187
SM
249
250end:
251 return status;
252}
253
254static
255int convert_weight_value(const bt_value *weight_value, double *weight,
256 const char *plugin_name, const char *source_cc_name,
77a5caaf
SM
257 const char *input, const char *input_type,
258 bt_logging_level log_level)
a1040187
SM
259{
260 enum bt_value_type weight_value_type;
261 int status;
262
263 weight_value_type = bt_value_get_type(weight_value);
264
265 if (weight_value_type == BT_VALUE_TYPE_REAL) {
266 *weight = bt_value_real_get(weight_value);
267 } else if (weight_value_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
268 /* Accept signed integer as a convenience for "return 0" or "return 1" in Python. */
269 *weight = bt_value_integer_signed_get(weight_value);
270 } else {
9e534aae 271 BT_LOGW("babeltrace.support-info query: unexpected type for weight: "
a1040187
SM
272 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
273 "expected-entry-type=%s, actual-entry-type=%s",
274 plugin_name, source_cc_name, input, input_type,
275 bt_common_value_type_string(BT_VALUE_TYPE_REAL),
276 bt_common_value_type_string(bt_value_get_type(weight_value)));
277 goto error;
278 }
279
280 if (*weight < 0.0 || *weight > 1.0) {
9e534aae 281 BT_LOGW("babeltrace.support-info query: weight value is out of range [0.0, 1.0]: "
a1040187
SM
282 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
283 "weight=%f",
284 plugin_name, source_cc_name, input, input_type, *weight);
285 goto error;
286 }
287
288 status = 0;
289 goto end;
290
291error:
292 status = -1;
293
294end:
295 return status;
296}
297
77a5caaf
SM
298static
299bt_query_executor_query_status simple_query(const bt_component_class *comp_cls,
300 const char *obj, const bt_value *params,
301 bt_logging_level log_level, const bt_value **result)
302{
303 bt_query_executor_query_status status;
304 bt_query_executor_set_logging_level_status set_logging_level_status;
305 bt_query_executor *query_exec;
306
307 query_exec = bt_query_executor_create(comp_cls, obj, params);
308 if (!query_exec) {
309 BT_AUTODISC_LOGE_APPEND_CAUSE("Cannot create a query executor.");
310 status = BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR;
311 goto end;
312 }
313
314 set_logging_level_status = bt_query_executor_set_logging_level(query_exec, log_level);
315 if (set_logging_level_status != BT_QUERY_EXECUTOR_SET_LOGGING_LEVEL_STATUS_OK) {
316 BT_AUTODISC_LOGE_APPEND_CAUSE(
317 "Cannot set query executor's logging level: "
318 "log-level=%s",
319 bt_common_logging_level_string(log_level));
320 status = (int) set_logging_level_status;
321 goto end;
322 }
323
324 status = bt_query_executor_query(query_exec, result);
325
326end:
327 bt_query_executor_put_ref(query_exec);
328
329 return status;
330}
331
332
a1040187
SM
333/*
334 * Query all known source components to see if any of them can handle `input`
335 * as the given `type`(arbitrary string, directory or file).
336 *
337 * If `plugin_restrict` is non-NULL, only query source component classes provided
338 * by the plugin with that name.
339 *
340 * If `component_class_restrict` is non-NULL, only query source component classes
341 * with that name.
a1040187
SM
342 */
343static
3f8644ec
SM
344auto_source_discovery_internal_status support_info_query_all_sources(
345 const char *input,
459f81ca
SM
346 const char *input_type,
347 uint64_t original_input_index,
77a5caaf 348 const bt_plugin **plugins,
459f81ca 349 size_t plugin_count,
a1040187
SM
350 const char *component_class_restrict,
351 enum bt_logging_level log_level,
3f8644ec
SM
352 struct auto_source_discovery *auto_disc,
353 const bt_interrupter *interrupter)
a1040187
SM
354{
355 bt_value_map_insert_entry_status insert_status;
356 bt_value *query_params = NULL;
3f8644ec 357 auto_source_discovery_internal_status status;
a1040187
SM
358 size_t i_plugins;
359 const struct bt_value *query_result = NULL;
360 struct {
361 const bt_component_class_source *source;
362 const bt_plugin *plugin;
363 const bt_value *group;
364 double weigth;
365 } winner = { NULL, NULL, NULL, 0 };
366
3f8644ec
SM
367 if (interrupter && bt_interrupter_is_set(interrupter)) {
368 status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_INTERRUPTED;
369 goto end;
370 }
371
a1040187
SM
372 query_params = bt_value_map_create();
373 if (!query_params) {
77a5caaf 374 BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to allocate a map value.");
a1040187
SM
375 goto error;
376 }
377
378 insert_status = bt_value_map_insert_string_entry(query_params, "input", input);
379 if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
77a5caaf 380 BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
a1040187
SM
381 goto error;
382 }
383
384 insert_status = bt_value_map_insert_string_entry(query_params, "type", input_type);
385 if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
77a5caaf 386 BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
a1040187
SM
387 goto error;
388 }
389
390 for (i_plugins = 0; i_plugins < plugin_count; i_plugins++) {
391 const bt_plugin *plugin;
392 const char *plugin_name;
393 uint64_t source_count;
394 uint64_t i_sources;
395
77a5caaf 396 plugin = plugins[i_plugins];
a1040187
SM
397 plugin_name = bt_plugin_get_name(plugin);
398
a1040187
SM
399 source_count = bt_plugin_get_source_component_class_count(plugin);
400
401 for (i_sources = 0; i_sources < source_count; i_sources++) {
402 const bt_component_class_source *source_cc;
403 const bt_component_class *cc;
404 const char *source_cc_name;
405 bt_query_executor_query_status query_status;
406
407 source_cc = bt_plugin_borrow_source_component_class_by_index_const(plugin, i_sources);
408 cc = bt_component_class_source_as_component_class_const(source_cc);
409 source_cc_name = bt_component_class_get_name(cc);
410
411 /*
412 * If the search is restricted to a specific component class, only consider the
413 * component classes with that name.
414 */
415 if (component_class_restrict && strcmp(component_class_restrict, source_cc_name) != 0) {
416 continue;
417 }
418
9e534aae 419 BT_LOGD("babeltrace.support-info query: before: component-class-name=source.%s.%s, input=%s, "
a1040187
SM
420 "type=%s", plugin_name, source_cc_name, input, input_type);
421
422 BT_VALUE_PUT_REF_AND_RESET(query_result);
77a5caaf
SM
423 query_status = simple_query(cc, "babeltrace.support-info",
424 query_params, log_level, &query_result);
a1040187
SM
425
426 if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_OK) {
427 double weight;
428 const bt_value *group_value = NULL;
429 enum bt_value_type query_result_type;
430
431 BT_ASSERT(query_result);
432
433 query_result_type = bt_value_get_type(query_result);
434
435 if (query_result_type == BT_VALUE_TYPE_REAL || query_result_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
77a5caaf 436 if (convert_weight_value(query_result, &weight, plugin_name, source_cc_name, input, input_type, log_level) != 0) {
a1040187
SM
437 /* convert_weight_value has already warned. */
438 continue;
439 }
440 } else if (query_result_type == BT_VALUE_TYPE_MAP) {
441 const bt_value *weight_value;
442
443 if (!bt_value_map_has_entry(query_result, "weight")) {
9e534aae 444 BT_LOGW("babeltrace.support-info query: result is missing `weight` entry: "
a1040187
SM
445 "component-class-name=source.%s.%s, input=%s, input-type=%s",
446 bt_plugin_get_name(plugin),
447 bt_component_class_get_name(cc), input,
448 input_type);
449 continue;
450 }
451
452 weight_value = bt_value_map_borrow_entry_value_const(query_result, "weight");
453 BT_ASSERT(weight_value);
454
77a5caaf 455 if (convert_weight_value(weight_value, &weight, plugin_name, source_cc_name, input, input_type, log_level) != 0) {
a1040187
SM
456 /* convert_weight_value has already warned. */
457 continue;
458 }
459
460 if (bt_value_map_has_entry(query_result, "group")) {
461 enum bt_value_type group_value_type;
462
463 group_value = bt_value_map_borrow_entry_value_const(query_result, "group");
464 BT_ASSERT(group_value);
465
466 group_value_type = bt_value_get_type(group_value);
467
468 if (group_value_type == BT_VALUE_TYPE_NULL) {
469 /* Do as if no value was passed. */
470 group_value = NULL;
471 } else if (bt_value_get_type(group_value) != BT_VALUE_TYPE_STRING) {
9e534aae 472 BT_LOGW("babeltrace.support-info query: unexpected type for entry `group`: "
a1040187
SM
473 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
474 "expected-entry-type=%s,%s, actual-entry-type=%s",
475 bt_plugin_get_name(plugin),
476 bt_component_class_get_name(cc), input,
477 input_type,
478 bt_common_value_type_string(BT_VALUE_TYPE_NULL),
479 bt_common_value_type_string(BT_VALUE_TYPE_STRING),
480 bt_common_value_type_string(bt_value_get_type(group_value)));
481 continue;
482 }
483 }
484 } else {
9e534aae 485 BT_LOGW("babeltrace.support-info query: unexpected result type: "
a1040187
SM
486 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
487 "expected-types=%s,%s,%s, actual-type=%s",
488 bt_plugin_get_name(plugin),
489 bt_component_class_get_name(cc), input,
490 input_type,
491 bt_common_value_type_string(BT_VALUE_TYPE_REAL),
492 bt_common_value_type_string(BT_VALUE_TYPE_MAP),
493 bt_common_value_type_string(BT_VALUE_TYPE_SIGNED_INTEGER),
494 bt_common_value_type_string(bt_value_get_type(query_result)));
495 continue;
496 }
497
9e534aae 498 BT_LOGD("babeltrace.support-info query: success: component-class-name=source.%s.%s, input=%s, "
a1040187
SM
499 "type=%s, weight=%f\n",
500 bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
501 input_type, weight);
502
503 if (weight > winner.weigth) {
504 winner.source = source_cc;
505 winner.plugin = plugin;
506
507 bt_value_put_ref(winner.group);
508 winner.group = group_value;
509 bt_value_get_ref(winner.group);
510
511 winner.weigth = weight;
512 }
513 } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_ERROR) {
77a5caaf 514 BT_AUTODISC_LOGE_APPEND_CAUSE("babeltrace.support-info query failed.");
a1040187
SM
515 goto error;
516 } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR) {
77a5caaf 517 BT_AUTODISC_LOGE_APPEND_CAUSE("Memory error.");
a1040187
SM
518 goto error;
519 } else {
9e534aae 520 BT_LOGD("babeltrace.support-info query: failure: component-class-name=source.%s.%s, input=%s, "
a1040187
SM
521 "type=%s, status=%s\n",
522 bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
523 input_type,
524 bt_common_func_status_string(query_status));
525 }
526 }
527 }
528
529 if (winner.source) {
530 const char *source_name;
531 const char *plugin_name;
532 const char *group;
533
534 source_name = bt_component_class_get_name(
535 bt_component_class_source_as_component_class_const(winner.source));
536 plugin_name = bt_plugin_get_name(winner.plugin);
537 group = winner.group ? bt_value_string_get(winner.group) : NULL;
538
539 BT_LOGI("Input %s is awarded to component class source.%s.%s with weight %f",
540 input, plugin_name, source_name, winner.weigth);
541
459f81ca 542 status = auto_source_discovery_add(auto_disc, plugin_name,
77a5caaf 543 source_name, group, input, original_input_index, log_level);
3f8644ec 544 if (status != AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_OK) {
a1040187
SM
545 goto error;
546 }
547 } else {
548 BT_LOGI("Input %s (%s) was not recognized by any source component class.",
549 input, input_type);
3f8644ec 550 status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_NO_MATCH;
a1040187
SM
551 }
552
553 goto end;
554
555error:
3f8644ec 556 status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_ERROR;
a1040187
SM
557
558end:
559 bt_value_put_ref(query_result);
560 bt_value_put_ref(query_params);
561 bt_value_put_ref(winner.group);
562
563 return status;
564}
565
566/*
567 * Look for a source component class that recognizes `input` as an arbitrary
568 * string.
569 *
570 * Same return value semantic as `support_info_query_all_sources`.
571 */
572
573static
3f8644ec
SM
574auto_source_discovery_internal_status auto_discover_source_for_input_as_string(
575 const char *input,
459f81ca 576 uint64_t original_input_index,
77a5caaf
SM
577 const bt_plugin **plugins,
578 size_t plugin_count,
a1040187
SM
579 const char *component_class_restrict,
580 enum bt_logging_level log_level,
3f8644ec
SM
581 struct auto_source_discovery *auto_disc,
582 const bt_interrupter *interrupter)
a1040187
SM
583{
584 return support_info_query_all_sources(input, "string",
77a5caaf 585 original_input_index, plugins, plugin_count,
3f8644ec
SM
586 component_class_restrict, log_level, auto_disc,
587 interrupter);
a1040187
SM
588}
589
590static
3f8644ec
SM
591auto_source_discovery_internal_status auto_discover_source_for_input_as_dir_or_file_rec(
592 GString *input,
459f81ca 593 uint64_t original_input_index,
77a5caaf
SM
594 const bt_plugin **plugins,
595 size_t plugin_count,
a1040187
SM
596 const char *component_class_restrict,
597 enum bt_logging_level log_level,
3f8644ec
SM
598 struct auto_source_discovery *auto_disc,
599 const bt_interrupter *interrupter)
a1040187 600{
3f8644ec 601 auto_source_discovery_internal_status status;
a1040187
SM
602 GError *error = NULL;
603
604 if (g_file_test(input->str, G_FILE_TEST_IS_REGULAR)) {
605 /* It's a file. */
606 status = support_info_query_all_sources(input->str,
77a5caaf 607 "file", original_input_index, plugins, plugin_count,
3f8644ec
SM
608 component_class_restrict, log_level, auto_disc,
609 interrupter);
a1040187
SM
610 } else if (g_file_test(input->str, G_FILE_TEST_IS_DIR)) {
611 GDir *dir;
612 const gchar *dirent;
613 gsize saved_input_len;
3f8644ec 614 int dir_status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_NO_MATCH;
a1040187
SM
615
616 /* It's a directory. */
617 status = support_info_query_all_sources(input->str,
77a5caaf
SM
618 "directory", original_input_index, plugins,
619 plugin_count, component_class_restrict, log_level,
3f8644ec 620 auto_disc, interrupter);
a1040187
SM
621
622 if (status < 0) {
623 /* Fatal error. */
624 goto error;
3f8644ec
SM
625 } else if (status == AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_OK ||
626 status == AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_INTERRUPTED) {
a1040187
SM
627 /*
628 * A component class claimed this input as a directory,
3f8644ec 629 * don't recurse. Or, we got interrupted.
a1040187
SM
630 */
631 goto end;
632 }
633
634 dir = g_dir_open(input->str, 0, &error);
635 if (!dir) {
636 const char *fmt = "Failed to open directory %s: %s";
637 BT_LOGW(fmt, input->str, error->message);
638
07415a8f 639 if (error->code == G_FILE_ERROR_ACCES) {
a1040187 640 /* This is not a fatal error, we just skip it. */
3f8644ec 641 status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_NO_MATCH;
a1040187
SM
642 goto end;
643 } else {
77a5caaf 644 BT_AUTODISC_LOGE_APPEND_CAUSE(fmt, input->str,
a1040187
SM
645 error->message);
646 goto error;
647 }
648 }
649
650 saved_input_len = input->len;
651
652 do {
653 errno = 0;
654 dirent = g_dir_read_name(dir);
655 if (dirent) {
656 g_string_append_c_inline(input, G_DIR_SEPARATOR);
657 g_string_append(input, dirent);
658
659 status = auto_discover_source_for_input_as_dir_or_file_rec(
77a5caaf 660 input, original_input_index, plugins, plugin_count,
3f8644ec
SM
661 component_class_restrict, log_level, auto_disc,
662 interrupter);
a1040187
SM
663
664 g_string_truncate(input, saved_input_len);
665
666 if (status < 0) {
667 /* Fatal error. */
668 goto error;
3f8644ec
SM
669 } else if (status == AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_INTERRUPTED) {
670 goto end;
671 } else if (status == AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_OK) {
672 dir_status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_OK;
a1040187
SM
673 }
674 } else if (errno != 0) {
675 BT_LOGW_ERRNO("Failed to read directory entry", ": dir=%s", input->str);
676 goto error;
677 }
8e01f2d9 678 } while (dirent);
a1040187
SM
679
680 status = dir_status;
681
682 g_dir_close(dir);
683 } else {
684 BT_LOGD("Skipping %s, not a file or directory", input->str);
3f8644ec 685 status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_NO_MATCH;
a1040187
SM
686 }
687
688 goto end;
689
690error:
3f8644ec 691 status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_ERROR;
a1040187
SM
692
693end:
694
695 if (error) {
696 g_error_free(error);
697 }
698
699 return status;
700}
701
702/*
703 * Look for a source component class that recognizes `input` as a directory or
704 * file. If `input` is a directory and is not directly recognized, recurse and
705 * apply the same logic to children nodes.
706 *
707 * Same return value semantic as `support_info_query_all_sources`.
708 */
709
710static
3f8644ec
SM
711auto_source_discovery_internal_status auto_discover_source_for_input_as_dir_or_file(
712 const char *input,
459f81ca 713 uint64_t original_input_index,
77a5caaf
SM
714 const bt_plugin **plugins,
715 size_t plugin_count,
a1040187
SM
716 const char *component_class_restrict,
717 enum bt_logging_level log_level,
3f8644ec
SM
718 struct auto_source_discovery *auto_disc,
719 const bt_interrupter *interrupter)
a1040187
SM
720{
721 GString *mutable_input;
3f8644ec 722 auto_source_discovery_internal_status status;
a1040187
SM
723
724 mutable_input = g_string_new(input);
725 if (!mutable_input) {
3f8644ec 726 status = AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_ERROR;
a1040187
SM
727 goto end;
728 }
729
730 status = auto_discover_source_for_input_as_dir_or_file_rec(
77a5caaf 731 mutable_input, original_input_index, plugins, plugin_count,
3f8644ec
SM
732 component_class_restrict, log_level, auto_disc,
733 interrupter);
a1040187
SM
734
735 g_string_free(mutable_input, TRUE);
736end:
737 return status;
738}
739
3f8644ec 740auto_source_discovery_status auto_discover_source_components(
a1040187 741 const bt_value *inputs,
77a5caaf
SM
742 const bt_plugin **plugins,
743 size_t plugin_count,
a1040187
SM
744 const char *component_class_restrict,
745 enum bt_logging_level log_level,
3f8644ec
SM
746 struct auto_source_discovery *auto_disc,
747 const bt_interrupter *interrupter)
a1040187
SM
748{
749 uint64_t i_inputs, input_count;
3f8644ec
SM
750 auto_source_discovery_internal_status internal_status;
751 auto_source_discovery_status status;
a1040187 752
1270d0e9 753 input_count = bt_value_array_get_length(inputs);
a1040187 754
a1040187
SM
755 for (i_inputs = 0; i_inputs < input_count; i_inputs++) {
756 const bt_value *input_value;
757 const char *input;
758
759 input_value = bt_value_array_borrow_element_by_index_const(inputs, i_inputs);
760 input = bt_value_string_get(input_value);
3f8644ec 761 internal_status = auto_discover_source_for_input_as_string(input, i_inputs,
77a5caaf 762 plugins, plugin_count, component_class_restrict,
3f8644ec
SM
763 log_level, auto_disc, interrupter);
764 if (internal_status < 0 || internal_status == AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_INTERRUPTED) {
765 /* Fatal error or we got interrupted. */
766 status = internal_status;
a1040187 767 goto end;
3f8644ec 768 } else if (internal_status == AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_OK) {
a1040187
SM
769 /* A component class has claimed this input as an arbitrary string. */
770 continue;
771 }
772
3f8644ec 773 internal_status = auto_discover_source_for_input_as_dir_or_file(input,
77a5caaf 774 i_inputs, plugins, plugin_count,
3f8644ec
SM
775 component_class_restrict, log_level, auto_disc, interrupter);
776 if (internal_status < 0 || internal_status == AUTO_SOURCE_DISCOVERY_INTERNAL_STATUS_INTERRUPTED) {
777 /* Fatal error or we got interrupted. */
778 status = internal_status;
a1040187 779 goto end;
3f8644ec 780 } else if (internal_status == 0) {
a1040187
SM
781 /*
782 * This input (or something under it) was recognized.
783 */
784 continue;
785 }
786
787 BT_LOGW("No trace was found based on input `%s`.", input);
788 }
789
3f8644ec 790 status = AUTO_SOURCE_DISCOVERY_STATUS_OK;
a1040187 791end:
a1040187
SM
792 return status;
793}
This page took 0.063921 seconds and 4 git commands to generate.