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