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