1d6dda97c5ee9b30661b9c3605311977c5411d8a
[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 struct bt_config *bt_config_print_ctf_metadata_create(
1162 const bt_value *plugin_paths)
1163 {
1164 struct bt_config *cfg;
1165
1166 /* Create config */
1167 cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_CTF_METADATA,
1168 plugin_paths, true);
1169 if (!cfg) {
1170 goto error;
1171 }
1172
1173 cfg->cmd_data.print_ctf_metadata.path = g_string_new(NULL);
1174 if (!cfg->cmd_data.print_ctf_metadata.path) {
1175 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1176 goto error;
1177 }
1178
1179 cfg->cmd_data.print_ctf_metadata.output_path = g_string_new(NULL);
1180 if (!cfg->cmd_data.print_ctf_metadata.output_path) {
1181 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1182 goto error;
1183 }
1184
1185 goto end;
1186
1187 error:
1188 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1189
1190 end:
1191 return cfg;
1192 }
1193
1194 static
1195 struct bt_config *bt_config_print_lttng_live_sessions_create(
1196 const bt_value *plugin_paths)
1197 {
1198 struct bt_config *cfg;
1199
1200 /* Create config */
1201 cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS,
1202 plugin_paths, true);
1203 if (!cfg) {
1204 goto error;
1205 }
1206
1207 cfg->cmd_data.print_lttng_live_sessions.url = g_string_new(NULL);
1208 if (!cfg->cmd_data.print_lttng_live_sessions.url) {
1209 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1210 goto error;
1211 }
1212
1213 cfg->cmd_data.print_lttng_live_sessions.output_path =
1214 g_string_new(NULL);
1215 if (!cfg->cmd_data.print_lttng_live_sessions.output_path) {
1216 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1217 goto error;
1218 }
1219
1220 goto end;
1221
1222 error:
1223 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1224
1225 end:
1226 return cfg;
1227 }
1228
1229 static
1230 int bt_config_append_plugin_paths_check_setuid_setgid(
1231 bt_value *plugin_paths, const char *arg)
1232 {
1233 int ret = 0;
1234
1235 if (bt_common_is_setuid_setgid()) {
1236 BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary.");
1237 goto end;
1238 }
1239
1240 if (bt_config_append_plugin_paths(plugin_paths, arg)) {
1241 BT_CLI_LOGE_APPEND_CAUSE("Invalid --plugin-path option's argument:\n %s",
1242 arg);
1243 ret = -1;
1244 goto end;
1245 }
1246
1247 end:
1248 return ret;
1249 }
1250
1251 /*
1252 * Prints the expected format for a --params option.
1253 */
1254 static
1255 void print_expected_params_format(FILE *fp)
1256 {
1257 fprintf(fp, "Expected format of PARAMS\n");
1258 fprintf(fp, "-------------------------\n");
1259 fprintf(fp, "\n");
1260 fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n");
1261 fprintf(fp, "\n");
1262 fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n");
1263 fprintf(fp, "where PARAM is the parameter name (C identifier plus the [:.-] characters),\n");
1264 fprintf(fp, "and VALUE can be one of:\n");
1265 fprintf(fp, "\n");
1266 fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n");
1267 fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n");
1268 fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n");
1269 fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n");
1270 fprintf(fp, " (`0x` prefix) unsigned (with `+` prefix) or signed 64-bit integer.\n");
1271 fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n");
1272 fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n");
1273 fprintf(fp, " the null and boolean value symbols above.\n");
1274 fprintf(fp, "* Double-quoted string (accepts escape characters).\n");
1275 fprintf(fp, "* Array, formatted as an opening `[`, a list of comma-separated values\n");
1276 fprintf(fp, " (as described by the current list) and a closing `]`.\n");
1277 fprintf(fp, "* Map, formatted as an opening `{`, a comma-separated list of PARAM=VALUE\n");
1278 fprintf(fp, " assignments and a closing `}`.\n");
1279 fprintf(fp, "\n");
1280 fprintf(fp, "You can put whitespaces allowed around individual `=` and `,` symbols.\n");
1281 fprintf(fp, "\n");
1282 fprintf(fp, "Example:\n");
1283 fprintf(fp, "\n");
1284 fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n");
1285 fprintf(fp, " play=+23, observe=3.14, simple=beef, needs-quotes=\"some string\",\n");
1286 fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\",\n");
1287 fprintf(fp, " things=[1, \"2\", 3]\n");
1288 fprintf(fp, "\n");
1289 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n");
1290 fprintf(fp, "babeltrace2 from a shell.\n");
1291 }
1292
1293 /*
1294 * Given argpar error status `status` and error `error`, return a formatted
1295 * error message describing the error.
1296 *
1297 * `argv` is the argument vector that was being parsed.
1298 *
1299 * `prefix_fmt` (formatted using the following arguments) is prepended to
1300 * the error message.
1301 *
1302 * The returned string must be freed by the caller.
1303 */
1304 static
1305 GString *__BT_ATTR_FORMAT_PRINTF(4, 5) format_arg_error(
1306 const struct argpar_error *error,
1307 const char **argv, unsigned int arg_index_offset,
1308 const char *prefix_fmt, ...)
1309 {
1310 GString *str = g_string_new(NULL);
1311 va_list args;
1312
1313 va_start(args, prefix_fmt);
1314 g_string_append_vprintf(str, prefix_fmt, args);
1315 va_end(args);
1316
1317 g_string_append(str, ": ");
1318
1319 switch (argpar_error_type(error))
1320 {
1321 case ARGPAR_ERROR_TYPE_MISSING_OPT_ARG:
1322 {
1323 bool is_short;
1324 const struct argpar_opt_descr *descr =
1325 argpar_error_opt_descr(error, &is_short);
1326 int orig_index = argpar_error_orig_index(error);
1327 const char *arg = argv[orig_index];
1328
1329 if (is_short) {
1330 g_string_append_printf(
1331 str,
1332 WHILE_PARSING_ARG_N_FMT "Missing required argument for option `-%c`",
1333 arg_index_offset + orig_index + 1, arg, descr->short_name);
1334 } else {
1335 g_string_append_printf(
1336 str,
1337 WHILE_PARSING_ARG_N_FMT "Missing required argument for option `--%s`",
1338 arg_index_offset + orig_index + 1, arg, descr->long_name);
1339 }
1340
1341 break;
1342 }
1343 case ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG:
1344 {
1345 bool is_short;
1346 const struct argpar_opt_descr *descr =
1347 argpar_error_opt_descr(error, &is_short);
1348 int orig_index = argpar_error_orig_index(error);
1349 const char *arg = argv[orig_index];
1350
1351 if (is_short) {
1352 g_string_append_printf(
1353 str,
1354 WHILE_PARSING_ARG_N_FMT "Unexpected argument for option `-%c`",
1355 arg_index_offset + orig_index + 1, arg, descr->short_name);
1356 } else {
1357 g_string_append_printf(
1358 str,
1359 WHILE_PARSING_ARG_N_FMT "Unexpected argument for option `--%s`",
1360 arg_index_offset + orig_index + 1, arg, descr->long_name);
1361 }
1362
1363 break;
1364 }
1365 case ARGPAR_ERROR_TYPE_UNKNOWN_OPT:
1366 {
1367 int orig_index = argpar_error_orig_index(error);
1368 const char *unknown_opt = argpar_error_unknown_opt_name(error);
1369 const char *arg = argv[orig_index];
1370
1371 g_string_append_printf(
1372 str,
1373 WHILE_PARSING_ARG_N_FMT "Unknown option `%s`",
1374 arg_index_offset + orig_index + 1, arg, unknown_opt);
1375
1376 break;
1377 }
1378
1379 default:
1380 BT_ASSERT(0);
1381 }
1382
1383 return str;
1384 }
1385
1386 enum parse_next_item_status
1387 {
1388 PARSE_NEXT_ITEM_STATUS_OK = 0,
1389 PARSE_NEXT_ITEM_STATUS_END = 1,
1390 PARSE_NEXT_ITEM_STATUS_ERROR = -1,
1391 };
1392
1393 /*
1394 * Parse the next item using `iter`. Log and append an error if necessary.
1395 *
1396 * The item in `*item` on entry is freed, and the new item is also
1397 * returned in `*item`.
1398 */
1399 static
1400 enum parse_next_item_status parse_next_item(struct argpar_iter *iter,
1401 const struct argpar_item **item, const char **argv,
1402 const char *command, unsigned int consumed_args)
1403 {
1404 enum argpar_iter_next_status status;
1405 const struct argpar_error *error = NULL;
1406 enum parse_next_item_status ret;
1407
1408 ARGPAR_ITEM_DESTROY_AND_RESET(*item);
1409 status = argpar_iter_next(iter, item, &error);
1410
1411 switch (status) {
1412 case ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY:
1413 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1414 ret = PARSE_NEXT_ITEM_STATUS_ERROR;
1415 break;
1416 case ARGPAR_ITER_NEXT_STATUS_ERROR:
1417 {
1418 GString *err_str = format_arg_error(error, argv,
1419 consumed_args,
1420 "While parsing `%s` command's command-line arguments",
1421 command);
1422 BT_CLI_LOGE_APPEND_CAUSE("%s", err_str->str);
1423 g_string_free(err_str, TRUE);
1424 ret = PARSE_NEXT_ITEM_STATUS_ERROR;
1425 break;
1426 }
1427 case ARGPAR_ITER_NEXT_STATUS_END:
1428 ret = PARSE_NEXT_ITEM_STATUS_END;
1429 break;
1430 case ARGPAR_ITER_NEXT_STATUS_OK:
1431 ret = PARSE_NEXT_ITEM_STATUS_OK;
1432 break;
1433 default:
1434 bt_common_abort();
1435 }
1436
1437 argpar_error_destroy(error);
1438 return ret;
1439 }
1440
1441
1442 /*
1443 * Prints the help command usage.
1444 */
1445 static
1446 void print_help_usage(FILE *fp)
1447 {
1448 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] help [OPTIONS] PLUGIN\n");
1449 fprintf(fp, " babeltrace2 [GENERAL OPTIONS] help [OPTIONS] TYPE.PLUGIN.CLS\n");
1450 fprintf(fp, "\n");
1451 fprintf(fp, "Options:\n");
1452 fprintf(fp, "\n");
1453 fprintf(fp, " -h, --help Show this help and quit\n");
1454 fprintf(fp, "\n");
1455 fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n");
1456 fprintf(fp, "\n");
1457 fprintf(fp, "Use `babeltrace2 list-plugins` to show the list of available plugins.\n");
1458 }
1459
1460 static
1461 const struct argpar_opt_descr help_options[] = {
1462 /* id, short_name, long_name, with_arg */
1463 { OPT_HELP, 'h', "help", false },
1464 ARGPAR_OPT_DESCR_SENTINEL
1465 };
1466
1467 /*
1468 * Creates a Babeltrace config object from the arguments of a help
1469 * command.
1470 *
1471 * *retcode is set to the appropriate exit code to use.
1472 */
1473 static
1474 struct bt_config *bt_config_help_from_args(int argc, const char *argv[],
1475 int *retcode, const bt_value *plugin_paths,
1476 int default_log_level, unsigned int consumed_args)
1477 {
1478 struct bt_config *cfg = NULL;
1479 const char *plugin_comp_cls_arg = NULL;
1480 char *plugin_name = NULL, *comp_cls_name = NULL;
1481 GString *substring = NULL;
1482 size_t end_pos;
1483 struct argpar_iter *argpar_iter = NULL;
1484 const struct argpar_item *argpar_item = NULL;
1485
1486 *retcode = 0;
1487 cfg = bt_config_help_create(plugin_paths, default_log_level);
1488 if (!cfg) {
1489 goto error;
1490 }
1491
1492 argpar_iter = argpar_iter_create(argc, argv, help_options);
1493 if (!argpar_iter) {
1494 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1495 goto error;
1496 }
1497
1498 while (true) {
1499 enum parse_next_item_status status =
1500 parse_next_item(argpar_iter, &argpar_item, argv, "help",
1501 consumed_args);
1502
1503 if (status == PARSE_NEXT_ITEM_STATUS_ERROR) {
1504 goto error;
1505 } else if (status == PARSE_NEXT_ITEM_STATUS_END) {
1506 break;
1507 }
1508
1509 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
1510 const struct argpar_opt_descr *opt_descr =
1511 argpar_item_opt_descr(argpar_item);
1512
1513 switch (opt_descr->id) {
1514 case OPT_HELP:
1515 print_help_usage(stdout);
1516 *retcode = -1;
1517 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1518 goto end;
1519 default:
1520 bt_common_abort();
1521 }
1522 } else {
1523 const char *arg = argpar_item_non_opt_arg(argpar_item);
1524
1525 if (plugin_comp_cls_arg) {
1526 BT_CLI_LOGE_APPEND_CAUSE(
1527 "Extraneous command-line argument specified to `help` command: `%s`.",
1528 arg);
1529 goto error;
1530 }
1531
1532 plugin_comp_cls_arg = arg;
1533 }
1534 }
1535
1536 if (!plugin_comp_cls_arg) {
1537 BT_CLI_LOGE_APPEND_CAUSE(
1538 "Missing plugin name or component class descriptor.");
1539 goto error;
1540 }
1541
1542 /* Look for unescaped dots in the argument. */
1543 substring = bt_common_string_until(plugin_comp_cls_arg, ".\\", ".", &end_pos);
1544 if (!substring) {
1545 BT_CLI_LOGE_APPEND_CAUSE("Could not consume argument: arg=%s",
1546 plugin_comp_cls_arg);
1547 goto error;
1548 }
1549
1550 if (end_pos == strlen(plugin_comp_cls_arg)) {
1551 /* Didn't find an unescaped dot, treat it as a plugin name. */
1552 g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name,
1553 plugin_comp_cls_arg);
1554 } else {
1555 /*
1556 * Found an unescaped dot, treat it as a component class name.
1557 */
1558 plugin_comp_cls_names(plugin_comp_cls_arg, NULL, &plugin_name, &comp_cls_name,
1559 &cfg->cmd_data.help.cfg_component->type);
1560 if (!plugin_name || !comp_cls_name) {
1561 BT_CLI_LOGE_APPEND_CAUSE(
1562 "Could not parse argument as a component class name: arg=%s",
1563 plugin_comp_cls_arg);
1564 goto error;
1565 }
1566
1567 g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name,
1568 plugin_name);
1569 g_string_assign(cfg->cmd_data.help.cfg_component->comp_cls_name,
1570 comp_cls_name);
1571 }
1572
1573 goto end;
1574
1575 error:
1576 *retcode = 1;
1577 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1578
1579 end:
1580 g_free(plugin_name);
1581 g_free(comp_cls_name);
1582
1583 if (substring) {
1584 g_string_free(substring, TRUE);
1585 }
1586
1587 argpar_iter_destroy(argpar_iter);
1588 argpar_item_destroy(argpar_item);
1589
1590 return cfg;
1591 }
1592
1593 /*
1594 * Prints the help command usage.
1595 */
1596 static
1597 void print_query_usage(FILE *fp)
1598 {
1599 fprintf(fp, "Usage: babeltrace2 [GEN OPTS] query [OPTS] TYPE.PLUGIN.CLS OBJECT\n");
1600 fprintf(fp, "\n");
1601 fprintf(fp, "Options:\n");
1602 fprintf(fp, "\n");
1603 fprintf(fp, " -p, --params=PARAMS Set the query parameters to PARAMS (see the expected\n");
1604 fprintf(fp, " format of PARAMS below)\n");
1605 fprintf(fp, " -h, --help Show this help and quit\n");
1606 fprintf(fp, "\n\n");
1607 print_expected_params_format(fp);
1608 }
1609
1610 static
1611 const struct argpar_opt_descr query_options[] = {
1612 /* id, short_name, long_name, with_arg */
1613 { OPT_HELP, 'h', "help", false },
1614 { OPT_PARAMS, 'p', "params", true },
1615 ARGPAR_OPT_DESCR_SENTINEL
1616 };
1617
1618 /*
1619 * Creates a Babeltrace config object from the arguments of a query
1620 * command.
1621 *
1622 * *retcode is set to the appropriate exit code to use.
1623 */
1624 static
1625 struct bt_config *bt_config_query_from_args(int argc, const char *argv[],
1626 int *retcode, const bt_value *plugin_paths,
1627 int default_log_level, unsigned int consumed_args)
1628 {
1629 struct bt_config *cfg = NULL;
1630 const char *component_class_spec = NULL;
1631 const char *query_object = NULL;
1632 GString *error_str = NULL;
1633 struct argpar_iter *argpar_iter = NULL;
1634 const struct argpar_item *argpar_item = NULL;
1635
1636 bt_value *params = bt_value_map_create();
1637 if (!params) {
1638 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1639 goto error;
1640 }
1641
1642 *retcode = 0;
1643 cfg = bt_config_query_create(plugin_paths);
1644 if (!cfg) {
1645 goto error;
1646 }
1647
1648 error_str = g_string_new(NULL);
1649 if (!error_str) {
1650 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1651 goto error;
1652 }
1653
1654 argpar_iter = argpar_iter_create(argc, argv, query_options);
1655 if (!argpar_iter) {
1656 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1657 goto error;
1658 }
1659
1660 while (true) {
1661 enum parse_next_item_status status =
1662 parse_next_item(argpar_iter, &argpar_item, argv, "query",
1663 consumed_args);
1664
1665 if (status == PARSE_NEXT_ITEM_STATUS_ERROR) {
1666 goto error;
1667 } else if (status == PARSE_NEXT_ITEM_STATUS_END) {
1668 break;
1669 }
1670
1671 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
1672 const struct argpar_opt_descr *opt_descr =
1673 argpar_item_opt_descr(argpar_item);
1674 const char *arg = argpar_item_opt_arg(argpar_item);
1675
1676 switch (opt_descr->id) {
1677 case OPT_HELP:
1678 print_query_usage(stdout);
1679 *retcode = -1;
1680 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1681 goto end;
1682 case OPT_PARAMS:
1683 {
1684 bt_value *parsed_params = bt_param_parse(arg, error_str);
1685 bt_value_map_extend_status extend_status;
1686 if (!parsed_params) {
1687 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --params option's argument:\n %s",
1688 error_str->str);
1689 goto error;
1690 }
1691
1692 extend_status = bt_value_map_extend(params, parsed_params);
1693 BT_VALUE_PUT_REF_AND_RESET(parsed_params);
1694 if (extend_status) {
1695 BT_CLI_LOGE_APPEND_CAUSE("Cannot extend current parameters with --params option's argument:\n %s",
1696 arg);
1697 goto error;
1698 }
1699 break;
1700 }
1701 default:
1702 bt_common_abort();
1703 }
1704 } else {
1705 const char *arg = argpar_item_non_opt_arg(argpar_item);
1706
1707 /*
1708 * We need exactly two non-option arguments
1709 * which are the mandatory component class
1710 * specification and query object.
1711 */
1712 if (!component_class_spec) {
1713 component_class_spec = arg;
1714 } else if (!query_object) {
1715 query_object = arg;
1716 } else {
1717 BT_CLI_LOGE_APPEND_CAUSE("Extraneous command-line argument specified to `query` command: `%s`.",
1718 arg);
1719 goto error;
1720 }
1721 }
1722 }
1723
1724 if (!component_class_spec || !query_object) {
1725 print_query_usage(stdout);
1726 *retcode = -1;
1727 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1728 goto end;
1729 }
1730
1731 cfg->cmd_data.query.cfg_component =
1732 bt_config_component_from_arg(component_class_spec,
1733 default_log_level);
1734 if (!cfg->cmd_data.query.cfg_component) {
1735 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for component class specification:\n %s",
1736 component_class_spec);
1737 goto error;
1738 }
1739
1740 BT_ASSERT(params);
1741 BT_OBJECT_MOVE_REF(cfg->cmd_data.query.cfg_component->params, params);
1742
1743 if (strlen(query_object) == 0) {
1744 BT_CLI_LOGE_APPEND_CAUSE("Invalid empty object.");
1745 goto error;
1746 }
1747
1748 g_string_assign(cfg->cmd_data.query.object, query_object);
1749 goto end;
1750
1751 error:
1752 *retcode = 1;
1753 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1754
1755 end:
1756 argpar_iter_destroy(argpar_iter);
1757 argpar_item_destroy(argpar_item);
1758
1759 if (error_str) {
1760 g_string_free(error_str, TRUE);
1761 }
1762
1763 bt_value_put_ref(params);
1764 return cfg;
1765 }
1766
1767 /*
1768 * Prints the list-plugins command usage.
1769 */
1770 static
1771 void print_list_plugins_usage(FILE *fp)
1772 {
1773 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] list-plugins [OPTIONS]\n");
1774 fprintf(fp, "\n");
1775 fprintf(fp, "Options:\n");
1776 fprintf(fp, "\n");
1777 fprintf(fp, " -h, --help Show this help and quit\n");
1778 fprintf(fp, "\n");
1779 fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n");
1780 fprintf(fp, "\n");
1781 fprintf(fp, "Use `babeltrace2 help` to get help for a specific plugin or component class.\n");
1782 }
1783
1784 static
1785 const struct argpar_opt_descr list_plugins_options[] = {
1786 /* id, short_name, long_name, with_arg */
1787 { OPT_HELP, 'h', "help", false },
1788 ARGPAR_OPT_DESCR_SENTINEL
1789 };
1790
1791 /*
1792 * Creates a Babeltrace config object from the arguments of a
1793 * list-plugins command.
1794 *
1795 * *retcode is set to the appropriate exit code to use.
1796 */
1797 static
1798 struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[],
1799 int *retcode, const bt_value *plugin_paths, unsigned int consumed_args)
1800 {
1801 struct bt_config *cfg = NULL;
1802 struct argpar_iter *argpar_iter = NULL;
1803 const struct argpar_item *argpar_item = NULL;
1804
1805 *retcode = 0;
1806 cfg = bt_config_list_plugins_create(plugin_paths);
1807 if (!cfg) {
1808 goto error;
1809 }
1810
1811 argpar_iter = argpar_iter_create(argc, argv, list_plugins_options);
1812 if (!argpar_iter) {
1813 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1814 goto error;
1815 }
1816
1817 while (true) {
1818 enum parse_next_item_status status =
1819 parse_next_item(argpar_iter, &argpar_item, argv, "list-plugins",
1820 consumed_args);
1821
1822 if (status == PARSE_NEXT_ITEM_STATUS_ERROR) {
1823 goto error;
1824 } else if (status == PARSE_NEXT_ITEM_STATUS_END) {
1825 break;
1826 }
1827
1828 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
1829 const struct argpar_opt_descr *opt_descr =
1830 argpar_item_opt_descr(argpar_item);
1831
1832 switch (opt_descr->id) {
1833 case OPT_HELP:
1834 print_list_plugins_usage(stdout);
1835 *retcode = -1;
1836 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1837 goto end;
1838 default:
1839 bt_common_abort();
1840 }
1841 } else {
1842 BT_CLI_LOGE_APPEND_CAUSE(
1843 "Extraneous command-line argument specified to `list-plugins` command: `%s`.",
1844 argpar_item_non_opt_arg(argpar_item));
1845 goto error;
1846 }
1847 }
1848
1849 goto end;
1850
1851 error:
1852 *retcode = 1;
1853 BT_OBJECT_PUT_REF_AND_RESET(cfg);
1854
1855 end:
1856 argpar_iter_destroy(argpar_iter);
1857 argpar_item_destroy(argpar_item);
1858
1859 return cfg;
1860 }
1861
1862 /*
1863 * Prints the run command usage.
1864 */
1865 static
1866 void print_run_usage(FILE *fp)
1867 {
1868 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] run [OPTIONS]\n");
1869 fprintf(fp, "\n");
1870 fprintf(fp, "Options:\n");
1871 fprintf(fp, "\n");
1872 fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n");
1873 fprintf(fp, " for all the following components until\n");
1874 fprintf(fp, " --reset-base-params is encountered\n");
1875 fprintf(fp, " (see the expected format of PARAMS below)\n");
1876 fprintf(fp, " -c, --component=NAME:TYPE.PLUGIN.CLS\n");
1877 fprintf(fp, " Instantiate the component class CLS of type\n");
1878 fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n");
1879 fprintf(fp, " in the plugin PLUGIN, add it to the graph,\n");
1880 fprintf(fp, " and name it NAME");
1881 fprintf(fp, " -x, --connect=CONNECTION Connect two created components (see the\n");
1882 fprintf(fp, " expected format of CONNECTION below)\n");
1883 fprintf(fp, " -l, --log-level=LVL Set the log level of the current component to LVL\n");
1884 fprintf(fp, " (`N`, `T`, `D`, `I`, `W`, `E`, or `F`)\n");
1885 fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n");
1886 fprintf(fp, " current component (see the expected format\n");
1887 fprintf(fp, " of PARAMS below)\n");
1888 fprintf(fp, " -r, --reset-base-params Reset the current base parameters to an\n");
1889 fprintf(fp, " empty map\n");
1890 fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n");
1891 fprintf(fp, " the graph later, retry in DUR µs\n");
1892 fprintf(fp, " (default: 100000)\n");
1893 fprintf(fp, " -h, --help Show this help and quit\n");
1894 fprintf(fp, "\n");
1895 fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n");
1896 fprintf(fp, "\n\n");
1897 fprintf(fp, "Expected format of CONNECTION\n");
1898 fprintf(fp, "-----------------------------\n");
1899 fprintf(fp, "\n");
1900 fprintf(fp, " UPSTREAM[.UPSTREAM-PORT]:DOWNSTREAM[.DOWNSTREAM-PORT]\n");
1901 fprintf(fp, "\n");
1902 fprintf(fp, "UPSTREAM and DOWNSTREAM are names of the upstream and downstream\n");
1903 fprintf(fp, "components to connect together. You must escape the following characters\n\n");
1904 fprintf(fp, "with `\\`: `\\`, `.`, and `:`. You must set the name of the current\n");
1905 fprintf(fp, "component using the NAME prefix of the --component option.\n");
1906 fprintf(fp, "\n");
1907 fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are optional globbing patterns to\n");
1908 fprintf(fp, "identify the upstream and downstream ports to use for the connection.\n");
1909 fprintf(fp, "When the port is not specified, `*` is used.\n");
1910 fprintf(fp, "\n");
1911 fprintf(fp, "When a component named UPSTREAM has an available port which matches the\n");
1912 fprintf(fp, "UPSTREAM-PORT globbing pattern, it is connected to the first port which\n");
1913 fprintf(fp, "matches the DOWNSTREAM-PORT globbing pattern of the component named\n");
1914 fprintf(fp, "DOWNSTREAM.\n");
1915 fprintf(fp, "\n");
1916 fprintf(fp, "The only special character in UPSTREAM-PORT and DOWNSTREAM-PORT is `*`\n");
1917 fprintf(fp, "which matches anything. You must escape the following characters\n");
1918 fprintf(fp, "with `\\`: `\\`, `*`, `?`, `[`, `.`, and `:`.\n");
1919 fprintf(fp, "\n");
1920 fprintf(fp, "You can connect a source component to a filter or sink component. You\n");
1921 fprintf(fp, "can connect a filter component to a sink component.\n");
1922 fprintf(fp, "\n");
1923 fprintf(fp, "Examples:\n");
1924 fprintf(fp, "\n");
1925 fprintf(fp, " my-src:my-sink\n");
1926 fprintf(fp, " ctf-fs.*stream*:utils-muxer:*\n");
1927 fprintf(fp, "\n");
1928 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n");
1929 fprintf(fp, "babeltrace2 from a shell.\n");
1930 fprintf(fp, "\n\n");
1931 print_expected_params_format(fp);
1932 }
1933
1934 /*
1935 * Creates a Babeltrace config object from the arguments of a run
1936 * command.
1937 *
1938 * *retcode is set to the appropriate exit code to use.
1939 */
1940 static
1941 struct bt_config *bt_config_run_from_args(int argc, const char *argv[],
1942 int *retcode, const bt_value *plugin_paths,
1943 int default_log_level, unsigned int consumed_args)
1944 {
1945 struct bt_config_component *cur_cfg_comp = NULL;
1946 bt_value *cur_base_params = NULL;
1947 int ret = 0;
1948 struct bt_config *cfg = NULL;
1949 bt_value *instance_names = NULL;
1950 bt_value *connection_args = NULL;
1951 char error_buf[256] = { 0 };
1952 long retry_duration = -1;
1953 bt_value_map_extend_status extend_status;
1954 GString *error_str = NULL;
1955 struct argpar_iter *argpar_iter = NULL;
1956 const struct argpar_item *argpar_item = NULL;
1957
1958 static const struct argpar_opt_descr run_options[] = {
1959 { OPT_BASE_PARAMS, 'b', "base-params", true },
1960 { OPT_COMPONENT, 'c', "component", true },
1961 { OPT_CONNECT, 'x', "connect", true },
1962 { OPT_HELP, 'h', "help", false },
1963 { OPT_LOG_LEVEL, 'l', "log-level", true },
1964 { OPT_PARAMS, 'p', "params", true },
1965 { OPT_RESET_BASE_PARAMS, 'r', "reset-base-params", false },
1966 { OPT_RETRY_DURATION, '\0', "retry-duration", true },
1967 ARGPAR_OPT_DESCR_SENTINEL
1968 };
1969
1970 *retcode = 0;
1971
1972 error_str = g_string_new(NULL);
1973 if (!error_str) {
1974 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1975 goto error;
1976 }
1977
1978 if (argc < 1) {
1979 print_run_usage(stdout);
1980 *retcode = -1;
1981 goto end;
1982 }
1983
1984 cfg = bt_config_run_create(plugin_paths);
1985 if (!cfg) {
1986 goto error;
1987 }
1988
1989 cfg->cmd_data.run.retry_duration_us = 100000;
1990 cur_base_params = bt_value_map_create();
1991 if (!cur_base_params) {
1992 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1993 goto error;
1994 }
1995
1996 instance_names = bt_value_map_create();
1997 if (!instance_names) {
1998 BT_CLI_LOGE_APPEND_CAUSE_OOM();
1999 goto error;
2000 }
2001
2002 connection_args = bt_value_array_create();
2003 if (!connection_args) {
2004 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2005 goto error;
2006 }
2007
2008 argpar_iter = argpar_iter_create(argc, argv, run_options);
2009 if (!argpar_iter) {
2010 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2011 goto error;
2012 }
2013
2014 while (true) {
2015 enum parse_next_item_status status;
2016 const struct argpar_opt_descr *opt_descr;
2017 const char *arg;
2018
2019 status = parse_next_item(argpar_iter, &argpar_item, argv, "run",
2020 consumed_args);
2021 if (status == PARSE_NEXT_ITEM_STATUS_ERROR) {
2022 goto error;
2023 } else if (status == PARSE_NEXT_ITEM_STATUS_END) {
2024 break;
2025 }
2026
2027 /* This command does not accept non-option arguments.*/
2028 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_NON_OPT) {
2029 BT_CLI_LOGE_APPEND_CAUSE("Unexpected argument: `%s`",
2030 argpar_item_non_opt_arg(argpar_item));
2031 goto error;
2032 }
2033
2034 opt_descr = argpar_item_opt_descr(argpar_item);
2035 arg = argpar_item_opt_arg(argpar_item);
2036
2037 switch (opt_descr->id) {
2038 case OPT_HELP:
2039 print_run_usage(stdout);
2040 *retcode = -1;
2041 BT_OBJECT_PUT_REF_AND_RESET(cfg);
2042 goto end;
2043 case OPT_COMPONENT:
2044 {
2045 enum bt_config_component_dest dest;
2046
2047 BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp);
2048 cur_cfg_comp = bt_config_component_from_arg(arg,
2049 default_log_level);
2050 if (!cur_cfg_comp) {
2051 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --component option's argument:\n %s",
2052 arg);
2053 goto error;
2054 }
2055
2056 switch (cur_cfg_comp->type) {
2057 case BT_COMPONENT_CLASS_TYPE_SOURCE:
2058 dest = BT_CONFIG_COMPONENT_DEST_SOURCE;
2059 break;
2060 case BT_COMPONENT_CLASS_TYPE_FILTER:
2061 dest = BT_CONFIG_COMPONENT_DEST_FILTER;
2062 break;
2063 case BT_COMPONENT_CLASS_TYPE_SINK:
2064 dest = BT_CONFIG_COMPONENT_DEST_SINK;
2065 break;
2066 default:
2067 bt_common_abort();
2068 }
2069
2070 BT_ASSERT(cur_base_params);
2071 bt_value_put_ref(cur_cfg_comp->params);
2072 if (bt_value_copy(cur_base_params,
2073 &cur_cfg_comp->params) < 0) {
2074 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2075 goto error;
2076 }
2077
2078 ret = add_run_cfg_comp_check_name(cfg,
2079 cur_cfg_comp, dest,
2080 instance_names);
2081 if (ret) {
2082 goto error;
2083 }
2084
2085 break;
2086 }
2087 case OPT_PARAMS:
2088 {
2089 bt_value *params;
2090
2091 if (!cur_cfg_comp) {
2092 BT_CLI_LOGE_APPEND_CAUSE("Cannot add parameters to unavailable component:\n %s",
2093 arg);
2094 goto error;
2095 }
2096
2097 params = bt_param_parse(arg, error_str);
2098 if (!params) {
2099 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --params option's argument:\n %s",
2100 error_str->str);
2101 goto error;
2102 }
2103
2104 extend_status = bt_value_map_extend(cur_cfg_comp->params,
2105 params);
2106 BT_VALUE_PUT_REF_AND_RESET(params);
2107 if (extend_status != BT_VALUE_MAP_EXTEND_STATUS_OK) {
2108 BT_CLI_LOGE_APPEND_CAUSE("Cannot extend current component parameters with --params option's argument:\n %s",
2109 arg);
2110 goto error;
2111 }
2112
2113 break;
2114 }
2115 case OPT_LOG_LEVEL:
2116 if (!cur_cfg_comp) {
2117 BT_CLI_LOGE_APPEND_CAUSE("Cannot set the log level of unavailable component:\n %s",
2118 arg);
2119 goto error;
2120 }
2121
2122 cur_cfg_comp->log_level =
2123 bt_log_get_level_from_string(arg);
2124 if (cur_cfg_comp->log_level < 0) {
2125 BT_CLI_LOGE_APPEND_CAUSE("Invalid argument for --log-level option:\n %s",
2126 arg);
2127 goto error;
2128 }
2129 break;
2130 case OPT_BASE_PARAMS:
2131 {
2132 bt_value *params = bt_param_parse(arg, error_str);
2133
2134 if (!params) {
2135 BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --base-params option's argument:\n %s",
2136 error_str->str);
2137 goto error;
2138 }
2139
2140 BT_OBJECT_MOVE_REF(cur_base_params, params);
2141 break;
2142 }
2143 case OPT_RESET_BASE_PARAMS:
2144 BT_VALUE_PUT_REF_AND_RESET(cur_base_params);
2145 cur_base_params = bt_value_map_create();
2146 if (!cur_base_params) {
2147 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2148 goto error;
2149 }
2150 break;
2151 case OPT_CONNECT:
2152 if (bt_value_array_append_string_element(
2153 connection_args, arg)) {
2154 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2155 goto error;
2156 }
2157 break;
2158 case OPT_RETRY_DURATION: {
2159 gchar *end;
2160 size_t arg_len = strlen(arg);
2161
2162 retry_duration = g_ascii_strtoll(arg, &end, 10);
2163
2164 if (arg_len == 0 || end != (arg + arg_len)) {
2165 BT_CLI_LOGE_APPEND_CAUSE(
2166 "Could not parse --retry-duration option's argument as an unsigned integer: `%s`",
2167 arg);
2168 goto error;
2169 }
2170
2171 if (retry_duration < 0) {
2172 BT_CLI_LOGE_APPEND_CAUSE("--retry-duration option's argument must be positive or 0: %ld",
2173 retry_duration);
2174 goto error;
2175 }
2176
2177 cfg->cmd_data.run.retry_duration_us =
2178 (uint64_t) retry_duration;
2179 break;
2180 }
2181 default:
2182 bt_common_abort();
2183 }
2184 }
2185
2186 BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp);
2187
2188 if (cfg->cmd_data.run.sources->len == 0) {
2189 BT_CLI_LOGE_APPEND_CAUSE("Incomplete graph: no source component.");
2190 goto error;
2191 }
2192
2193 if (cfg->cmd_data.run.sinks->len == 0) {
2194 BT_CLI_LOGE_APPEND_CAUSE("Incomplete graph: no sink component.");
2195 goto error;
2196 }
2197
2198 ret = bt_config_cli_args_create_connections(cfg,
2199 connection_args,
2200 error_buf, 256);
2201 if (ret) {
2202 BT_CLI_LOGE_APPEND_CAUSE("Cannot creation connections:\n%s", error_buf);
2203 goto error;
2204 }
2205
2206 goto end;
2207
2208 error:
2209 *retcode = 1;
2210 BT_OBJECT_PUT_REF_AND_RESET(cfg);
2211
2212 end:
2213 if (error_str) {
2214 g_string_free(error_str, TRUE);
2215 }
2216
2217 argpar_iter_destroy(argpar_iter);
2218 argpar_item_destroy(argpar_item);
2219
2220 BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp);
2221 BT_VALUE_PUT_REF_AND_RESET(cur_base_params);
2222 BT_VALUE_PUT_REF_AND_RESET(instance_names);
2223 BT_VALUE_PUT_REF_AND_RESET(connection_args);
2224 return cfg;
2225 }
2226
2227 static
2228 struct bt_config *bt_config_run_from_args_array(const bt_value *run_args,
2229 int *retcode, const bt_value *plugin_paths,
2230 int default_log_level)
2231 {
2232 struct bt_config *cfg = NULL;
2233 const char **argv;
2234 uint64_t i, len = bt_value_array_get_length(run_args);
2235
2236 BT_ASSERT(len <= SIZE_MAX);
2237 argv = calloc((size_t) len, sizeof(*argv));
2238 if (!argv) {
2239 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2240 goto end;
2241 }
2242
2243 for (i = 0; i < len; i++) {
2244 const bt_value *arg_value =
2245 bt_value_array_borrow_element_by_index_const(run_args,
2246 i);
2247 const char *arg;
2248
2249 arg = bt_value_string_get(arg_value);
2250 BT_ASSERT(arg);
2251 argv[i] = arg;
2252 }
2253
2254 cfg = bt_config_run_from_args((int) len, argv, retcode,
2255 plugin_paths, default_log_level, 0);
2256
2257 end:
2258 free(argv);
2259 return cfg;
2260 }
2261
2262 /*
2263 * Prints the convert command usage.
2264 */
2265 static
2266 void print_convert_usage(FILE *fp)
2267 {
2268 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [convert] [OPTIONS] [PATH/URL]\n");
2269 fprintf(fp, "\n");
2270 fprintf(fp, "Options:\n");
2271 fprintf(fp, "\n");
2272 fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n");
2273 fprintf(fp, " Instantiate the component class CLS of type\n");
2274 fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n");
2275 fprintf(fp, " in the plugin PLUGIN, add it to the\n");
2276 fprintf(fp, " conversion graph, and optionally name it\n");
2277 fprintf(fp, " NAME\n");
2278 fprintf(fp, " -l, --log-level=LVL Set the log level of the current component to LVL\n");
2279 fprintf(fp, " (`N`, `T`, `D`, `I`, `W`, `E`, or `F`)\n");
2280 fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n");
2281 fprintf(fp, " current component (see the expected format\n");
2282 fprintf(fp, " of PARAMS below)\n");
2283 fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n");
2284 fprintf(fp, " the graph later, retry in DUR µs\n");
2285 fprintf(fp, " (default: 100000)\n");
2286 fprintf(fp, " dynamic plugins can be loaded\n");
2287 fprintf(fp, " --run-args Print the equivalent arguments for the\n");
2288 fprintf(fp, " `run` command to the standard output,\n");
2289 fprintf(fp, " formatted for a shell, and quit\n");
2290 fprintf(fp, " --run-args-0 Print the equivalent arguments for the\n");
2291 fprintf(fp, " `run` command to the standard output,\n");
2292 fprintf(fp, " formatted for `xargs -0`, and quit\n");
2293 fprintf(fp, " --stream-intersection Only process events when all streams\n");
2294 fprintf(fp, " are active\n");
2295 fprintf(fp, " -h, --help Show this help and quit\n");
2296 fprintf(fp, "\n");
2297 fprintf(fp, "Implicit `source.ctf.fs` component options:\n");
2298 fprintf(fp, "\n");
2299 fprintf(fp, " --clock-force-correlate Force the origin of all clocks\n");
2300 fprintf(fp, " to the Unix epoch\n");
2301 fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n");
2302 fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS ns\n");
2303 fprintf(fp, "\n");
2304 fprintf(fp, "Implicit `sink.text.pretty` component options:\n");
2305 fprintf(fp, "\n");
2306 fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n");
2307 fprintf(fp, " --clock-date Print timestamp dates\n");
2308 fprintf(fp, " --clock-gmt Print and parse timestamps in the GMT\n");
2309 fprintf(fp, " time zone instead of the local time zone\n");
2310 fprintf(fp, " --clock-seconds Print the timestamps as `SEC.NS` instead\n");
2311 fprintf(fp, " of `hh:mm:ss.nnnnnnnnn`\n");
2312 fprintf(fp, " --color=(never | auto | always)\n");
2313 fprintf(fp, " Never, automatically, or always emit\n");
2314 fprintf(fp, " console color codes\n");
2315 fprintf(fp, " -f, --fields=FIELD[,FIELD]... Print additional fields; FIELD can be:\n");
2316 fprintf(fp, " `all`, `trace`, `trace:hostname`,\n");
2317 fprintf(fp, " `trace:domain`, `trace:procname`,\n");
2318 fprintf(fp, " `trace:vpid`, `loglevel`, `emf`\n");
2319 fprintf(fp, " -n, --names=NAME[,NAME]... Print field names; NAME can be:\n");
2320 fprintf(fp, " `payload` (or `arg` or `args`), `none`,\n");
2321 fprintf(fp, " `all`, `scope`, `header`, `context`\n");
2322 fprintf(fp, " (or `ctx`)\n");
2323 fprintf(fp, " --no-delta Do not print time delta between\n");
2324 fprintf(fp, " consecutive events\n");
2325 fprintf(fp, " -w, --output=PATH Write output text to PATH instead of\n");
2326 fprintf(fp, " the standard output\n");
2327 fprintf(fp, "\n");
2328 fprintf(fp, "Implicit `filter.utils.trimmer` component options:\n");
2329 fprintf(fp, "\n");
2330 fprintf(fp, " -b, --begin=BEGIN Set the beginning time of the conversion\n");
2331 fprintf(fp, " time range to BEGIN (see the format of\n");
2332 fprintf(fp, " BEGIN below)\n");
2333 fprintf(fp, " -e, --end=END Set the end time of the conversion time\n");
2334 fprintf(fp, " range to END (see the format of END below)\n");
2335 fprintf(fp, " -t, --timerange=TIMERANGE Set conversion time range to TIMERANGE:\n");
2336 fprintf(fp, " BEGIN,END or [BEGIN,END] (literally `[` and\n");
2337 fprintf(fp, " `]`) (see the format of BEGIN/END below)\n");
2338 fprintf(fp, "\n");
2339 fprintf(fp, "Implicit `filter.lttng-utils.debug-info` component options:\n");
2340 fprintf(fp, "\n");
2341 fprintf(fp, " --debug-info Create an implicit\n");
2342 fprintf(fp, " `filter.lttng-utils.debug-info` component\n");
2343 fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n");
2344 fprintf(fp, " instead of `/usr/lib/debug`\n");
2345 fprintf(fp, " --debug-info-full-path Show full debug info source and\n");
2346 fprintf(fp, " binary paths instead of just names\n");
2347 fprintf(fp, " --debug-info-target-prefix=DIR\n");
2348 fprintf(fp, " Use directory DIR as a prefix when\n");
2349 fprintf(fp, " looking up executables during debug\n");
2350 fprintf(fp, " info analysis\n");
2351 fprintf(fp, "\n");
2352 fprintf(fp, "Legacy options that still work:\n");
2353 fprintf(fp, "\n");
2354 fprintf(fp, " -i, --input-format=(ctf | lttng-live)\n");
2355 fprintf(fp, " `ctf`:\n");
2356 fprintf(fp, " Create an implicit `source.ctf.fs`\n");
2357 fprintf(fp, " component\n");
2358 fprintf(fp, " `lttng-live`:\n");
2359 fprintf(fp, " Create an implicit `source.ctf.lttng-live`\n");
2360 fprintf(fp, " component\n");
2361 fprintf(fp, " -o, --output-format=(text | ctf | dummy | ctf-metadata)\n");
2362 fprintf(fp, " `text`:\n");
2363 fprintf(fp, " Create an implicit `sink.text.pretty`\n");
2364 fprintf(fp, " component\n");
2365 fprintf(fp, " `ctf`:\n");
2366 fprintf(fp, " Create an implicit `sink.ctf.fs`\n");
2367 fprintf(fp, " component\n");
2368 fprintf(fp, " `dummy`:\n");
2369 fprintf(fp, " Create an implicit `sink.utils.dummy`\n");
2370 fprintf(fp, " component\n");
2371 fprintf(fp, " `ctf-metadata`:\n");
2372 fprintf(fp, " Query the `source.ctf.fs` component class\n");
2373 fprintf(fp, " for metadata text and quit\n");
2374 fprintf(fp, "\n");
2375 fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n");
2376 fprintf(fp, "\n\n");
2377 fprintf(fp, "Format of BEGIN and END\n");
2378 fprintf(fp, "-----------------------\n");
2379 fprintf(fp, "\n");
2380 fprintf(fp, " [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n");
2381 fprintf(fp, "\n\n");
2382 print_expected_params_format(fp);
2383 }
2384
2385 static
2386 const struct argpar_opt_descr convert_options[] = {
2387 /* id, short_name, long_name, with_arg */
2388 { OPT_BEGIN, 'b', "begin", true },
2389 { OPT_CLOCK_CYCLES, '\0', "clock-cycles", false },
2390 { OPT_CLOCK_DATE, '\0', "clock-date", false },
2391 { OPT_CLOCK_FORCE_CORRELATE, '\0', "clock-force-correlate", false },
2392 { OPT_CLOCK_GMT, '\0', "clock-gmt", false },
2393 { OPT_CLOCK_OFFSET, '\0', "clock-offset", true },
2394 { OPT_CLOCK_OFFSET_NS, '\0', "clock-offset-ns", true },
2395 { OPT_CLOCK_SECONDS, '\0', "clock-seconds", false },
2396 { OPT_COLOR, '\0', "color", true },
2397 { OPT_COMPONENT, 'c', "component", true },
2398 { OPT_DEBUG, 'd', "debug", false },
2399 { OPT_DEBUG_INFO_DIR, '\0', "debug-info-dir", true },
2400 { OPT_DEBUG_INFO_FULL_PATH, '\0', "debug-info-full-path", false },
2401 { OPT_DEBUG_INFO_TARGET_PREFIX, '\0', "debug-info-target-prefix", true },
2402 { OPT_END, 'e', "end", true },
2403 { OPT_FIELDS, 'f', "fields", true },
2404 { OPT_HELP, 'h', "help", false },
2405 { OPT_INPUT_FORMAT, 'i', "input-format", true },
2406 { OPT_LOG_LEVEL, 'l', "log-level", true },
2407 { OPT_NAMES, 'n', "names", true },
2408 { OPT_DEBUG_INFO, '\0', "debug-info", false },
2409 { OPT_NO_DELTA, '\0', "no-delta", false },
2410 { OPT_OMIT_HOME_PLUGIN_PATH, '\0', "omit-home-plugin-path", false },
2411 { OPT_OMIT_SYSTEM_PLUGIN_PATH, '\0', "omit-system-plugin-path", false },
2412 { OPT_OUTPUT, 'w', "output", true },
2413 { OPT_OUTPUT_FORMAT, 'o', "output-format", true },
2414 { OPT_PARAMS, 'p', "params", true },
2415 { OPT_PLUGIN_PATH, '\0', "plugin-path", true },
2416 { OPT_RETRY_DURATION, '\0', "retry-duration", true },
2417 { OPT_RUN_ARGS, '\0', "run-args", false },
2418 { OPT_RUN_ARGS_0, '\0', "run-args-0", false },
2419 { OPT_STREAM_INTERSECTION, '\0', "stream-intersection", false },
2420 { OPT_TIMERANGE, '\0', "timerange", true },
2421 { OPT_VERBOSE, 'v', "verbose", false },
2422 ARGPAR_OPT_DESCR_SENTINEL
2423 };
2424
2425 static
2426 GString *get_component_auto_name(const char *prefix,
2427 const bt_value *existing_names)
2428 {
2429 unsigned int i = 0;
2430 GString *auto_name = g_string_new(NULL);
2431
2432 if (!auto_name) {
2433 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2434 goto end;
2435 }
2436
2437 if (!bt_value_map_has_entry(existing_names, prefix)) {
2438 g_string_assign(auto_name, prefix);
2439 goto end;
2440 }
2441
2442 do {
2443 g_string_printf(auto_name, "%s-%d", prefix, i);
2444 i++;
2445 } while (bt_value_map_has_entry(existing_names, auto_name->str));
2446
2447 end:
2448 return auto_name;
2449 }
2450
2451 struct implicit_component_args {
2452 bool exists;
2453
2454 /* The component class name (e.g. src.ctf.fs). */
2455 GString *comp_arg;
2456
2457 /* The component instance name. */
2458 GString *name_arg;
2459
2460 GString *params_arg;
2461 bt_value *extra_params;
2462 };
2463
2464 static
2465 int assign_name_to_implicit_component(struct implicit_component_args *args,
2466 const char *prefix, bt_value *existing_names,
2467 GList **comp_names, bool append_to_comp_names)
2468 {
2469 int ret = 0;
2470 GString *name = NULL;
2471
2472 if (!args->exists) {
2473 goto end;
2474 }
2475
2476 name = get_component_auto_name(prefix,
2477 existing_names);
2478
2479 if (!name) {
2480 ret = -1;
2481 goto end;
2482 }
2483
2484 g_string_assign(args->name_arg, name->str);
2485
2486 if (bt_value_map_insert_entry(existing_names, name->str,
2487 bt_value_null)) {
2488 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2489 ret = -1;
2490 goto end;
2491 }
2492
2493 if (append_to_comp_names) {
2494 *comp_names = g_list_append(*comp_names, name);
2495 name = NULL;
2496 }
2497
2498 end:
2499 if (name) {
2500 g_string_free(name, TRUE);
2501 }
2502
2503 return ret;
2504 }
2505
2506 static
2507 int append_run_args_for_implicit_component(
2508 struct implicit_component_args *impl_args,
2509 bt_value *run_args)
2510 {
2511 int ret = 0;
2512 uint64_t i;
2513 GString *component_arg_for_run = NULL;
2514
2515 if (!impl_args->exists) {
2516 goto end;
2517 }
2518
2519 component_arg_for_run = g_string_new(NULL);
2520 if (!component_arg_for_run) {
2521 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2522 goto error;
2523 }
2524
2525 /* Build the full `name:type.plugin.cls`. */
2526 BT_ASSERT(!strchr(impl_args->name_arg->str, '\\'));
2527 BT_ASSERT(!strchr(impl_args->name_arg->str, ':'));
2528 g_string_printf(component_arg_for_run, "%s:%s",
2529 impl_args->name_arg->str, impl_args->comp_arg->str);
2530
2531 if (bt_value_array_append_string_element(run_args, "--component")) {
2532 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2533 goto error;
2534 }
2535
2536 if (bt_value_array_append_string_element(run_args,
2537 component_arg_for_run->str)) {
2538 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2539 goto error;
2540 }
2541
2542 if (impl_args->params_arg->len > 0) {
2543 if (bt_value_array_append_string_element(run_args, "--params")) {
2544 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2545 goto error;
2546 }
2547
2548 if (bt_value_array_append_string_element(run_args,
2549 impl_args->params_arg->str)) {
2550 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2551 goto error;
2552 }
2553 }
2554
2555 for (i = 0; i < bt_value_array_get_length(impl_args->extra_params); i++) {
2556 const bt_value *elem;
2557 const char *arg;
2558
2559 elem = bt_value_array_borrow_element_by_index(
2560 impl_args->extra_params, i);
2561
2562 BT_ASSERT(bt_value_is_string(elem));
2563 arg = bt_value_string_get(elem);
2564 ret = bt_value_array_append_string_element(run_args, arg);
2565 if (ret) {
2566 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2567 goto error;
2568 }
2569 }
2570
2571 goto end;
2572
2573 error:
2574 ret = -1;
2575
2576 end:
2577 if (component_arg_for_run) {
2578 g_string_free(component_arg_for_run, TRUE);
2579 }
2580
2581 return ret;
2582 }
2583
2584 /* Free the fields of a `struct implicit_component_args`. */
2585
2586 static
2587 void finalize_implicit_component_args(struct implicit_component_args *args)
2588 {
2589 BT_ASSERT(args);
2590
2591 if (args->comp_arg) {
2592 g_string_free(args->comp_arg, TRUE);
2593 }
2594
2595 if (args->name_arg) {
2596 g_string_free(args->name_arg, TRUE);
2597 }
2598
2599 if (args->params_arg) {
2600 g_string_free(args->params_arg, TRUE);
2601 }
2602
2603 bt_value_put_ref(args->extra_params);
2604 }
2605
2606 /* Destroy a dynamically-allocated `struct implicit_component_args`. */
2607
2608 static
2609 void destroy_implicit_component_args(struct implicit_component_args *args)
2610 {
2611 finalize_implicit_component_args(args);
2612 g_free(args);
2613 }
2614
2615 /* Initialize the fields of an already allocated `struct implicit_component_args`. */
2616
2617 static
2618 int init_implicit_component_args(struct implicit_component_args *args,
2619 const char *comp_arg, bool exists)
2620 {
2621 int ret = 0;
2622
2623 args->exists = exists;
2624 args->comp_arg = g_string_new(comp_arg);
2625 args->name_arg = g_string_new(NULL);
2626 args->params_arg = g_string_new(NULL);
2627 args->extra_params = bt_value_array_create();
2628
2629 if (!args->comp_arg || !args->name_arg ||
2630 !args->params_arg || !args->extra_params) {
2631 ret = -1;
2632 finalize_implicit_component_args(args);
2633 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2634 goto end;
2635 }
2636
2637 end:
2638 return ret;
2639 }
2640
2641 /* Dynamically allocate and initialize a `struct implicit_component_args`. */
2642
2643 static
2644 struct implicit_component_args *create_implicit_component_args(
2645 const char *comp_arg)
2646 {
2647 struct implicit_component_args *args;
2648 int status;
2649
2650 args = g_new(struct implicit_component_args, 1);
2651 if (!args) {
2652 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2653 goto end;
2654 }
2655
2656 status = init_implicit_component_args(args, comp_arg, true);
2657 if (status != 0) {
2658 g_free(args);
2659 args = NULL;
2660 }
2661
2662 end:
2663 return args;
2664 }
2665
2666 static
2667 void append_implicit_component_param(struct implicit_component_args *args,
2668 const char *key, const char *value)
2669 {
2670 BT_ASSERT(args);
2671 BT_ASSERT(key);
2672 BT_ASSERT(value);
2673 append_param_arg(args->params_arg, key, value);
2674 }
2675
2676 /*
2677 * Append the given parameter (`key=value`) to all component specifications
2678 * in `implicit_comp_args` (an array of `struct implicit_component_args *`)
2679 * which match `comp_arg`.
2680 *
2681 * Return the number of matching components.
2682 */
2683
2684 static
2685 int append_multiple_implicit_components_param(GPtrArray *implicit_comp_args,
2686 const char *comp_arg, const char *key, const char *value)
2687 {
2688 int i;
2689 int n = 0;
2690
2691 for (i = 0; i < implicit_comp_args->len; i++) {
2692 struct implicit_component_args *args = implicit_comp_args->pdata[i];
2693
2694 if (strcmp(args->comp_arg->str, comp_arg) == 0) {
2695 append_implicit_component_param(args, key, value);
2696 n++;
2697 }
2698 }
2699
2700 return n;
2701 }
2702
2703 /* Escape value to make it suitable to use as a string parameter value. */
2704 static
2705 gchar *escape_string_value(const char *value)
2706 {
2707 GString *ret;
2708 const char *in;
2709
2710 ret = g_string_new(NULL);
2711 if (!ret) {
2712 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2713 goto end;
2714 }
2715
2716 in = value;
2717 while (*in) {
2718 switch (*in) {
2719 case '"':
2720 case '\\':
2721 g_string_append_c(ret, '\\');
2722 break;
2723 }
2724
2725 g_string_append_c(ret, *in);
2726
2727 in++;
2728 }
2729
2730 end:
2731 return g_string_free(ret, FALSE);
2732 }
2733
2734 static
2735 int bt_value_to_cli_param_value_append(const bt_value *value, GString *buf)
2736 {
2737 BT_ASSERT(buf);
2738
2739 int ret = -1;
2740
2741 switch (bt_value_get_type(value)) {
2742 case BT_VALUE_TYPE_STRING:
2743 {
2744 const char *str_value = bt_value_string_get(value);
2745 gchar *escaped_str_value;
2746
2747 escaped_str_value = escape_string_value(str_value);
2748 if (!escaped_str_value) {
2749 goto end;
2750 }
2751
2752 g_string_append_printf(buf, "\"%s\"", escaped_str_value);
2753
2754 g_free(escaped_str_value);
2755 break;
2756 }
2757 case BT_VALUE_TYPE_ARRAY: {
2758 g_string_append_c(buf, '[');
2759 uint64_t sz = bt_value_array_get_length(value);
2760 for (uint64_t i = 0; i < sz; i++) {
2761 const bt_value *item;
2762
2763 if (i > 0) {
2764 g_string_append(buf, ", ");
2765 }
2766
2767 item = bt_value_array_borrow_element_by_index_const(
2768 value, i);
2769 ret = bt_value_to_cli_param_value_append(item, buf);
2770
2771 if (ret) {
2772 goto end;
2773 }
2774 }
2775 g_string_append_c(buf, ']');
2776 break;
2777 }
2778 default:
2779 bt_common_abort();
2780 }
2781
2782 ret = 0;
2783
2784 end:
2785 return ret;
2786 }
2787
2788 /*
2789 * Convert `value` to its equivalent representation as a command line parameter
2790 * value.
2791 */
2792
2793 static
2794 gchar *bt_value_to_cli_param_value(bt_value *value)
2795 {
2796 GString *buf;
2797 gchar *result = NULL;
2798
2799 buf = g_string_new(NULL);
2800 if (!buf) {
2801 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2802 goto error;
2803 }
2804
2805 if (bt_value_to_cli_param_value_append(value, buf)) {
2806 goto error;
2807 }
2808
2809 result = g_string_free(buf, FALSE);
2810 buf = NULL;
2811
2812 goto end;
2813
2814 error:
2815 if (buf) {
2816 g_string_free(buf, TRUE);
2817 }
2818
2819 end:
2820 return result;
2821 }
2822
2823 static
2824 int append_parameter_to_args(bt_value *args, const char *key, bt_value *value)
2825 {
2826 BT_ASSERT(args);
2827 BT_ASSERT(bt_value_get_type(args) == BT_VALUE_TYPE_ARRAY);
2828 BT_ASSERT(key);
2829 BT_ASSERT(value);
2830
2831 int ret = 0;
2832 gchar *str_value = NULL;
2833 GString *parameter = NULL;
2834
2835 if (bt_value_array_append_string_element(args, "--params")) {
2836 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2837 ret = -1;
2838 goto end;
2839 }
2840
2841 str_value = bt_value_to_cli_param_value(value);
2842 if (!str_value) {
2843 ret = -1;
2844 goto end;
2845 }
2846
2847 parameter = g_string_new(NULL);
2848 if (!parameter) {
2849 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2850 ret = -1;
2851 goto end;
2852 }
2853
2854 g_string_printf(parameter, "%s=%s", key, str_value);
2855
2856 if (bt_value_array_append_string_element(args, parameter->str)) {
2857 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2858 ret = -1;
2859 goto end;
2860 }
2861
2862 end:
2863 if (parameter) {
2864 g_string_free(parameter, TRUE);
2865 parameter = NULL;
2866 }
2867
2868 if (str_value) {
2869 g_free(str_value);
2870 str_value = NULL;
2871 }
2872
2873 return ret;
2874 }
2875
2876 static
2877 int append_string_parameter_to_args(bt_value *args, const char *key, const char *value)
2878 {
2879 bt_value *str_value;
2880 int ret;
2881
2882 str_value = bt_value_string_create_init(value);
2883
2884 if (!str_value) {
2885 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2886 ret = -1;
2887 goto end;
2888 }
2889
2890 ret = append_parameter_to_args(args, key, str_value);
2891
2892 end:
2893 BT_VALUE_PUT_REF_AND_RESET(str_value);
2894 return ret;
2895 }
2896
2897 static
2898 int append_implicit_component_extra_param(struct implicit_component_args *args,
2899 const char *key, const char *value)
2900 {
2901 return append_string_parameter_to_args(args->extra_params, key, value);
2902 }
2903
2904 /*
2905 * Escapes `.`, `:`, and `\` of `input` with `\`.
2906 */
2907 static
2908 GString *escape_dot_colon(const char *input)
2909 {
2910 GString *output = g_string_new(NULL);
2911 const char *ch;
2912
2913 if (!output) {
2914 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2915 goto end;
2916 }
2917
2918 for (ch = input; *ch != '\0'; ch++) {
2919 if (*ch == '\\' || *ch == '.' || *ch == ':') {
2920 g_string_append_c(output, '\\');
2921 }
2922
2923 g_string_append_c(output, *ch);
2924 }
2925
2926 end:
2927 return output;
2928 }
2929
2930 /*
2931 * Appends a --connect option to a list of arguments. `upstream_name`
2932 * and `downstream_name` are escaped with escape_dot_colon() in this
2933 * function.
2934 */
2935 static
2936 int append_connect_arg(bt_value *run_args,
2937 const char *upstream_name, const char *downstream_name)
2938 {
2939 int ret = 0;
2940 GString *e_upstream_name = escape_dot_colon(upstream_name);
2941 GString *e_downstream_name = escape_dot_colon(downstream_name);
2942 GString *arg = g_string_new(NULL);
2943
2944 if (!e_upstream_name || !e_downstream_name || !arg) {
2945 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2946 ret = -1;
2947 goto end;
2948 }
2949
2950 ret = bt_value_array_append_string_element(run_args, "--connect");
2951 if (ret) {
2952 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2953 ret = -1;
2954 goto end;
2955 }
2956
2957 g_string_append(arg, e_upstream_name->str);
2958 g_string_append_c(arg, ':');
2959 g_string_append(arg, e_downstream_name->str);
2960 ret = bt_value_array_append_string_element(run_args, arg->str);
2961 if (ret) {
2962 BT_CLI_LOGE_APPEND_CAUSE_OOM();
2963 ret = -1;
2964 goto end;
2965 }
2966
2967 end:
2968 if (arg) {
2969 g_string_free(arg, TRUE);
2970 }
2971
2972 if (e_upstream_name) {
2973 g_string_free(e_upstream_name, TRUE);
2974 }
2975
2976 if (e_downstream_name) {
2977 g_string_free(e_downstream_name, TRUE);
2978 }
2979
2980 return ret;
2981 }
2982
2983 /*
2984 * Appends the run command's --connect options for the convert command.
2985 */
2986 static
2987 int convert_auto_connect(bt_value *run_args,
2988 GList *source_names, GList *filter_names,
2989 GList *sink_names)
2990 {
2991 int ret = 0;
2992 GList *source_at = source_names;
2993 GList *filter_at = filter_names;
2994 GList *filter_prev;
2995 GList *sink_at = sink_names;
2996
2997 BT_ASSERT(source_names);
2998 BT_ASSERT(filter_names);
2999 BT_ASSERT(sink_names);
3000
3001 /* Connect all sources to the first filter */
3002 for (source_at = source_names; source_at; source_at = g_list_next(source_at)) {
3003 GString *source_name = source_at->data;
3004 GString *filter_name = filter_at->data;
3005
3006 ret = append_connect_arg(run_args, source_name->str,
3007 filter_name->str);
3008 if (ret) {
3009 goto error;
3010 }
3011 }
3012
3013 filter_prev = filter_at;
3014 filter_at = g_list_next(filter_at);
3015
3016 /* Connect remaining filters */
3017 for (; filter_at; filter_prev = filter_at, filter_at = g_list_next(filter_at)) {
3018 GString *filter_name = filter_at->data;
3019 GString *filter_prev_name = filter_prev->data;
3020
3021 ret = append_connect_arg(run_args, filter_prev_name->str,
3022 filter_name->str);
3023 if (ret) {
3024 goto error;
3025 }
3026 }
3027
3028 /* Connect last filter to all sinks */
3029 for (sink_at = sink_names; sink_at; sink_at = g_list_next(sink_at)) {
3030 GString *filter_name = filter_prev->data;
3031 GString *sink_name = sink_at->data;
3032
3033 ret = append_connect_arg(run_args, filter_name->str,
3034 sink_name->str);
3035 if (ret) {
3036 goto error;
3037 }
3038 }
3039
3040 goto end;
3041
3042 error:
3043 ret = -1;
3044
3045 end:
3046 return ret;
3047 }
3048
3049 static
3050 int split_timerange(const char *arg, char **begin, char **end)
3051 {
3052 int ret = 0;
3053 const char *ch = arg;
3054 size_t end_pos;
3055 GString *g_begin = NULL;
3056 GString *g_end = NULL;
3057
3058 BT_ASSERT(arg);
3059
3060 if (*ch == '[') {
3061 ch++;
3062 }
3063
3064 g_begin = bt_common_string_until(ch, "", ",", &end_pos);
3065 if (!g_begin || ch[end_pos] != ',' || g_begin->len == 0) {
3066 goto error;
3067 }
3068
3069 ch += end_pos + 1;
3070
3071 g_end = bt_common_string_until(ch, "", "]", &end_pos);
3072 if (!g_end || g_end->len == 0) {
3073 goto error;
3074 }
3075
3076 BT_ASSERT(begin);
3077 BT_ASSERT(end);
3078 *begin = g_begin->str;
3079 *end = g_end->str;
3080 g_string_free(g_begin, FALSE);
3081 g_string_free(g_end, FALSE);
3082 g_begin = NULL;
3083 g_end = NULL;
3084 goto end;
3085
3086 error:
3087 ret = -1;
3088
3089 end:
3090 if (g_begin) {
3091 g_string_free(g_begin, TRUE);
3092 }
3093
3094 if (g_end) {
3095 g_string_free(g_end, TRUE);
3096 }
3097
3098 return ret;
3099 }
3100
3101 static
3102 int g_list_prepend_gstring(GList **list, const char *string)
3103 {
3104 int ret = 0;
3105 GString *gs = g_string_new(string);
3106
3107 BT_ASSERT(list);
3108
3109 if (!gs) {
3110 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3111 goto end;
3112 }
3113
3114 *list = g_list_prepend(*list, gs);
3115
3116 end:
3117 return ret;
3118 }
3119
3120 /*
3121 * Create `struct implicit_component_args` structures for each of the
3122 * source components we identified. Add them to `component_args`.
3123 *
3124 * `non_opts` is an array of the non-option arguments passed on the command
3125 * line.
3126 *
3127 * `non_opt_params` is an array where each element is an array of
3128 * strings containing all the arguments to `--params` that apply to the
3129 * non-option argument at the same index. For example, if, for a
3130 * non-option argument, the following `--params` options applied:
3131 *
3132 * --params=a=2 --params=b=3,c=4
3133 *
3134 * its entry in `non_opt_params` would contain
3135 *
3136 * ["a=2", "b=3,c=4"]
3137 */
3138
3139 static
3140 int create_implicit_component_args_from_auto_discovered_sources(
3141 const struct auto_source_discovery *auto_disc,
3142 const bt_value *non_opts,
3143 const bt_value *non_opt_params,
3144 const bt_value *non_opt_loglevels,
3145 GPtrArray *component_args)
3146 {
3147 gchar *cc_name = NULL;
3148 struct implicit_component_args *comp = NULL;
3149 int status;
3150 guint i, len;
3151
3152 len = auto_disc->results->len;
3153
3154 for (i = 0; i < len; i++) {
3155 struct auto_source_discovery_result *res =
3156 g_ptr_array_index(auto_disc->results, i);
3157 uint64_t orig_indices_i, orig_indices_count;
3158
3159 g_free(cc_name);
3160 cc_name = g_strdup_printf("source.%s.%s", res->plugin_name, res->source_cc_name);
3161 if (!cc_name) {
3162 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3163 goto error;
3164 }
3165
3166 comp = create_implicit_component_args(cc_name);
3167 if (!comp) {
3168 goto error;
3169 }
3170
3171 /*
3172 * Append parameters and log levels of all the
3173 * non-option arguments that contributed to this
3174 * component instance coming into existence.
3175 */
3176 orig_indices_count = bt_value_array_get_length(res->original_input_indices);
3177 for (orig_indices_i = 0; orig_indices_i < orig_indices_count; orig_indices_i++) {
3178 const bt_value *orig_idx_value =
3179 bt_value_array_borrow_element_by_index(
3180 res->original_input_indices, orig_indices_i);
3181 uint64_t orig_idx = bt_value_integer_unsigned_get(orig_idx_value);
3182 const bt_value *params_array =
3183 bt_value_array_borrow_element_by_index_const(
3184 non_opt_params, orig_idx);
3185 uint64_t params_i, params_count;
3186 const bt_value *loglevel_value;
3187
3188 params_count = bt_value_array_get_length(params_array);
3189 for (params_i = 0; params_i < params_count; params_i++) {
3190 const bt_value *params_value =
3191 bt_value_array_borrow_element_by_index_const(
3192 params_array, params_i);
3193 const char *params = bt_value_string_get(params_value);
3194 bt_value_array_append_element_status append_status;
3195
3196 append_status = bt_value_array_append_string_element(
3197 comp->extra_params, "--params");
3198 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3199 BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element.");
3200 goto error;
3201 }
3202
3203 append_status = bt_value_array_append_string_element(
3204 comp->extra_params, params);
3205 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3206 BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element.");
3207 goto error;
3208 }
3209 }
3210
3211 loglevel_value = bt_value_array_borrow_element_by_index_const(
3212 non_opt_loglevels, orig_idx);
3213 if (bt_value_get_type(loglevel_value) == BT_VALUE_TYPE_STRING) {
3214 const char *loglevel = bt_value_string_get(loglevel_value);
3215 bt_value_array_append_element_status append_status;
3216
3217 append_status = bt_value_array_append_string_element(
3218 comp->extra_params, "--log-level");
3219 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3220 BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element.");
3221 goto error;
3222 }
3223
3224 append_status = bt_value_array_append_string_element(
3225 comp->extra_params, loglevel);
3226 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3227 BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element.");
3228 goto error;
3229 }
3230 }
3231 }
3232
3233 /*
3234 * If single input and a src.ctf.fs component, provide the
3235 * relative path from the path passed on the command line to the
3236 * found trace.
3237 */
3238 if (bt_value_array_get_length(res->inputs) == 1 &&
3239 strcmp(res->plugin_name, "ctf") == 0 &&
3240 strcmp(res->source_cc_name, "fs") == 0) {
3241 const bt_value *orig_idx_value =
3242 bt_value_array_borrow_element_by_index(
3243 res->original_input_indices, 0);
3244 uint64_t orig_idx = bt_value_integer_unsigned_get(orig_idx_value);
3245 const bt_value *non_opt_value =
3246 bt_value_array_borrow_element_by_index_const(
3247 non_opts, orig_idx);
3248 const char *non_opt = bt_value_string_get(non_opt_value);
3249 const bt_value *input_value =
3250 bt_value_array_borrow_element_by_index_const(
3251 res->inputs, 0);
3252 const char *input = bt_value_string_get(input_value);
3253
3254 BT_ASSERT(orig_indices_count == 1);
3255 BT_ASSERT(g_str_has_prefix(input, non_opt));
3256
3257 input += strlen(non_opt);
3258
3259 while (G_IS_DIR_SEPARATOR(*input)) {
3260 input++;
3261 }
3262
3263 if (strlen(input) > 0) {
3264 append_string_parameter_to_args(comp->extra_params,
3265 "trace-name", input);
3266 }
3267 }
3268
3269 status = append_parameter_to_args(comp->extra_params, "inputs", res->inputs);
3270 if (status != 0) {
3271 goto error;
3272 }
3273
3274 g_ptr_array_add(component_args, comp);
3275 comp = NULL;
3276 }
3277
3278 status = 0;
3279 goto end;
3280
3281 error:
3282 status = -1;
3283
3284 end:
3285 g_free(cc_name);
3286
3287 if (comp) {
3288 destroy_implicit_component_args(comp);
3289 }
3290
3291 return status;
3292 }
3293
3294 /*
3295 * As we iterate the arguments to the convert command, this tracks what is the
3296 * type of the current item, to which some contextual options (e.g. --params)
3297 * apply to.
3298 */
3299 enum convert_current_item_type {
3300 /* There is no current item. */
3301 CONVERT_CURRENT_ITEM_TYPE_NONE,
3302
3303 /* Current item is a component. */
3304 CONVERT_CURRENT_ITEM_TYPE_COMPONENT,
3305
3306 /* Current item is a non-option argument. */
3307 CONVERT_CURRENT_ITEM_TYPE_NON_OPT,
3308 };
3309
3310 /*
3311 * Creates a Babeltrace config object from the arguments of a convert
3312 * command.
3313 *
3314 * *retcode is set to the appropriate exit code to use.
3315 */
3316 static
3317 struct bt_config *bt_config_convert_from_args(int argc, const char *argv[],
3318 int *retcode, const bt_value *plugin_paths,
3319 int *default_log_level, const bt_interrupter *interrupter,
3320 unsigned int consumed_args)
3321 {
3322 enum convert_current_item_type current_item_type =
3323 CONVERT_CURRENT_ITEM_TYPE_NONE;
3324 int ret = 0;
3325 struct bt_config *cfg = NULL;
3326 bool got_input_format_opt = false;
3327 bool got_output_format_opt = false;
3328 bool trimmer_has_begin = false;
3329 bool trimmer_has_end = false;
3330 bool stream_intersection_mode = false;
3331 bool print_run_args = false;
3332 bool print_run_args_0 = false;
3333 bool print_ctf_metadata = false;
3334 bt_value *run_args = NULL;
3335 bt_value *all_names = NULL;
3336 GList *source_names = NULL;
3337 GList *filter_names = NULL;
3338 GList *sink_names = NULL;
3339 bt_value *non_opts = NULL;
3340 bt_value *non_opt_params = NULL;
3341 bt_value *non_opt_loglevels = NULL;
3342 struct implicit_component_args implicit_ctf_output_args = { 0 };
3343 struct implicit_component_args implicit_lttng_live_args = { 0 };
3344 struct implicit_component_args implicit_dummy_args = { 0 };
3345 struct implicit_component_args implicit_text_args = { 0 };
3346 struct implicit_component_args implicit_debug_info_args = { 0 };
3347 struct implicit_component_args implicit_muxer_args = { 0 };
3348 struct implicit_component_args implicit_trimmer_args = { 0 };
3349 char error_buf[256] = { 0 };
3350 size_t i;
3351 struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 };
3352 char *output = NULL;
3353 struct auto_source_discovery auto_disc = { NULL };
3354 GString *auto_disc_comp_name = NULL;
3355 struct argpar_iter *argpar_iter = NULL;
3356 const struct argpar_item *argpar_item = NULL;
3357 GString *name_gstr = NULL;
3358 GString *component_arg_for_run = NULL;
3359 bt_value *live_inputs_array_val = NULL;
3360
3361 /*
3362 * Array of `struct implicit_component_args *` created for the sources
3363 * we have auto-discovered.
3364 */
3365 GPtrArray *discovered_source_args = NULL;
3366
3367 /*
3368 * If set, restrict automatic source discovery to this component class
3369 * of this plugin.
3370 */
3371 const char *auto_source_discovery_restrict_plugin_name = NULL;
3372 const char *auto_source_discovery_restrict_component_class_name = NULL;
3373
3374 bool ctf_fs_source_force_clock_class_unix_epoch_origin = false;
3375 gchar *ctf_fs_source_clock_class_offset_arg = NULL;
3376 gchar *ctf_fs_source_clock_class_offset_ns_arg = NULL;
3377 *retcode = 0;
3378
3379 if (argc < 1) {
3380 print_convert_usage(stdout);
3381 *retcode = -1;
3382 goto end;
3383 }
3384
3385 if (init_implicit_component_args(&implicit_ctf_output_args,
3386 "sink.ctf.fs", false)) {
3387 goto error;
3388 }
3389
3390 if (init_implicit_component_args(&implicit_lttng_live_args,
3391 "source.ctf.lttng-live", false)) {
3392 goto error;
3393 }
3394
3395 if (init_implicit_component_args(&implicit_text_args,
3396 "sink.text.pretty", false)) {
3397 goto error;
3398 }
3399
3400 if (init_implicit_component_args(&implicit_dummy_args,
3401 "sink.utils.dummy", false)) {
3402 goto error;
3403 }
3404
3405 if (init_implicit_component_args(&implicit_debug_info_args,
3406 "filter.lttng-utils.debug-info", false)) {
3407 goto error;
3408 }
3409
3410 if (init_implicit_component_args(&implicit_muxer_args,
3411 "filter.utils.muxer", true)) {
3412 goto error;
3413 }
3414
3415 if (init_implicit_component_args(&implicit_trimmer_args,
3416 "filter.utils.trimmer", false)) {
3417 goto error;
3418 }
3419
3420 all_names = bt_value_map_create();
3421 if (!all_names) {
3422 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3423 goto error;
3424 }
3425
3426 run_args = bt_value_array_create();
3427 if (!run_args) {
3428 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3429 goto error;
3430 }
3431
3432 component_arg_for_run = g_string_new(NULL);
3433 if (!component_arg_for_run) {
3434 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3435 goto error;
3436 }
3437
3438 non_opts = bt_value_array_create();
3439 if (!non_opts) {
3440 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3441 goto error;
3442 }
3443
3444 non_opt_params = bt_value_array_create();
3445 if (!non_opt_params) {
3446 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3447 goto error;
3448 }
3449
3450 non_opt_loglevels = bt_value_array_create();
3451 if (!non_opt_loglevels) {
3452 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3453 goto error;
3454 }
3455
3456 if (auto_source_discovery_init(&auto_disc) != 0) {
3457 goto error;
3458 }
3459
3460 discovered_source_args =
3461 g_ptr_array_new_with_free_func((GDestroyNotify) destroy_implicit_component_args);
3462 if (!discovered_source_args) {
3463 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3464 goto error;
3465 }
3466
3467 auto_disc_comp_name = g_string_new(NULL);
3468 if (!auto_disc_comp_name) {
3469 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3470 goto error;
3471 }
3472
3473 /*
3474 * First pass: collect all arguments which need to be passed
3475 * as is to the run command. This pass can also add --name
3476 * arguments if needed to automatically name unnamed component
3477 * instances.
3478 */
3479 argpar_iter = argpar_iter_create(argc, argv, convert_options);
3480 if (!argpar_iter) {
3481 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3482 goto error;
3483 }
3484
3485 while (true) {
3486 enum parse_next_item_status status;
3487 char *name = NULL;
3488 char *plugin_name = NULL;
3489 char *comp_cls_name = NULL;
3490
3491 status = parse_next_item(argpar_iter, &argpar_item, argv, "convert",
3492 consumed_args);
3493 if (status == PARSE_NEXT_ITEM_STATUS_ERROR) {
3494 goto error;
3495 } else if (status == PARSE_NEXT_ITEM_STATUS_END) {
3496 break;
3497 }
3498
3499 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
3500 const struct argpar_opt_descr *opt_descr =
3501 argpar_item_opt_descr(argpar_item);
3502 const char *arg = argpar_item_opt_arg(argpar_item);
3503
3504 switch (opt_descr->id) {
3505 case OPT_HELP:
3506 print_convert_usage(stdout);
3507 *retcode = -1;
3508 BT_OBJECT_PUT_REF_AND_RESET(cfg);
3509 goto end;
3510 case OPT_COMPONENT:
3511 {
3512 bt_component_class_type type;
3513
3514 current_item_type = CONVERT_CURRENT_ITEM_TYPE_COMPONENT;
3515
3516 /* Parse the argument */
3517 plugin_comp_cls_names(arg, &name, &plugin_name,
3518 &comp_cls_name, &type);
3519 if (!plugin_name || !comp_cls_name) {
3520 BT_CLI_LOGE_APPEND_CAUSE(
3521 "Invalid format for --component option's argument:\n %s",
3522 arg);
3523 goto error;
3524 }
3525
3526 if (name) {
3527 /*
3528 * Name was given by the user, verify it isn't
3529 * taken.
3530 */
3531 if (bt_value_map_has_entry(all_names, name)) {
3532 BT_CLI_LOGE_APPEND_CAUSE(
3533 "Duplicate component instance name:\n %s",
3534 name);
3535 goto error;
3536 }
3537
3538 name_gstr = g_string_new(name);
3539 if (!name_gstr) {
3540 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3541 goto error;
3542 }
3543
3544 g_string_assign(component_arg_for_run, arg);
3545 } else {
3546 /* Name not given by user, generate one. */
3547 name_gstr = get_component_auto_name(arg, all_names);
3548 if (!name_gstr) {
3549 goto error;
3550 }
3551
3552 g_string_printf(component_arg_for_run, "%s:%s",
3553 name_gstr->str, arg);
3554 }
3555
3556 if (bt_value_array_append_string_element(run_args,
3557 "--component")) {
3558 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3559 goto error;
3560 }
3561
3562 if (bt_value_array_append_string_element(run_args,
3563 component_arg_for_run->str)) {
3564 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3565 goto error;
3566 }
3567
3568 /*
3569 * Remember this name globally, for the uniqueness of
3570 * all component names.
3571 */
3572 if (bt_value_map_insert_entry(all_names,
3573 name_gstr->str, bt_value_null)) {
3574 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3575 goto error;
3576 }
3577
3578 /*
3579 * Remember this name specifically for the type of the
3580 * component. This is to create connection arguments.
3581 *
3582 * The list takes ownership of `name_gstr`.
3583 */
3584 switch (type) {
3585 case BT_COMPONENT_CLASS_TYPE_SOURCE:
3586 source_names = g_list_append(source_names, name_gstr);
3587 break;
3588 case BT_COMPONENT_CLASS_TYPE_FILTER:
3589 filter_names = g_list_append(filter_names, name_gstr);
3590 break;
3591 case BT_COMPONENT_CLASS_TYPE_SINK:
3592 sink_names = g_list_append(sink_names, name_gstr);
3593 break;
3594 default:
3595 bt_common_abort();
3596 }
3597 name_gstr = NULL;
3598
3599 free(name);
3600 free(plugin_name);
3601 free(comp_cls_name);
3602 name = NULL;
3603 plugin_name = NULL;
3604 comp_cls_name = NULL;
3605 break;
3606 }
3607 case OPT_PARAMS:
3608 if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_COMPONENT) {
3609 /*
3610 * The current item is a component (--component option),
3611 * pass it directly to the run args.
3612 */
3613 if (bt_value_array_append_string_element(run_args,
3614 "--params")) {
3615 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3616 goto error;
3617 }
3618
3619 if (bt_value_array_append_string_element(run_args, arg)) {
3620 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3621 goto error;
3622 }
3623 } else if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_NON_OPT) {
3624 /*
3625 * The current item is a
3626 * non-option argument, record
3627 * it in `non_opt_params`.
3628 */
3629 bt_value *array;
3630 bt_value_array_append_element_status append_element_status;
3631 uint64_t idx = bt_value_array_get_length(non_opt_params) - 1;
3632
3633 array = bt_value_array_borrow_element_by_index(non_opt_params, idx);
3634
3635 append_element_status = bt_value_array_append_string_element(array, arg);
3636 if (append_element_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3637 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3638 goto error;
3639 }
3640 } else {
3641 BT_CLI_LOGE_APPEND_CAUSE(
3642 "No current component (--component option) or non-option argument of which to set parameters:\n %s",
3643 arg);
3644 goto error;
3645 }
3646 break;
3647 case OPT_LOG_LEVEL:
3648 if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_COMPONENT) {
3649 if (bt_value_array_append_string_element(run_args, "--log-level")) {
3650 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3651 goto error;
3652 }
3653
3654 if (bt_value_array_append_string_element(run_args, arg)) {
3655 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3656 goto error;
3657 }
3658 } else if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_NON_OPT) {
3659 uint64_t idx = bt_value_array_get_length(non_opt_loglevels) - 1;
3660 enum bt_value_array_set_element_by_index_status set_element_status;
3661 bt_value *log_level_str_value;
3662
3663 log_level_str_value = bt_value_string_create_init(arg);
3664 if (!log_level_str_value) {
3665 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3666 goto error;
3667 }
3668
3669 set_element_status =
3670 bt_value_array_set_element_by_index(non_opt_loglevels,
3671 idx, log_level_str_value);
3672 bt_value_put_ref(log_level_str_value);
3673 if (set_element_status != BT_VALUE_ARRAY_SET_ELEMENT_BY_INDEX_STATUS_OK) {
3674 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3675 goto error;
3676 }
3677 } else {
3678 BT_CLI_LOGE_APPEND_CAUSE(
3679 "No current component (--component option) or non-option argument to assign a log level to:\n %s",
3680 arg);
3681 goto error;
3682 }
3683
3684 break;
3685 case OPT_RETRY_DURATION:
3686 if (bt_value_array_append_string_element(run_args,
3687 "--retry-duration")) {
3688 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3689 goto error;
3690 }
3691
3692 if (bt_value_array_append_string_element(run_args, arg)) {
3693 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3694 goto error;
3695 }
3696 break;
3697 case OPT_BEGIN:
3698 case OPT_CLOCK_CYCLES:
3699 case OPT_CLOCK_DATE:
3700 case OPT_CLOCK_FORCE_CORRELATE:
3701 case OPT_CLOCK_GMT:
3702 case OPT_CLOCK_OFFSET:
3703 case OPT_CLOCK_OFFSET_NS:
3704 case OPT_CLOCK_SECONDS:
3705 case OPT_COLOR:
3706 case OPT_DEBUG:
3707 case OPT_DEBUG_INFO:
3708 case OPT_DEBUG_INFO_DIR:
3709 case OPT_DEBUG_INFO_FULL_PATH:
3710 case OPT_DEBUG_INFO_TARGET_PREFIX:
3711 case OPT_END:
3712 case OPT_FIELDS:
3713 case OPT_INPUT_FORMAT:
3714 case OPT_NAMES:
3715 case OPT_NO_DELTA:
3716 case OPT_OUTPUT_FORMAT:
3717 case OPT_OUTPUT:
3718 case OPT_RUN_ARGS:
3719 case OPT_RUN_ARGS_0:
3720 case OPT_STREAM_INTERSECTION:
3721 case OPT_TIMERANGE:
3722 case OPT_VERBOSE:
3723 /* Ignore in this pass */
3724 break;
3725 default:
3726 bt_common_abort();
3727 }
3728 } else {
3729 const char *arg = argpar_item_non_opt_arg(argpar_item);
3730 bt_value_array_append_element_status append_status;
3731
3732 current_item_type = CONVERT_CURRENT_ITEM_TYPE_NON_OPT;
3733
3734 append_status = bt_value_array_append_string_element(non_opts, arg);
3735 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3736 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3737 goto error;
3738 }
3739
3740 append_status = bt_value_array_append_empty_array_element(
3741 non_opt_params, NULL);
3742 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3743 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3744 goto error;
3745 }
3746
3747 append_status = bt_value_array_append_element(non_opt_loglevels, bt_value_null);
3748 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3749 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3750 goto error;
3751 }
3752 }
3753 }
3754
3755 /*
3756 * Second pass: transform the convert-specific options and
3757 * arguments into implicit component instances for the run
3758 * command.
3759 */
3760 argpar_iter_destroy(argpar_iter);
3761 argpar_iter = argpar_iter_create(argc, argv, convert_options);
3762 if (!argpar_iter) {
3763 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3764 goto error;
3765 }
3766
3767 while (true) {
3768 enum parse_next_item_status status;
3769 const struct argpar_opt_descr *opt_descr;
3770 const char *arg;
3771
3772 status = parse_next_item(argpar_iter, &argpar_item, argv, "convert",
3773 consumed_args);
3774 if (status == PARSE_NEXT_ITEM_STATUS_ERROR) {
3775 goto error;
3776 } else if (status == PARSE_NEXT_ITEM_STATUS_END) {
3777 break;
3778 }
3779
3780 if (argpar_item_type(argpar_item) != ARGPAR_ITEM_TYPE_OPT) {
3781 continue;
3782 }
3783
3784 opt_descr = argpar_item_opt_descr(argpar_item);
3785 arg = argpar_item_opt_arg(argpar_item);
3786
3787 switch (opt_descr->id) {
3788 case OPT_BEGIN:
3789 if (trimmer_has_begin) {
3790 BT_CLI_LOGE_APPEND_CAUSE("At --begin option: --begin or --timerange option already specified\n %s\n",
3791 arg);
3792 goto error;
3793 }
3794
3795 trimmer_has_begin = true;
3796 ret = append_implicit_component_extra_param(
3797 &implicit_trimmer_args, "begin", arg);
3798 implicit_trimmer_args.exists = true;
3799 if (ret) {
3800 goto error;
3801 }
3802 break;
3803 case OPT_END:
3804 if (trimmer_has_end) {
3805 BT_CLI_LOGE_APPEND_CAUSE("At --end option: --end or --timerange option already specified\n %s\n",
3806 arg);
3807 goto error;
3808 }
3809
3810 trimmer_has_end = true;
3811 ret = append_implicit_component_extra_param(
3812 &implicit_trimmer_args, "end", arg);
3813 implicit_trimmer_args.exists = true;
3814 if (ret) {
3815 goto error;
3816 }
3817 break;
3818 case OPT_TIMERANGE:
3819 {
3820 char *begin;
3821 char *end;
3822
3823 if (trimmer_has_begin || trimmer_has_end) {
3824 BT_CLI_LOGE_APPEND_CAUSE("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n",
3825 arg);
3826 goto error;
3827 }
3828
3829 ret = split_timerange(arg, &begin, &end);
3830 if (ret) {
3831 BT_CLI_LOGE_APPEND_CAUSE("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s",
3832 arg);
3833 goto error;
3834 }
3835
3836 ret = append_implicit_component_extra_param(
3837 &implicit_trimmer_args, "begin", begin);
3838 ret |= append_implicit_component_extra_param(
3839 &implicit_trimmer_args, "end", end);
3840 implicit_trimmer_args.exists = true;
3841 free(begin);
3842 free(end);
3843 if (ret) {
3844 goto error;
3845 }
3846 break;
3847 }
3848 case OPT_CLOCK_CYCLES:
3849 append_implicit_component_param(
3850 &implicit_text_args, "clock-cycles", "yes");
3851 implicit_text_args.exists = true;
3852 break;
3853 case OPT_CLOCK_DATE:
3854 append_implicit_component_param(
3855 &implicit_text_args, "clock-date", "yes");
3856 implicit_text_args.exists = true;
3857 break;
3858 case OPT_CLOCK_FORCE_CORRELATE:
3859 ctf_fs_source_force_clock_class_unix_epoch_origin = true;
3860 break;
3861 case OPT_CLOCK_GMT:
3862 append_implicit_component_param(
3863 &implicit_text_args, "clock-gmt", "yes");
3864 append_implicit_component_param(
3865 &implicit_trimmer_args, "gmt", "yes");
3866 implicit_text_args.exists = true;
3867 break;
3868 case OPT_CLOCK_OFFSET:
3869 if (ctf_fs_source_clock_class_offset_arg) {
3870 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset option\n");
3871 goto error;
3872 }
3873
3874 ctf_fs_source_clock_class_offset_arg = g_strdup(arg);
3875 if (!ctf_fs_source_clock_class_offset_arg) {
3876 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3877 goto error;
3878 }
3879 break;
3880 case OPT_CLOCK_OFFSET_NS:
3881 if (ctf_fs_source_clock_class_offset_ns_arg) {
3882 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset-ns option\n");
3883 goto error;
3884 }
3885
3886 ctf_fs_source_clock_class_offset_ns_arg = g_strdup(arg);
3887 if (!ctf_fs_source_clock_class_offset_ns_arg) {
3888 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3889 goto error;
3890 }
3891 break;
3892 case OPT_CLOCK_SECONDS:
3893 append_implicit_component_param(
3894 &implicit_text_args, "clock-seconds", "yes");
3895 implicit_text_args.exists = true;
3896 break;
3897 case OPT_COLOR:
3898 implicit_text_args.exists = true;
3899 ret = append_implicit_component_extra_param(
3900 &implicit_text_args, "color", arg);
3901 if (ret) {
3902 goto error;
3903 }
3904 break;
3905 case OPT_DEBUG_INFO:
3906 implicit_debug_info_args.exists = true;
3907 break;
3908 case OPT_DEBUG_INFO_DIR:
3909 implicit_debug_info_args.exists = true;
3910 ret = append_implicit_component_extra_param(
3911 &implicit_debug_info_args, "debug-info-dir", arg);
3912 if (ret) {
3913 goto error;
3914 }
3915 break;
3916 case OPT_DEBUG_INFO_FULL_PATH:
3917 implicit_debug_info_args.exists = true;
3918 append_implicit_component_param(
3919 &implicit_debug_info_args, "full-path", "yes");
3920 break;
3921 case OPT_DEBUG_INFO_TARGET_PREFIX:
3922 implicit_debug_info_args.exists = true;
3923 ret = append_implicit_component_extra_param(
3924 &implicit_debug_info_args,
3925 "target-prefix", arg);
3926 if (ret) {
3927 goto error;
3928 }
3929 break;
3930 case OPT_FIELDS:
3931 {
3932 bt_value *fields = fields_from_arg(arg);
3933
3934 if (!fields) {
3935 goto error;
3936 }
3937
3938 implicit_text_args.exists = true;
3939 ret = insert_flat_params_from_array(
3940 implicit_text_args.params_arg,
3941 fields, "field");
3942 bt_value_put_ref(fields);
3943 if (ret) {
3944 goto error;
3945 }
3946 break;
3947 }
3948 case OPT_NAMES:
3949 {
3950 bt_value *names = names_from_arg(arg);
3951
3952 if (!names) {
3953 goto error;
3954 }
3955
3956 implicit_text_args.exists = true;
3957 ret = insert_flat_params_from_array(
3958 implicit_text_args.params_arg,
3959 names, "name");
3960 bt_value_put_ref(names);
3961 if (ret) {
3962 goto error;
3963 }
3964 break;
3965 }
3966 case OPT_NO_DELTA:
3967 append_implicit_component_param(
3968 &implicit_text_args, "no-delta", "yes");
3969 implicit_text_args.exists = true;
3970 break;
3971 case OPT_INPUT_FORMAT:
3972 if (got_input_format_opt) {
3973 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --input-format option.");
3974 goto error;
3975 }
3976
3977 got_input_format_opt = true;
3978
3979 if (strcmp(arg, "ctf") == 0) {
3980 auto_source_discovery_restrict_plugin_name = "ctf";
3981 auto_source_discovery_restrict_component_class_name = "fs";
3982 } else if (strcmp(arg, "lttng-live") == 0) {
3983 auto_source_discovery_restrict_plugin_name = "ctf";
3984 auto_source_discovery_restrict_component_class_name = "lttng-live";
3985 implicit_lttng_live_args.exists = true;
3986 } else {
3987 BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy input format:\n %s",
3988 arg);
3989 goto error;
3990 }
3991 break;
3992 case OPT_OUTPUT_FORMAT:
3993 if (got_output_format_opt) {
3994 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output-format option.");
3995 goto error;
3996 }
3997
3998 got_output_format_opt = true;
3999
4000 if (strcmp(arg, "text") == 0) {
4001 implicit_text_args.exists = true;
4002 } else if (strcmp(arg, "ctf") == 0) {
4003 implicit_ctf_output_args.exists = true;
4004 } else if (strcmp(arg, "dummy") == 0) {
4005 implicit_dummy_args.exists = true;
4006 } else if (strcmp(arg, "ctf-metadata") == 0) {
4007 print_ctf_metadata = true;
4008 } else {
4009 BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy output format:\n %s",
4010 arg);
4011 goto error;
4012 }
4013 break;
4014 case OPT_OUTPUT:
4015 if (output) {
4016 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output option");
4017 goto error;
4018 }
4019
4020 output = strdup(arg);
4021 if (!output) {
4022 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4023 goto error;
4024 }
4025 break;
4026 case OPT_RUN_ARGS:
4027 if (print_run_args_0) {
4028 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0.");
4029 goto error;
4030 }
4031
4032 print_run_args = true;
4033 break;
4034 case OPT_RUN_ARGS_0:
4035 if (print_run_args) {
4036 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0.");
4037 goto error;
4038 }
4039
4040 print_run_args_0 = true;
4041 break;
4042 case OPT_STREAM_INTERSECTION:
4043 /*
4044 * Applies to all traces implementing the
4045 * babeltrace.trace-infos query.
4046 */
4047 stream_intersection_mode = true;
4048 break;
4049 case OPT_VERBOSE:
4050 *default_log_level =
4051 logging_level_min(*default_log_level, BT_LOG_INFO);
4052 break;
4053 case OPT_DEBUG:
4054 *default_log_level =
4055 logging_level_min(*default_log_level, BT_LOG_TRACE);
4056 break;
4057 case OPT_COMPONENT:
4058 case OPT_HELP:
4059 case OPT_LOG_LEVEL:
4060 case OPT_OMIT_HOME_PLUGIN_PATH:
4061 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
4062 case OPT_PARAMS:
4063 case OPT_PLUGIN_PATH:
4064 case OPT_RETRY_DURATION:
4065 /* Ignore in this pass */
4066 break;
4067 default:
4068 bt_common_abort();
4069 }
4070 }
4071
4072 set_auto_log_levels(default_log_level);
4073
4074 /*
4075 * Legacy behaviour: --verbose used to make the `text` output
4076 * format print more information. --verbose is now equivalent to
4077 * the INFO log level, which is why we compare to `BT_LOG_INFO`
4078 * here.
4079 */
4080 if (*default_log_level == BT_LOG_INFO) {
4081 append_implicit_component_param(&implicit_text_args,
4082 "verbose", "yes");
4083 }
4084
4085 /* Print CTF metadata or print LTTng live sessions */
4086 if (print_ctf_metadata) {
4087 const bt_value *bt_val_non_opt;
4088
4089 if (bt_value_array_is_empty(non_opts)) {
4090 BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf-metadata specified without a path.");
4091 goto error;
4092 }
4093
4094 if (bt_value_array_get_length(non_opts) > 1) {
4095 BT_CLI_LOGE_APPEND_CAUSE("Too many paths specified for --output-format=ctf-metadata.");
4096 goto error;
4097 }
4098
4099 cfg = bt_config_print_ctf_metadata_create(plugin_paths);
4100 if (!cfg) {
4101 goto error;
4102 }
4103
4104 bt_val_non_opt = bt_value_array_borrow_element_by_index_const(non_opts, 0);
4105 g_string_assign(cfg->cmd_data.print_ctf_metadata.path,
4106 bt_value_string_get(bt_val_non_opt));
4107
4108 if (output) {
4109 g_string_assign(
4110 cfg->cmd_data.print_ctf_metadata.output_path,
4111 output);
4112 }
4113
4114 goto end;
4115 }
4116
4117 /*
4118 * If -o ctf was specified, make sure an output path (--output)
4119 * was also specified. --output does not imply -o ctf because
4120 * it's also used for the default, implicit -o text if -o ctf
4121 * is not specified.
4122 */
4123 if (implicit_ctf_output_args.exists) {
4124 if (!output) {
4125 BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf specified without --output (trace output path).");
4126 goto error;
4127 }
4128
4129 /*
4130 * At this point we know that -o ctf AND --output were
4131 * specified. Make sure that no options were specified
4132 * which would imply -o text because --output would be
4133 * ambiguous in this case. For example, this is wrong:
4134 *
4135 * babeltrace2 --names=all -o ctf --output=/tmp/path my-trace
4136 *
4137 * because --names=all implies -o text, and --output
4138 * could apply to both the sink.text.pretty and
4139 * sink.ctf.fs implicit components.
4140 */
4141 if (implicit_text_args.exists) {
4142 BT_CLI_LOGE_APPEND_CAUSE("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text.");
4143 goto error;
4144 }
4145 }
4146
4147 /*
4148 * If -o dummy and -o ctf were not specified, and if there are
4149 * no explicit sink components, then use an implicit
4150 * `sink.text.pretty` component.
4151 */
4152 if (!implicit_dummy_args.exists && !implicit_ctf_output_args.exists &&
4153 !sink_names) {
4154 implicit_text_args.exists = true;
4155 }
4156
4157 /*
4158 * Set implicit `sink.text.pretty` or `sink.ctf.fs` component's
4159 * `path` parameter if --output was specified.
4160 */
4161 if (output) {
4162 if (implicit_text_args.exists) {
4163 append_implicit_component_extra_param(&implicit_text_args,
4164 "path", output);
4165 } else if (implicit_ctf_output_args.exists) {
4166 append_implicit_component_extra_param(&implicit_ctf_output_args,
4167 "path", output);
4168 }
4169 }
4170
4171 /* Decide where the non-option argument(s) go */
4172 if (bt_value_array_get_length(non_opts) > 0) {
4173 if (implicit_lttng_live_args.exists) {
4174 const bt_value *bt_val_non_opt;
4175
4176 if (bt_value_array_get_length(non_opts) > 1) {
4177 BT_CLI_LOGE_APPEND_CAUSE("Too many URLs specified for --input-format=lttng-live.");
4178 goto error;
4179 }
4180
4181 bt_val_non_opt = bt_value_array_borrow_element_by_index_const(non_opts, 0);
4182 lttng_live_url_parts =
4183 bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_non_opt),
4184 error_buf, sizeof(error_buf));
4185 if (!lttng_live_url_parts.proto) {
4186 BT_CLI_LOGE_APPEND_CAUSE("Invalid LTTng live URL format: %s.",
4187 error_buf);
4188 goto error;
4189 }
4190
4191 if (!lttng_live_url_parts.session_name) {
4192 /* Print LTTng live sessions */
4193 cfg = bt_config_print_lttng_live_sessions_create(
4194 plugin_paths);
4195 if (!cfg) {
4196 goto error;
4197 }
4198
4199 g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url,
4200 bt_value_string_get(bt_val_non_opt));
4201
4202 if (output) {
4203 g_string_assign(
4204 cfg->cmd_data.print_lttng_live_sessions.output_path,
4205 output);
4206 }
4207
4208 goto end;
4209 }
4210
4211 live_inputs_array_val = bt_value_array_create();
4212 if (!live_inputs_array_val) {
4213 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4214 goto error;
4215 }
4216
4217 if (bt_value_array_append_string_element(
4218 live_inputs_array_val,
4219 bt_value_string_get(bt_val_non_opt))) {
4220 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4221 goto error;
4222 }
4223
4224 ret = append_parameter_to_args(
4225 implicit_lttng_live_args.extra_params,
4226 "inputs", live_inputs_array_val);
4227 if (ret) {
4228 goto error;
4229 }
4230
4231 ret = append_implicit_component_extra_param(
4232 &implicit_lttng_live_args,
4233 "session-not-found-action", "end");
4234 if (ret) {
4235 goto error;
4236 }
4237 } else {
4238 int status;
4239 size_t plugin_count;
4240 const bt_plugin **plugins;
4241 const bt_plugin *plugin;
4242
4243 status = require_loaded_plugins(plugin_paths);
4244 if (status != 0) {
4245 goto error;
4246 }
4247
4248 if (auto_source_discovery_restrict_plugin_name) {
4249 plugin_count = 1;
4250 plugin = borrow_loaded_plugin_by_name(auto_source_discovery_restrict_plugin_name);
4251 plugins = &plugin;
4252 } else {
4253 plugin_count = get_loaded_plugins_count();
4254 plugins = borrow_loaded_plugins();
4255 }
4256
4257 status = auto_discover_source_components(non_opts, plugins, plugin_count,
4258 auto_source_discovery_restrict_component_class_name,
4259 *default_log_level, &auto_disc, interrupter);
4260
4261 if (status != 0) {
4262 if (status == AUTO_SOURCE_DISCOVERY_STATUS_INTERRUPTED) {
4263 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(
4264 "Babeltrace CLI", "Automatic source discovery interrupted by the user");
4265 }
4266 goto error;
4267 }
4268
4269 status = create_implicit_component_args_from_auto_discovered_sources(
4270 &auto_disc, non_opts, non_opt_params, non_opt_loglevels,
4271 discovered_source_args);
4272 if (status != 0) {
4273 goto error;
4274 }
4275 }
4276 }
4277
4278
4279 /*
4280 * If --clock-force-correlated was given, apply it to any src.ctf.fs
4281 * component.
4282 */
4283 if (ctf_fs_source_force_clock_class_unix_epoch_origin) {
4284 int n;
4285
4286 n = append_multiple_implicit_components_param(
4287 discovered_source_args, "source.ctf.fs", "force-clock-class-origin-unix-epoch",
4288 "yes");
4289 if (n == 0) {
4290 BT_CLI_LOGE_APPEND_CAUSE("--clock-force-correlate specified, but no source.ctf.fs component instantiated.");
4291 goto error;
4292 }
4293 }
4294
4295 /* If --clock-offset was given, apply it to any src.ctf.fs component. */
4296 if (ctf_fs_source_clock_class_offset_arg) {
4297 int n;
4298
4299 n = append_multiple_implicit_components_param(
4300 discovered_source_args, "source.ctf.fs", "clock-class-offset-s",
4301 ctf_fs_source_clock_class_offset_arg);
4302
4303 if (n == 0) {
4304 BT_CLI_LOGE_APPEND_CAUSE("--clock-offset specified, but no source.ctf.fs component instantiated.");
4305 goto error;
4306 }
4307 }
4308
4309 /* If --clock-offset-ns was given, apply it to any src.ctf.fs component. */
4310 if (ctf_fs_source_clock_class_offset_ns_arg) {
4311 int n;
4312
4313 n = append_multiple_implicit_components_param(
4314 discovered_source_args, "source.ctf.fs", "clock-class-offset-ns",
4315 ctf_fs_source_clock_class_offset_ns_arg);
4316
4317 if (n == 0) {
4318 BT_CLI_LOGE_APPEND_CAUSE("--clock-offset-ns specified, but no source.ctf.fs component instantiated.");
4319 goto error;
4320 }
4321 }
4322
4323 /*
4324 * If the implicit `source.ctf.lttng-live` component exists,
4325 * make sure there's at least one non-option argument (which is
4326 * the URL).
4327 */
4328 if (implicit_lttng_live_args.exists && bt_value_array_is_empty(non_opts)) {
4329 BT_CLI_LOGE_APPEND_CAUSE("Missing URL for implicit `%s` component.",
4330 implicit_lttng_live_args.comp_arg->str);
4331 goto error;
4332 }
4333
4334 /* Assign names to implicit components */
4335 for (i = 0; i < discovered_source_args->len; i++) {
4336 struct implicit_component_args *args;
4337 int j;
4338
4339 args = discovered_source_args->pdata[i];
4340
4341 g_string_printf(auto_disc_comp_name, "auto-disc-%s", args->comp_arg->str);
4342
4343 /* Give it a name like `auto-disc-src-ctf-fs`. */
4344 for (j = 0; j < auto_disc_comp_name->len; j++) {
4345 if (auto_disc_comp_name->str[j] == '.') {
4346 auto_disc_comp_name->str[j] = '-';
4347 }
4348 }
4349
4350 ret = assign_name_to_implicit_component(args,
4351 auto_disc_comp_name->str, all_names, &source_names, true);
4352 if (ret) {
4353 goto error;
4354 }
4355 }
4356
4357 ret = assign_name_to_implicit_component(&implicit_lttng_live_args,
4358 "lttng-live", all_names, &source_names, true);
4359 if (ret) {
4360 goto error;
4361 }
4362
4363 ret = assign_name_to_implicit_component(&implicit_text_args,
4364 "pretty", all_names, &sink_names, true);
4365 if (ret) {
4366 goto error;
4367 }
4368
4369 ret = assign_name_to_implicit_component(&implicit_ctf_output_args,
4370 "sink-ctf-fs", all_names, &sink_names, true);
4371 if (ret) {
4372 goto error;
4373 }
4374
4375 ret = assign_name_to_implicit_component(&implicit_dummy_args,
4376 "dummy", all_names, &sink_names, true);
4377 if (ret) {
4378 goto error;
4379 }
4380
4381 ret = assign_name_to_implicit_component(&implicit_muxer_args,
4382 "muxer", all_names, NULL, false);
4383 if (ret) {
4384 goto error;
4385 }
4386
4387 ret = assign_name_to_implicit_component(&implicit_trimmer_args,
4388 "trimmer", all_names, NULL, false);
4389 if (ret) {
4390 goto error;
4391 }
4392
4393 ret = assign_name_to_implicit_component(&implicit_debug_info_args,
4394 "debug-info", all_names, NULL, false);
4395 if (ret) {
4396 goto error;
4397 }
4398
4399 /* Make sure there's at least one source and one sink */
4400 if (!source_names) {
4401 BT_CLI_LOGE_APPEND_CAUSE("No source component.");
4402 goto error;
4403 }
4404
4405 if (!sink_names) {
4406 BT_CLI_LOGE_APPEND_CAUSE("No sink component.");
4407 goto error;
4408 }
4409
4410 /* Make sure there's a single sink component */
4411 if (g_list_length(sink_names) != 1) {
4412 BT_CLI_LOGE_APPEND_CAUSE(
4413 "More than one sink component specified.");
4414 goto error;
4415 }
4416
4417 /*
4418 * Prepend the muxer, the trimmer, and the debug info to the
4419 * filter chain so that we have:
4420 *
4421 * sources -> muxer -> [trimmer] -> [debug info] ->
4422 * [user filters] -> sinks
4423 */
4424 if (implicit_debug_info_args.exists) {
4425 if (g_list_prepend_gstring(&filter_names,
4426 implicit_debug_info_args.name_arg->str)) {
4427 goto error;
4428 }
4429 }
4430
4431 if (implicit_trimmer_args.exists) {
4432 if (g_list_prepend_gstring(&filter_names,
4433 implicit_trimmer_args.name_arg->str)) {
4434 goto error;
4435 }
4436 }
4437
4438 if (g_list_prepend_gstring(&filter_names,
4439 implicit_muxer_args.name_arg->str)) {
4440 goto error;
4441 }
4442
4443 /*
4444 * Append the equivalent run arguments for the implicit
4445 * components.
4446 */
4447 for (i = 0; i < discovered_source_args->len; i++) {
4448 struct implicit_component_args *args =
4449 discovered_source_args->pdata[i];
4450
4451 ret = append_run_args_for_implicit_component(args, run_args);
4452 if (ret) {
4453 goto error;
4454 }
4455 }
4456
4457 ret = append_run_args_for_implicit_component(&implicit_lttng_live_args,
4458 run_args);
4459 if (ret) {
4460 goto error;
4461 }
4462
4463 ret = append_run_args_for_implicit_component(&implicit_text_args,
4464 run_args);
4465 if (ret) {
4466 goto error;
4467 }
4468
4469 ret = append_run_args_for_implicit_component(&implicit_ctf_output_args,
4470 run_args);
4471 if (ret) {
4472 goto error;
4473 }
4474
4475 ret = append_run_args_for_implicit_component(&implicit_dummy_args,
4476 run_args);
4477 if (ret) {
4478 goto error;
4479 }
4480
4481 ret = append_run_args_for_implicit_component(&implicit_muxer_args,
4482 run_args);
4483 if (ret) {
4484 goto error;
4485 }
4486
4487 ret = append_run_args_for_implicit_component(&implicit_trimmer_args,
4488 run_args);
4489 if (ret) {
4490 goto error;
4491 }
4492
4493 ret = append_run_args_for_implicit_component(&implicit_debug_info_args,
4494 run_args);
4495 if (ret) {
4496 goto error;
4497 }
4498
4499 /* Auto-connect components */
4500 ret = convert_auto_connect(run_args, source_names, filter_names,
4501 sink_names);
4502 if (ret) {
4503 BT_CLI_LOGE_APPEND_CAUSE("Cannot auto-connect components.");
4504 goto error;
4505 }
4506
4507 /*
4508 * We have all the run command arguments now. Depending on
4509 * --run-args, we pass this to the run command or print them
4510 * here.
4511 */
4512 if (print_run_args || print_run_args_0) {
4513 uint64_t args_idx, args_len;
4514 if (stream_intersection_mode) {
4515 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --stream-intersection with --run-args or --run-args-0.");
4516 goto error;
4517 }
4518
4519 args_len = bt_value_array_get_length(run_args);
4520 for (args_idx = 0; args_idx < args_len; args_idx++) {
4521 const bt_value *arg_value =
4522 bt_value_array_borrow_element_by_index(run_args,
4523 args_idx);
4524 const char *arg;
4525 GString *quoted = NULL;
4526 const char *arg_to_print;
4527
4528 arg = bt_value_string_get(arg_value);
4529
4530 if (print_run_args) {
4531 quoted = bt_common_shell_quote(arg, true);
4532 if (!quoted) {
4533 goto error;
4534 }
4535
4536 arg_to_print = quoted->str;
4537 } else {
4538 arg_to_print = arg;
4539 }
4540
4541 printf("%s", arg_to_print);
4542
4543 if (quoted) {
4544 g_string_free(quoted, TRUE);
4545 }
4546
4547 if (args_idx < args_len - 1) {
4548 if (print_run_args) {
4549 putchar(' ');
4550 } else {
4551 putchar('\0');
4552 }
4553 }
4554 }
4555
4556 *retcode = -1;
4557 BT_OBJECT_PUT_REF_AND_RESET(cfg);
4558 goto end;
4559 }
4560
4561 cfg = bt_config_run_from_args_array(run_args, retcode,
4562 plugin_paths, *default_log_level);
4563 if (!cfg) {
4564 goto error;
4565 }
4566
4567 cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode;
4568 goto end;
4569
4570 error:
4571 *retcode = 1;
4572 BT_OBJECT_PUT_REF_AND_RESET(cfg);
4573
4574 end:
4575 argpar_iter_destroy(argpar_iter);
4576 argpar_item_destroy(argpar_item);
4577
4578 free(output);
4579
4580 if (component_arg_for_run) {
4581 g_string_free(component_arg_for_run, TRUE);
4582 }
4583
4584 if (name_gstr) {
4585 g_string_free(name_gstr, TRUE);
4586 }
4587
4588 bt_value_put_ref(live_inputs_array_val);
4589 bt_value_put_ref(run_args);
4590 bt_value_put_ref(all_names);
4591 destroy_glist_of_gstring(source_names);
4592 destroy_glist_of_gstring(filter_names);
4593 destroy_glist_of_gstring(sink_names);
4594 bt_value_put_ref(non_opt_params);
4595 bt_value_put_ref(non_opt_loglevels);
4596 bt_value_put_ref(non_opts);
4597 finalize_implicit_component_args(&implicit_ctf_output_args);
4598 finalize_implicit_component_args(&implicit_lttng_live_args);
4599 finalize_implicit_component_args(&implicit_dummy_args);
4600 finalize_implicit_component_args(&implicit_text_args);
4601 finalize_implicit_component_args(&implicit_debug_info_args);
4602 finalize_implicit_component_args(&implicit_muxer_args);
4603 finalize_implicit_component_args(&implicit_trimmer_args);
4604 bt_common_destroy_lttng_live_url_parts(&lttng_live_url_parts);
4605 auto_source_discovery_fini(&auto_disc);
4606
4607 if (discovered_source_args) {
4608 g_ptr_array_free(discovered_source_args, TRUE);
4609 }
4610
4611 g_free(ctf_fs_source_clock_class_offset_arg);
4612 g_free(ctf_fs_source_clock_class_offset_ns_arg);
4613
4614 if (auto_disc_comp_name) {
4615 g_string_free(auto_disc_comp_name, TRUE);
4616 }
4617
4618 return cfg;
4619 }
4620
4621 /*
4622 * Prints the Babeltrace 2.x general usage.
4623 */
4624 static
4625 void print_gen_usage(FILE *fp)
4626 {
4627 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS]\n");
4628 fprintf(fp, "\n");
4629 fprintf(fp, "General options:\n");
4630 fprintf(fp, "\n");
4631 fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=T)\n");
4632 fprintf(fp, " -h, --help Show this help and quit\n");
4633 fprintf(fp, " -l, --log-level=LVL Set the default log level to LVL (`N`, `T`, `D`,\n");
4634 fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n");
4635 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
4636 fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n");
4637 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
4638 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
4639 fprintf(fp, " dynamic plugins can be loaded\n");
4640 fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n");
4641 fprintf(fp, " -V, --version Show version and quit\n");
4642 fprintf(fp, "\n");
4643 fprintf(fp, "Available commands:\n");
4644 fprintf(fp, "\n");
4645 fprintf(fp, " convert Convert and trim traces (default)\n");
4646 fprintf(fp, " help Get help for a plugin or a component class\n");
4647 fprintf(fp, " list-plugins List available plugins and their content\n");
4648 fprintf(fp, " query Query objects from a component class\n");
4649 fprintf(fp, " run Build a processing graph and run it\n");
4650 fprintf(fp, "\n");
4651 fprintf(fp, "Use `babeltrace2 COMMAND --help` to show the help of COMMAND.\n");
4652 }
4653
4654 struct bt_config *bt_config_cli_args_create(int argc, const char *argv[],
4655 int *retcode, bool omit_system_plugin_path,
4656 bool omit_home_plugin_path,
4657 const bt_value *initial_plugin_paths,
4658 const bt_interrupter *interrupter)
4659 {
4660 struct bt_config *config = NULL;
4661 int top_level_argc;
4662 const char **top_level_argv;
4663 int command_argc = -1;
4664 const char **command_argv = NULL;
4665 const char *command_name = NULL;
4666 int default_log_level = -1;
4667 struct argpar_iter *argpar_iter = NULL;
4668 const struct argpar_item *argpar_item = NULL;
4669 const struct argpar_error *argpar_error = NULL;
4670 bt_value *plugin_paths = NULL;
4671 unsigned int consumed_args;
4672
4673 /* Top-level option descriptions. */
4674 static const struct argpar_opt_descr descrs[] = {
4675 { OPT_DEBUG, 'd', "debug", false },
4676 { OPT_HELP, 'h', "help", false },
4677 { OPT_LOG_LEVEL, 'l', "log-level", true },
4678 { OPT_VERBOSE, 'v', "verbose", false },
4679 { OPT_VERSION, 'V', "version", false},
4680 { OPT_OMIT_HOME_PLUGIN_PATH, '\0', "omit-home-plugin-path", false },
4681 { OPT_OMIT_SYSTEM_PLUGIN_PATH, '\0', "omit-system-plugin-path", false },
4682 { OPT_PLUGIN_PATH, '\0', "plugin-path", true },
4683 ARGPAR_OPT_DESCR_SENTINEL
4684 };
4685
4686 enum command_type {
4687 COMMAND_TYPE_NONE = -1,
4688 COMMAND_TYPE_RUN = 0,
4689 COMMAND_TYPE_CONVERT,
4690 COMMAND_TYPE_LIST_PLUGINS,
4691 COMMAND_TYPE_HELP,
4692 COMMAND_TYPE_QUERY,
4693 } command_type = COMMAND_TYPE_NONE;
4694
4695 *retcode = -1;
4696
4697 if (!initial_plugin_paths) {
4698 plugin_paths = bt_value_array_create();
4699 if (!plugin_paths) {
4700 goto error;
4701 }
4702 } else {
4703 bt_value_copy_status copy_status = bt_value_copy(
4704 initial_plugin_paths, &plugin_paths);
4705 if (copy_status) {
4706 goto error;
4707 }
4708 }
4709
4710 BT_ASSERT(plugin_paths);
4711
4712 /*
4713 * The `BABELTRACE_PLUGIN_PATH` paths take precedence over the
4714 * `--plugin-path` option's paths, so append it now before
4715 * parsing the general options.
4716 */
4717 if (append_env_var_plugin_paths(plugin_paths)) {
4718 goto error;
4719 }
4720
4721 if (argc <= 1) {
4722 print_version();
4723 puts("");
4724 print_gen_usage(stdout);
4725 goto end;
4726 }
4727
4728 /* Skip first argument, the name of the program. */
4729 top_level_argc = argc - 1;
4730 top_level_argv = argv + 1;
4731
4732 argpar_iter = argpar_iter_create(top_level_argc, top_level_argv, descrs);
4733 if (!argpar_iter) {
4734 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4735 goto error;
4736 }
4737
4738 while (true) {
4739 enum argpar_iter_next_status status;
4740
4741 ARGPAR_ITEM_DESTROY_AND_RESET(argpar_item);
4742 status = argpar_iter_next(argpar_iter, &argpar_item, &argpar_error);
4743
4744 switch (status) {
4745 case ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY:
4746 BT_CLI_LOGE_APPEND_CAUSE_OOM();
4747 goto error;
4748 case ARGPAR_ITER_NEXT_STATUS_ERROR:
4749 {
4750 if (argpar_error_type(argpar_error)
4751 != ARGPAR_ERROR_TYPE_UNKNOWN_OPT) {
4752 GString *err_str = format_arg_error(argpar_error, top_level_argv,
4753 0, "While parsing command-line arguments");
4754 BT_CLI_LOGE_APPEND_CAUSE("%s", err_str->str);
4755 g_string_free(err_str, TRUE);
4756 goto error;
4757 }
4758
4759 break;
4760 }
4761 default:
4762 break;
4763 }
4764
4765 if (status == ARGPAR_ITER_NEXT_STATUS_END) {
4766 break;
4767 }
4768
4769 if (status == ARGPAR_ITER_NEXT_STATUS_ERROR) {
4770 BT_ASSERT(argpar_error_type(argpar_error) ==
4771 ARGPAR_ERROR_TYPE_UNKNOWN_OPT);
4772 /*
4773 * Unknown option, assume this is implicitly the
4774 * convert command, stop processing arguments.
4775 */
4776 break;
4777 }
4778
4779 if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) {
4780 const struct argpar_opt_descr *opt_descr =
4781 argpar_item_opt_descr(argpar_item);
4782 const char *arg = argpar_item_opt_arg(argpar_item);
4783
4784 switch (opt_descr->id) {
4785 case OPT_DEBUG:
4786 default_log_level =
4787 logging_level_min(default_log_level, BT_LOG_TRACE);
4788 break;
4789 case OPT_VERBOSE:
4790 default_log_level =
4791 logging_level_min(default_log_level, BT_LOG_INFO);
4792 break;
4793 case OPT_LOG_LEVEL:
4794 {
4795 int level = bt_log_get_level_from_string(arg);
4796
4797 if (level < 0) {
4798 BT_CLI_LOGE_APPEND_CAUSE(
4799 "Invalid argument for --log-level option:\n %s",
4800 arg);
4801 goto error;
4802 }
4803
4804 default_log_level =
4805 logging_level_min(default_log_level, level);
4806 break;
4807 }
4808 case OPT_PLUGIN_PATH:
4809 if (bt_config_append_plugin_paths_check_setuid_setgid(
4810 plugin_paths, arg)) {
4811 goto error;
4812 }
4813 break;
4814 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
4815 omit_system_plugin_path = true;
4816 break;
4817 case OPT_OMIT_HOME_PLUGIN_PATH:
4818 omit_home_plugin_path = true;
4819 break;
4820 case OPT_VERSION:
4821 print_version();
4822 goto end;
4823 case OPT_HELP:
4824 print_gen_usage(stdout);
4825 goto end;
4826 default:
4827 bt_common_abort();
4828 }
4829 } else {
4830 const char *arg = argpar_item_non_opt_arg(argpar_item);
4831 unsigned int orig_index = argpar_item_non_opt_orig_index(argpar_item);
4832
4833 /*
4834 * First unknown argument: is it a known command
4835 * name?
4836 */
4837 command_argc = top_level_argc - orig_index - 1;
4838 command_argv = &top_level_argv[orig_index + 1];
4839
4840 if (strcmp(arg, "convert") == 0) {
4841 command_type = COMMAND_TYPE_CONVERT;
4842 command_name = "convert";
4843 } else if (strcmp(arg, "list-plugins") == 0) {
4844 command_type = COMMAND_TYPE_LIST_PLUGINS;
4845 command_name = "list-plugins";
4846 } else if (strcmp(arg, "help") == 0) {
4847 command_type = COMMAND_TYPE_HELP;
4848 command_name = "help";
4849 } else if (strcmp(arg, "query") == 0) {
4850 command_type = COMMAND_TYPE_QUERY;
4851 command_name = "query";
4852 } else if (strcmp(arg, "run") == 0) {
4853 command_type = COMMAND_TYPE_RUN;
4854 command_name = "run";
4855 } else {
4856 /*
4857 * Non-option argument, but not a known
4858 * command name: assume the default
4859 * `convert` command.
4860 */
4861 command_type = COMMAND_TYPE_CONVERT;
4862 command_name = "convert";
4863 command_argc++;
4864 command_argv--;
4865 }
4866
4867 /* Stop processing arguments. */
4868 break;
4869 }
4870 }
4871
4872 if (command_type == COMMAND_TYPE_NONE) {
4873 unsigned int ingested_orig_args = argpar_iter_ingested_orig_args(argpar_iter);
4874
4875 if (ingested_orig_args == top_level_argc) {
4876 /*
4877 * We only got non-help, non-version general options
4878 * like --verbose and --debug, without any other
4879 * arguments, so we can't do anything useful: print the
4880 * usage and quit.
4881 */
4882 print_gen_usage(stdout);
4883 goto end;
4884 }
4885
4886 /*
4887 * We stopped on an unknown option argument (and therefore
4888 * didn't see a command name). Assume `convert` command.
4889 */
4890 command_type = COMMAND_TYPE_CONVERT;
4891 command_name = "convert";
4892 command_argc = top_level_argc - ingested_orig_args;
4893 command_argv = &top_level_argv[ingested_orig_args];
4894 }
4895
4896 BT_ASSERT(command_argv);
4897 BT_ASSERT(command_argc >= 0);
4898
4899 /*
4900 * For all commands other than `convert`, we now know the log level to
4901 * use, so we can apply it with `set_auto_log_levels`.
4902 *
4903 * The convert command has `--debug` and `--verbose` arguments that are
4904 * equivalent to the top-level arguments of the same name. So after it
4905 * has parsed its arguments, `bt_config_convert_from_args` calls
4906 * `set_auto_log_levels` itself.
4907 */
4908 if (command_type != COMMAND_TYPE_CONVERT) {
4909 set_auto_log_levels(&default_log_level);
4910 }
4911
4912 /*
4913 * At this point, `plugin_paths` contains the initial plugin
4914 * paths, the paths from the `BABELTRACE_PLUGIN_PATH` paths, and
4915 * the paths from the `--plugin-path` option.
4916 *
4917 * Now append the user and system plugin paths.
4918 */
4919 if (append_home_and_system_plugin_paths(plugin_paths,
4920 omit_system_plugin_path, omit_home_plugin_path)) {
4921 goto error;
4922 }
4923
4924 consumed_args = argpar_iter_ingested_orig_args(argpar_iter);
4925
4926 switch (command_type) {
4927 case COMMAND_TYPE_RUN:
4928 config = bt_config_run_from_args(command_argc, command_argv,
4929 retcode, plugin_paths,
4930 default_log_level, consumed_args);
4931 break;
4932 case COMMAND_TYPE_CONVERT:
4933 config = bt_config_convert_from_args(command_argc, command_argv,
4934 retcode, plugin_paths, &default_log_level, interrupter,
4935 consumed_args);
4936 break;
4937 case COMMAND_TYPE_LIST_PLUGINS:
4938 config = bt_config_list_plugins_from_args(command_argc,
4939 command_argv, retcode, plugin_paths, consumed_args);
4940 break;
4941 case COMMAND_TYPE_HELP:
4942 config = bt_config_help_from_args(command_argc,
4943 command_argv, retcode, plugin_paths,
4944 default_log_level, consumed_args);
4945 break;
4946 case COMMAND_TYPE_QUERY:
4947 config = bt_config_query_from_args(command_argc,
4948 command_argv, retcode, plugin_paths,
4949 default_log_level, consumed_args);
4950 break;
4951 default:
4952 bt_common_abort();
4953 }
4954
4955 if (config) {
4956 BT_ASSERT(default_log_level >= BT_LOG_TRACE);
4957 config->log_level = default_log_level;
4958 config->command_name = command_name;
4959 }
4960
4961 goto end;
4962
4963 error:
4964 *retcode = 1;
4965
4966 end:
4967 argpar_error_destroy(argpar_error);
4968 argpar_item_destroy(argpar_item);
4969 argpar_iter_destroy(argpar_iter);
4970 bt_value_put_ref(plugin_paths);
4971 return config;
4972 }
This page took 0.186767 seconds and 3 git commands to generate.