cli: make config cli args functions return a status enum
[babeltrace.git] / src / cli / babeltrace2-cfg-cli-args.c
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
5 *
6 * Babeltrace trace converter - parameter parsing
7 */
8
9 #define BT_LOG_TAG "CLI/CFG-CLI-ARGS"
10 #include "logging.h"
11
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include "common/assert.h"
16 #include <stdio.h>
17 #include <stdbool.h>
18 #include <inttypes.h>
19 #include <babeltrace2/babeltrace.h>
20 #include "common/common.h"
21 #include <glib.h>
22 #include <sys/types.h>
23 #include "argpar/argpar.h"
24 #include "babeltrace2-cfg.h"
25 #include "babeltrace2-cfg-cli-args.h"
26 #include "babeltrace2-cfg-cli-args-connect.h"
27 #include "param-parse/param-parse.h"
28 #include "babeltrace2-log-level.h"
29 #include "babeltrace2-plugins.h"
30 #include "babeltrace2-query.h"
31 #include "autodisc/autodisc.h"
32 #include "common/version.h"
33
34 #define BT_CLI_LOGE_APPEND_CAUSE_OOM() BT_CLI_LOGE_APPEND_CAUSE("Out of memory.")
35 #define WHILE_PARSING_ARG_N_FMT "While parsing argument #%d (`%s`): "
36
37 /*
38 * Returns the plugin name, component class name, component class type,
39 * and component name from a command-line --component option's argument.
40 * arg must have the following format:
41 *
42 * [NAME:]TYPE.PLUGIN.CLS
43 *
44 * where NAME is the optional component name, TYPE is either `source`,
45 * `filter`, or `sink`, PLUGIN is the plugin name, and CLS is the
46 * component class name.
47 *
48 * On success, both *plugin and *component are not NULL. *plugin
49 * and *comp_cls are owned by the caller. On success, *name can be NULL
50 * if no component class name was found, and *comp_cls_type is set.
51 */
52 static
53 void plugin_comp_cls_names(const char *arg, char **name, char **plugin,
54 char **comp_cls, bt_component_class_type *comp_cls_type)
55 {
56 const char *at = arg;
57 GString *gs_name = NULL;
58 GString *gs_comp_cls_type = NULL;
59 GString *gs_plugin = NULL;
60 GString *gs_comp_cls = NULL;
61 size_t end_pos;
62
63 BT_ASSERT(arg);
64 BT_ASSERT(plugin);
65 BT_ASSERT(comp_cls);
66 BT_ASSERT(comp_cls_type);
67
68 if (!bt_common_string_is_printable(arg)) {
69 BT_CLI_LOGE_APPEND_CAUSE("Argument contains a non-printable character.");
70 goto error;
71 }
72
73 /* Parse the component name */
74 gs_name = bt_common_string_until(at, ".:\\", ":", &end_pos);
75 if (!gs_name) {
76 goto error;
77 }
78
79 if (arg[end_pos] == ':') {
80 at += end_pos + 1;
81 } else {
82 /* No name */
83 g_string_assign(gs_name, "");
84 }
85
86 /* Parse the component class type */
87 gs_comp_cls_type = bt_common_string_until(at, ".:\\", ".", &end_pos);
88 if (!gs_comp_cls_type || at[end_pos] == '\0') {
89 BT_CLI_LOGE_APPEND_CAUSE("Missing component class type (`source`, `filter`, or `sink`).");
90 goto error;
91 }
92
93 if (strcmp(gs_comp_cls_type->str, "source") == 0 ||
94 strcmp(gs_comp_cls_type->str, "src") == 0) {
95 *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SOURCE;
96 } else if (strcmp(gs_comp_cls_type->str, "filter") == 0 ||
97 strcmp(gs_comp_cls_type->str, "flt") == 0) {
98 *comp_cls_type = BT_COMPONENT_CLASS_TYPE_FILTER;
99 } else if (strcmp(gs_comp_cls_type->str, "sink") == 0) {
100 *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SINK;
101 } else {
102 BT_CLI_LOGE_APPEND_CAUSE("Unknown component class type: `%s`.",
103 gs_comp_cls_type->str);
104 goto error;
105 }
106
107 at += end_pos + 1;
108
109 /* Parse the plugin name */
110 gs_plugin = bt_common_string_until(at, ".:\\", ".", &end_pos);
111 if (!gs_plugin || gs_plugin->len == 0 || at[end_pos] == '\0') {
112 BT_CLI_LOGE_APPEND_CAUSE("Missing plugin or component class name.");
113 goto error;
114 }
115
116 at += end_pos + 1;
117
118 /* Parse the component class name */
119 gs_comp_cls = bt_common_string_until(at, ".:\\", ".", &end_pos);
120 if (!gs_comp_cls || gs_comp_cls->len == 0) {
121 BT_CLI_LOGE_APPEND_CAUSE("Missing component class name.");
122 goto error;
123 }
124
125 if (at[end_pos] != '\0') {
126 /* Found a non-escaped `.` */
127 goto error;
128 }
129
130 if (name) {
131 if (gs_name->len == 0) {
132 *name = NULL;
133 g_string_free(gs_name, TRUE);
134 } else {
135 *name = gs_name->str;
136 g_string_free(gs_name, FALSE);
137 }
138 } else {
139 g_string_free(gs_name, TRUE);
140 }
141
142 *plugin = gs_plugin->str;
143 *comp_cls = gs_comp_cls->str;
144 g_string_free(gs_plugin, FALSE);
145 g_string_free(gs_comp_cls, FALSE);
146 gs_name = NULL;
147 gs_plugin = NULL;
148 gs_comp_cls = NULL;
149 goto end;
150
151 error:
152 if (name) {
153 *name = NULL;
154 }
155
156 *plugin = NULL;
157 *comp_cls = NULL;
158
159 end:
160 if (gs_name) {
161 g_string_free(gs_name, TRUE);
162 }
163
164 if (gs_plugin) {
165 g_string_free(gs_plugin, TRUE);
166 }
167
168 if (gs_comp_cls) {
169 g_string_free(gs_comp_cls, TRUE);
170 }
171
172 if (gs_comp_cls_type) {
173 g_string_free(gs_comp_cls_type, TRUE);
174 }
175
176 return;
177 }
178
179 static
180 void print_and_indent(const char *str)
181 {
182 const char *ch = &str[0];
183
184 for (; *ch != '\0'; ch++) {
185 if (*ch == '\n') {
186 if (ch[1] != '\0') {
187 printf("\n ");
188 }
189 } else {
190 printf("%c", *ch);
191 }
192 }
193
194 printf("\n");
195 }
196
197 /*
198 * Prints the Babeltrace version.
199 */
200 static
201 void print_version(void)
202 {
203 bool has_extra_name = strlen(BT_VERSION_EXTRA_NAME) > 0;
204 bool has_extra_description = strlen(BT_VERSION_EXTRA_DESCRIPTION) > 0;
205 bool has_extra_patch_names = strlen(BT_VERSION_EXTRA_PATCHES) > 0;
206 bool has_extra = has_extra_name || has_extra_description ||
207 has_extra_patch_names;
208
209 printf("%sBabeltrace %s%s",
210 bt_common_color_bold(),
211 VERSION,
212 bt_common_color_reset());
213
214 if (strlen(BT_VERSION_NAME) > 0) {
215 printf(" \"%s%s%s%s\"",
216 bt_common_color_fg_bright_blue(),
217 bt_common_color_bold(),
218 BT_VERSION_NAME,
219 bt_common_color_reset());
220 }
221
222 if (strlen(BT_VERSION_GIT) > 0) {
223 printf(" [%s%s%s]",
224 bt_common_color_fg_yellow(),
225 BT_VERSION_GIT,
226 bt_common_color_reset());
227 }
228
229 printf("\n");
230
231 if (strlen(BT_VERSION_DESCRIPTION) > 0) {
232 unsigned int columns;
233 GString *descr;
234
235 if (bt_common_get_term_size(&columns, NULL) < 0) {
236 /* Width not found: default to 80 */
237 columns = 80;
238 }
239
240 descr = bt_common_fold(BT_VERSION_DESCRIPTION, columns, 0);
241 BT_ASSERT(descr);
242 printf("\n%s\n", descr->str);
243 g_string_free(descr, TRUE);
244 }
245
246 if (has_extra) {
247 printf("\n");
248
249 if (has_extra_name) {
250 printf("%sExtra name%s: %s\n",
251 bt_common_color_fg_cyan(),
252 bt_common_color_reset(),
253 BT_VERSION_EXTRA_NAME);
254 }
255
256 if (has_extra_description) {
257 printf("%sExtra description%s:\n ",
258 bt_common_color_fg_cyan(),
259 bt_common_color_reset());
260 print_and_indent(BT_VERSION_EXTRA_DESCRIPTION);
261 }
262
263 if (has_extra_patch_names) {
264 printf("%sExtra patch names%s:\n ",
265 bt_common_color_fg_cyan(),
266 bt_common_color_reset());
267 print_and_indent(BT_VERSION_EXTRA_PATCHES);
268 }
269 }
270 }
271
272 /*
273 * Destroys a component configuration.
274 */
275 static
276 void bt_config_component_destroy(bt_object *obj)
277 {
278 struct bt_config_component *bt_config_component =
279 container_of(obj, struct bt_config_component, base);
280
281 if (!obj) {
282 goto end;
283 }
284
285 if (bt_config_component->plugin_name) {
286 g_string_free(bt_config_component->plugin_name, TRUE);
287 }
288
289 if (bt_config_component->comp_cls_name) {
290 g_string_free(bt_config_component->comp_cls_name, TRUE);
291 }
292
293 if (bt_config_component->instance_name) {
294 g_string_free(bt_config_component->instance_name, TRUE);
295 }
296
297 BT_VALUE_PUT_REF_AND_RESET(bt_config_component->params);
298 g_free(bt_config_component);
299
300 end:
301 return;
302 }
303
304 /*
305 * Creates a component configuration using the given plugin name and
306 * component name. `plugin_name` and `comp_cls_name` are copied (belong
307 * to the return value).
308 *
309 * Return value is owned by the caller.
310 */
311 static
312 struct bt_config_component *bt_config_component_create(
313 bt_component_class_type type,
314 const char *plugin_name, const char *comp_cls_name,
315 int init_log_level)
316 {
317 struct bt_config_component *cfg_component = NULL;
318
319 cfg_component = g_new0(struct bt_config_component, 1);
320 if (!cfg_component) {
321 BT_CLI_LOGE_APPEND_CAUSE_OOM();
322 goto error;
323 }
324
325 bt_object_init_shared(&cfg_component->base,
326 bt_config_component_destroy);
327 cfg_component->type = type;
328 cfg_component->plugin_name = g_string_new(plugin_name);
329 if (!cfg_component->plugin_name) {
330 BT_CLI_LOGE_APPEND_CAUSE_OOM();
331 goto error;
332 }
333
334 cfg_component->comp_cls_name = g_string_new(comp_cls_name);
335 if (!cfg_component->comp_cls_name) {
336 BT_CLI_LOGE_APPEND_CAUSE_OOM();
337 goto error;
338 }
339
340 cfg_component->instance_name = g_string_new(NULL);
341 if (!cfg_component->instance_name) {
342 BT_CLI_LOGE_APPEND_CAUSE_OOM();
343 goto error;
344 }
345
346 cfg_component->log_level = init_log_level;
347
348 /* Start with empty parameters */
349 cfg_component->params = bt_value_map_create();
350 if (!cfg_component->params) {
351 BT_CLI_LOGE_APPEND_CAUSE_OOM();
352 goto error;
353 }
354
355 goto end;
356
357 error:
358 BT_OBJECT_PUT_REF_AND_RESET(cfg_component);
359
360 end:
361 return cfg_component;
362 }
363
364 /*
365 * Creates a component configuration from a command-line --component
366 * option's argument.
367 */
368 static
369 struct bt_config_component *bt_config_component_from_arg(const char *arg,
370 int init_log_level)
371 {
372 struct bt_config_component *cfg_comp = NULL;
373 char *name = NULL;
374 char *plugin_name = NULL;
375 char *comp_cls_name = NULL;
376 bt_component_class_type type;
377
378 plugin_comp_cls_names(arg, &name, &plugin_name, &comp_cls_name, &type);
379 if (!plugin_name || !comp_cls_name) {
380 goto error;
381 }
382
383 cfg_comp = bt_config_component_create(type, plugin_name, comp_cls_name,
384 init_log_level);
385 if (!cfg_comp) {
386 goto error;
387 }
388
389 if (name) {
390 g_string_assign(cfg_comp->instance_name, name);
391 }
392
393 goto end;
394
395 error:
396 BT_OBJECT_PUT_REF_AND_RESET(cfg_comp);
397
398 end:
399 g_free(name);
400 g_free(plugin_name);
401 g_free(comp_cls_name);
402 return cfg_comp;
403 }
404
405 /*
406 * Destroys a configuration.
407 */
408 static
409 void bt_config_destroy(bt_object *obj)
410 {
411 struct bt_config *cfg =
412 container_of(obj, struct bt_config, base);
413
414 if (!obj) {
415 goto end;
416 }
417
418 BT_VALUE_PUT_REF_AND_RESET(cfg->plugin_paths);
419
420 switch (cfg->command) {
421 case BT_CONFIG_COMMAND_RUN:
422 if (cfg->cmd_data.run.sources) {
423 g_ptr_array_free(cfg->cmd_data.run.sources, TRUE);
424 }
425
426 if (cfg->cmd_data.run.filters) {
427 g_ptr_array_free(cfg->cmd_data.run.filters, TRUE);
428 }
429
430 if (cfg->cmd_data.run.sinks) {
431 g_ptr_array_free(cfg->cmd_data.run.sinks, TRUE);
432 }
433
434 if (cfg->cmd_data.run.connections) {
435 g_ptr_array_free(cfg->cmd_data.run.connections,
436 TRUE);
437 }
438 break;
439 case BT_CONFIG_COMMAND_LIST_PLUGINS:
440 break;
441 case BT_CONFIG_COMMAND_HELP:
442 BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.help.cfg_component);
443 break;
444 case BT_CONFIG_COMMAND_QUERY:
445 BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.query.cfg_component);
446
447 if (cfg->cmd_data.query.object) {
448 g_string_free(cfg->cmd_data.query.object, TRUE);
449 }
450 break;
451 case BT_CONFIG_COMMAND_PRINT_CTF_METADATA:
452 if (cfg->cmd_data.print_ctf_metadata.path) {
453 g_string_free(cfg->cmd_data.print_ctf_metadata.path,
454 TRUE);
455 g_string_free(
456 cfg->cmd_data.print_ctf_metadata.output_path,
457 TRUE);
458 }
459 break;
460 case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS:
461 if (cfg->cmd_data.print_lttng_live_sessions.url) {
462 g_string_free(
463 cfg->cmd_data.print_lttng_live_sessions.url,
464 TRUE);
465 g_string_free(
466 cfg->cmd_data.print_lttng_live_sessions.output_path,
467 TRUE);
468 }
469 break;
470 default:
471 bt_common_abort();
472 }
473
474 g_free(cfg);
475
476 end:
477 return;
478 }
479
480 static
481 void destroy_glist_of_gstring(GList *list)
482 {
483 GList *at;
484
485 if (!list) {
486 return;
487 }
488
489 for (at = list; at; at = g_list_next(at)) {
490 g_string_free(at->data, TRUE);
491 }
492
493 g_list_free(list);
494 }
495
496 /*
497 * Creates a simple lexical scanner for parsing comma-delimited names
498 * and fields.
499 *
500 * Return value is owned by the caller.
501 */
502 static
503 GScanner *create_csv_identifiers_scanner(void)
504 {
505 GScanner *scanner;
506 GScannerConfig scanner_config = {
507 .cset_skip_characters = (gchar *) " \t\n",
508 .cset_identifier_first = (gchar *) G_CSET_a_2_z G_CSET_A_2_Z "_",
509 .cset_identifier_nth = (gchar *) G_CSET_a_2_z G_CSET_A_2_Z ":_-",
510 .case_sensitive = TRUE,
511 .cpair_comment_single = NULL,
512 .skip_comment_multi = TRUE,
513 .skip_comment_single = TRUE,
514 .scan_comment_multi = FALSE,
515 .scan_identifier = TRUE,
516 .scan_identifier_1char = TRUE,
517 .scan_identifier_NULL = FALSE,
518 .scan_symbols = FALSE,
519 .symbol_2_token = FALSE,
520 .scope_0_fallback = FALSE,
521 .scan_binary = FALSE,
522 .scan_octal = FALSE,
523 .scan_float = FALSE,
524 .scan_hex = FALSE,
525 .scan_hex_dollar = FALSE,
526 .numbers_2_int = FALSE,
527 .int_2_float = FALSE,
528 .store_int64 = FALSE,
529 .scan_string_sq = FALSE,
530 .scan_string_dq = FALSE,
531 .identifier_2_string = FALSE,
532 .char_2_token = TRUE,
533 };
534
535 scanner = g_scanner_new(&scanner_config);
536 if (!scanner) {
537 BT_CLI_LOGE_APPEND_CAUSE_OOM();
538 }
539
540 return scanner;
541 }
542
543 /*
544 * Converts a comma-delimited list of known names (--names option) to
545 * an array value object containing those names as string value objects.
546 *
547 * Return value is owned by the caller.
548 */
549 static
550 bt_value *names_from_arg(const char *arg)
551 {
552 GScanner *scanner = NULL;
553 bt_value *names = NULL;
554 bool found_all = false, found_none = false, found_item = false;
555
556 names = bt_value_array_create();
557 if (!names) {
558 BT_CLI_LOGE_APPEND_CAUSE_OOM();
559 goto error;
560 }
561
562 scanner = create_csv_identifiers_scanner();
563 if (!scanner) {
564 goto error;
565 }
566
567 g_scanner_input_text(scanner, arg, strlen(arg));
568
569 while (true) {
570 GTokenType token_type = g_scanner_get_next_token(scanner);
571
572 switch (token_type) {
573 case G_TOKEN_IDENTIFIER:
574 {
575 const char *identifier = scanner->value.v_identifier;
576
577 if (strcmp(identifier, "payload") == 0 ||
578 strcmp(identifier, "args") == 0 ||
579 strcmp(identifier, "arg") == 0) {
580 found_item = true;
581 if (bt_value_array_append_string_element(names,
582 "payload")) {
583 goto error;
584 }
585 } else if (strcmp(identifier, "context") == 0 ||
586 strcmp(identifier, "ctx") == 0) {
587 found_item = true;
588 if (bt_value_array_append_string_element(names,
589 "context")) {
590 goto error;
591 }
592 } else if (strcmp(identifier, "scope") == 0 ||
593 strcmp(identifier, "header") == 0) {
594 found_item = true;
595 if (bt_value_array_append_string_element(names,
596 identifier)) {
597 goto error;
598 }
599 } else if (strcmp(identifier, "all") == 0) {
600 found_all = true;
601 if (bt_value_array_append_string_element(names,
602 identifier)) {
603 goto error;
604 }
605 } else if (strcmp(identifier, "none") == 0) {
606 found_none = true;
607 if (bt_value_array_append_string_element(names,
608 identifier)) {
609 goto error;
610 }
611 } else {
612 BT_CLI_LOGE_APPEND_CAUSE("Unknown name: `%s`.",
613 identifier);
614 goto error;
615 }
616 break;
617 }
618 case G_TOKEN_COMMA:
619 continue;
620 case G_TOKEN_EOF:
621 goto end;
622 default:
623 goto error;
624 }
625 }
626
627 end:
628 if (found_none && found_all) {
629 BT_CLI_LOGE_APPEND_CAUSE("Only either `all` or `none` can be specified in the list given to the --names option, but not both.");
630 goto error;
631 }
632 /*
633 * Legacy behavior is to clear the defaults (show none) when at
634 * least one item is specified.
635 */
636 if (found_item && !found_none && !found_all) {
637 if (bt_value_array_append_string_element(names, "none")) {
638 goto error;
639 }
640 }
641 if (scanner) {
642 g_scanner_destroy(scanner);
643 }
644 return names;
645
646 error:
647 BT_VALUE_PUT_REF_AND_RESET(names);
648 if (scanner) {
649 g_scanner_destroy(scanner);
650 }
651 return names;
652 }
653
654 /*
655 * Converts a comma-delimited list of known fields (--fields option) to
656 * an array value object containing those fields as string
657 * value objects.
658 *
659 * Return value is owned by the caller.
660 */
661 static
662 bt_value *fields_from_arg(const char *arg)
663 {
664 GScanner *scanner = NULL;
665 bt_value *fields;
666
667 fields = bt_value_array_create();
668 if (!fields) {
669 BT_CLI_LOGE_APPEND_CAUSE_OOM();
670 goto error;
671 }
672
673 scanner = create_csv_identifiers_scanner();
674 if (!scanner) {
675 goto error;
676 }
677
678 g_scanner_input_text(scanner, arg, strlen(arg));
679
680 while (true) {
681 GTokenType token_type = g_scanner_get_next_token(scanner);
682
683 switch (token_type) {
684 case G_TOKEN_IDENTIFIER:
685 {
686 const char *identifier = scanner->value.v_identifier;
687
688 if (strcmp(identifier, "trace") == 0 ||
689 strcmp(identifier, "trace:hostname") == 0 ||
690 strcmp(identifier, "trace:domain") == 0 ||
691 strcmp(identifier, "trace:procname") == 0 ||
692 strcmp(identifier, "trace:vpid") == 0 ||
693 strcmp(identifier, "loglevel") == 0 ||
694 strcmp(identifier, "emf") == 0 ||
695 strcmp(identifier, "callsite") == 0 ||
696 strcmp(identifier, "all") == 0) {
697 if (bt_value_array_append_string_element(fields,
698 identifier)) {
699 goto error;
700 }
701 } else {
702 BT_CLI_LOGE_APPEND_CAUSE("Unknown field: `%s`.",
703 identifier);
704 goto error;
705 }
706 break;
707 }
708 case G_TOKEN_COMMA:
709 continue;
710 case G_TOKEN_EOF:
711 goto end;
712 default:
713 goto error;
714 }
715 }
716
717 goto end;
718
719 error:
720 BT_VALUE_PUT_REF_AND_RESET(fields);
721
722 end:
723 if (scanner) {
724 g_scanner_destroy(scanner);
725 }
726 return fields;
727 }
728
729 static
730 void append_param_arg(GString *params_arg, const char *key, const char *value)
731 {
732 BT_ASSERT(params_arg);
733 BT_ASSERT(key);
734 BT_ASSERT(value);
735
736 if (params_arg->len != 0) {
737 g_string_append_c(params_arg, ',');
738 }
739
740 g_string_append(params_arg, key);
741 g_string_append_c(params_arg, '=');
742 g_string_append(params_arg, value);
743 }
744
745 /*
746 * Inserts the equivalent "prefix-NAME=yes" strings into params_arg
747 * where the names are in names_array.
748 */
749 static
750 int insert_flat_params_from_array(GString *params_arg,
751 const bt_value *names_array, const char *prefix)
752 {
753 int ret = 0;
754 uint64_t i;
755 GString *tmpstr = NULL, *default_value = NULL;
756 bool default_set = false, non_default_set = false;
757
758 /*
759 * names_array may be NULL if no CLI options were specified to
760 * trigger its creation.
761 */
762 if (!names_array) {
763 goto end;
764 }
765
766 tmpstr = g_string_new(NULL);
767 if (!tmpstr) {
768 BT_CLI_LOGE_APPEND_CAUSE_OOM();
769 ret = -1;
770 goto end;
771 }
772
773 default_value = g_string_new(NULL);
774 if (!default_value) {
775 BT_CLI_LOGE_APPEND_CAUSE_OOM();
776 ret = -1;
777 goto end;
778 }
779
780 for (i = 0; i < bt_value_array_get_length(names_array); i++) {
781 const bt_value *str_obj =
782 bt_value_array_borrow_element_by_index_const(names_array,
783 i);
784 const char *suffix;
785 bool is_default = false;
786
787 suffix = bt_value_string_get(str_obj);
788
789 g_string_assign(tmpstr, prefix);
790 g_string_append(tmpstr, "-");
791
792 /* Special-case for "all" and "none". */
793 if (strcmp(suffix, "all") == 0) {
794 is_default = true;
795 g_string_assign(default_value, "show");
796 } else if (strcmp(suffix, "none") == 0) {
797 is_default = true;
798 g_string_assign(default_value, "hide");
799 }
800 if (is_default) {
801 default_set = true;
802 g_string_append(tmpstr, "default");
803 append_param_arg(params_arg, tmpstr->str,
804 default_value->str);
805 } else {
806 non_default_set = true;
807 g_string_append(tmpstr, suffix);
808 append_param_arg(params_arg, tmpstr->str, "yes");
809 }
810 }
811
812 /* Implicit field-default=hide if any non-default option is set. */
813 if (non_default_set && !default_set) {
814 g_string_assign(tmpstr, prefix);
815 g_string_append(tmpstr, "-default");
816 g_string_assign(default_value, "hide");
817 append_param_arg(params_arg, tmpstr->str, default_value->str);
818 }
819
820 end:
821 if (default_value) {
822 g_string_free(default_value, TRUE);
823 }
824
825 if (tmpstr) {
826 g_string_free(tmpstr, TRUE);
827 }
828
829 return ret;
830 }
831
832 /* argpar options */
833 enum {
834 OPT_NONE = 0,
835 OPT_BASE_PARAMS,
836 OPT_BEGIN,
837 OPT_CLOCK_CYCLES,
838 OPT_CLOCK_DATE,
839 OPT_CLOCK_FORCE_CORRELATE,
840 OPT_CLOCK_GMT,
841 OPT_CLOCK_OFFSET,
842 OPT_CLOCK_OFFSET_NS,
843 OPT_CLOCK_SECONDS,
844 OPT_COLOR,
845 OPT_COMPONENT,
846 OPT_CONNECT,
847 OPT_DEBUG,
848 OPT_DEBUG_INFO,
849 OPT_DEBUG_INFO_DIR,
850 OPT_DEBUG_INFO_FULL_PATH,
851 OPT_DEBUG_INFO_TARGET_PREFIX,
852 OPT_END,
853 OPT_FIELDS,
854 OPT_HELP,
855 OPT_INPUT_FORMAT,
856 OPT_LIST,
857 OPT_LOG_LEVEL,
858 OPT_NAMES,
859 OPT_NO_DELTA,
860 OPT_OMIT_HOME_PLUGIN_PATH,
861 OPT_OMIT_SYSTEM_PLUGIN_PATH,
862 OPT_OUTPUT,
863 OPT_OUTPUT_FORMAT,
864 OPT_PARAMS,
865 OPT_PLUGIN_PATH,
866 OPT_RESET_BASE_PARAMS,
867 OPT_RETRY_DURATION,
868 OPT_RUN_ARGS,
869 OPT_RUN_ARGS_0,
870 OPT_STREAM_INTERSECTION,
871 OPT_TIMERANGE,
872 OPT_VERBOSE,
873 OPT_VERSION,
874 };
875
876 enum bt_config_component_dest {
877 BT_CONFIG_COMPONENT_DEST_UNKNOWN = -1,
878 BT_CONFIG_COMPONENT_DEST_SOURCE,
879 BT_CONFIG_COMPONENT_DEST_FILTER,
880 BT_CONFIG_COMPONENT_DEST_SINK,
881 };
882
883 /*
884 * Adds a configuration component to the appropriate configuration
885 * array depending on the destination.
886 */
887 static
888 void add_run_cfg_comp(struct bt_config *cfg,
889 struct bt_config_component *cfg_comp,
890 enum bt_config_component_dest dest)
891 {
892 bt_object_get_ref(cfg_comp);
893
894 switch (dest) {
895 case BT_CONFIG_COMPONENT_DEST_SOURCE:
896 g_ptr_array_add(cfg->cmd_data.run.sources, cfg_comp);
897 break;
898 case BT_CONFIG_COMPONENT_DEST_FILTER:
899 g_ptr_array_add(cfg->cmd_data.run.filters, cfg_comp);
900 break;
901 case BT_CONFIG_COMPONENT_DEST_SINK:
902 g_ptr_array_add(cfg->cmd_data.run.sinks, cfg_comp);
903 break;
904 default:
905 bt_common_abort();
906 }
907 }
908
909 static
910 int add_run_cfg_comp_check_name(struct bt_config *cfg,
911 struct bt_config_component *cfg_comp,
912 enum bt_config_component_dest dest,
913 bt_value *instance_names)
914 {
915 int ret = 0;
916
917 if (cfg_comp->instance_name->len == 0) {
918 BT_CLI_LOGE_APPEND_CAUSE("Found an unnamed component.");
919 ret = -1;
920 goto end;
921 }
922
923 if (bt_value_map_has_entry(instance_names,
924 cfg_comp->instance_name->str)) {
925 BT_CLI_LOGE_APPEND_CAUSE("Duplicate component instance name:\n %s",
926 cfg_comp->instance_name->str);
927 ret = -1;
928 goto end;
929 }
930
931 if (bt_value_map_insert_entry(instance_names,
932 cfg_comp->instance_name->str, bt_value_null)) {
933 BT_CLI_LOGE_APPEND_CAUSE_OOM();
934 ret = -1;
935 goto end;
936 }
937
938 add_run_cfg_comp(cfg, cfg_comp, dest);
939
940 end:
941 return ret;
942 }
943
944 static
945 int append_env_var_plugin_paths(bt_value *plugin_paths)
946 {
947 int ret = 0;
948 const char *envvar;
949
950 if (bt_common_is_setuid_setgid()) {
951 BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary.");
952 goto end;
953 }
954
955 envvar = getenv("BABELTRACE_PLUGIN_PATH");
956 if (!envvar) {
957 goto end;
958 }
959
960 ret = bt_config_append_plugin_paths(plugin_paths, envvar);
961
962 end:
963 if (ret) {
964 BT_CLI_LOGE_APPEND_CAUSE("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH.");
965 }
966
967 return ret;
968 }
969
970 static
971 int append_home_and_system_plugin_paths(bt_value *plugin_paths,
972 bool omit_system_plugin_path, bool omit_home_plugin_path)
973 {
974 int ret;
975
976 if (!omit_home_plugin_path) {
977 if (bt_common_is_setuid_setgid()) {
978 BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary.");
979 } else {
980 char *home_plugin_dir = bt_common_get_home_plugin_path(
981 BT_LOG_OUTPUT_LEVEL);
982
983 if (home_plugin_dir) {
984 ret = bt_config_append_plugin_paths(
985 plugin_paths, home_plugin_dir);
986 free(home_plugin_dir);
987
988 if (ret) {
989 BT_CLI_LOGE_APPEND_CAUSE("Invalid home plugin path.");
990 goto error;
991 }
992 }
993 }
994 }
995
996 if (!omit_system_plugin_path) {
997 if (bt_config_append_plugin_paths(plugin_paths,
998 bt_common_get_system_plugin_path())) {
999 BT_CLI_LOGE_APPEND_CAUSE("Invalid system plugin path.");
1000 goto error;
1001 }
1002 }
1003 return 0;
1004 error:
1005 BT_CLI_LOGE_APPEND_CAUSE("Cannot append home and system plugin paths.");
1006 return -1;
1007 }
1008
1009 static
1010 struct bt_config *bt_config_base_create(enum bt_config_command command,
1011 const bt_value *plugin_paths, bool needs_plugins)
1012 {
1013 struct bt_config *cfg;
1014
1015 /* Create config */
1016 cfg = g_new0(struct bt_config, 1);
1017 if (!cfg) {
1018 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1019 goto error;
1020 }
1021
1022 bt_object_init_shared(&cfg->base, bt_config_destroy);
1023 cfg->command = command;
1024 cfg->command_needs_plugins = needs_plugins;
1025
1026 if (plugin_paths) {
1027 bt_value *plugin_paths_copy;
1028
1029 (void) bt_value_copy(plugin_paths,
1030 &plugin_paths_copy);
1031 cfg->plugin_paths = plugin_paths_copy;
1032 } else {
1033 cfg->plugin_paths = bt_value_array_create();
1034 if (!cfg->plugin_paths) {
1035 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1036 goto error;
1037 }
1038 }
1039
1040 goto end;
1041
1042 error:
1043 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1044
1045 end:
1046 return cfg;
1047 }
1048
1049 static
1050 struct bt_config *bt_config_run_create(const bt_value *plugin_paths)
1051 {
1052 struct bt_config *cfg;
1053
1054 /* Create config */
1055 cfg = bt_config_base_create(BT_CONFIG_COMMAND_RUN,
1056 plugin_paths, true);
1057 if (!cfg) {
1058 goto error;
1059 }
1060
1061 cfg->cmd_data.run.sources = g_ptr_array_new_with_free_func(
1062 (GDestroyNotify) bt_object_put_ref);
1063 if (!cfg->cmd_data.run.sources) {
1064 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1065 goto error;
1066 }
1067
1068 cfg->cmd_data.run.filters = g_ptr_array_new_with_free_func(
1069 (GDestroyNotify) bt_object_put_ref);
1070 if (!cfg->cmd_data.run.filters) {
1071 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1072 goto error;
1073 }
1074
1075 cfg->cmd_data.run.sinks = g_ptr_array_new_with_free_func(
1076 (GDestroyNotify) bt_object_put_ref);
1077 if (!cfg->cmd_data.run.sinks) {
1078 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1079 goto error;
1080 }
1081
1082 cfg->cmd_data.run.connections = g_ptr_array_new_with_free_func(
1083 (GDestroyNotify) bt_config_connection_destroy);
1084 if (!cfg->cmd_data.run.connections) {
1085 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1086 goto error;
1087 }
1088
1089 goto end;
1090
1091 error:
1092 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1093
1094 end:
1095 return cfg;
1096 }
1097
1098 static
1099 struct bt_config *bt_config_list_plugins_create(const bt_value *plugin_paths)
1100 {
1101 return bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS,
1102 plugin_paths, true);
1103 }
1104
1105 static
1106 struct bt_config *bt_config_help_create(const bt_value *plugin_paths,
1107 int default_log_level)
1108 {
1109 struct bt_config *cfg;
1110
1111 /* Create config */
1112 cfg = bt_config_base_create(BT_CONFIG_COMMAND_HELP,
1113 plugin_paths, true);
1114 if (!cfg) {
1115 goto error;
1116 }
1117
1118 cfg->cmd_data.help.cfg_component =
1119 bt_config_component_create(-1, NULL, NULL, default_log_level);
1120 if (!cfg->cmd_data.help.cfg_component) {
1121 goto error;
1122 }
1123
1124 goto end;
1125
1126 error:
1127 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1128
1129 end:
1130 return cfg;
1131 }
1132
1133 static
1134 struct bt_config *bt_config_query_create(const bt_value *plugin_paths)
1135 {
1136 struct bt_config *cfg;
1137
1138 /* Create config */
1139 cfg = bt_config_base_create(BT_CONFIG_COMMAND_QUERY,
1140 plugin_paths, true);
1141 if (!cfg) {
1142 goto error;
1143 }
1144
1145 cfg->cmd_data.query.object = g_string_new(NULL);
1146 if (!cfg->cmd_data.query.object) {
1147 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1148 goto error;
1149 }
1150
1151 goto end;
1152
1153 error:
1154 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1155
1156 end:
1157 return cfg;
1158 }
1159
1160 static
1161 enum bt_config_cli_args_status bt_config_print_ctf_metadata_create(
1162 const bt_value *plugin_paths, struct bt_config **cfg_out)
1163 {
1164 struct bt_config *cfg;
1165 enum bt_config_cli_args_status status;
1166
1167 /* Create config */
1168 cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_CTF_METADATA,
1169 plugin_paths, true);
1170 if (!cfg) {
1171 goto error;
1172 }
1173
1174 cfg->cmd_data.print_ctf_metadata.path = g_string_new(NULL);
1175 if (!cfg->cmd_data.print_ctf_metadata.path) {
1176 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1177 goto error;
1178 }
1179
1180 cfg->cmd_data.print_ctf_metadata.output_path = g_string_new(NULL);
1181 if (!cfg->cmd_data.print_ctf_metadata.output_path) {
1182 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1183 goto error;
1184 }
1185
1186 BT_OBJECT_MOVE_REF(*cfg_out, cfg);
1187 status = BT_CONFIG_CLI_ARGS_STATUS_OK;
1188 goto end;
1189
1190 error:
1191 status = BT_CONFIG_CLI_ARGS_STATUS_ERROR;
1192
1193 end:
1194 bt_object_put_ref(cfg);
1195 return status;
1196 }
1197
1198 static
1199 enum bt_config_cli_args_status bt_config_print_lttng_live_sessions_create(
1200 const bt_value *plugin_paths, struct bt_config **cfg_out)
1201 {
1202 struct bt_config *cfg;
1203 enum bt_config_cli_args_status status;
1204
1205 /* Create config */
1206 cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS,
1207 plugin_paths, true);
1208 if (!cfg) {
1209 goto error;
1210 }
1211
1212 cfg->cmd_data.print_lttng_live_sessions.url = g_string_new(NULL);
1213 if (!cfg->cmd_data.print_lttng_live_sessions.url) {
1214 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1215 goto error;
1216 }
1217
1218 cfg->cmd_data.print_lttng_live_sessions.output_path =
1219 g_string_new(NULL);
1220 if (!cfg->cmd_data.print_lttng_live_sessions.output_path) {
1221 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1222 goto error;
1223 }
1224
1225 BT_OBJECT_MOVE_REF(*cfg_out, cfg);
1226 status = BT_CONFIG_CLI_ARGS_STATUS_OK;
1227 goto end;
1228
1229 error:
1230 status = BT_CONFIG_CLI_ARGS_STATUS_ERROR;
1231
1232 end:
1233 bt_object_put_ref(cfg);
1234 return status;
1235 }
1236
1237 static
1238 int bt_config_append_plugin_paths_check_setuid_setgid(
1239 bt_value *plugin_paths, const char *arg)
1240 {
1241 int ret = 0;
1242
1243 if (bt_common_is_setuid_setgid()) {
1244 BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary.");
1245 goto end;
1246 }
1247
1248 if (bt_config_append_plugin_paths(plugin_paths, arg)) {
1249 BT_CLI_LOGE_APPEND_CAUSE("Invalid --plugin-path option's argument:\n %s",
1250 arg);
1251 ret = -1;
1252 goto end;
1253 }
1254
1255 end:
1256 return ret;
1257 }
1258
1259 /*
1260 * Prints the expected format for a --params option.
1261 */
1262 static
1263 void print_expected_params_format(FILE *fp)
1264 {
1265 fprintf(fp, "Expected format of PARAMS\n");
1266 fprintf(fp, "-------------------------\n");
1267 fprintf(fp, "\n");
1268 fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n");
1269 fprintf(fp, "\n");
1270 fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n");
1271 fprintf(fp, "where PARAM is the parameter name (C identifier plus the [:.-] characters),\n");
1272 fprintf(fp, "and VALUE can be one of:\n");
1273 fprintf(fp, "\n");
1274 fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n");
1275 fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n");
1276 fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n");
1277 fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n");
1278 fprintf(fp, " (`0x` prefix) unsigned (with `+` prefix) or signed 64-bit integer.\n");
1279 fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n");
1280 fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n");
1281 fprintf(fp, " the null and boolean value symbols above.\n");
1282 fprintf(fp, "* Double-quoted string (accepts escape characters).\n");
1283 fprintf(fp, "* Array, formatted as an opening `[`, a list of comma-separated values\n");
1284 fprintf(fp, " (as described by the current list) and a closing `]`.\n");
1285 fprintf(fp, "* Map, formatted as an opening `{`, a comma-separated list of PARAM=VALUE\n");
1286 fprintf(fp, " assignments and a closing `}`.\n");
1287 fprintf(fp, "\n");
1288 fprintf(fp, "You can put whitespaces allowed around individual `=` and `,` symbols.\n");
1289 fprintf(fp, "\n");
1290 fprintf(fp, "Example:\n");
1291 fprintf(fp, "\n");
1292 fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n");
1293 fprintf(fp, " play=+23, observe=3.14, simple=beef, needs-quotes=\"some string\",\n");
1294 fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\",\n");
1295 fprintf(fp, " things=[1, \"2\", 3]\n");
1296 fprintf(fp, "\n");
1297 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n");
1298 fprintf(fp, "babeltrace2 from a shell.\n");
1299 }
1300
1301 /*
1302 * Given argpar error status `status` and error `error`, return a formatted
1303 * error message describing the error.
1304 *
1305 * `argv` is the argument vector that was being parsed.
1306 *
1307 * `prefix_fmt` (formatted using the following arguments) is prepended to
1308 * the error message.
1309 *
1310 * The returned string must be freed by the caller.
1311 */
1312 static
1313 GString *__BT_ATTR_FORMAT_PRINTF(4, 5) format_arg_error(
1314 const struct argpar_error *error,
1315 const char **argv, unsigned int arg_index_offset,
1316 const char *prefix_fmt, ...)
1317 {
1318 GString *str = g_string_new(NULL);
1319 va_list args;
1320
1321 va_start(args, prefix_fmt);
1322 g_string_append_vprintf(str, prefix_fmt, args);
1323 va_end(args);
1324
1325 g_string_append(str, ": ");
1326
1327 switch (argpar_error_type(error))
1328 {
1329 case ARGPAR_ERROR_TYPE_MISSING_OPT_ARG:
1330 {
1331 bool is_short;
1332 const struct argpar_opt_descr *descr =
1333 argpar_error_opt_descr(error, &is_short);
1334 int orig_index = argpar_error_orig_index(error);
1335 const char *arg = argv[orig_index];
1336
1337 if (is_short) {
1338 g_string_append_printf(
1339 str,
1340 WHILE_PARSING_ARG_N_FMT "Missing required argument for option `-%c`",
1341 arg_index_offset + orig_index + 1, arg, descr->short_name);
1342 } else {
1343 g_string_append_printf(
1344 str,
1345 WHILE_PARSING_ARG_N_FMT "Missing required argument for option `--%s`",
1346 arg_index_offset + orig_index + 1, arg, descr->long_name);
1347 }
1348
1349 break;
1350 }
1351 case ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG:
1352 {
1353 bool is_short;
1354 const struct argpar_opt_descr *descr =
1355 argpar_error_opt_descr(error, &is_short);
1356 int orig_index = argpar_error_orig_index(error);
1357 const char *arg = argv[orig_index];
1358
1359 if (is_short) {
1360 g_string_append_printf(
1361 str,
1362 WHILE_PARSING_ARG_N_FMT "Unexpected argument for option `-%c`",
1363 arg_index_offset + orig_index + 1, arg, descr->short_name);
1364 } else {
1365 g_string_append_printf(
1366 str,
1367 WHILE_PARSING_ARG_N_FMT "Unexpected argument for option `--%s`",
1368 arg_index_offset + orig_index + 1, arg, descr->long_name);
1369 }
1370
1371 break;
1372 }
1373 case ARGPAR_ERROR_TYPE_UNKNOWN_OPT:
1374 {
1375 int orig_index = argpar_error_orig_index(error);
1376 const char *unknown_opt = argpar_error_unknown_opt_name(error);
1377 const char *arg = argv[orig_index];
1378
1379 g_string_append_printf(
1380 str,
1381 WHILE_PARSING_ARG_N_FMT "Unknown option `%s`",
1382 arg_index_offset + orig_index + 1, arg, unknown_opt);
1383
1384 break;
1385 }
1386
1387 default:
1388 BT_ASSERT(0);
1389 }
1390
1391 return str;
1392 }
1393
1394 enum parse_next_item_status
1395 {
1396 PARSE_NEXT_ITEM_STATUS_OK = 0,
1397 PARSE_NEXT_ITEM_STATUS_END = 1,
1398 PARSE_NEXT_ITEM_STATUS_ERROR = -1,
1399 };
1400
1401 /*
1402 * Parse the next item using `iter`. Log and append an error if necessary.
1403 *
1404 * The item in `*item` on entry is freed, and the new item is also
1405 * returned in `*item`.
1406 */
1407 static
1408 enum parse_next_item_status parse_next_item(struct argpar_iter *iter,
1409 const struct argpar_item **item, const char **argv,
1410 const char *command, unsigned int consumed_args)
1411 {
1412 enum argpar_iter_next_status status;
1413 const struct argpar_error *error = NULL;
1414 enum parse_next_item_status ret;
1415
1416 ARGPAR_ITEM_DESTROY_AND_RESET(*item);
1417 status = argpar_iter_next(iter, item, &error);
1418
1419 switch (status) {
1420 case ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY:
1421 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1422 ret = PARSE_NEXT_ITEM_STATUS_ERROR;
1423 break;
1424 case ARGPAR_ITER_NEXT_STATUS_ERROR:
1425 {
1426 GString *err_str = format_arg_error(error, argv,
1427 consumed_args,
1428 "While parsing `%s` command's command-line arguments",
1429 command);
1430 BT_CLI_LOGE_APPEND_CAUSE("%s", err_str->str);
1431 g_string_free(err_str, TRUE);
1432 ret = PARSE_NEXT_ITEM_STATUS_ERROR;
1433 break;
1434 }
1435 case ARGPAR_ITER_NEXT_STATUS_END:
1436 ret = PARSE_NEXT_ITEM_STATUS_END;
1437 break;
1438 case ARGPAR_ITER_NEXT_STATUS_OK:
1439 ret = PARSE_NEXT_ITEM_STATUS_OK;
1440 break;
1441 default:
1442 bt_common_abort();
1443 }
1444
1445 argpar_error_destroy(error);
1446 return ret;
1447 }
1448
1449
1450 /*
1451 * Prints the help command usage.
1452 */
1453 static
1454 void print_help_usage(FILE *fp)
1455 {
1456 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] help [OPTIONS] PLUGIN\n");
1457 fprintf(fp, " babeltrace2 [GENERAL OPTIONS] help [OPTIONS] TYPE.PLUGIN.CLS\n");
1458 fprintf(fp, "\n");
1459 fprintf(fp, "Options:\n");
1460 fprintf(fp, "\n");
1461 fprintf(fp, " -h, --help Show this help and quit\n");
1462 fprintf(fp, "\n");
1463 fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n");
1464 fprintf(fp, "\n");
1465 fprintf(fp, "Use `babeltrace2 list-plugins` to show the list of available plugins.\n");
1466 }
1467
1468 static
1469 const struct argpar_opt_descr help_options[] = {
1470 /* id, short_name, long_name, with_arg */
1471 { OPT_HELP, 'h', "help", false },
1472 ARGPAR_OPT_DESCR_SENTINEL
1473 };
1474
1475 /*
1476 * Creates a Babeltrace config object from the arguments of a help
1477 * command.
1478 */
1479 static
1480 enum bt_config_cli_args_status bt_config_help_from_args(int argc,
1481 const char *argv[], struct bt_config **cfg_out,
1482 const bt_value *plugin_paths, int default_log_level,
1483 unsigned int consumed_args)
1484 {
1485 enum bt_config_cli_args_status status;
1486 struct bt_config *cfg;
1487 const char *plugin_comp_cls_arg = NULL;
1488 char *plugin_name = NULL, *comp_cls_name = NULL;
1489 GString *substring = NULL;
1490 size_t end_pos;
1491 struct argpar_iter *argpar_iter = NULL;
1492 const struct argpar_item *argpar_item = NULL;
1493
1494 cfg = bt_config_help_create(plugin_paths, default_log_level);
1495 if (!cfg) {
1496 goto error;
1497 }
1498
1499 argpar_iter = argpar_iter_create(argc, argv, help_options);
1500 if (!argpar_iter) {
1501 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1502 goto error;
1503 }
1504
1505 while (true) {
1506 enum parse_next_item_status parse_status =
1507 parse_next_item(argpar_iter, &argpar_item, argv, "help",
1508 consumed_args);
1509
1510 if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) {
1511 goto error;
1512 } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) {
1513 break;
1514 }
1515
1516 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
1517 const struct argpar_opt_descr *opt_descr =
1518 argpar_item_opt_descr(argpar_item);
1519
1520 switch (opt_descr->id) {
1521 case OPT_HELP:
1522 print_help_usage(stdout);
1523 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
1524 goto end;
1525 default:
1526 bt_common_abort();
1527 }
1528 } else {
1529 const char *arg = argpar_item_non_opt_arg(argpar_item);
1530
1531 if (plugin_comp_cls_arg) {
1532 BT_CLI_LOGE_APPEND_CAUSE(
1533 "Extraneous command-line argument specified to `help` command: `%s`.",
1534 arg);
1535 goto error;
1536 }
1537
1538 plugin_comp_cls_arg = arg;
1539 }
1540 }
1541
1542 if (!plugin_comp_cls_arg) {
1543 BT_CLI_LOGE_APPEND_CAUSE(
1544 "Missing plugin name or component class descriptor.");
1545 goto error;
1546 }
1547
1548 /* Look for unescaped dots in the argument. */
1549 substring = bt_common_string_until(plugin_comp_cls_arg, ".\\", ".", &end_pos);
1550 if (!substring) {
1551 BT_CLI_LOGE_APPEND_CAUSE("Could not consume argument: arg=%s",
1552 plugin_comp_cls_arg);
1553 goto error;
1554 }
1555
1556 if (end_pos == strlen(plugin_comp_cls_arg)) {
1557 /* Didn't find an unescaped dot, treat it as a plugin name. */
1558 g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name,
1559 plugin_comp_cls_arg);
1560 } else {
1561 /*
1562 * Found an unescaped dot, treat it as a component class name.
1563 */
1564 plugin_comp_cls_names(plugin_comp_cls_arg, NULL, &plugin_name, &comp_cls_name,
1565 &cfg->cmd_data.help.cfg_component->type);
1566 if (!plugin_name || !comp_cls_name) {
1567 BT_CLI_LOGE_APPEND_CAUSE(
1568 "Could not parse argument as a component class name: arg=%s",
1569 plugin_comp_cls_arg);
1570 goto error;
1571 }
1572
1573 g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name,
1574 plugin_name);
1575 g_string_assign(cfg->cmd_data.help.cfg_component->comp_cls_name,
1576 comp_cls_name);
1577 }
1578
1579 BT_OBJECT_MOVE_REF(*cfg_out, cfg);
1580 status = BT_CONFIG_CLI_ARGS_STATUS_OK;
1581 goto end;
1582
1583 error:
1584 status = BT_CONFIG_CLI_ARGS_STATUS_ERROR;
1585
1586 end:
1587 g_free(plugin_name);
1588 g_free(comp_cls_name);
1589
1590 if (substring) {
1591 g_string_free(substring, TRUE);
1592 }
1593
1594 argpar_iter_destroy(argpar_iter);
1595 argpar_item_destroy(argpar_item);
1596 bt_object_put_ref(cfg);
1597
1598 return status;
1599 }
1600
1601 /*
1602 * Prints the help command usage.
1603 */
1604 static
1605 void print_query_usage(FILE *fp)
1606 {
1607 fprintf(fp, "Usage: babeltrace2 [GEN OPTS] query [OPTS] TYPE.PLUGIN.CLS OBJECT\n");
1608 fprintf(fp, "\n");
1609 fprintf(fp, "Options:\n");
1610 fprintf(fp, "\n");
1611 fprintf(fp, " -p, --params=PARAMS Set the query parameters to PARAMS (see the expected\n");
1612 fprintf(fp, " format of PARAMS below)\n");
1613 fprintf(fp, " -h, --help Show this help and quit\n");
1614 fprintf(fp, "\n\n");
1615 print_expected_params_format(fp);
1616 }
1617
1618 static
1619 const struct argpar_opt_descr query_options[] = {
1620 /* id, short_name, long_name, with_arg */
1621 { OPT_HELP, 'h', "help", false },
1622 { OPT_PARAMS, 'p', "params", true },
1623 ARGPAR_OPT_DESCR_SENTINEL
1624 };
1625
1626 /*
1627 * Creates a Babeltrace config object from the arguments of a query
1628 * command.
1629 */
1630 static
1631 enum bt_config_cli_args_status bt_config_query_from_args(int argc,
1632 const char *argv[], struct bt_config **cfg_out,
1633 const bt_value *plugin_paths, int default_log_level,
1634 unsigned int consumed_args)
1635 {
1636 enum bt_config_cli_args_status status;
1637 struct bt_config *cfg = NULL;
1638 const char *component_class_spec = NULL;
1639 const char *query_object = NULL;
1640 GString *error_str = NULL;
1641 struct argpar_iter *argpar_iter = NULL;
1642 const struct argpar_item *argpar_item = NULL;
1643
1644 bt_value *params = bt_value_map_create();
1645 if (!params) {
1646 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1647 goto error;
1648 }
1649
1650 cfg = bt_config_query_create(plugin_paths);
1651 if (!cfg) {
1652 goto error;
1653 }
1654
1655 error_str = g_string_new(NULL);
1656 if (!error_str) {
1657 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1658 goto error;
1659 }
1660
1661 argpar_iter = argpar_iter_create(argc, argv, query_options);
1662 if (!argpar_iter) {
1663 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1664 goto error;
1665 }
1666
1667 while (true) {
1668 enum parse_next_item_status parse_status =
1669 parse_next_item(argpar_iter, &argpar_item, argv, "query",
1670 consumed_args);
1671
1672 if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) {
1673 goto error;
1674 } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) {
1675 break;
1676 }
1677
1678 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
1679 const struct argpar_opt_descr *opt_descr =
1680 argpar_item_opt_descr(argpar_item);
1681 const char *arg = argpar_item_opt_arg(argpar_item);
1682
1683 switch (opt_descr->id) {
1684 case OPT_HELP:
1685 print_query_usage(stdout);
1686 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
1687 goto end;
1688 case OPT_PARAMS:
1689 {
1690 bt_value *parsed_params = bt_param_parse(arg, error_str);
1691 bt_value_map_extend_status extend_status;
1692 if (!parsed_params) {
1693 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --params option's argument:\n %s",
1694 error_str->str);
1695 goto error;
1696 }
1697
1698 extend_status = bt_value_map_extend(params, parsed_params);
1699 BT_VALUE_PUT_REF_AND_RESET(parsed_params);
1700 if (extend_status) {
1701 BT_CLI_LOGE_APPEND_CAUSE("Cannot extend current parameters with --params option's argument:\n %s",
1702 arg);
1703 goto error;
1704 }
1705 break;
1706 }
1707 default:
1708 bt_common_abort();
1709 }
1710 } else {
1711 const char *arg = argpar_item_non_opt_arg(argpar_item);
1712
1713 /*
1714 * We need exactly two non-option arguments
1715 * which are the mandatory component class
1716 * specification and query object.
1717 */
1718 if (!component_class_spec) {
1719 component_class_spec = arg;
1720 } else if (!query_object) {
1721 query_object = arg;
1722 } else {
1723 BT_CLI_LOGE_APPEND_CAUSE("Extraneous command-line argument specified to `query` command: `%s`.",
1724 arg);
1725 goto error;
1726 }
1727 }
1728 }
1729
1730 if (!component_class_spec || !query_object) {
1731 print_query_usage(stdout);
1732 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
1733 goto end;
1734 }
1735
1736 cfg->cmd_data.query.cfg_component =
1737 bt_config_component_from_arg(component_class_spec,
1738 default_log_level);
1739 if (!cfg->cmd_data.query.cfg_component) {
1740 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for component class specification:\n %s",
1741 component_class_spec);
1742 goto error;
1743 }
1744
1745 BT_ASSERT(params);
1746 BT_OBJECT_MOVE_REF(cfg->cmd_data.query.cfg_component->params, params);
1747
1748 if (strlen(query_object) == 0) {
1749 BT_CLI_LOGE_APPEND_CAUSE("Invalid empty object.");
1750 goto error;
1751 }
1752
1753 g_string_assign(cfg->cmd_data.query.object, query_object);
1754 BT_OBJECT_MOVE_REF(*cfg_out, cfg);
1755 status = BT_CONFIG_CLI_ARGS_STATUS_OK;
1756 goto end;
1757
1758 error:
1759 status = BT_CONFIG_CLI_ARGS_STATUS_ERROR;
1760
1761 end:
1762 argpar_iter_destroy(argpar_iter);
1763 argpar_item_destroy(argpar_item);
1764
1765 if (error_str) {
1766 g_string_free(error_str, TRUE);
1767 }
1768
1769 bt_value_put_ref(params);
1770 bt_object_put_ref(cfg);
1771
1772 return status;
1773 }
1774
1775 /*
1776 * Prints the list-plugins command usage.
1777 */
1778 static
1779 void print_list_plugins_usage(FILE *fp)
1780 {
1781 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] list-plugins [OPTIONS]\n");
1782 fprintf(fp, "\n");
1783 fprintf(fp, "Options:\n");
1784 fprintf(fp, "\n");
1785 fprintf(fp, " -h, --help Show this help and quit\n");
1786 fprintf(fp, "\n");
1787 fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n");
1788 fprintf(fp, "\n");
1789 fprintf(fp, "Use `babeltrace2 help` to get help for a specific plugin or component class.\n");
1790 }
1791
1792 static
1793 const struct argpar_opt_descr list_plugins_options[] = {
1794 /* id, short_name, long_name, with_arg */
1795 { OPT_HELP, 'h', "help", false },
1796 ARGPAR_OPT_DESCR_SENTINEL
1797 };
1798
1799 /*
1800 * Creates a Babeltrace config object from the arguments of a
1801 * list-plugins command.
1802 */
1803 static
1804 enum bt_config_cli_args_status bt_config_list_plugins_from_args(int argc,
1805 const char *argv[], struct bt_config **cfg_out,
1806 const bt_value *plugin_paths, unsigned int consumed_args)
1807 {
1808 enum bt_config_cli_args_status status;
1809 struct bt_config *cfg = NULL;
1810 struct argpar_iter *argpar_iter = NULL;
1811 const struct argpar_item *argpar_item = NULL;
1812
1813 cfg = bt_config_list_plugins_create(plugin_paths);
1814 if (!cfg) {
1815 goto error;
1816 }
1817
1818 argpar_iter = argpar_iter_create(argc, argv, list_plugins_options);
1819 if (!argpar_iter) {
1820 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1821 goto error;
1822 }
1823
1824 while (true) {
1825 enum parse_next_item_status parse_status =
1826 parse_next_item(argpar_iter, &argpar_item, argv, "list-plugins",
1827 consumed_args);
1828
1829 if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) {
1830 goto error;
1831 } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) {
1832 break;
1833 }
1834
1835 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
1836 const struct argpar_opt_descr *opt_descr =
1837 argpar_item_opt_descr(argpar_item);
1838
1839 switch (opt_descr->id) {
1840 case OPT_HELP:
1841 print_list_plugins_usage(stdout);
1842 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
1843 goto end;
1844 default:
1845 bt_common_abort();
1846 }
1847 } else {
1848 BT_CLI_LOGE_APPEND_CAUSE(
1849 "Extraneous command-line argument specified to `list-plugins` command: `%s`.",
1850 argpar_item_non_opt_arg(argpar_item));
1851 goto error;
1852 }
1853 }
1854
1855 BT_OBJECT_MOVE_REF(*cfg_out, cfg);
1856 status = BT_CONFIG_CLI_ARGS_STATUS_OK;
1857 goto end;
1858
1859 error:
1860 status = BT_CONFIG_CLI_ARGS_STATUS_ERROR;
1861
1862 end:
1863 argpar_iter_destroy(argpar_iter);
1864 argpar_item_destroy(argpar_item);
1865 bt_object_put_ref(cfg);
1866
1867 return status;
1868 }
1869
1870 /*
1871 * Prints the run command usage.
1872 */
1873 static
1874 void print_run_usage(FILE *fp)
1875 {
1876 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] run [OPTIONS]\n");
1877 fprintf(fp, "\n");
1878 fprintf(fp, "Options:\n");
1879 fprintf(fp, "\n");
1880 fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n");
1881 fprintf(fp, " for all the following components until\n");
1882 fprintf(fp, " --reset-base-params is encountered\n");
1883 fprintf(fp, " (see the expected format of PARAMS below)\n");
1884 fprintf(fp, " -c, --component=NAME:TYPE.PLUGIN.CLS\n");
1885 fprintf(fp, " Instantiate the component class CLS of type\n");
1886 fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n");
1887 fprintf(fp, " in the plugin PLUGIN, add it to the graph,\n");
1888 fprintf(fp, " and name it NAME");
1889 fprintf(fp, " -x, --connect=CONNECTION Connect two created components (see the\n");
1890 fprintf(fp, " expected format of CONNECTION below)\n");
1891 fprintf(fp, " -l, --log-level=LVL Set the log level of the current component to LVL\n");
1892 fprintf(fp, " (`N`, `T`, `D`, `I`, `W`, `E`, or `F`)\n");
1893 fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n");
1894 fprintf(fp, " current component (see the expected format\n");
1895 fprintf(fp, " of PARAMS below)\n");
1896 fprintf(fp, " -r, --reset-base-params Reset the current base parameters to an\n");
1897 fprintf(fp, " empty map\n");
1898 fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n");
1899 fprintf(fp, " the graph later, retry in DUR µs\n");
1900 fprintf(fp, " (default: 100000)\n");
1901 fprintf(fp, " -h, --help Show this help and quit\n");
1902 fprintf(fp, "\n");
1903 fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n");
1904 fprintf(fp, "\n\n");
1905 fprintf(fp, "Expected format of CONNECTION\n");
1906 fprintf(fp, "-----------------------------\n");
1907 fprintf(fp, "\n");
1908 fprintf(fp, " UPSTREAM[.UPSTREAM-PORT]:DOWNSTREAM[.DOWNSTREAM-PORT]\n");
1909 fprintf(fp, "\n");
1910 fprintf(fp, "UPSTREAM and DOWNSTREAM are names of the upstream and downstream\n");
1911 fprintf(fp, "components to connect together. You must escape the following characters\n\n");
1912 fprintf(fp, "with `\\`: `\\`, `.`, and `:`. You must set the name of the current\n");
1913 fprintf(fp, "component using the NAME prefix of the --component option.\n");
1914 fprintf(fp, "\n");
1915 fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are optional globbing patterns to\n");
1916 fprintf(fp, "identify the upstream and downstream ports to use for the connection.\n");
1917 fprintf(fp, "When the port is not specified, `*` is used.\n");
1918 fprintf(fp, "\n");
1919 fprintf(fp, "When a component named UPSTREAM has an available port which matches the\n");
1920 fprintf(fp, "UPSTREAM-PORT globbing pattern, it is connected to the first port which\n");
1921 fprintf(fp, "matches the DOWNSTREAM-PORT globbing pattern of the component named\n");
1922 fprintf(fp, "DOWNSTREAM.\n");
1923 fprintf(fp, "\n");
1924 fprintf(fp, "The only special character in UPSTREAM-PORT and DOWNSTREAM-PORT is `*`\n");
1925 fprintf(fp, "which matches anything. You must escape the following characters\n");
1926 fprintf(fp, "with `\\`: `\\`, `*`, `?`, `[`, `.`, and `:`.\n");
1927 fprintf(fp, "\n");
1928 fprintf(fp, "You can connect a source component to a filter or sink component. You\n");
1929 fprintf(fp, "can connect a filter component to a sink component.\n");
1930 fprintf(fp, "\n");
1931 fprintf(fp, "Examples:\n");
1932 fprintf(fp, "\n");
1933 fprintf(fp, " my-src:my-sink\n");
1934 fprintf(fp, " ctf-fs.*stream*:utils-muxer:*\n");
1935 fprintf(fp, "\n");
1936 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n");
1937 fprintf(fp, "babeltrace2 from a shell.\n");
1938 fprintf(fp, "\n\n");
1939 print_expected_params_format(fp);
1940 }
1941
1942 /*
1943 * Creates a Babeltrace config object from the arguments of a run
1944 * command.
1945 */
1946 static
1947 enum bt_config_cli_args_status bt_config_run_from_args(int argc, const char *argv[],
1948 struct bt_config **cfg_out, const bt_value *plugin_paths,
1949 int default_log_level, unsigned int consumed_args)
1950 {
1951 enum bt_config_cli_args_status status;
1952 struct bt_config_component *cur_cfg_comp = NULL;
1953 bt_value *cur_base_params = NULL;
1954 int ret = 0;
1955 struct bt_config *cfg = NULL;
1956 bt_value *instance_names = NULL;
1957 bt_value *connection_args = NULL;
1958 char error_buf[256] = { 0 };
1959 long retry_duration = -1;
1960 bt_value_map_extend_status extend_status;
1961 GString *error_str = NULL;
1962 struct argpar_iter *argpar_iter = NULL;
1963 const struct argpar_item *argpar_item = NULL;
1964
1965 static const struct argpar_opt_descr run_options[] = {
1966 { OPT_BASE_PARAMS, 'b', "base-params", true },
1967 { OPT_COMPONENT, 'c', "component", true },
1968 { OPT_CONNECT, 'x', "connect", true },
1969 { OPT_HELP, 'h', "help", false },
1970 { OPT_LOG_LEVEL, 'l', "log-level", true },
1971 { OPT_PARAMS, 'p', "params", true },
1972 { OPT_RESET_BASE_PARAMS, 'r', "reset-base-params", false },
1973 { OPT_RETRY_DURATION, '\0', "retry-duration", true },
1974 ARGPAR_OPT_DESCR_SENTINEL
1975 };
1976
1977 error_str = g_string_new(NULL);
1978 if (!error_str) {
1979 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1980 goto error;
1981 }
1982
1983 if (argc < 1) {
1984 print_run_usage(stdout);
1985 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
1986 goto end;
1987 }
1988
1989 cfg = bt_config_run_create(plugin_paths);
1990 if (!cfg) {
1991 goto error;
1992 }
1993
1994 cfg->cmd_data.run.retry_duration_us = 100000;
1995 cur_base_params = bt_value_map_create();
1996 if (!cur_base_params) {
1997 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1998 goto error;
1999 }
2000
2001 instance_names = bt_value_map_create();
2002 if (!instance_names) {
2003 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2004 goto error;
2005 }
2006
2007 connection_args = bt_value_array_create();
2008 if (!connection_args) {
2009 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2010 goto error;
2011 }
2012
2013 argpar_iter = argpar_iter_create(argc, argv, run_options);
2014 if (!argpar_iter) {
2015 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2016 goto error;
2017 }
2018
2019 while (true) {
2020 enum parse_next_item_status parse_status;
2021 const struct argpar_opt_descr *opt_descr;
2022 const char *arg;
2023
2024 parse_status = parse_next_item(argpar_iter, &argpar_item, argv, "run",
2025 consumed_args);
2026 if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) {
2027 goto error;
2028 } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) {
2029 break;
2030 }
2031
2032 /* This command does not accept non-option arguments.*/
2033 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_NON_OPT) {
2034 BT_CLI_LOGE_APPEND_CAUSE("Unexpected argument: `%s`",
2035 argpar_item_non_opt_arg(argpar_item));
2036 goto error;
2037 }
2038
2039 opt_descr = argpar_item_opt_descr(argpar_item);
2040 arg = argpar_item_opt_arg(argpar_item);
2041
2042 switch (opt_descr->id) {
2043 case OPT_HELP:
2044 print_run_usage(stdout);
2045 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
2046 goto end;
2047 case OPT_COMPONENT:
2048 {
2049 enum bt_config_component_dest dest;
2050
2051 BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp);
2052 cur_cfg_comp = bt_config_component_from_arg(arg,
2053 default_log_level);
2054 if (!cur_cfg_comp) {
2055 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --component option's argument:\n %s",
2056 arg);
2057 goto error;
2058 }
2059
2060 switch (cur_cfg_comp->type) {
2061 case BT_COMPONENT_CLASS_TYPE_SOURCE:
2062 dest = BT_CONFIG_COMPONENT_DEST_SOURCE;
2063 break;
2064 case BT_COMPONENT_CLASS_TYPE_FILTER:
2065 dest = BT_CONFIG_COMPONENT_DEST_FILTER;
2066 break;
2067 case BT_COMPONENT_CLASS_TYPE_SINK:
2068 dest = BT_CONFIG_COMPONENT_DEST_SINK;
2069 break;
2070 default:
2071 bt_common_abort();
2072 }
2073
2074 BT_ASSERT(cur_base_params);
2075 bt_value_put_ref(cur_cfg_comp->params);
2076 if (bt_value_copy(cur_base_params,
2077 &cur_cfg_comp->params) < 0) {
2078 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2079 goto error;
2080 }
2081
2082 ret = add_run_cfg_comp_check_name(cfg,
2083 cur_cfg_comp, dest,
2084 instance_names);
2085 if (ret) {
2086 goto error;
2087 }
2088
2089 break;
2090 }
2091 case OPT_PARAMS:
2092 {
2093 bt_value *params;
2094
2095 if (!cur_cfg_comp) {
2096 BT_CLI_LOGE_APPEND_CAUSE("Cannot add parameters to unavailable component:\n %s",
2097 arg);
2098 goto error;
2099 }
2100
2101 params = bt_param_parse(arg, error_str);
2102 if (!params) {
2103 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --params option's argument:\n %s",
2104 error_str->str);
2105 goto error;
2106 }
2107
2108 extend_status = bt_value_map_extend(cur_cfg_comp->params,
2109 params);
2110 BT_VALUE_PUT_REF_AND_RESET(params);
2111 if (extend_status != BT_VALUE_MAP_EXTEND_STATUS_OK) {
2112 BT_CLI_LOGE_APPEND_CAUSE("Cannot extend current component parameters with --params option's argument:\n %s",
2113 arg);
2114 goto error;
2115 }
2116
2117 break;
2118 }
2119 case OPT_LOG_LEVEL:
2120 if (!cur_cfg_comp) {
2121 BT_CLI_LOGE_APPEND_CAUSE("Cannot set the log level of unavailable component:\n %s",
2122 arg);
2123 goto error;
2124 }
2125
2126 cur_cfg_comp->log_level =
2127 bt_log_get_level_from_string(arg);
2128 if (cur_cfg_comp->log_level < 0) {
2129 BT_CLI_LOGE_APPEND_CAUSE("Invalid argument for --log-level option:\n %s",
2130 arg);
2131 goto error;
2132 }
2133 break;
2134 case OPT_BASE_PARAMS:
2135 {
2136 bt_value *params = bt_param_parse(arg, error_str);
2137
2138 if (!params) {
2139 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --base-params option's argument:\n %s",
2140 error_str->str);
2141 goto error;
2142 }
2143
2144 BT_OBJECT_MOVE_REF(cur_base_params, params);
2145 break;
2146 }
2147 case OPT_RESET_BASE_PARAMS:
2148 BT_VALUE_PUT_REF_AND_RESET(cur_base_params);
2149 cur_base_params = bt_value_map_create();
2150 if (!cur_base_params) {
2151 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2152 goto error;
2153 }
2154 break;
2155 case OPT_CONNECT:
2156 if (bt_value_array_append_string_element(
2157 connection_args, arg)) {
2158 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2159 goto error;
2160 }
2161 break;
2162 case OPT_RETRY_DURATION: {
2163 gchar *end;
2164 size_t arg_len = strlen(arg);
2165
2166 retry_duration = g_ascii_strtoll(arg, &end, 10);
2167
2168 if (arg_len == 0 || end != (arg + arg_len)) {
2169 BT_CLI_LOGE_APPEND_CAUSE(
2170 "Could not parse --retry-duration option's argument as an unsigned integer: `%s`",
2171 arg);
2172 goto error;
2173 }
2174
2175 if (retry_duration < 0) {
2176 BT_CLI_LOGE_APPEND_CAUSE("--retry-duration option's argument must be positive or 0: %ld",
2177 retry_duration);
2178 goto error;
2179 }
2180
2181 cfg->cmd_data.run.retry_duration_us =
2182 (uint64_t) retry_duration;
2183 break;
2184 }
2185 default:
2186 bt_common_abort();
2187 }
2188 }
2189
2190 BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp);
2191
2192 if (cfg->cmd_data.run.sources->len == 0) {
2193 BT_CLI_LOGE_APPEND_CAUSE("Incomplete graph: no source component.");
2194 goto error;
2195 }
2196
2197 if (cfg->cmd_data.run.sinks->len == 0) {
2198 BT_CLI_LOGE_APPEND_CAUSE("Incomplete graph: no sink component.");
2199 goto error;
2200 }
2201
2202 ret = bt_config_cli_args_create_connections(cfg,
2203 connection_args,
2204 error_buf, 256);
2205 if (ret) {
2206 BT_CLI_LOGE_APPEND_CAUSE("Cannot creation connections:\n%s", error_buf);
2207 goto error;
2208 }
2209
2210 BT_OBJECT_MOVE_REF(*cfg_out, cfg);
2211 status = BT_CONFIG_CLI_ARGS_STATUS_OK;
2212 goto end;
2213
2214 error:
2215 status = BT_CONFIG_CLI_ARGS_STATUS_ERROR;
2216
2217 end:
2218 if (error_str) {
2219 g_string_free(error_str, TRUE);
2220 }
2221
2222 argpar_iter_destroy(argpar_iter);
2223 argpar_item_destroy(argpar_item);
2224
2225 BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp);
2226 BT_VALUE_PUT_REF_AND_RESET(cur_base_params);
2227 BT_VALUE_PUT_REF_AND_RESET(instance_names);
2228 BT_VALUE_PUT_REF_AND_RESET(connection_args);
2229 bt_object_put_ref(cfg);
2230
2231 return status;
2232 }
2233
2234 static
2235 enum bt_config_cli_args_status bt_config_run_from_args_array(
2236 const bt_value *run_args, struct bt_config **cfg,
2237 const bt_value *plugin_paths,
2238 int default_log_level)
2239 {
2240 enum bt_config_cli_args_status status;
2241 const char **argv;
2242 uint64_t i, len = bt_value_array_get_length(run_args);
2243
2244 BT_ASSERT(len <= SIZE_MAX);
2245 argv = calloc((size_t) len, sizeof(*argv));
2246 if (!argv) {
2247 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2248 goto error;
2249 }
2250
2251 for (i = 0; i < len; i++) {
2252 const bt_value *arg_value =
2253 bt_value_array_borrow_element_by_index_const(run_args,
2254 i);
2255 const char *arg;
2256
2257 arg = bt_value_string_get(arg_value);
2258 BT_ASSERT(arg);
2259 argv[i] = arg;
2260 }
2261
2262 status = bt_config_run_from_args((int) len, argv, cfg,
2263 plugin_paths, default_log_level, 0);
2264
2265 goto end;
2266
2267 error:
2268 status = BT_CONFIG_CLI_ARGS_STATUS_ERROR;
2269
2270 end:
2271 free(argv);
2272 return status;
2273 }
2274
2275 /*
2276 * Prints the convert command usage.
2277 */
2278 static
2279 void print_convert_usage(FILE *fp)
2280 {
2281 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [convert] [OPTIONS] [PATH/URL]\n");
2282 fprintf(fp, "\n");
2283 fprintf(fp, "Options:\n");
2284 fprintf(fp, "\n");
2285 fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n");
2286 fprintf(fp, " Instantiate the component class CLS of type\n");
2287 fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n");
2288 fprintf(fp, " in the plugin PLUGIN, add it to the\n");
2289 fprintf(fp, " conversion graph, and optionally name it\n");
2290 fprintf(fp, " NAME\n");
2291 fprintf(fp, " -l, --log-level=LVL Set the log level of the current component to LVL\n");
2292 fprintf(fp, " (`N`, `T`, `D`, `I`, `W`, `E`, or `F`)\n");
2293 fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n");
2294 fprintf(fp, " current component (see the expected format\n");
2295 fprintf(fp, " of PARAMS below)\n");
2296 fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n");
2297 fprintf(fp, " the graph later, retry in DUR µs\n");
2298 fprintf(fp, " (default: 100000)\n");
2299 fprintf(fp, " dynamic plugins can be loaded\n");
2300 fprintf(fp, " --run-args Print the equivalent arguments for the\n");
2301 fprintf(fp, " `run` command to the standard output,\n");
2302 fprintf(fp, " formatted for a shell, and quit\n");
2303 fprintf(fp, " --run-args-0 Print the equivalent arguments for the\n");
2304 fprintf(fp, " `run` command to the standard output,\n");
2305 fprintf(fp, " formatted for `xargs -0`, and quit\n");
2306 fprintf(fp, " --stream-intersection Only process events when all streams\n");
2307 fprintf(fp, " are active\n");
2308 fprintf(fp, " -h, --help Show this help and quit\n");
2309 fprintf(fp, "\n");
2310 fprintf(fp, "Implicit `source.ctf.fs` component options:\n");
2311 fprintf(fp, "\n");
2312 fprintf(fp, " --clock-force-correlate Force the origin of all clocks\n");
2313 fprintf(fp, " to the Unix epoch\n");
2314 fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n");
2315 fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS ns\n");
2316 fprintf(fp, "\n");
2317 fprintf(fp, "Implicit `sink.text.pretty` component options:\n");
2318 fprintf(fp, "\n");
2319 fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n");
2320 fprintf(fp, " --clock-date Print timestamp dates\n");
2321 fprintf(fp, " --clock-gmt Print and parse timestamps in the GMT\n");
2322 fprintf(fp, " time zone instead of the local time zone\n");
2323 fprintf(fp, " --clock-seconds Print the timestamps as `SEC.NS` instead\n");
2324 fprintf(fp, " of `hh:mm:ss.nnnnnnnnn`\n");
2325 fprintf(fp, " --color=(never | auto | always)\n");
2326 fprintf(fp, " Never, automatically, or always emit\n");
2327 fprintf(fp, " console color codes\n");
2328 fprintf(fp, " -f, --fields=FIELD[,FIELD]... Print additional fields; FIELD can be:\n");
2329 fprintf(fp, " `all`, `trace`, `trace:hostname`,\n");
2330 fprintf(fp, " `trace:domain`, `trace:procname`,\n");
2331 fprintf(fp, " `trace:vpid`, `loglevel`, `emf`\n");
2332 fprintf(fp, " -n, --names=NAME[,NAME]... Print field names; NAME can be:\n");
2333 fprintf(fp, " `payload` (or `arg` or `args`), `none`,\n");
2334 fprintf(fp, " `all`, `scope`, `header`, `context`\n");
2335 fprintf(fp, " (or `ctx`)\n");
2336 fprintf(fp, " --no-delta Do not print time delta between\n");
2337 fprintf(fp, " consecutive events\n");
2338 fprintf(fp, " -w, --output=PATH Write output text to PATH instead of\n");
2339 fprintf(fp, " the standard output\n");
2340 fprintf(fp, "\n");
2341 fprintf(fp, "Implicit `filter.utils.trimmer` component options:\n");
2342 fprintf(fp, "\n");
2343 fprintf(fp, " -b, --begin=BEGIN Set the beginning time of the conversion\n");
2344 fprintf(fp, " time range to BEGIN (see the format of\n");
2345 fprintf(fp, " BEGIN below)\n");
2346 fprintf(fp, " -e, --end=END Set the end time of the conversion time\n");
2347 fprintf(fp, " range to END (see the format of END below)\n");
2348 fprintf(fp, " -t, --timerange=TIMERANGE Set conversion time range to TIMERANGE:\n");
2349 fprintf(fp, " BEGIN,END or [BEGIN,END] (literally `[` and\n");
2350 fprintf(fp, " `]`) (see the format of BEGIN/END below)\n");
2351 fprintf(fp, "\n");
2352 fprintf(fp, "Implicit `filter.lttng-utils.debug-info` component options:\n");
2353 fprintf(fp, "\n");
2354 fprintf(fp, " --debug-info Create an implicit\n");
2355 fprintf(fp, " `filter.lttng-utils.debug-info` component\n");
2356 fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n");
2357 fprintf(fp, " instead of `/usr/lib/debug`\n");
2358 fprintf(fp, " --debug-info-full-path Show full debug info source and\n");
2359 fprintf(fp, " binary paths instead of just names\n");
2360 fprintf(fp, " --debug-info-target-prefix=DIR\n");
2361 fprintf(fp, " Use directory DIR as a prefix when\n");
2362 fprintf(fp, " looking up executables during debug\n");
2363 fprintf(fp, " info analysis\n");
2364 fprintf(fp, "\n");
2365 fprintf(fp, "Legacy options that still work:\n");
2366 fprintf(fp, "\n");
2367 fprintf(fp, " -i, --input-format=(ctf | lttng-live)\n");
2368 fprintf(fp, " `ctf`:\n");
2369 fprintf(fp, " Create an implicit `source.ctf.fs`\n");
2370 fprintf(fp, " component\n");
2371 fprintf(fp, " `lttng-live`:\n");
2372 fprintf(fp, " Create an implicit `source.ctf.lttng-live`\n");
2373 fprintf(fp, " component\n");
2374 fprintf(fp, " -o, --output-format=(text | ctf | dummy | ctf-metadata)\n");
2375 fprintf(fp, " `text`:\n");
2376 fprintf(fp, " Create an implicit `sink.text.pretty`\n");
2377 fprintf(fp, " component\n");
2378 fprintf(fp, " `ctf`:\n");
2379 fprintf(fp, " Create an implicit `sink.ctf.fs`\n");
2380 fprintf(fp, " component\n");
2381 fprintf(fp, " `dummy`:\n");
2382 fprintf(fp, " Create an implicit `sink.utils.dummy`\n");
2383 fprintf(fp, " component\n");
2384 fprintf(fp, " `ctf-metadata`:\n");
2385 fprintf(fp, " Query the `source.ctf.fs` component class\n");
2386 fprintf(fp, " for metadata text and quit\n");
2387 fprintf(fp, "\n");
2388 fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n");
2389 fprintf(fp, "\n\n");
2390 fprintf(fp, "Format of BEGIN and END\n");
2391 fprintf(fp, "-----------------------\n");
2392 fprintf(fp, "\n");
2393 fprintf(fp, " [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n");
2394 fprintf(fp, "\n\n");
2395 print_expected_params_format(fp);
2396 }
2397
2398 static
2399 const struct argpar_opt_descr convert_options[] = {
2400 /* id, short_name, long_name, with_arg */
2401 { OPT_BEGIN, 'b', "begin", true },
2402 { OPT_CLOCK_CYCLES, '\0', "clock-cycles", false },
2403 { OPT_CLOCK_DATE, '\0', "clock-date", false },
2404 { OPT_CLOCK_FORCE_CORRELATE, '\0', "clock-force-correlate", false },
2405 { OPT_CLOCK_GMT, '\0', "clock-gmt", false },
2406 { OPT_CLOCK_OFFSET, '\0', "clock-offset", true },
2407 { OPT_CLOCK_OFFSET_NS, '\0', "clock-offset-ns", true },
2408 { OPT_CLOCK_SECONDS, '\0', "clock-seconds", false },
2409 { OPT_COLOR, '\0', "color", true },
2410 { OPT_COMPONENT, 'c', "component", true },
2411 { OPT_DEBUG, 'd', "debug", false },
2412 { OPT_DEBUG_INFO_DIR, '\0', "debug-info-dir", true },
2413 { OPT_DEBUG_INFO_FULL_PATH, '\0', "debug-info-full-path", false },
2414 { OPT_DEBUG_INFO_TARGET_PREFIX, '\0', "debug-info-target-prefix", true },
2415 { OPT_END, 'e', "end", true },
2416 { OPT_FIELDS, 'f', "fields", true },
2417 { OPT_HELP, 'h', "help", false },
2418 { OPT_INPUT_FORMAT, 'i', "input-format", true },
2419 { OPT_LOG_LEVEL, 'l', "log-level", true },
2420 { OPT_NAMES, 'n', "names", true },
2421 { OPT_DEBUG_INFO, '\0', "debug-info", false },
2422 { OPT_NO_DELTA, '\0', "no-delta", false },
2423 { OPT_OMIT_HOME_PLUGIN_PATH, '\0', "omit-home-plugin-path", false },
2424 { OPT_OMIT_SYSTEM_PLUGIN_PATH, '\0', "omit-system-plugin-path", false },
2425 { OPT_OUTPUT, 'w', "output", true },
2426 { OPT_OUTPUT_FORMAT, 'o', "output-format", true },
2427 { OPT_PARAMS, 'p', "params", true },
2428 { OPT_PLUGIN_PATH, '\0', "plugin-path", true },
2429 { OPT_RETRY_DURATION, '\0', "retry-duration", true },
2430 { OPT_RUN_ARGS, '\0', "run-args", false },
2431 { OPT_RUN_ARGS_0, '\0', "run-args-0", false },
2432 { OPT_STREAM_INTERSECTION, '\0', "stream-intersection", false },
2433 { OPT_TIMERANGE, '\0', "timerange", true },
2434 { OPT_VERBOSE, 'v', "verbose", false },
2435 ARGPAR_OPT_DESCR_SENTINEL
2436 };
2437
2438 static
2439 GString *get_component_auto_name(const char *prefix,
2440 const bt_value *existing_names)
2441 {
2442 unsigned int i = 0;
2443 GString *auto_name = g_string_new(NULL);
2444
2445 if (!auto_name) {
2446 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2447 goto end;
2448 }
2449
2450 if (!bt_value_map_has_entry(existing_names, prefix)) {
2451 g_string_assign(auto_name, prefix);
2452 goto end;
2453 }
2454
2455 do {
2456 g_string_printf(auto_name, "%s-%d", prefix, i);
2457 i++;
2458 } while (bt_value_map_has_entry(existing_names, auto_name->str));
2459
2460 end:
2461 return auto_name;
2462 }
2463
2464 struct implicit_component_args {
2465 bool exists;
2466
2467 /* The component class name (e.g. src.ctf.fs). */
2468 GString *comp_arg;
2469
2470 /* The component instance name. */
2471 GString *name_arg;
2472
2473 GString *params_arg;
2474 bt_value *extra_params;
2475 };
2476
2477 static
2478 int assign_name_to_implicit_component(struct implicit_component_args *args,
2479 const char *prefix, bt_value *existing_names,
2480 GList **comp_names, bool append_to_comp_names)
2481 {
2482 int ret = 0;
2483 GString *name = NULL;
2484
2485 if (!args->exists) {
2486 goto end;
2487 }
2488
2489 name = get_component_auto_name(prefix,
2490 existing_names);
2491
2492 if (!name) {
2493 ret = -1;
2494 goto end;
2495 }
2496
2497 g_string_assign(args->name_arg, name->str);
2498
2499 if (bt_value_map_insert_entry(existing_names, name->str,
2500 bt_value_null)) {
2501 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2502 ret = -1;
2503 goto end;
2504 }
2505
2506 if (append_to_comp_names) {
2507 *comp_names = g_list_append(*comp_names, name);
2508 name = NULL;
2509 }
2510
2511 end:
2512 if (name) {
2513 g_string_free(name, TRUE);
2514 }
2515
2516 return ret;
2517 }
2518
2519 static
2520 int append_run_args_for_implicit_component(
2521 struct implicit_component_args *impl_args,
2522 bt_value *run_args)
2523 {
2524 int ret = 0;
2525 uint64_t i;
2526 GString *component_arg_for_run = NULL;
2527
2528 if (!impl_args->exists) {
2529 goto end;
2530 }
2531
2532 component_arg_for_run = g_string_new(NULL);
2533 if (!component_arg_for_run) {
2534 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2535 goto error;
2536 }
2537
2538 /* Build the full `name:type.plugin.cls`. */
2539 BT_ASSERT(!strchr(impl_args->name_arg->str, '\\'));
2540 BT_ASSERT(!strchr(impl_args->name_arg->str, ':'));
2541 g_string_printf(component_arg_for_run, "%s:%s",
2542 impl_args->name_arg->str, impl_args->comp_arg->str);
2543
2544 if (bt_value_array_append_string_element(run_args, "--component")) {
2545 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2546 goto error;
2547 }
2548
2549 if (bt_value_array_append_string_element(run_args,
2550 component_arg_for_run->str)) {
2551 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2552 goto error;
2553 }
2554
2555 if (impl_args->params_arg->len > 0) {
2556 if (bt_value_array_append_string_element(run_args, "--params")) {
2557 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2558 goto error;
2559 }
2560
2561 if (bt_value_array_append_string_element(run_args,
2562 impl_args->params_arg->str)) {
2563 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2564 goto error;
2565 }
2566 }
2567
2568 for (i = 0; i < bt_value_array_get_length(impl_args->extra_params); i++) {
2569 const bt_value *elem;
2570 const char *arg;
2571
2572 elem = bt_value_array_borrow_element_by_index(
2573 impl_args->extra_params, i);
2574
2575 BT_ASSERT(bt_value_is_string(elem));
2576 arg = bt_value_string_get(elem);
2577 ret = bt_value_array_append_string_element(run_args, arg);
2578 if (ret) {
2579 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2580 goto error;
2581 }
2582 }
2583
2584 goto end;
2585
2586 error:
2587 ret = -1;
2588
2589 end:
2590 if (component_arg_for_run) {
2591 g_string_free(component_arg_for_run, TRUE);
2592 }
2593
2594 return ret;
2595 }
2596
2597 /* Free the fields of a `struct implicit_component_args`. */
2598
2599 static
2600 void finalize_implicit_component_args(struct implicit_component_args *args)
2601 {
2602 BT_ASSERT(args);
2603
2604 if (args->comp_arg) {
2605 g_string_free(args->comp_arg, TRUE);
2606 }
2607
2608 if (args->name_arg) {
2609 g_string_free(args->name_arg, TRUE);
2610 }
2611
2612 if (args->params_arg) {
2613 g_string_free(args->params_arg, TRUE);
2614 }
2615
2616 bt_value_put_ref(args->extra_params);
2617 }
2618
2619 /* Destroy a dynamically-allocated `struct implicit_component_args`. */
2620
2621 static
2622 void destroy_implicit_component_args(struct implicit_component_args *args)
2623 {
2624 finalize_implicit_component_args(args);
2625 g_free(args);
2626 }
2627
2628 /* Initialize the fields of an already allocated `struct implicit_component_args`. */
2629
2630 static
2631 int init_implicit_component_args(struct implicit_component_args *args,
2632 const char *comp_arg, bool exists)
2633 {
2634 int ret = 0;
2635
2636 args->exists = exists;
2637 args->comp_arg = g_string_new(comp_arg);
2638 args->name_arg = g_string_new(NULL);
2639 args->params_arg = g_string_new(NULL);
2640 args->extra_params = bt_value_array_create();
2641
2642 if (!args->comp_arg || !args->name_arg ||
2643 !args->params_arg || !args->extra_params) {
2644 ret = -1;
2645 finalize_implicit_component_args(args);
2646 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2647 goto end;
2648 }
2649
2650 end:
2651 return ret;
2652 }
2653
2654 /* Dynamically allocate and initialize a `struct implicit_component_args`. */
2655
2656 static
2657 struct implicit_component_args *create_implicit_component_args(
2658 const char *comp_arg)
2659 {
2660 struct implicit_component_args *args;
2661 int status;
2662
2663 args = g_new(struct implicit_component_args, 1);
2664 if (!args) {
2665 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2666 goto end;
2667 }
2668
2669 status = init_implicit_component_args(args, comp_arg, true);
2670 if (status != 0) {
2671 g_free(args);
2672 args = NULL;
2673 }
2674
2675 end:
2676 return args;
2677 }
2678
2679 static
2680 void append_implicit_component_param(struct implicit_component_args *args,
2681 const char *key, const char *value)
2682 {
2683 BT_ASSERT(args);
2684 BT_ASSERT(key);
2685 BT_ASSERT(value);
2686 append_param_arg(args->params_arg, key, value);
2687 }
2688
2689 /*
2690 * Append the given parameter (`key=value`) to all component specifications
2691 * in `implicit_comp_args` (an array of `struct implicit_component_args *`)
2692 * which match `comp_arg`.
2693 *
2694 * Return the number of matching components.
2695 */
2696
2697 static
2698 int append_multiple_implicit_components_param(GPtrArray *implicit_comp_args,
2699 const char *comp_arg, const char *key, const char *value)
2700 {
2701 int i;
2702 int n = 0;
2703
2704 for (i = 0; i < implicit_comp_args->len; i++) {
2705 struct implicit_component_args *args = implicit_comp_args->pdata[i];
2706
2707 if (strcmp(args->comp_arg->str, comp_arg) == 0) {
2708 append_implicit_component_param(args, key, value);
2709 n++;
2710 }
2711 }
2712
2713 return n;
2714 }
2715
2716 /* Escape value to make it suitable to use as a string parameter value. */
2717 static
2718 gchar *escape_string_value(const char *value)
2719 {
2720 GString *ret;
2721 const char *in;
2722
2723 ret = g_string_new(NULL);
2724 if (!ret) {
2725 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2726 goto end;
2727 }
2728
2729 in = value;
2730 while (*in) {
2731 switch (*in) {
2732 case '"':
2733 case '\\':
2734 g_string_append_c(ret, '\\');
2735 break;
2736 }
2737
2738 g_string_append_c(ret, *in);
2739
2740 in++;
2741 }
2742
2743 end:
2744 return g_string_free(ret, FALSE);
2745 }
2746
2747 static
2748 int bt_value_to_cli_param_value_append(const bt_value *value, GString *buf)
2749 {
2750 BT_ASSERT(buf);
2751
2752 int ret = -1;
2753
2754 switch (bt_value_get_type(value)) {
2755 case BT_VALUE_TYPE_STRING:
2756 {
2757 const char *str_value = bt_value_string_get(value);
2758 gchar *escaped_str_value;
2759
2760 escaped_str_value = escape_string_value(str_value);
2761 if (!escaped_str_value) {
2762 goto end;
2763 }
2764
2765 g_string_append_printf(buf, "\"%s\"", escaped_str_value);
2766
2767 g_free(escaped_str_value);
2768 break;
2769 }
2770 case BT_VALUE_TYPE_ARRAY: {
2771 g_string_append_c(buf, '[');
2772 uint64_t sz = bt_value_array_get_length(value);
2773 for (uint64_t i = 0; i < sz; i++) {
2774 const bt_value *item;
2775
2776 if (i > 0) {
2777 g_string_append(buf, ", ");
2778 }
2779
2780 item = bt_value_array_borrow_element_by_index_const(
2781 value, i);
2782 ret = bt_value_to_cli_param_value_append(item, buf);
2783
2784 if (ret) {
2785 goto end;
2786 }
2787 }
2788 g_string_append_c(buf, ']');
2789 break;
2790 }
2791 default:
2792 bt_common_abort();
2793 }
2794
2795 ret = 0;
2796
2797 end:
2798 return ret;
2799 }
2800
2801 /*
2802 * Convert `value` to its equivalent representation as a command line parameter
2803 * value.
2804 */
2805
2806 static
2807 gchar *bt_value_to_cli_param_value(bt_value *value)
2808 {
2809 GString *buf;
2810 gchar *result = NULL;
2811
2812 buf = g_string_new(NULL);
2813 if (!buf) {
2814 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2815 goto error;
2816 }
2817
2818 if (bt_value_to_cli_param_value_append(value, buf)) {
2819 goto error;
2820 }
2821
2822 result = g_string_free(buf, FALSE);
2823 buf = NULL;
2824
2825 goto end;
2826
2827 error:
2828 if (buf) {
2829 g_string_free(buf, TRUE);
2830 }
2831
2832 end:
2833 return result;
2834 }
2835
2836 static
2837 int append_parameter_to_args(bt_value *args, const char *key, bt_value *value)
2838 {
2839 BT_ASSERT(args);
2840 BT_ASSERT(bt_value_get_type(args) == BT_VALUE_TYPE_ARRAY);
2841 BT_ASSERT(key);
2842 BT_ASSERT(value);
2843
2844 int ret = 0;
2845 gchar *str_value = NULL;
2846 GString *parameter = NULL;
2847
2848 if (bt_value_array_append_string_element(args, "--params")) {
2849 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2850 ret = -1;
2851 goto end;
2852 }
2853
2854 str_value = bt_value_to_cli_param_value(value);
2855 if (!str_value) {
2856 ret = -1;
2857 goto end;
2858 }
2859
2860 parameter = g_string_new(NULL);
2861 if (!parameter) {
2862 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2863 ret = -1;
2864 goto end;
2865 }
2866
2867 g_string_printf(parameter, "%s=%s", key, str_value);
2868
2869 if (bt_value_array_append_string_element(args, parameter->str)) {
2870 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2871 ret = -1;
2872 goto end;
2873 }
2874
2875 end:
2876 if (parameter) {
2877 g_string_free(parameter, TRUE);
2878 parameter = NULL;
2879 }
2880
2881 if (str_value) {
2882 g_free(str_value);
2883 str_value = NULL;
2884 }
2885
2886 return ret;
2887 }
2888
2889 static
2890 int append_string_parameter_to_args(bt_value *args, const char *key, const char *value)
2891 {
2892 bt_value *str_value;
2893 int ret;
2894
2895 str_value = bt_value_string_create_init(value);
2896
2897 if (!str_value) {
2898 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2899 ret = -1;
2900 goto end;
2901 }
2902
2903 ret = append_parameter_to_args(args, key, str_value);
2904
2905 end:
2906 BT_VALUE_PUT_REF_AND_RESET(str_value);
2907 return ret;
2908 }
2909
2910 static
2911 int append_implicit_component_extra_param(struct implicit_component_args *args,
2912 const char *key, const char *value)
2913 {
2914 return append_string_parameter_to_args(args->extra_params, key, value);
2915 }
2916
2917 /*
2918 * Escapes `.`, `:`, and `\` of `input` with `\`.
2919 */
2920 static
2921 GString *escape_dot_colon(const char *input)
2922 {
2923 GString *output = g_string_new(NULL);
2924 const char *ch;
2925
2926 if (!output) {
2927 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2928 goto end;
2929 }
2930
2931 for (ch = input; *ch != '\0'; ch++) {
2932 if (*ch == '\\' || *ch == '.' || *ch == ':') {
2933 g_string_append_c(output, '\\');
2934 }
2935
2936 g_string_append_c(output, *ch);
2937 }
2938
2939 end:
2940 return output;
2941 }
2942
2943 /*
2944 * Appends a --connect option to a list of arguments. `upstream_name`
2945 * and `downstream_name` are escaped with escape_dot_colon() in this
2946 * function.
2947 */
2948 static
2949 int append_connect_arg(bt_value *run_args,
2950 const char *upstream_name, const char *downstream_name)
2951 {
2952 int ret = 0;
2953 GString *e_upstream_name = escape_dot_colon(upstream_name);
2954 GString *e_downstream_name = escape_dot_colon(downstream_name);
2955 GString *arg = g_string_new(NULL);
2956
2957 if (!e_upstream_name || !e_downstream_name || !arg) {
2958 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2959 ret = -1;
2960 goto end;
2961 }
2962
2963 ret = bt_value_array_append_string_element(run_args, "--connect");
2964 if (ret) {
2965 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2966 ret = -1;
2967 goto end;
2968 }
2969
2970 g_string_append(arg, e_upstream_name->str);
2971 g_string_append_c(arg, ':');
2972 g_string_append(arg, e_downstream_name->str);
2973 ret = bt_value_array_append_string_element(run_args, arg->str);
2974 if (ret) {
2975 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2976 ret = -1;
2977 goto end;
2978 }
2979
2980 end:
2981 if (arg) {
2982 g_string_free(arg, TRUE);
2983 }
2984
2985 if (e_upstream_name) {
2986 g_string_free(e_upstream_name, TRUE);
2987 }
2988
2989 if (e_downstream_name) {
2990 g_string_free(e_downstream_name, TRUE);
2991 }
2992
2993 return ret;
2994 }
2995
2996 /*
2997 * Appends the run command's --connect options for the convert command.
2998 */
2999 static
3000 int convert_auto_connect(bt_value *run_args,
3001 GList *source_names, GList *filter_names,
3002 GList *sink_names)
3003 {
3004 int ret = 0;
3005 GList *source_at = source_names;
3006 GList *filter_at = filter_names;
3007 GList *filter_prev;
3008 GList *sink_at = sink_names;
3009
3010 BT_ASSERT(source_names);
3011 BT_ASSERT(filter_names);
3012 BT_ASSERT(sink_names);
3013
3014 /* Connect all sources to the first filter */
3015 for (source_at = source_names; source_at; source_at = g_list_next(source_at)) {
3016 GString *source_name = source_at->data;
3017 GString *filter_name = filter_at->data;
3018
3019 ret = append_connect_arg(run_args, source_name->str,
3020 filter_name->str);
3021 if (ret) {
3022 goto error;
3023 }
3024 }
3025
3026 filter_prev = filter_at;
3027 filter_at = g_list_next(filter_at);
3028
3029 /* Connect remaining filters */
3030 for (; filter_at; filter_prev = filter_at, filter_at = g_list_next(filter_at)) {
3031 GString *filter_name = filter_at->data;
3032 GString *filter_prev_name = filter_prev->data;
3033
3034 ret = append_connect_arg(run_args, filter_prev_name->str,
3035 filter_name->str);
3036 if (ret) {
3037 goto error;
3038 }
3039 }
3040
3041 /* Connect last filter to all sinks */
3042 for (sink_at = sink_names; sink_at; sink_at = g_list_next(sink_at)) {
3043 GString *filter_name = filter_prev->data;
3044 GString *sink_name = sink_at->data;
3045
3046 ret = append_connect_arg(run_args, filter_name->str,
3047 sink_name->str);
3048 if (ret) {
3049 goto error;
3050 }
3051 }
3052
3053 goto end;
3054
3055 error:
3056 ret = -1;
3057
3058 end:
3059 return ret;
3060 }
3061
3062 static
3063 int split_timerange(const char *arg, char **begin, char **end)
3064 {
3065 int ret = 0;
3066 const char *ch = arg;
3067 size_t end_pos;
3068 GString *g_begin = NULL;
3069 GString *g_end = NULL;
3070
3071 BT_ASSERT(arg);
3072
3073 if (*ch == '[') {
3074 ch++;
3075 }
3076
3077 g_begin = bt_common_string_until(ch, "", ",", &end_pos);
3078 if (!g_begin || ch[end_pos] != ',' || g_begin->len == 0) {
3079 goto error;
3080 }
3081
3082 ch += end_pos + 1;
3083
3084 g_end = bt_common_string_until(ch, "", "]", &end_pos);
3085 if (!g_end || g_end->len == 0) {
3086 goto error;
3087 }
3088
3089 BT_ASSERT(begin);
3090 BT_ASSERT(end);
3091 *begin = g_begin->str;
3092 *end = g_end->str;
3093 g_string_free(g_begin, FALSE);
3094 g_string_free(g_end, FALSE);
3095 g_begin = NULL;
3096 g_end = NULL;
3097 goto end;
3098
3099 error:
3100 ret = -1;
3101
3102 end:
3103 if (g_begin) {
3104 g_string_free(g_begin, TRUE);
3105 }
3106
3107 if (g_end) {
3108 g_string_free(g_end, TRUE);
3109 }
3110
3111 return ret;
3112 }
3113
3114 static
3115 int g_list_prepend_gstring(GList **list, const char *string)
3116 {
3117 int ret = 0;
3118 GString *gs = g_string_new(string);
3119
3120 BT_ASSERT(list);
3121
3122 if (!gs) {
3123 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3124 goto end;
3125 }
3126
3127 *list = g_list_prepend(*list, gs);
3128
3129 end:
3130 return ret;
3131 }
3132
3133 /*
3134 * Create `struct implicit_component_args` structures for each of the
3135 * source components we identified. Add them to `component_args`.
3136 *
3137 * `non_opts` is an array of the non-option arguments passed on the command
3138 * line.
3139 *
3140 * `non_opt_params` is an array where each element is an array of
3141 * strings containing all the arguments to `--params` that apply to the
3142 * non-option argument at the same index. For example, if, for a
3143 * non-option argument, the following `--params` options applied:
3144 *
3145 * --params=a=2 --params=b=3,c=4
3146 *
3147 * its entry in `non_opt_params` would contain
3148 *
3149 * ["a=2", "b=3,c=4"]
3150 */
3151
3152 static
3153 int create_implicit_component_args_from_auto_discovered_sources(
3154 const struct auto_source_discovery *auto_disc,
3155 const bt_value *non_opts,
3156 const bt_value *non_opt_params,
3157 const bt_value *non_opt_loglevels,
3158 GPtrArray *component_args)
3159 {
3160 gchar *cc_name = NULL;
3161 struct implicit_component_args *comp = NULL;
3162 int status;
3163 guint i, len;
3164
3165 len = auto_disc->results->len;
3166
3167 for (i = 0; i < len; i++) {
3168 struct auto_source_discovery_result *res =
3169 g_ptr_array_index(auto_disc->results, i);
3170 uint64_t orig_indices_i, orig_indices_count;
3171
3172 g_free(cc_name);
3173 cc_name = g_strdup_printf("source.%s.%s", res->plugin_name, res->source_cc_name);
3174 if (!cc_name) {
3175 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3176 goto error;
3177 }
3178
3179 comp = create_implicit_component_args(cc_name);
3180 if (!comp) {
3181 goto error;
3182 }
3183
3184 /*
3185 * Append parameters and log levels of all the
3186 * non-option arguments that contributed to this
3187 * component instance coming into existence.
3188 */
3189 orig_indices_count = bt_value_array_get_length(res->original_input_indices);
3190 for (orig_indices_i = 0; orig_indices_i < orig_indices_count; orig_indices_i++) {
3191 const bt_value *orig_idx_value =
3192 bt_value_array_borrow_element_by_index(
3193 res->original_input_indices, orig_indices_i);
3194 uint64_t orig_idx = bt_value_integer_unsigned_get(orig_idx_value);
3195 const bt_value *params_array =
3196 bt_value_array_borrow_element_by_index_const(
3197 non_opt_params, orig_idx);
3198 uint64_t params_i, params_count;
3199 const bt_value *loglevel_value;
3200
3201 params_count = bt_value_array_get_length(params_array);
3202 for (params_i = 0; params_i < params_count; params_i++) {
3203 const bt_value *params_value =
3204 bt_value_array_borrow_element_by_index_const(
3205 params_array, params_i);
3206 const char *params = bt_value_string_get(params_value);
3207 bt_value_array_append_element_status append_status;
3208
3209 append_status = bt_value_array_append_string_element(
3210 comp->extra_params, "--params");
3211 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3212 BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element.");
3213 goto error;
3214 }
3215
3216 append_status = bt_value_array_append_string_element(
3217 comp->extra_params, params);
3218 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3219 BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element.");
3220 goto error;
3221 }
3222 }
3223
3224 loglevel_value = bt_value_array_borrow_element_by_index_const(
3225 non_opt_loglevels, orig_idx);
3226 if (bt_value_get_type(loglevel_value) == BT_VALUE_TYPE_STRING) {
3227 const char *loglevel = bt_value_string_get(loglevel_value);
3228 bt_value_array_append_element_status append_status;
3229
3230 append_status = bt_value_array_append_string_element(
3231 comp->extra_params, "--log-level");
3232 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3233 BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element.");
3234 goto error;
3235 }
3236
3237 append_status = bt_value_array_append_string_element(
3238 comp->extra_params, loglevel);
3239 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3240 BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element.");
3241 goto error;
3242 }
3243 }
3244 }
3245
3246 /*
3247 * If single input and a src.ctf.fs component, provide the
3248 * relative path from the path passed on the command line to the
3249 * found trace.
3250 */
3251 if (bt_value_array_get_length(res->inputs) == 1 &&
3252 strcmp(res->plugin_name, "ctf") == 0 &&
3253 strcmp(res->source_cc_name, "fs") == 0) {
3254 const bt_value *orig_idx_value =
3255 bt_value_array_borrow_element_by_index(
3256 res->original_input_indices, 0);
3257 uint64_t orig_idx = bt_value_integer_unsigned_get(orig_idx_value);
3258 const bt_value *non_opt_value =
3259 bt_value_array_borrow_element_by_index_const(
3260 non_opts, orig_idx);
3261 const char *non_opt = bt_value_string_get(non_opt_value);
3262 const bt_value *input_value =
3263 bt_value_array_borrow_element_by_index_const(
3264 res->inputs, 0);
3265 const char *input = bt_value_string_get(input_value);
3266
3267 BT_ASSERT(orig_indices_count == 1);
3268 BT_ASSERT(g_str_has_prefix(input, non_opt));
3269
3270 input += strlen(non_opt);
3271
3272 while (G_IS_DIR_SEPARATOR(*input)) {
3273 input++;
3274 }
3275
3276 if (strlen(input) > 0) {
3277 append_string_parameter_to_args(comp->extra_params,
3278 "trace-name", input);
3279 }
3280 }
3281
3282 status = append_parameter_to_args(comp->extra_params, "inputs", res->inputs);
3283 if (status != 0) {
3284 goto error;
3285 }
3286
3287 g_ptr_array_add(component_args, comp);
3288 comp = NULL;
3289 }
3290
3291 status = 0;
3292 goto end;
3293
3294 error:
3295 status = -1;
3296
3297 end:
3298 g_free(cc_name);
3299
3300 if (comp) {
3301 destroy_implicit_component_args(comp);
3302 }
3303
3304 return status;
3305 }
3306
3307 /*
3308 * As we iterate the arguments to the convert command, this tracks what is the
3309 * type of the current item, to which some contextual options (e.g. --params)
3310 * apply to.
3311 */
3312 enum convert_current_item_type {
3313 /* There is no current item. */
3314 CONVERT_CURRENT_ITEM_TYPE_NONE,
3315
3316 /* Current item is a component. */
3317 CONVERT_CURRENT_ITEM_TYPE_COMPONENT,
3318
3319 /* Current item is a non-option argument. */
3320 CONVERT_CURRENT_ITEM_TYPE_NON_OPT,
3321 };
3322
3323 /*
3324 * Creates a Babeltrace config object from the arguments of a convert
3325 * command.
3326 */
3327 static
3328 enum bt_config_cli_args_status bt_config_convert_from_args(int argc,
3329 const char *argv[], struct bt_config **cfg_out,
3330 const bt_value *plugin_paths,
3331 int *default_log_level, const bt_interrupter *interrupter,
3332 unsigned int consumed_args)
3333 {
3334 enum bt_config_cli_args_status status;
3335 enum convert_current_item_type current_item_type =
3336 CONVERT_CURRENT_ITEM_TYPE_NONE;
3337 int ret;
3338 struct bt_config *cfg = NULL;
3339 bool got_input_format_opt = false;
3340 bool got_output_format_opt = false;
3341 bool trimmer_has_begin = false;
3342 bool trimmer_has_end = false;
3343 bool stream_intersection_mode = false;
3344 bool print_run_args = false;
3345 bool print_run_args_0 = false;
3346 bool print_ctf_metadata = false;
3347 bt_value *run_args = NULL;
3348 bt_value *all_names = NULL;
3349 GList *source_names = NULL;
3350 GList *filter_names = NULL;
3351 GList *sink_names = NULL;
3352 bt_value *non_opts = NULL;
3353 bt_value *non_opt_params = NULL;
3354 bt_value *non_opt_loglevels = NULL;
3355 struct implicit_component_args implicit_ctf_output_args = { 0 };
3356 struct implicit_component_args implicit_lttng_live_args = { 0 };
3357 struct implicit_component_args implicit_dummy_args = { 0 };
3358 struct implicit_component_args implicit_text_args = { 0 };
3359 struct implicit_component_args implicit_debug_info_args = { 0 };
3360 struct implicit_component_args implicit_muxer_args = { 0 };
3361 struct implicit_component_args implicit_trimmer_args = { 0 };
3362 char error_buf[256] = { 0 };
3363 size_t i;
3364 struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 };
3365 char *output = NULL;
3366 struct auto_source_discovery auto_disc = { NULL };
3367 GString *auto_disc_comp_name = NULL;
3368 struct argpar_iter *argpar_iter = NULL;
3369 const struct argpar_item *argpar_item = NULL;
3370 GString *name_gstr = NULL;
3371 GString *component_arg_for_run = NULL;
3372 bt_value *live_inputs_array_val = NULL;
3373
3374 /*
3375 * Array of `struct implicit_component_args *` created for the sources
3376 * we have auto-discovered.
3377 */
3378 GPtrArray *discovered_source_args = NULL;
3379
3380 /*
3381 * If set, restrict automatic source discovery to this component class
3382 * of this plugin.
3383 */
3384 const char *auto_source_discovery_restrict_plugin_name = NULL;
3385 const char *auto_source_discovery_restrict_component_class_name = NULL;
3386
3387 bool ctf_fs_source_force_clock_class_unix_epoch_origin = false;
3388 gchar *ctf_fs_source_clock_class_offset_arg = NULL;
3389 gchar *ctf_fs_source_clock_class_offset_ns_arg = NULL;
3390
3391 if (argc < 1) {
3392 print_convert_usage(stdout);
3393 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
3394 goto end;
3395 }
3396
3397 if (init_implicit_component_args(&implicit_ctf_output_args,
3398 "sink.ctf.fs", false)) {
3399 goto error;
3400 }
3401
3402 if (init_implicit_component_args(&implicit_lttng_live_args,
3403 "source.ctf.lttng-live", false)) {
3404 goto error;
3405 }
3406
3407 if (init_implicit_component_args(&implicit_text_args,
3408 "sink.text.pretty", false)) {
3409 goto error;
3410 }
3411
3412 if (init_implicit_component_args(&implicit_dummy_args,
3413 "sink.utils.dummy", false)) {
3414 goto error;
3415 }
3416
3417 if (init_implicit_component_args(&implicit_debug_info_args,
3418 "filter.lttng-utils.debug-info", false)) {
3419 goto error;
3420 }
3421
3422 if (init_implicit_component_args(&implicit_muxer_args,
3423 "filter.utils.muxer", true)) {
3424 goto error;
3425 }
3426
3427 if (init_implicit_component_args(&implicit_trimmer_args,
3428 "filter.utils.trimmer", false)) {
3429 goto error;
3430 }
3431
3432 all_names = bt_value_map_create();
3433 if (!all_names) {
3434 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3435 goto error;
3436 }
3437
3438 run_args = bt_value_array_create();
3439 if (!run_args) {
3440 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3441 goto error;
3442 }
3443
3444 component_arg_for_run = g_string_new(NULL);
3445 if (!component_arg_for_run) {
3446 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3447 goto error;
3448 }
3449
3450 non_opts = bt_value_array_create();
3451 if (!non_opts) {
3452 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3453 goto error;
3454 }
3455
3456 non_opt_params = bt_value_array_create();
3457 if (!non_opt_params) {
3458 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3459 goto error;
3460 }
3461
3462 non_opt_loglevels = bt_value_array_create();
3463 if (!non_opt_loglevels) {
3464 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3465 goto error;
3466 }
3467
3468 if (auto_source_discovery_init(&auto_disc) != 0) {
3469 goto error;
3470 }
3471
3472 discovered_source_args =
3473 g_ptr_array_new_with_free_func((GDestroyNotify) destroy_implicit_component_args);
3474 if (!discovered_source_args) {
3475 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3476 goto error;
3477 }
3478
3479 auto_disc_comp_name = g_string_new(NULL);
3480 if (!auto_disc_comp_name) {
3481 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3482 goto error;
3483 }
3484
3485 /*
3486 * First pass: collect all arguments which need to be passed
3487 * as is to the run command. This pass can also add --name
3488 * arguments if needed to automatically name unnamed component
3489 * instances.
3490 */
3491 argpar_iter = argpar_iter_create(argc, argv, convert_options);
3492 if (!argpar_iter) {
3493 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3494 goto error;
3495 }
3496
3497 while (true) {
3498 enum parse_next_item_status parse_status;
3499 char *name = NULL;
3500 char *plugin_name = NULL;
3501 char *comp_cls_name = NULL;
3502
3503 parse_status = parse_next_item(argpar_iter, &argpar_item, argv, "convert",
3504 consumed_args);
3505 if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) {
3506 goto error;
3507 } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) {
3508 break;
3509 }
3510
3511 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
3512 const struct argpar_opt_descr *opt_descr =
3513 argpar_item_opt_descr(argpar_item);
3514 const char *arg = argpar_item_opt_arg(argpar_item);
3515
3516 switch (opt_descr->id) {
3517 case OPT_HELP:
3518 print_convert_usage(stdout);
3519 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
3520 goto end;
3521 case OPT_COMPONENT:
3522 {
3523 bt_component_class_type type;
3524
3525 current_item_type = CONVERT_CURRENT_ITEM_TYPE_COMPONENT;
3526
3527 /* Parse the argument */
3528 plugin_comp_cls_names(arg, &name, &plugin_name,
3529 &comp_cls_name, &type);
3530 if (!plugin_name || !comp_cls_name) {
3531 BT_CLI_LOGE_APPEND_CAUSE(
3532 "Invalid format for --component option's argument:\n %s",
3533 arg);
3534 goto error;
3535 }
3536
3537 if (name) {
3538 /*
3539 * Name was given by the user, verify it isn't
3540 * taken.
3541 */
3542 if (bt_value_map_has_entry(all_names, name)) {
3543 BT_CLI_LOGE_APPEND_CAUSE(
3544 "Duplicate component instance name:\n %s",
3545 name);
3546 goto error;
3547 }
3548
3549 name_gstr = g_string_new(name);
3550 if (!name_gstr) {
3551 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3552 goto error;
3553 }
3554
3555 g_string_assign(component_arg_for_run, arg);
3556 } else {
3557 /* Name not given by user, generate one. */
3558 name_gstr = get_component_auto_name(arg, all_names);
3559 if (!name_gstr) {
3560 goto error;
3561 }
3562
3563 g_string_printf(component_arg_for_run, "%s:%s",
3564 name_gstr->str, arg);
3565 }
3566
3567 if (bt_value_array_append_string_element(run_args,
3568 "--component")) {
3569 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3570 goto error;
3571 }
3572
3573 if (bt_value_array_append_string_element(run_args,
3574 component_arg_for_run->str)) {
3575 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3576 goto error;
3577 }
3578
3579 /*
3580 * Remember this name globally, for the uniqueness of
3581 * all component names.
3582 */
3583 if (bt_value_map_insert_entry(all_names,
3584 name_gstr->str, bt_value_null)) {
3585 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3586 goto error;
3587 }
3588
3589 /*
3590 * Remember this name specifically for the type of the
3591 * component. This is to create connection arguments.
3592 *
3593 * The list takes ownership of `name_gstr`.
3594 */
3595 switch (type) {
3596 case BT_COMPONENT_CLASS_TYPE_SOURCE:
3597 source_names = g_list_append(source_names, name_gstr);
3598 break;
3599 case BT_COMPONENT_CLASS_TYPE_FILTER:
3600 filter_names = g_list_append(filter_names, name_gstr);
3601 break;
3602 case BT_COMPONENT_CLASS_TYPE_SINK:
3603 sink_names = g_list_append(sink_names, name_gstr);
3604 break;
3605 default:
3606 bt_common_abort();
3607 }
3608 name_gstr = NULL;
3609
3610 free(name);
3611 free(plugin_name);
3612 free(comp_cls_name);
3613 name = NULL;
3614 plugin_name = NULL;
3615 comp_cls_name = NULL;
3616 break;
3617 }
3618 case OPT_PARAMS:
3619 if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_COMPONENT) {
3620 /*
3621 * The current item is a component (--component option),
3622 * pass it directly to the run args.
3623 */
3624 if (bt_value_array_append_string_element(run_args,
3625 "--params")) {
3626 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3627 goto error;
3628 }
3629
3630 if (bt_value_array_append_string_element(run_args, arg)) {
3631 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3632 goto error;
3633 }
3634 } else if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_NON_OPT) {
3635 /*
3636 * The current item is a
3637 * non-option argument, record
3638 * it in `non_opt_params`.
3639 */
3640 bt_value *array;
3641 bt_value_array_append_element_status append_element_status;
3642 uint64_t idx = bt_value_array_get_length(non_opt_params) - 1;
3643
3644 array = bt_value_array_borrow_element_by_index(non_opt_params, idx);
3645
3646 append_element_status = bt_value_array_append_string_element(array, arg);
3647 if (append_element_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3648 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3649 goto error;
3650 }
3651 } else {
3652 BT_CLI_LOGE_APPEND_CAUSE(
3653 "No current component (--component option) or non-option argument of which to set parameters:\n %s",
3654 arg);
3655 goto error;
3656 }
3657 break;
3658 case OPT_LOG_LEVEL:
3659 if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_COMPONENT) {
3660 if (bt_value_array_append_string_element(run_args, "--log-level")) {
3661 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3662 goto error;
3663 }
3664
3665 if (bt_value_array_append_string_element(run_args, arg)) {
3666 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3667 goto error;
3668 }
3669 } else if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_NON_OPT) {
3670 uint64_t idx = bt_value_array_get_length(non_opt_loglevels) - 1;
3671 enum bt_value_array_set_element_by_index_status set_element_status;
3672 bt_value *log_level_str_value;
3673
3674 log_level_str_value = bt_value_string_create_init(arg);
3675 if (!log_level_str_value) {
3676 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3677 goto error;
3678 }
3679
3680 set_element_status =
3681 bt_value_array_set_element_by_index(non_opt_loglevels,
3682 idx, log_level_str_value);
3683 bt_value_put_ref(log_level_str_value);
3684 if (set_element_status != BT_VALUE_ARRAY_SET_ELEMENT_BY_INDEX_STATUS_OK) {
3685 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3686 goto error;
3687 }
3688 } else {
3689 BT_CLI_LOGE_APPEND_CAUSE(
3690 "No current component (--component option) or non-option argument to assign a log level to:\n %s",
3691 arg);
3692 goto error;
3693 }
3694
3695 break;
3696 case OPT_RETRY_DURATION:
3697 if (bt_value_array_append_string_element(run_args,
3698 "--retry-duration")) {
3699 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3700 goto error;
3701 }
3702
3703 if (bt_value_array_append_string_element(run_args, arg)) {
3704 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3705 goto error;
3706 }
3707 break;
3708 case OPT_BEGIN:
3709 case OPT_CLOCK_CYCLES:
3710 case OPT_CLOCK_DATE:
3711 case OPT_CLOCK_FORCE_CORRELATE:
3712 case OPT_CLOCK_GMT:
3713 case OPT_CLOCK_OFFSET:
3714 case OPT_CLOCK_OFFSET_NS:
3715 case OPT_CLOCK_SECONDS:
3716 case OPT_COLOR:
3717 case OPT_DEBUG:
3718 case OPT_DEBUG_INFO:
3719 case OPT_DEBUG_INFO_DIR:
3720 case OPT_DEBUG_INFO_FULL_PATH:
3721 case OPT_DEBUG_INFO_TARGET_PREFIX:
3722 case OPT_END:
3723 case OPT_FIELDS:
3724 case OPT_INPUT_FORMAT:
3725 case OPT_NAMES:
3726 case OPT_NO_DELTA:
3727 case OPT_OUTPUT_FORMAT:
3728 case OPT_OUTPUT:
3729 case OPT_RUN_ARGS:
3730 case OPT_RUN_ARGS_0:
3731 case OPT_STREAM_INTERSECTION:
3732 case OPT_TIMERANGE:
3733 case OPT_VERBOSE:
3734 /* Ignore in this pass */
3735 break;
3736 default:
3737 bt_common_abort();
3738 }
3739 } else {
3740 const char *arg = argpar_item_non_opt_arg(argpar_item);
3741 bt_value_array_append_element_status append_status;
3742
3743 current_item_type = CONVERT_CURRENT_ITEM_TYPE_NON_OPT;
3744
3745 append_status = bt_value_array_append_string_element(non_opts, arg);
3746 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3747 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3748 goto error;
3749 }
3750
3751 append_status = bt_value_array_append_empty_array_element(
3752 non_opt_params, NULL);
3753 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3754 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3755 goto error;
3756 }
3757
3758 append_status = bt_value_array_append_element(non_opt_loglevels, bt_value_null);
3759 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3760 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3761 goto error;
3762 }
3763 }
3764 }
3765
3766 /*
3767 * Second pass: transform the convert-specific options and
3768 * arguments into implicit component instances for the run
3769 * command.
3770 */
3771 argpar_iter_destroy(argpar_iter);
3772 argpar_iter = argpar_iter_create(argc, argv, convert_options);
3773 if (!argpar_iter) {
3774 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3775 goto error;
3776 }
3777
3778 while (true) {
3779 enum parse_next_item_status parse_status;
3780 const struct argpar_opt_descr *opt_descr;
3781 const char *arg;
3782
3783 parse_status = parse_next_item(argpar_iter, &argpar_item, argv, "convert",
3784 consumed_args);
3785 if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) {
3786 goto error;
3787 } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) {
3788 break;
3789 }
3790
3791 if (argpar_item_type(argpar_item) != ARGPAR_ITEM_TYPE_OPT) {
3792 continue;
3793 }
3794
3795 opt_descr = argpar_item_opt_descr(argpar_item);
3796 arg = argpar_item_opt_arg(argpar_item);
3797
3798 switch (opt_descr->id) {
3799 case OPT_BEGIN:
3800 if (trimmer_has_begin) {
3801 BT_CLI_LOGE_APPEND_CAUSE("At --begin option: --begin or --timerange option already specified\n %s\n",
3802 arg);
3803 goto error;
3804 }
3805
3806 trimmer_has_begin = true;
3807 ret = append_implicit_component_extra_param(
3808 &implicit_trimmer_args, "begin", arg);
3809 implicit_trimmer_args.exists = true;
3810 if (ret) {
3811 goto error;
3812 }
3813 break;
3814 case OPT_END:
3815 if (trimmer_has_end) {
3816 BT_CLI_LOGE_APPEND_CAUSE("At --end option: --end or --timerange option already specified\n %s\n",
3817 arg);
3818 goto error;
3819 }
3820
3821 trimmer_has_end = true;
3822 ret = append_implicit_component_extra_param(
3823 &implicit_trimmer_args, "end", arg);
3824 implicit_trimmer_args.exists = true;
3825 if (ret) {
3826 goto error;
3827 }
3828 break;
3829 case OPT_TIMERANGE:
3830 {
3831 char *begin;
3832 char *end;
3833
3834 if (trimmer_has_begin || trimmer_has_end) {
3835 BT_CLI_LOGE_APPEND_CAUSE("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n",
3836 arg);
3837 goto error;
3838 }
3839
3840 ret = split_timerange(arg, &begin, &end);
3841 if (ret) {
3842 BT_CLI_LOGE_APPEND_CAUSE("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s",
3843 arg);
3844 goto error;
3845 }
3846
3847 ret = append_implicit_component_extra_param(
3848 &implicit_trimmer_args, "begin", begin);
3849 ret |= append_implicit_component_extra_param(
3850 &implicit_trimmer_args, "end", end);
3851 implicit_trimmer_args.exists = true;
3852 free(begin);
3853 free(end);
3854 if (ret) {
3855 goto error;
3856 }
3857 break;
3858 }
3859 case OPT_CLOCK_CYCLES:
3860 append_implicit_component_param(
3861 &implicit_text_args, "clock-cycles", "yes");
3862 implicit_text_args.exists = true;
3863 break;
3864 case OPT_CLOCK_DATE:
3865 append_implicit_component_param(
3866 &implicit_text_args, "clock-date", "yes");
3867 implicit_text_args.exists = true;
3868 break;
3869 case OPT_CLOCK_FORCE_CORRELATE:
3870 ctf_fs_source_force_clock_class_unix_epoch_origin = true;
3871 break;
3872 case OPT_CLOCK_GMT:
3873 append_implicit_component_param(
3874 &implicit_text_args, "clock-gmt", "yes");
3875 append_implicit_component_param(
3876 &implicit_trimmer_args, "gmt", "yes");
3877 implicit_text_args.exists = true;
3878 break;
3879 case OPT_CLOCK_OFFSET:
3880 if (ctf_fs_source_clock_class_offset_arg) {
3881 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset option\n");
3882 goto error;
3883 }
3884
3885 ctf_fs_source_clock_class_offset_arg = g_strdup(arg);
3886 if (!ctf_fs_source_clock_class_offset_arg) {
3887 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3888 goto error;
3889 }
3890 break;
3891 case OPT_CLOCK_OFFSET_NS:
3892 if (ctf_fs_source_clock_class_offset_ns_arg) {
3893 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset-ns option\n");
3894 goto error;
3895 }
3896
3897 ctf_fs_source_clock_class_offset_ns_arg = g_strdup(arg);
3898 if (!ctf_fs_source_clock_class_offset_ns_arg) {
3899 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3900 goto error;
3901 }
3902 break;
3903 case OPT_CLOCK_SECONDS:
3904 append_implicit_component_param(
3905 &implicit_text_args, "clock-seconds", "yes");
3906 implicit_text_args.exists = true;
3907 break;
3908 case OPT_COLOR:
3909 implicit_text_args.exists = true;
3910 ret = append_implicit_component_extra_param(
3911 &implicit_text_args, "color", arg);
3912 if (ret) {
3913 goto error;
3914 }
3915 break;
3916 case OPT_DEBUG_INFO:
3917 implicit_debug_info_args.exists = true;
3918 break;
3919 case OPT_DEBUG_INFO_DIR:
3920 implicit_debug_info_args.exists = true;
3921 ret = append_implicit_component_extra_param(
3922 &implicit_debug_info_args, "debug-info-dir", arg);
3923 if (ret) {
3924 goto error;
3925 }
3926 break;
3927 case OPT_DEBUG_INFO_FULL_PATH:
3928 implicit_debug_info_args.exists = true;
3929 append_implicit_component_param(
3930 &implicit_debug_info_args, "full-path", "yes");
3931 break;
3932 case OPT_DEBUG_INFO_TARGET_PREFIX:
3933 implicit_debug_info_args.exists = true;
3934 ret = append_implicit_component_extra_param(
3935 &implicit_debug_info_args,
3936 "target-prefix", arg);
3937 if (ret) {
3938 goto error;
3939 }
3940 break;
3941 case OPT_FIELDS:
3942 {
3943 bt_value *fields = fields_from_arg(arg);
3944
3945 if (!fields) {
3946 goto error;
3947 }
3948
3949 implicit_text_args.exists = true;
3950 ret = insert_flat_params_from_array(
3951 implicit_text_args.params_arg,
3952 fields, "field");
3953 bt_value_put_ref(fields);
3954 if (ret) {
3955 goto error;
3956 }
3957 break;
3958 }
3959 case OPT_NAMES:
3960 {
3961 bt_value *names = names_from_arg(arg);
3962
3963 if (!names) {
3964 goto error;
3965 }
3966
3967 implicit_text_args.exists = true;
3968 ret = insert_flat_params_from_array(
3969 implicit_text_args.params_arg,
3970 names, "name");
3971 bt_value_put_ref(names);
3972 if (ret) {
3973 goto error;
3974 }
3975 break;
3976 }
3977 case OPT_NO_DELTA:
3978 append_implicit_component_param(
3979 &implicit_text_args, "no-delta", "yes");
3980 implicit_text_args.exists = true;
3981 break;
3982 case OPT_INPUT_FORMAT:
3983 if (got_input_format_opt) {
3984 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --input-format option.");
3985 goto error;
3986 }
3987
3988 got_input_format_opt = true;
3989
3990 if (strcmp(arg, "ctf") == 0) {
3991 auto_source_discovery_restrict_plugin_name = "ctf";
3992 auto_source_discovery_restrict_component_class_name = "fs";
3993 } else if (strcmp(arg, "lttng-live") == 0) {
3994 auto_source_discovery_restrict_plugin_name = "ctf";
3995 auto_source_discovery_restrict_component_class_name = "lttng-live";
3996 implicit_lttng_live_args.exists = true;
3997 } else {
3998 BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy input format:\n %s",
3999 arg);
4000 goto error;
4001 }
4002 break;
4003 case OPT_OUTPUT_FORMAT:
4004 if (got_output_format_opt) {
4005 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output-format option.");
4006 goto error;
4007 }
4008
4009 got_output_format_opt = true;
4010
4011 if (strcmp(arg, "text") == 0) {
4012 implicit_text_args.exists = true;
4013 } else if (strcmp(arg, "ctf") == 0) {
4014 implicit_ctf_output_args.exists = true;
4015 } else if (strcmp(arg, "dummy") == 0) {
4016 implicit_dummy_args.exists = true;
4017 } else if (strcmp(arg, "ctf-metadata") == 0) {
4018 print_ctf_metadata = true;
4019 } else {
4020 BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy output format:\n %s",
4021 arg);
4022 goto error;
4023 }
4024 break;
4025 case OPT_OUTPUT:
4026 if (output) {
4027 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output option");
4028 goto error;
4029 }
4030
4031 output = strdup(arg);
4032 if (!output) {
4033 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4034 goto error;
4035 }
4036 break;
4037 case OPT_RUN_ARGS:
4038 if (print_run_args_0) {
4039 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0.");
4040 goto error;
4041 }
4042
4043 print_run_args = true;
4044 break;
4045 case OPT_RUN_ARGS_0:
4046 if (print_run_args) {
4047 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0.");
4048 goto error;
4049 }
4050
4051 print_run_args_0 = true;
4052 break;
4053 case OPT_STREAM_INTERSECTION:
4054 /*
4055 * Applies to all traces implementing the
4056 * babeltrace.trace-infos query.
4057 */
4058 stream_intersection_mode = true;
4059 break;
4060 case OPT_VERBOSE:
4061 *default_log_level =
4062 logging_level_min(*default_log_level, BT_LOG_INFO);
4063 break;
4064 case OPT_DEBUG:
4065 *default_log_level =
4066 logging_level_min(*default_log_level, BT_LOG_TRACE);
4067 break;
4068 case OPT_COMPONENT:
4069 case OPT_HELP:
4070 case OPT_LOG_LEVEL:
4071 case OPT_OMIT_HOME_PLUGIN_PATH:
4072 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
4073 case OPT_PARAMS:
4074 case OPT_PLUGIN_PATH:
4075 case OPT_RETRY_DURATION:
4076 /* Ignore in this pass */
4077 break;
4078 default:
4079 bt_common_abort();
4080 }
4081 }
4082
4083 set_auto_log_levels(default_log_level);
4084
4085 /*
4086 * Legacy behaviour: --verbose used to make the `text` output
4087 * format print more information. --verbose is now equivalent to
4088 * the INFO log level, which is why we compare to `BT_LOG_INFO`
4089 * here.
4090 */
4091 if (*default_log_level == BT_LOG_INFO) {
4092 append_implicit_component_param(&implicit_text_args,
4093 "verbose", "yes");
4094 }
4095
4096 /* Print CTF metadata or print LTTng live sessions */
4097 if (print_ctf_metadata) {
4098 const bt_value *bt_val_non_opt;
4099
4100 if (bt_value_array_is_empty(non_opts)) {
4101 BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf-metadata specified without a path.");
4102 goto error;
4103 }
4104
4105 if (bt_value_array_get_length(non_opts) > 1) {
4106 BT_CLI_LOGE_APPEND_CAUSE("Too many paths specified for --output-format=ctf-metadata.");
4107 goto error;
4108 }
4109
4110 status = bt_config_print_ctf_metadata_create(plugin_paths, &cfg);
4111 if (status != BT_CONFIG_CLI_ARGS_STATUS_OK) {
4112 goto end;
4113 }
4114
4115 bt_val_non_opt = bt_value_array_borrow_element_by_index_const(non_opts, 0);
4116 g_string_assign(cfg->cmd_data.print_ctf_metadata.path,
4117 bt_value_string_get(bt_val_non_opt));
4118
4119 if (output) {
4120 g_string_assign(
4121 cfg->cmd_data.print_ctf_metadata.output_path,
4122 output);
4123 }
4124
4125 BT_OBJECT_MOVE_REF(*cfg_out, cfg);
4126 goto end;
4127 }
4128
4129 /*
4130 * If -o ctf was specified, make sure an output path (--output)
4131 * was also specified. --output does not imply -o ctf because
4132 * it's also used for the default, implicit -o text if -o ctf
4133 * is not specified.
4134 */
4135 if (implicit_ctf_output_args.exists) {
4136 if (!output) {
4137 BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf specified without --output (trace output path).");
4138 goto error;
4139 }
4140
4141 /*
4142 * At this point we know that -o ctf AND --output were
4143 * specified. Make sure that no options were specified
4144 * which would imply -o text because --output would be
4145 * ambiguous in this case. For example, this is wrong:
4146 *
4147 * babeltrace2 --names=all -o ctf --output=/tmp/path my-trace
4148 *
4149 * because --names=all implies -o text, and --output
4150 * could apply to both the sink.text.pretty and
4151 * sink.ctf.fs implicit components.
4152 */
4153 if (implicit_text_args.exists) {
4154 BT_CLI_LOGE_APPEND_CAUSE("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text.");
4155 goto error;
4156 }
4157 }
4158
4159 /*
4160 * If -o dummy and -o ctf were not specified, and if there are
4161 * no explicit sink components, then use an implicit
4162 * `sink.text.pretty` component.
4163 */
4164 if (!implicit_dummy_args.exists && !implicit_ctf_output_args.exists &&
4165 !sink_names) {
4166 implicit_text_args.exists = true;
4167 }
4168
4169 /*
4170 * Set implicit `sink.text.pretty` or `sink.ctf.fs` component's
4171 * `path` parameter if --output was specified.
4172 */
4173 if (output) {
4174 if (implicit_text_args.exists) {
4175 append_implicit_component_extra_param(&implicit_text_args,
4176 "path", output);
4177 } else if (implicit_ctf_output_args.exists) {
4178 append_implicit_component_extra_param(&implicit_ctf_output_args,
4179 "path", output);
4180 }
4181 }
4182
4183 /* Decide where the non-option argument(s) go */
4184 if (bt_value_array_get_length(non_opts) > 0) {
4185 if (implicit_lttng_live_args.exists) {
4186 const bt_value *bt_val_non_opt;
4187
4188 if (bt_value_array_get_length(non_opts) > 1) {
4189 BT_CLI_LOGE_APPEND_CAUSE("Too many URLs specified for --input-format=lttng-live.");
4190 goto error;
4191 }
4192
4193 bt_val_non_opt = bt_value_array_borrow_element_by_index_const(non_opts, 0);
4194 lttng_live_url_parts =
4195 bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_non_opt),
4196 error_buf, sizeof(error_buf));
4197 if (!lttng_live_url_parts.proto) {
4198 BT_CLI_LOGE_APPEND_CAUSE("Invalid LTTng live URL format: %s.",
4199 error_buf);
4200 goto error;
4201 }
4202
4203 if (!lttng_live_url_parts.session_name) {
4204 /* Print LTTng live sessions */
4205 status = bt_config_print_lttng_live_sessions_create(
4206 plugin_paths, &cfg);
4207 if (status != BT_CONFIG_CLI_ARGS_STATUS_OK) {
4208 goto end;
4209 }
4210
4211 g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url,
4212 bt_value_string_get(bt_val_non_opt));
4213
4214 if (output) {
4215 g_string_assign(
4216 cfg->cmd_data.print_lttng_live_sessions.output_path,
4217 output);
4218 }
4219
4220 BT_OBJECT_MOVE_REF(*cfg_out, cfg);
4221 goto end;
4222 }
4223
4224 live_inputs_array_val = bt_value_array_create();
4225 if (!live_inputs_array_val) {
4226 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4227 goto error;
4228 }
4229
4230 if (bt_value_array_append_string_element(
4231 live_inputs_array_val,
4232 bt_value_string_get(bt_val_non_opt))) {
4233 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4234 goto error;
4235 }
4236
4237 ret = append_parameter_to_args(
4238 implicit_lttng_live_args.extra_params,
4239 "inputs", live_inputs_array_val);
4240 if (ret) {
4241 goto error;
4242 }
4243
4244 ret = append_implicit_component_extra_param(
4245 &implicit_lttng_live_args,
4246 "session-not-found-action", "end");
4247 if (ret) {
4248 goto error;
4249 }
4250 } else {
4251 size_t plugin_count;
4252 const bt_plugin **plugins;
4253 const bt_plugin *plugin;
4254 auto_source_discovery_status auto_disc_status;
4255
4256 ret = require_loaded_plugins(plugin_paths);
4257 if (ret != 0) {
4258 goto error;
4259 }
4260
4261 if (auto_source_discovery_restrict_plugin_name) {
4262 plugin_count = 1;
4263 plugin = borrow_loaded_plugin_by_name(auto_source_discovery_restrict_plugin_name);
4264 plugins = &plugin;
4265 } else {
4266 plugin_count = get_loaded_plugins_count();
4267 plugins = borrow_loaded_plugins();
4268 }
4269
4270 auto_disc_status = auto_discover_source_components(
4271 non_opts, plugins, plugin_count,
4272 auto_source_discovery_restrict_component_class_name,
4273 *default_log_level, &auto_disc, interrupter);
4274
4275 if (auto_disc_status != AUTO_SOURCE_DISCOVERY_STATUS_OK) {
4276 if (auto_disc_status == AUTO_SOURCE_DISCOVERY_STATUS_INTERRUPTED) {
4277 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(
4278 "Babeltrace CLI", "Automatic source discovery interrupted by the user");
4279 }
4280 goto error;
4281 }
4282
4283 ret = create_implicit_component_args_from_auto_discovered_sources(
4284 &auto_disc, non_opts, non_opt_params, non_opt_loglevels,
4285 discovered_source_args);
4286 if (ret != 0) {
4287 goto error;
4288 }
4289 }
4290 }
4291
4292
4293 /*
4294 * If --clock-force-correlated was given, apply it to any src.ctf.fs
4295 * component.
4296 */
4297 if (ctf_fs_source_force_clock_class_unix_epoch_origin) {
4298 int n;
4299
4300 n = append_multiple_implicit_components_param(
4301 discovered_source_args, "source.ctf.fs", "force-clock-class-origin-unix-epoch",
4302 "yes");
4303 if (n == 0) {
4304 BT_CLI_LOGE_APPEND_CAUSE("--clock-force-correlate specified, but no source.ctf.fs component instantiated.");
4305 goto error;
4306 }
4307 }
4308
4309 /* If --clock-offset was given, apply it to any src.ctf.fs component. */
4310 if (ctf_fs_source_clock_class_offset_arg) {
4311 int n;
4312
4313 n = append_multiple_implicit_components_param(
4314 discovered_source_args, "source.ctf.fs", "clock-class-offset-s",
4315 ctf_fs_source_clock_class_offset_arg);
4316
4317 if (n == 0) {
4318 BT_CLI_LOGE_APPEND_CAUSE("--clock-offset specified, but no source.ctf.fs component instantiated.");
4319 goto error;
4320 }
4321 }
4322
4323 /* If --clock-offset-ns was given, apply it to any src.ctf.fs component. */
4324 if (ctf_fs_source_clock_class_offset_ns_arg) {
4325 int n;
4326
4327 n = append_multiple_implicit_components_param(
4328 discovered_source_args, "source.ctf.fs", "clock-class-offset-ns",
4329 ctf_fs_source_clock_class_offset_ns_arg);
4330
4331 if (n == 0) {
4332 BT_CLI_LOGE_APPEND_CAUSE("--clock-offset-ns specified, but no source.ctf.fs component instantiated.");
4333 goto error;
4334 }
4335 }
4336
4337 /*
4338 * If the implicit `source.ctf.lttng-live` component exists,
4339 * make sure there's at least one non-option argument (which is
4340 * the URL).
4341 */
4342 if (implicit_lttng_live_args.exists && bt_value_array_is_empty(non_opts)) {
4343 BT_CLI_LOGE_APPEND_CAUSE("Missing URL for implicit `%s` component.",
4344 implicit_lttng_live_args.comp_arg->str);
4345 goto error;
4346 }
4347
4348 /* Assign names to implicit components */
4349 for (i = 0; i < discovered_source_args->len; i++) {
4350 struct implicit_component_args *args;
4351 int j;
4352
4353 args = discovered_source_args->pdata[i];
4354
4355 g_string_printf(auto_disc_comp_name, "auto-disc-%s", args->comp_arg->str);
4356
4357 /* Give it a name like `auto-disc-src-ctf-fs`. */
4358 for (j = 0; j < auto_disc_comp_name->len; j++) {
4359 if (auto_disc_comp_name->str[j] == '.') {
4360 auto_disc_comp_name->str[j] = '-';
4361 }
4362 }
4363
4364 ret = assign_name_to_implicit_component(args,
4365 auto_disc_comp_name->str, all_names, &source_names, true);
4366 if (ret) {
4367 goto error;
4368 }
4369 }
4370
4371 ret = assign_name_to_implicit_component(&implicit_lttng_live_args,
4372 "lttng-live", all_names, &source_names, true);
4373 if (ret) {
4374 goto error;
4375 }
4376
4377 ret = assign_name_to_implicit_component(&implicit_text_args,
4378 "pretty", all_names, &sink_names, true);
4379 if (ret) {
4380 goto error;
4381 }
4382
4383 ret = assign_name_to_implicit_component(&implicit_ctf_output_args,
4384 "sink-ctf-fs", all_names, &sink_names, true);
4385 if (ret) {
4386 goto error;
4387 }
4388
4389 ret = assign_name_to_implicit_component(&implicit_dummy_args,
4390 "dummy", all_names, &sink_names, true);
4391 if (ret) {
4392 goto error;
4393 }
4394
4395 ret = assign_name_to_implicit_component(&implicit_muxer_args,
4396 "muxer", all_names, NULL, false);
4397 if (ret) {
4398 goto error;
4399 }
4400
4401 ret = assign_name_to_implicit_component(&implicit_trimmer_args,
4402 "trimmer", all_names, NULL, false);
4403 if (ret) {
4404 goto error;
4405 }
4406
4407 ret = assign_name_to_implicit_component(&implicit_debug_info_args,
4408 "debug-info", all_names, NULL, false);
4409 if (ret) {
4410 goto error;
4411 }
4412
4413 /* Make sure there's at least one source and one sink */
4414 if (!source_names) {
4415 BT_CLI_LOGE_APPEND_CAUSE("No source component.");
4416 goto error;
4417 }
4418
4419 if (!sink_names) {
4420 BT_CLI_LOGE_APPEND_CAUSE("No sink component.");
4421 goto error;
4422 }
4423
4424 /* Make sure there's a single sink component */
4425 if (g_list_length(sink_names) != 1) {
4426 BT_CLI_LOGE_APPEND_CAUSE(
4427 "More than one sink component specified.");
4428 goto error;
4429 }
4430
4431 /*
4432 * Prepend the muxer, the trimmer, and the debug info to the
4433 * filter chain so that we have:
4434 *
4435 * sources -> muxer -> [trimmer] -> [debug info] ->
4436 * [user filters] -> sinks
4437 */
4438 if (implicit_debug_info_args.exists) {
4439 if (g_list_prepend_gstring(&filter_names,
4440 implicit_debug_info_args.name_arg->str)) {
4441 goto error;
4442 }
4443 }
4444
4445 if (implicit_trimmer_args.exists) {
4446 if (g_list_prepend_gstring(&filter_names,
4447 implicit_trimmer_args.name_arg->str)) {
4448 goto error;
4449 }
4450 }
4451
4452 if (g_list_prepend_gstring(&filter_names,
4453 implicit_muxer_args.name_arg->str)) {
4454 goto error;
4455 }
4456
4457 /*
4458 * Append the equivalent run arguments for the implicit
4459 * components.
4460 */
4461 for (i = 0; i < discovered_source_args->len; i++) {
4462 struct implicit_component_args *args =
4463 discovered_source_args->pdata[i];
4464
4465 ret = append_run_args_for_implicit_component(args, run_args);
4466 if (ret) {
4467 goto error;
4468 }
4469 }
4470
4471 ret = append_run_args_for_implicit_component(&implicit_lttng_live_args,
4472 run_args);
4473 if (ret) {
4474 goto error;
4475 }
4476
4477 ret = append_run_args_for_implicit_component(&implicit_text_args,
4478 run_args);
4479 if (ret) {
4480 goto error;
4481 }
4482
4483 ret = append_run_args_for_implicit_component(&implicit_ctf_output_args,
4484 run_args);
4485 if (ret) {
4486 goto error;
4487 }
4488
4489 ret = append_run_args_for_implicit_component(&implicit_dummy_args,
4490 run_args);
4491 if (ret) {
4492 goto error;
4493 }
4494
4495 ret = append_run_args_for_implicit_component(&implicit_muxer_args,
4496 run_args);
4497 if (ret) {
4498 goto error;
4499 }
4500
4501 ret = append_run_args_for_implicit_component(&implicit_trimmer_args,
4502 run_args);
4503 if (ret) {
4504 goto error;
4505 }
4506
4507 ret = append_run_args_for_implicit_component(&implicit_debug_info_args,
4508 run_args);
4509 if (ret) {
4510 goto error;
4511 }
4512
4513 /* Auto-connect components */
4514 ret = convert_auto_connect(run_args, source_names, filter_names,
4515 sink_names);
4516 if (ret) {
4517 BT_CLI_LOGE_APPEND_CAUSE("Cannot auto-connect components.");
4518 goto error;
4519 }
4520
4521 /*
4522 * We have all the run command arguments now. Depending on
4523 * --run-args, we pass this to the run command or print them
4524 * here.
4525 */
4526 if (print_run_args || print_run_args_0) {
4527 uint64_t args_idx, args_len;
4528 if (stream_intersection_mode) {
4529 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --stream-intersection with --run-args or --run-args-0.");
4530 goto error;
4531 }
4532
4533 args_len = bt_value_array_get_length(run_args);
4534 for (args_idx = 0; args_idx < args_len; args_idx++) {
4535 const bt_value *arg_value =
4536 bt_value_array_borrow_element_by_index(run_args,
4537 args_idx);
4538 const char *arg;
4539 GString *quoted = NULL;
4540 const char *arg_to_print;
4541
4542 arg = bt_value_string_get(arg_value);
4543
4544 if (print_run_args) {
4545 quoted = bt_common_shell_quote(arg, true);
4546 if (!quoted) {
4547 goto error;
4548 }
4549
4550 arg_to_print = quoted->str;
4551 } else {
4552 arg_to_print = arg;
4553 }
4554
4555 printf("%s", arg_to_print);
4556
4557 if (quoted) {
4558 g_string_free(quoted, TRUE);
4559 }
4560
4561 if (args_idx < args_len - 1) {
4562 if (print_run_args) {
4563 putchar(' ');
4564 } else {
4565 putchar('\0');
4566 }
4567 }
4568 }
4569
4570 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
4571 goto end;
4572 }
4573
4574 status = bt_config_run_from_args_array(run_args, &cfg,
4575 plugin_paths, *default_log_level);
4576 if (status != BT_CONFIG_CLI_ARGS_STATUS_OK) {
4577 goto end;
4578 }
4579
4580 cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode;
4581 BT_OBJECT_MOVE_REF(*cfg_out, cfg);
4582 goto end;
4583
4584 error:
4585 status = BT_CONFIG_CLI_ARGS_STATUS_ERROR;
4586
4587 end:
4588 argpar_iter_destroy(argpar_iter);
4589 argpar_item_destroy(argpar_item);
4590
4591 free(output);
4592
4593 if (component_arg_for_run) {
4594 g_string_free(component_arg_for_run, TRUE);
4595 }
4596
4597 if (name_gstr) {
4598 g_string_free(name_gstr, TRUE);
4599 }
4600
4601 bt_value_put_ref(live_inputs_array_val);
4602 bt_value_put_ref(run_args);
4603 bt_value_put_ref(all_names);
4604 destroy_glist_of_gstring(source_names);
4605 destroy_glist_of_gstring(filter_names);
4606 destroy_glist_of_gstring(sink_names);
4607 bt_value_put_ref(non_opt_params);
4608 bt_value_put_ref(non_opt_loglevels);
4609 bt_value_put_ref(non_opts);
4610 finalize_implicit_component_args(&implicit_ctf_output_args);
4611 finalize_implicit_component_args(&implicit_lttng_live_args);
4612 finalize_implicit_component_args(&implicit_dummy_args);
4613 finalize_implicit_component_args(&implicit_text_args);
4614 finalize_implicit_component_args(&implicit_debug_info_args);
4615 finalize_implicit_component_args(&implicit_muxer_args);
4616 finalize_implicit_component_args(&implicit_trimmer_args);
4617 bt_common_destroy_lttng_live_url_parts(&lttng_live_url_parts);
4618 auto_source_discovery_fini(&auto_disc);
4619
4620 if (discovered_source_args) {
4621 g_ptr_array_free(discovered_source_args, TRUE);
4622 }
4623
4624 g_free(ctf_fs_source_clock_class_offset_arg);
4625 g_free(ctf_fs_source_clock_class_offset_ns_arg);
4626
4627 if (auto_disc_comp_name) {
4628 g_string_free(auto_disc_comp_name, TRUE);
4629 }
4630
4631 bt_object_put_ref(cfg);
4632
4633 return status;
4634 }
4635
4636 /*
4637 * Prints the Babeltrace 2.x general usage.
4638 */
4639 static
4640 void print_gen_usage(FILE *fp)
4641 {
4642 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS]\n");
4643 fprintf(fp, "\n");
4644 fprintf(fp, "General options:\n");
4645 fprintf(fp, "\n");
4646 fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=T)\n");
4647 fprintf(fp, " -h, --help Show this help and quit\n");
4648 fprintf(fp, " -l, --log-level=LVL Set the default log level to LVL (`N`, `T`, `D`,\n");
4649 fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n");
4650 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
4651 fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n");
4652 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
4653 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
4654 fprintf(fp, " dynamic plugins can be loaded\n");
4655 fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n");
4656 fprintf(fp, " -V, --version Show version and quit\n");
4657 fprintf(fp, "\n");
4658 fprintf(fp, "Available commands:\n");
4659 fprintf(fp, "\n");
4660 fprintf(fp, " convert Convert and trim traces (default)\n");
4661 fprintf(fp, " help Get help for a plugin or a component class\n");
4662 fprintf(fp, " list-plugins List available plugins and their content\n");
4663 fprintf(fp, " query Query objects from a component class\n");
4664 fprintf(fp, " run Build a processing graph and run it\n");
4665 fprintf(fp, "\n");
4666 fprintf(fp, "Use `babeltrace2 COMMAND --help` to show the help of COMMAND.\n");
4667 }
4668
4669 enum bt_config_cli_args_status bt_config_cli_args_create(int argc,
4670 const char *argv[], struct bt_config **cfg,
4671 bool omit_system_plugin_path,
4672 bool omit_home_plugin_path,
4673 const bt_value *initial_plugin_paths,
4674 const bt_interrupter *interrupter)
4675 {
4676 enum bt_config_cli_args_status status;
4677 int top_level_argc;
4678 const char **top_level_argv;
4679 int command_argc = -1;
4680 const char **command_argv = NULL;
4681 const char *command_name = NULL;
4682 int default_log_level = -1;
4683 struct argpar_iter *argpar_iter = NULL;
4684 const struct argpar_item *argpar_item = NULL;
4685 const struct argpar_error *argpar_error = NULL;
4686 bt_value *plugin_paths = NULL;
4687 unsigned int consumed_args;
4688
4689 /* Top-level option descriptions. */
4690 static const struct argpar_opt_descr descrs[] = {
4691 { OPT_DEBUG, 'd', "debug", false },
4692 { OPT_HELP, 'h', "help", false },
4693 { OPT_LOG_LEVEL, 'l', "log-level", true },
4694 { OPT_VERBOSE, 'v', "verbose", false },
4695 { OPT_VERSION, 'V', "version", false},
4696 { OPT_OMIT_HOME_PLUGIN_PATH, '\0', "omit-home-plugin-path", false },
4697 { OPT_OMIT_SYSTEM_PLUGIN_PATH, '\0', "omit-system-plugin-path", false },
4698 { OPT_PLUGIN_PATH, '\0', "plugin-path", true },
4699 ARGPAR_OPT_DESCR_SENTINEL
4700 };
4701
4702 enum command_type {
4703 COMMAND_TYPE_NONE = -1,
4704 COMMAND_TYPE_RUN = 0,
4705 COMMAND_TYPE_CONVERT,
4706 COMMAND_TYPE_LIST_PLUGINS,
4707 COMMAND_TYPE_HELP,
4708 COMMAND_TYPE_QUERY,
4709 } command_type = COMMAND_TYPE_NONE;
4710
4711 if (!initial_plugin_paths) {
4712 plugin_paths = bt_value_array_create();
4713 if (!plugin_paths) {
4714 goto error;
4715 }
4716 } else {
4717 bt_value_copy_status copy_status = bt_value_copy(
4718 initial_plugin_paths, &plugin_paths);
4719 if (copy_status) {
4720 goto error;
4721 }
4722 }
4723
4724 BT_ASSERT(plugin_paths);
4725
4726 /*
4727 * The `BABELTRACE_PLUGIN_PATH` paths take precedence over the
4728 * `--plugin-path` option's paths, so append it now before
4729 * parsing the general options.
4730 */
4731 if (append_env_var_plugin_paths(plugin_paths)) {
4732 goto error;
4733 }
4734
4735 if (argc <= 1) {
4736 print_version();
4737 puts("");
4738 print_gen_usage(stdout);
4739 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
4740 goto end;
4741 }
4742
4743 /* Skip first argument, the name of the program. */
4744 top_level_argc = argc - 1;
4745 top_level_argv = argv + 1;
4746
4747 argpar_iter = argpar_iter_create(top_level_argc, top_level_argv, descrs);
4748 if (!argpar_iter) {
4749 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4750 goto error;
4751 }
4752
4753 while (true) {
4754 enum argpar_iter_next_status argpar_status;
4755
4756 ARGPAR_ITEM_DESTROY_AND_RESET(argpar_item);
4757 argpar_status = argpar_iter_next(argpar_iter, &argpar_item, &argpar_error);
4758
4759 switch (argpar_status) {
4760 case ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY:
4761 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4762 goto error;
4763 case ARGPAR_ITER_NEXT_STATUS_ERROR:
4764 {
4765 if (argpar_error_type(argpar_error)
4766 != ARGPAR_ERROR_TYPE_UNKNOWN_OPT) {
4767 GString *err_str = format_arg_error(argpar_error, top_level_argv,
4768 0, "While parsing command-line arguments");
4769 BT_CLI_LOGE_APPEND_CAUSE("%s", err_str->str);
4770 g_string_free(err_str, TRUE);
4771 goto error;
4772 }
4773
4774 break;
4775 }
4776 default:
4777 break;
4778 }
4779
4780 if (argpar_status == ARGPAR_ITER_NEXT_STATUS_END) {
4781 break;
4782 }
4783
4784 if (argpar_status == ARGPAR_ITER_NEXT_STATUS_ERROR) {
4785 BT_ASSERT(argpar_error_type(argpar_error) ==
4786 ARGPAR_ERROR_TYPE_UNKNOWN_OPT);
4787 /*
4788 * Unknown option, assume this is implicitly the
4789 * convert command, stop processing arguments.
4790 */
4791 break;
4792 }
4793
4794 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
4795 const struct argpar_opt_descr *opt_descr =
4796 argpar_item_opt_descr(argpar_item);
4797 const char *arg = argpar_item_opt_arg(argpar_item);
4798
4799 switch (opt_descr->id) {
4800 case OPT_DEBUG:
4801 default_log_level =
4802 logging_level_min(default_log_level, BT_LOG_TRACE);
4803 break;
4804 case OPT_VERBOSE:
4805 default_log_level =
4806 logging_level_min(default_log_level, BT_LOG_INFO);
4807 break;
4808 case OPT_LOG_LEVEL:
4809 {
4810 int level = bt_log_get_level_from_string(arg);
4811
4812 if (level < 0) {
4813 BT_CLI_LOGE_APPEND_CAUSE(
4814 "Invalid argument for --log-level option:\n %s",
4815 arg);
4816 goto error;
4817 }
4818
4819 default_log_level =
4820 logging_level_min(default_log_level, level);
4821 break;
4822 }
4823 case OPT_PLUGIN_PATH:
4824 if (bt_config_append_plugin_paths_check_setuid_setgid(
4825 plugin_paths, arg)) {
4826 goto error;
4827 }
4828 break;
4829 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
4830 omit_system_plugin_path = true;
4831 break;
4832 case OPT_OMIT_HOME_PLUGIN_PATH:
4833 omit_home_plugin_path = true;
4834 break;
4835 case OPT_VERSION:
4836 print_version();
4837 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
4838 goto end;
4839 case OPT_HELP:
4840 print_gen_usage(stdout);
4841 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
4842 goto end;
4843 default:
4844 bt_common_abort();
4845 }
4846 } else {
4847 const char *arg = argpar_item_non_opt_arg(argpar_item);
4848 unsigned int orig_index = argpar_item_non_opt_orig_index(argpar_item);
4849
4850 /*
4851 * First unknown argument: is it a known command
4852 * name?
4853 */
4854 command_argc = top_level_argc - orig_index - 1;
4855 command_argv = &top_level_argv[orig_index + 1];
4856
4857 if (strcmp(arg, "convert") == 0) {
4858 command_type = COMMAND_TYPE_CONVERT;
4859 command_name = "convert";
4860 } else if (strcmp(arg, "list-plugins") == 0) {
4861 command_type = COMMAND_TYPE_LIST_PLUGINS;
4862 command_name = "list-plugins";
4863 } else if (strcmp(arg, "help") == 0) {
4864 command_type = COMMAND_TYPE_HELP;
4865 command_name = "help";
4866 } else if (strcmp(arg, "query") == 0) {
4867 command_type = COMMAND_TYPE_QUERY;
4868 command_name = "query";
4869 } else if (strcmp(arg, "run") == 0) {
4870 command_type = COMMAND_TYPE_RUN;
4871 command_name = "run";
4872 } else {
4873 /*
4874 * Non-option argument, but not a known
4875 * command name: assume the default
4876 * `convert` command.
4877 */
4878 command_type = COMMAND_TYPE_CONVERT;
4879 command_name = "convert";
4880 command_argc++;
4881 command_argv--;
4882 }
4883
4884 /* Stop processing arguments. */
4885 break;
4886 }
4887 }
4888
4889 if (command_type == COMMAND_TYPE_NONE) {
4890 unsigned int ingested_orig_args = argpar_iter_ingested_orig_args(argpar_iter);
4891
4892 if (ingested_orig_args == top_level_argc) {
4893 /*
4894 * We only got non-help, non-version general options
4895 * like --verbose and --debug, without any other
4896 * arguments, so we can't do anything useful: print the
4897 * usage and quit.
4898 */
4899 print_gen_usage(stdout);
4900 status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY;
4901 goto end;
4902 }
4903
4904 /*
4905 * We stopped on an unknown option argument (and therefore
4906 * didn't see a command name). Assume `convert` command.
4907 */
4908 command_type = COMMAND_TYPE_CONVERT;
4909 command_name = "convert";
4910 command_argc = top_level_argc - ingested_orig_args;
4911 command_argv = &top_level_argv[ingested_orig_args];
4912 }
4913
4914 BT_ASSERT(command_argv);
4915 BT_ASSERT(command_argc >= 0);
4916
4917 /*
4918 * For all commands other than `convert`, we now know the log level to
4919 * use, so we can apply it with `set_auto_log_levels`.
4920 *
4921 * The convert command has `--debug` and `--verbose` arguments that are
4922 * equivalent to the top-level arguments of the same name. So after it
4923 * has parsed its arguments, `bt_config_convert_from_args` calls
4924 * `set_auto_log_levels` itself.
4925 */
4926 if (command_type != COMMAND_TYPE_CONVERT) {
4927 set_auto_log_levels(&default_log_level);
4928 }
4929
4930 /*
4931 * At this point, `plugin_paths` contains the initial plugin
4932 * paths, the paths from the `BABELTRACE_PLUGIN_PATH` paths, and
4933 * the paths from the `--plugin-path` option.
4934 *
4935 * Now append the user and system plugin paths.
4936 */
4937 if (append_home_and_system_plugin_paths(plugin_paths,
4938 omit_system_plugin_path, omit_home_plugin_path)) {
4939 goto error;
4940 }
4941
4942 consumed_args = argpar_iter_ingested_orig_args(argpar_iter);
4943
4944 switch (command_type) {
4945 case COMMAND_TYPE_RUN:
4946 status = bt_config_run_from_args(command_argc, command_argv,
4947 cfg, plugin_paths,
4948 default_log_level, consumed_args);
4949 break;
4950 case COMMAND_TYPE_CONVERT:
4951 status = bt_config_convert_from_args(command_argc, command_argv,
4952 cfg, plugin_paths, &default_log_level, interrupter,
4953 consumed_args);
4954 break;
4955 case COMMAND_TYPE_LIST_PLUGINS:
4956 status = bt_config_list_plugins_from_args(command_argc,
4957 command_argv, cfg, plugin_paths, consumed_args);
4958 break;
4959 case COMMAND_TYPE_HELP:
4960 status = bt_config_help_from_args(command_argc,
4961 command_argv, cfg, plugin_paths,
4962 default_log_level, consumed_args);
4963 break;
4964 case COMMAND_TYPE_QUERY:
4965 status = bt_config_query_from_args(command_argc,
4966 command_argv, cfg, plugin_paths,
4967 default_log_level, consumed_args);
4968 break;
4969 default:
4970 bt_common_abort();
4971 }
4972
4973 if (status == BT_CONFIG_CLI_ARGS_STATUS_ERROR) {
4974 goto error;
4975 } else if (status == BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY) {
4976 goto end;
4977 }
4978
4979 BT_ASSERT(status == BT_CONFIG_CLI_ARGS_STATUS_OK);
4980 BT_ASSERT(*cfg);
4981 BT_ASSERT(default_log_level >= BT_LOG_TRACE);
4982 (*cfg)->log_level = default_log_level;
4983 (*cfg)->command_name = command_name;
4984
4985 status = BT_CONFIG_CLI_ARGS_STATUS_OK;
4986 goto end;
4987
4988 error:
4989 status = BT_CONFIG_CLI_ARGS_STATUS_ERROR;
4990
4991 end:
4992 argpar_error_destroy(argpar_error);
4993 argpar_item_destroy(argpar_item);
4994 argpar_iter_destroy(argpar_iter);
4995 bt_value_put_ref(plugin_paths);
4996 return status;
4997 }
This page took 0.132734 seconds and 4 git commands to generate.