cli: fix bt_plugin leak when using `-i ctf`
[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 (GIT_VERSION[0] == '\0') {
201 puts("Babeltrace " VERSION);
202 } else {
203 puts("Babeltrace " VERSION " - " GIT_VERSION);
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 bt_value *log_level_str_value;
3444
3445 log_level_str_value = bt_value_string_create_init(arg);
3446 if (!log_level_str_value) {
3447 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3448 goto error;
3449 }
3450
3451 if (bt_value_array_set_element_by_index(non_opt_loglevels, idx,
3452 log_level_str_value)) {
3453 bt_value_put_ref(log_level_str_value);
3454 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3455 goto error;
3456 }
3457 } else {
3458 BT_CLI_LOGE_APPEND_CAUSE(
3459 "No current component (--component option) or non-option argument to assign a log level to:\n %s",
3460 arg);
3461 goto error;
3462 }
3463
3464 break;
3465 case OPT_RETRY_DURATION:
3466 if (bt_value_array_append_string_element(run_args,
3467 "--retry-duration")) {
3468 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3469 goto error;
3470 }
3471
3472 if (bt_value_array_append_string_element(run_args, arg)) {
3473 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3474 goto error;
3475 }
3476 break;
3477 case OPT_BEGIN:
3478 case OPT_CLOCK_CYCLES:
3479 case OPT_CLOCK_DATE:
3480 case OPT_CLOCK_FORCE_CORRELATE:
3481 case OPT_CLOCK_GMT:
3482 case OPT_CLOCK_OFFSET:
3483 case OPT_CLOCK_OFFSET_NS:
3484 case OPT_CLOCK_SECONDS:
3485 case OPT_COLOR:
3486 case OPT_DEBUG:
3487 case OPT_DEBUG_INFO:
3488 case OPT_DEBUG_INFO_DIR:
3489 case OPT_DEBUG_INFO_FULL_PATH:
3490 case OPT_DEBUG_INFO_TARGET_PREFIX:
3491 case OPT_END:
3492 case OPT_FIELDS:
3493 case OPT_INPUT_FORMAT:
3494 case OPT_NAMES:
3495 case OPT_NO_DELTA:
3496 case OPT_OUTPUT_FORMAT:
3497 case OPT_OUTPUT:
3498 case OPT_RUN_ARGS:
3499 case OPT_RUN_ARGS_0:
3500 case OPT_STREAM_INTERSECTION:
3501 case OPT_TIMERANGE:
3502 case OPT_VERBOSE:
3503 /* Ignore in this pass */
3504 break;
3505 default:
3506 BT_CLI_LOGE_APPEND_CAUSE("Unknown command-line option specified (option code %d).",
3507 argpar_item_opt->descr->id);
3508 goto error;
3509 }
3510 } else if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) {
3511 struct argpar_item_non_opt *argpar_item_non_opt;
3512 bt_value_array_append_element_status append_status;
3513
3514 current_item_type = CONVERT_CURRENT_ITEM_TYPE_NON_OPT;
3515
3516 argpar_item_non_opt = (struct argpar_item_non_opt *) argpar_item;
3517
3518 append_status = bt_value_array_append_string_element(non_opts,
3519 argpar_item_non_opt->arg);
3520 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3521 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3522 goto error;
3523 }
3524
3525 append_status = bt_value_array_append_empty_array_element(
3526 non_opt_params, NULL);
3527 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3528 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3529 goto error;
3530 }
3531
3532 append_status = bt_value_array_append_element(non_opt_loglevels, bt_value_null);
3533 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3534 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3535 goto error;
3536 }
3537 } else {
3538 bt_common_abort();
3539 }
3540 }
3541
3542 /*
3543 * Second pass: transform the convert-specific options and
3544 * arguments into implicit component instances for the run
3545 * command.
3546 */
3547 for (i = 0; i < argpar_parse_ret.items->n_items; i++) {
3548 struct argpar_item *argpar_item =
3549 argpar_parse_ret.items->items[i];
3550 struct argpar_item_opt *argpar_item_opt;
3551 const char *arg;
3552
3553 if (argpar_item->type != ARGPAR_ITEM_TYPE_OPT) {
3554 continue;
3555 }
3556
3557 argpar_item_opt = (struct argpar_item_opt *) argpar_item;
3558 arg = argpar_item_opt->arg;
3559
3560 switch (argpar_item_opt->descr->id) {
3561 case OPT_BEGIN:
3562 if (trimmer_has_begin) {
3563 printf("At --begin option: --begin or --timerange option already specified\n %s\n",
3564 arg);
3565 goto error;
3566 }
3567
3568 trimmer_has_begin = true;
3569 ret = append_implicit_component_extra_param(
3570 &implicit_trimmer_args, "begin", arg);
3571 implicit_trimmer_args.exists = true;
3572 if (ret) {
3573 goto error;
3574 }
3575 break;
3576 case OPT_END:
3577 if (trimmer_has_end) {
3578 printf("At --end option: --end or --timerange option already specified\n %s\n",
3579 arg);
3580 goto error;
3581 }
3582
3583 trimmer_has_end = true;
3584 ret = append_implicit_component_extra_param(
3585 &implicit_trimmer_args, "end", arg);
3586 implicit_trimmer_args.exists = true;
3587 if (ret) {
3588 goto error;
3589 }
3590 break;
3591 case OPT_TIMERANGE:
3592 {
3593 char *begin;
3594 char *end;
3595
3596 if (trimmer_has_begin || trimmer_has_end) {
3597 printf("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n",
3598 arg);
3599 goto error;
3600 }
3601
3602 ret = split_timerange(arg, &begin, &end);
3603 if (ret) {
3604 BT_CLI_LOGE_APPEND_CAUSE("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s",
3605 arg);
3606 goto error;
3607 }
3608
3609 ret = append_implicit_component_extra_param(
3610 &implicit_trimmer_args, "begin", begin);
3611 ret |= append_implicit_component_extra_param(
3612 &implicit_trimmer_args, "end", end);
3613 implicit_trimmer_args.exists = true;
3614 free(begin);
3615 free(end);
3616 if (ret) {
3617 goto error;
3618 }
3619 break;
3620 }
3621 case OPT_CLOCK_CYCLES:
3622 append_implicit_component_param(
3623 &implicit_text_args, "clock-cycles", "yes");
3624 implicit_text_args.exists = true;
3625 break;
3626 case OPT_CLOCK_DATE:
3627 append_implicit_component_param(
3628 &implicit_text_args, "clock-date", "yes");
3629 implicit_text_args.exists = true;
3630 break;
3631 case OPT_CLOCK_FORCE_CORRELATE:
3632 ctf_fs_source_force_clock_class_unix_epoch_origin = true;
3633 break;
3634 case OPT_CLOCK_GMT:
3635 append_implicit_component_param(
3636 &implicit_text_args, "clock-gmt", "yes");
3637 append_implicit_component_param(
3638 &implicit_trimmer_args, "gmt", "yes");
3639 implicit_text_args.exists = true;
3640 break;
3641 case OPT_CLOCK_OFFSET:
3642 if (ctf_fs_source_clock_class_offset_arg) {
3643 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset option\n");
3644 goto error;
3645 }
3646
3647 ctf_fs_source_clock_class_offset_arg = g_strdup(arg);
3648 if (!ctf_fs_source_clock_class_offset_arg) {
3649 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3650 goto error;
3651 }
3652 break;
3653 case OPT_CLOCK_OFFSET_NS:
3654 if (ctf_fs_source_clock_class_offset_ns_arg) {
3655 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset-ns option\n");
3656 goto error;
3657 }
3658
3659 ctf_fs_source_clock_class_offset_ns_arg = g_strdup(arg);
3660 if (!ctf_fs_source_clock_class_offset_ns_arg) {
3661 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3662 goto error;
3663 }
3664 break;
3665 case OPT_CLOCK_SECONDS:
3666 append_implicit_component_param(
3667 &implicit_text_args, "clock-seconds", "yes");
3668 implicit_text_args.exists = true;
3669 break;
3670 case OPT_COLOR:
3671 implicit_text_args.exists = true;
3672 ret = append_implicit_component_extra_param(
3673 &implicit_text_args, "color", arg);
3674 if (ret) {
3675 goto error;
3676 }
3677 break;
3678 case OPT_DEBUG_INFO:
3679 implicit_debug_info_args.exists = true;
3680 break;
3681 case OPT_DEBUG_INFO_DIR:
3682 implicit_debug_info_args.exists = true;
3683 ret = append_implicit_component_extra_param(
3684 &implicit_debug_info_args, "debug-info-dir", arg);
3685 if (ret) {
3686 goto error;
3687 }
3688 break;
3689 case OPT_DEBUG_INFO_FULL_PATH:
3690 implicit_debug_info_args.exists = true;
3691 append_implicit_component_param(
3692 &implicit_debug_info_args, "full-path", "yes");
3693 break;
3694 case OPT_DEBUG_INFO_TARGET_PREFIX:
3695 implicit_debug_info_args.exists = true;
3696 ret = append_implicit_component_extra_param(
3697 &implicit_debug_info_args,
3698 "target-prefix", arg);
3699 if (ret) {
3700 goto error;
3701 }
3702 break;
3703 case OPT_FIELDS:
3704 {
3705 bt_value *fields = fields_from_arg(arg);
3706
3707 if (!fields) {
3708 goto error;
3709 }
3710
3711 implicit_text_args.exists = true;
3712 ret = insert_flat_params_from_array(
3713 implicit_text_args.params_arg,
3714 fields, "field");
3715 bt_value_put_ref(fields);
3716 if (ret) {
3717 goto error;
3718 }
3719 break;
3720 }
3721 case OPT_NAMES:
3722 {
3723 bt_value *names = names_from_arg(arg);
3724
3725 if (!names) {
3726 goto error;
3727 }
3728
3729 implicit_text_args.exists = true;
3730 ret = insert_flat_params_from_array(
3731 implicit_text_args.params_arg,
3732 names, "name");
3733 bt_value_put_ref(names);
3734 if (ret) {
3735 goto error;
3736 }
3737 break;
3738 }
3739 case OPT_NO_DELTA:
3740 append_implicit_component_param(
3741 &implicit_text_args, "no-delta", "yes");
3742 implicit_text_args.exists = true;
3743 break;
3744 case OPT_INPUT_FORMAT:
3745 if (got_input_format_opt) {
3746 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --input-format option.");
3747 goto error;
3748 }
3749
3750 got_input_format_opt = true;
3751
3752 if (strcmp(arg, "ctf") == 0) {
3753 auto_source_discovery_restrict_plugin_name = "ctf";
3754 auto_source_discovery_restrict_component_class_name = "fs";
3755 } else if (strcmp(arg, "lttng-live") == 0) {
3756 auto_source_discovery_restrict_plugin_name = "ctf";
3757 auto_source_discovery_restrict_component_class_name = "lttng-live";
3758 implicit_lttng_live_args.exists = true;
3759 } else {
3760 BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy input format:\n %s",
3761 arg);
3762 goto error;
3763 }
3764 break;
3765 case OPT_OUTPUT_FORMAT:
3766 if (got_output_format_opt) {
3767 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output-format option.");
3768 goto error;
3769 }
3770
3771 got_output_format_opt = true;
3772
3773 if (strcmp(arg, "text") == 0) {
3774 implicit_text_args.exists = true;
3775 } else if (strcmp(arg, "ctf") == 0) {
3776 implicit_ctf_output_args.exists = true;
3777 } else if (strcmp(arg, "dummy") == 0) {
3778 implicit_dummy_args.exists = true;
3779 } else if (strcmp(arg, "ctf-metadata") == 0) {
3780 print_ctf_metadata = true;
3781 } else {
3782 BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy output format:\n %s",
3783 arg);
3784 goto error;
3785 }
3786 break;
3787 case OPT_OUTPUT:
3788 if (output) {
3789 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output option");
3790 goto error;
3791 }
3792
3793 output = strdup(arg);
3794 if (!output) {
3795 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3796 goto error;
3797 }
3798 break;
3799 case OPT_RUN_ARGS:
3800 if (print_run_args_0) {
3801 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0.");
3802 goto error;
3803 }
3804
3805 print_run_args = true;
3806 break;
3807 case OPT_RUN_ARGS_0:
3808 if (print_run_args) {
3809 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0.");
3810 goto error;
3811 }
3812
3813 print_run_args_0 = true;
3814 break;
3815 case OPT_STREAM_INTERSECTION:
3816 /*
3817 * Applies to all traces implementing the
3818 * babeltrace.trace-infos query.
3819 */
3820 stream_intersection_mode = true;
3821 break;
3822 case OPT_VERBOSE:
3823 *default_log_level =
3824 logging_level_min(*default_log_level, BT_LOG_INFO);
3825 break;
3826 case OPT_DEBUG:
3827 *default_log_level =
3828 logging_level_min(*default_log_level, BT_LOG_TRACE);
3829 break;
3830 default:
3831 break;
3832 }
3833 }
3834
3835 set_auto_log_levels(default_log_level);
3836
3837 /*
3838 * Legacy behaviour: --verbose used to make the `text` output
3839 * format print more information. --verbose is now equivalent to
3840 * the INFO log level, which is why we compare to `BT_LOG_INFO`
3841 * here.
3842 */
3843 if (*default_log_level == BT_LOG_INFO) {
3844 append_implicit_component_param(&implicit_text_args,
3845 "verbose", "yes");
3846 }
3847
3848 /* Print CTF metadata or print LTTng live sessions */
3849 if (print_ctf_metadata) {
3850 const bt_value *bt_val_non_opt;
3851
3852 if (bt_value_array_is_empty(non_opts)) {
3853 BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf-metadata specified without a path.");
3854 goto error;
3855 }
3856
3857 if (bt_value_array_get_length(non_opts) > 1) {
3858 BT_CLI_LOGE_APPEND_CAUSE("Too many paths specified for --output-format=ctf-metadata.");
3859 goto error;
3860 }
3861
3862 cfg = bt_config_print_ctf_metadata_create(plugin_paths);
3863 if (!cfg) {
3864 goto error;
3865 }
3866
3867 bt_val_non_opt = bt_value_array_borrow_element_by_index_const(non_opts, 0);
3868 g_string_assign(cfg->cmd_data.print_ctf_metadata.path,
3869 bt_value_string_get(bt_val_non_opt));
3870
3871 if (output) {
3872 g_string_assign(
3873 cfg->cmd_data.print_ctf_metadata.output_path,
3874 output);
3875 }
3876
3877 goto end;
3878 }
3879
3880 /*
3881 * If -o ctf was specified, make sure an output path (--output)
3882 * was also specified. --output does not imply -o ctf because
3883 * it's also used for the default, implicit -o text if -o ctf
3884 * is not specified.
3885 */
3886 if (implicit_ctf_output_args.exists) {
3887 if (!output) {
3888 BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf specified without --output (trace output path).");
3889 goto error;
3890 }
3891
3892 /*
3893 * At this point we know that -o ctf AND --output were
3894 * specified. Make sure that no options were specified
3895 * which would imply -o text because --output would be
3896 * ambiguous in this case. For example, this is wrong:
3897 *
3898 * babeltrace2 --names=all -o ctf --output=/tmp/path my-trace
3899 *
3900 * because --names=all implies -o text, and --output
3901 * could apply to both the sink.text.pretty and
3902 * sink.ctf.fs implicit components.
3903 */
3904 if (implicit_text_args.exists) {
3905 BT_CLI_LOGE_APPEND_CAUSE("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text.");
3906 goto error;
3907 }
3908 }
3909
3910 /*
3911 * If -o dummy and -o ctf were not specified, and if there are
3912 * no explicit sink components, then use an implicit
3913 * `sink.text.pretty` component.
3914 */
3915 if (!implicit_dummy_args.exists && !implicit_ctf_output_args.exists &&
3916 !sink_names) {
3917 implicit_text_args.exists = true;
3918 }
3919
3920 /*
3921 * Set implicit `sink.text.pretty` or `sink.ctf.fs` component's
3922 * `path` parameter if --output was specified.
3923 */
3924 if (output) {
3925 if (implicit_text_args.exists) {
3926 append_implicit_component_extra_param(&implicit_text_args,
3927 "path", output);
3928 } else if (implicit_ctf_output_args.exists) {
3929 append_implicit_component_extra_param(&implicit_ctf_output_args,
3930 "path", output);
3931 }
3932 }
3933
3934 /* Decide where the non-option argument(s) go */
3935 if (bt_value_array_get_length(non_opts) > 0) {
3936 if (implicit_lttng_live_args.exists) {
3937 const bt_value *bt_val_non_opt;
3938
3939 if (bt_value_array_get_length(non_opts) > 1) {
3940 BT_CLI_LOGE_APPEND_CAUSE("Too many URLs specified for --input-format=lttng-live.");
3941 goto error;
3942 }
3943
3944 bt_val_non_opt = bt_value_array_borrow_element_by_index_const(non_opts, 0);
3945 lttng_live_url_parts =
3946 bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_non_opt),
3947 error_buf, sizeof(error_buf));
3948 if (!lttng_live_url_parts.proto) {
3949 BT_CLI_LOGE_APPEND_CAUSE("Invalid LTTng live URL format: %s.",
3950 error_buf);
3951 goto error;
3952 }
3953
3954 if (!lttng_live_url_parts.session_name) {
3955 /* Print LTTng live sessions */
3956 cfg = bt_config_print_lttng_live_sessions_create(
3957 plugin_paths);
3958 if (!cfg) {
3959 goto error;
3960 }
3961
3962 g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url,
3963 bt_value_string_get(bt_val_non_opt));
3964
3965 if (output) {
3966 g_string_assign(
3967 cfg->cmd_data.print_lttng_live_sessions.output_path,
3968 output);
3969 }
3970
3971 goto end;
3972 }
3973
3974 live_inputs_array_val = bt_value_array_create();
3975 if (!live_inputs_array_val) {
3976 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3977 goto error;
3978 }
3979
3980 if (bt_value_array_append_string_element(
3981 live_inputs_array_val,
3982 bt_value_string_get(bt_val_non_opt))) {
3983 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3984 goto error;
3985 }
3986
3987 ret = append_parameter_to_args(
3988 implicit_lttng_live_args.extra_params,
3989 "inputs", live_inputs_array_val);
3990 if (ret) {
3991 goto error;
3992 }
3993
3994 ret = append_implicit_component_extra_param(
3995 &implicit_lttng_live_args,
3996 "session-not-found-action", "end");
3997 if (ret) {
3998 goto error;
3999 }
4000 } else {
4001 int status;
4002 size_t plugin_count;
4003 const bt_plugin **plugins;
4004 const bt_plugin *plugin;
4005
4006 status = require_loaded_plugins(plugin_paths);
4007 if (status != 0) {
4008 goto error;
4009 }
4010
4011 if (auto_source_discovery_restrict_plugin_name) {
4012 plugin_count = 1;
4013 plugin = borrow_loaded_plugin_by_name(auto_source_discovery_restrict_plugin_name);
4014 plugins = &plugin;
4015 } else {
4016 plugin_count = get_loaded_plugins_count();
4017 plugins = borrow_loaded_plugins();
4018 }
4019
4020 status = auto_discover_source_components(non_opts, plugins, plugin_count,
4021 auto_source_discovery_restrict_component_class_name,
4022 *default_log_level, &auto_disc, interrupter);
4023
4024 if (status != 0) {
4025 if (status == AUTO_SOURCE_DISCOVERY_STATUS_INTERRUPTED) {
4026 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(
4027 "Babeltrace CLI", "Automatic source discovery interrupted by the user");
4028 }
4029 goto error;
4030 }
4031
4032 status = create_implicit_component_args_from_auto_discovered_sources(
4033 &auto_disc, non_opts, non_opt_params, non_opt_loglevels,
4034 discovered_source_args);
4035 if (status != 0) {
4036 goto error;
4037 }
4038 }
4039 }
4040
4041
4042 /*
4043 * If --clock-force-correlated was given, apply it to any src.ctf.fs
4044 * component.
4045 */
4046 if (ctf_fs_source_force_clock_class_unix_epoch_origin) {
4047 int n;
4048
4049 n = append_multiple_implicit_components_param(
4050 discovered_source_args, "source.ctf.fs", "force-clock-class-origin-unix-epoch",
4051 "yes");
4052 if (n == 0) {
4053 BT_CLI_LOGE_APPEND_CAUSE("--clock-force-correlate specified, but no source.ctf.fs component instantiated.");
4054 goto error;
4055 }
4056 }
4057
4058 /* If --clock-offset was given, apply it to any src.ctf.fs component. */
4059 if (ctf_fs_source_clock_class_offset_arg) {
4060 int n;
4061
4062 n = append_multiple_implicit_components_param(
4063 discovered_source_args, "source.ctf.fs", "clock-class-offset-s",
4064 ctf_fs_source_clock_class_offset_arg);
4065
4066 if (n == 0) {
4067 BT_CLI_LOGE_APPEND_CAUSE("--clock-offset specified, but no source.ctf.fs component instantiated.");
4068 goto error;
4069 }
4070 }
4071
4072 /* If --clock-offset-ns was given, apply it to any src.ctf.fs component. */
4073 if (ctf_fs_source_clock_class_offset_ns_arg) {
4074 int n;
4075
4076 n = append_multiple_implicit_components_param(
4077 discovered_source_args, "source.ctf.fs", "clock-class-offset-ns",
4078 ctf_fs_source_clock_class_offset_ns_arg);
4079
4080 if (n == 0) {
4081 BT_CLI_LOGE_APPEND_CAUSE("--clock-offset-ns specified, but no source.ctf.fs component instantiated.");
4082 goto error;
4083 }
4084 }
4085
4086 /*
4087 * If the implicit `source.ctf.lttng-live` component exists,
4088 * make sure there's at least one non-option argument (which is
4089 * the URL).
4090 */
4091 if (implicit_lttng_live_args.exists && bt_value_array_is_empty(non_opts)) {
4092 BT_CLI_LOGE_APPEND_CAUSE("Missing URL for implicit `%s` component.",
4093 implicit_lttng_live_args.comp_arg->str);
4094 goto error;
4095 }
4096
4097 /* Assign names to implicit components */
4098 for (i = 0; i < discovered_source_args->len; i++) {
4099 struct implicit_component_args *args;
4100 int j;
4101
4102 args = discovered_source_args->pdata[i];
4103
4104 g_string_printf(auto_disc_comp_name, "auto-disc-%s", args->comp_arg->str);
4105
4106 /* Give it a name like `auto-disc-src-ctf-fs`. */
4107 for (j = 0; j < auto_disc_comp_name->len; j++) {
4108 if (auto_disc_comp_name->str[j] == '.') {
4109 auto_disc_comp_name->str[j] = '-';
4110 }
4111 }
4112
4113 ret = assign_name_to_implicit_component(args,
4114 auto_disc_comp_name->str, all_names, &source_names, true);
4115 if (ret) {
4116 goto error;
4117 }
4118 }
4119
4120 ret = assign_name_to_implicit_component(&implicit_lttng_live_args,
4121 "lttng-live", all_names, &source_names, true);
4122 if (ret) {
4123 goto error;
4124 }
4125
4126 ret = assign_name_to_implicit_component(&implicit_text_args,
4127 "pretty", all_names, &sink_names, true);
4128 if (ret) {
4129 goto error;
4130 }
4131
4132 ret = assign_name_to_implicit_component(&implicit_ctf_output_args,
4133 "sink-ctf-fs", all_names, &sink_names, true);
4134 if (ret) {
4135 goto error;
4136 }
4137
4138 ret = assign_name_to_implicit_component(&implicit_dummy_args,
4139 "dummy", all_names, &sink_names, true);
4140 if (ret) {
4141 goto error;
4142 }
4143
4144 ret = assign_name_to_implicit_component(&implicit_muxer_args,
4145 "muxer", all_names, NULL, false);
4146 if (ret) {
4147 goto error;
4148 }
4149
4150 ret = assign_name_to_implicit_component(&implicit_trimmer_args,
4151 "trimmer", all_names, NULL, false);
4152 if (ret) {
4153 goto error;
4154 }
4155
4156 ret = assign_name_to_implicit_component(&implicit_debug_info_args,
4157 "debug-info", all_names, NULL, false);
4158 if (ret) {
4159 goto error;
4160 }
4161
4162 /* Make sure there's at least one source and one sink */
4163 if (!source_names) {
4164 BT_CLI_LOGE_APPEND_CAUSE("No source component.");
4165 goto error;
4166 }
4167
4168 if (!sink_names) {
4169 BT_CLI_LOGE_APPEND_CAUSE("No sink component.");
4170 goto error;
4171 }
4172
4173 /* Make sure there's a single sink component */
4174 if (g_list_length(sink_names) != 1) {
4175 BT_CLI_LOGE_APPEND_CAUSE(
4176 "More than one sink component specified.");
4177 goto error;
4178 }
4179
4180 /*
4181 * Prepend the muxer, the trimmer, and the debug info to the
4182 * filter chain so that we have:
4183 *
4184 * sources -> muxer -> [trimmer] -> [debug info] ->
4185 * [user filters] -> sinks
4186 */
4187 if (implicit_debug_info_args.exists) {
4188 if (g_list_prepend_gstring(&filter_names,
4189 implicit_debug_info_args.name_arg->str)) {
4190 goto error;
4191 }
4192 }
4193
4194 if (implicit_trimmer_args.exists) {
4195 if (g_list_prepend_gstring(&filter_names,
4196 implicit_trimmer_args.name_arg->str)) {
4197 goto error;
4198 }
4199 }
4200
4201 if (g_list_prepend_gstring(&filter_names,
4202 implicit_muxer_args.name_arg->str)) {
4203 goto error;
4204 }
4205
4206 /*
4207 * Append the equivalent run arguments for the implicit
4208 * components.
4209 */
4210 for (i = 0; i < discovered_source_args->len; i++) {
4211 struct implicit_component_args *args =
4212 discovered_source_args->pdata[i];
4213
4214 ret = append_run_args_for_implicit_component(args, run_args);
4215 if (ret) {
4216 goto error;
4217 }
4218 }
4219
4220 ret = append_run_args_for_implicit_component(&implicit_lttng_live_args,
4221 run_args);
4222 if (ret) {
4223 goto error;
4224 }
4225
4226 ret = append_run_args_for_implicit_component(&implicit_text_args,
4227 run_args);
4228 if (ret) {
4229 goto error;
4230 }
4231
4232 ret = append_run_args_for_implicit_component(&implicit_ctf_output_args,
4233 run_args);
4234 if (ret) {
4235 goto error;
4236 }
4237
4238 ret = append_run_args_for_implicit_component(&implicit_dummy_args,
4239 run_args);
4240 if (ret) {
4241 goto error;
4242 }
4243
4244 ret = append_run_args_for_implicit_component(&implicit_muxer_args,
4245 run_args);
4246 if (ret) {
4247 goto error;
4248 }
4249
4250 ret = append_run_args_for_implicit_component(&implicit_trimmer_args,
4251 run_args);
4252 if (ret) {
4253 goto error;
4254 }
4255
4256 ret = append_run_args_for_implicit_component(&implicit_debug_info_args,
4257 run_args);
4258 if (ret) {
4259 goto error;
4260 }
4261
4262 /* Auto-connect components */
4263 ret = convert_auto_connect(run_args, source_names, filter_names,
4264 sink_names);
4265 if (ret) {
4266 BT_CLI_LOGE_APPEND_CAUSE("Cannot auto-connect components.");
4267 goto error;
4268 }
4269
4270 /*
4271 * We have all the run command arguments now. Depending on
4272 * --run-args, we pass this to the run command or print them
4273 * here.
4274 */
4275 if (print_run_args || print_run_args_0) {
4276 uint64_t args_idx, args_len;
4277 if (stream_intersection_mode) {
4278 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --stream-intersection with --run-args or --run-args-0.");
4279 goto error;
4280 }
4281
4282 args_len = bt_value_array_get_length(run_args);
4283 for (args_idx = 0; args_idx < args_len; args_idx++) {
4284 const bt_value *arg_value =
4285 bt_value_array_borrow_element_by_index(run_args,
4286 args_idx);
4287 const char *arg;
4288 GString *quoted = NULL;
4289 const char *arg_to_print;
4290
4291 arg = bt_value_string_get(arg_value);
4292
4293 if (print_run_args) {
4294 quoted = bt_common_shell_quote(arg, true);
4295 if (!quoted) {
4296 goto error;
4297 }
4298
4299 arg_to_print = quoted->str;
4300 } else {
4301 arg_to_print = arg;
4302 }
4303
4304 printf("%s", arg_to_print);
4305
4306 if (quoted) {
4307 g_string_free(quoted, TRUE);
4308 }
4309
4310 if (args_idx < args_len - 1) {
4311 if (print_run_args) {
4312 putchar(' ');
4313 } else {
4314 putchar('\0');
4315 }
4316 }
4317 }
4318
4319 *retcode = -1;
4320 BT_OBJECT_PUT_REF_AND_RESET(cfg);
4321 goto end;
4322 }
4323
4324 cfg = bt_config_run_from_args_array(run_args, retcode,
4325 plugin_paths, *default_log_level);
4326 if (!cfg) {
4327 goto error;
4328 }
4329
4330 cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode;
4331 goto end;
4332
4333 error:
4334 *retcode = 1;
4335 BT_OBJECT_PUT_REF_AND_RESET(cfg);
4336
4337 end:
4338 argpar_parse_ret_fini(&argpar_parse_ret);
4339
4340 free(output);
4341
4342 if (component_arg_for_run) {
4343 g_string_free(component_arg_for_run, TRUE);
4344 }
4345
4346 if (name_gstr) {
4347 g_string_free(name_gstr, TRUE);
4348 }
4349
4350 bt_value_put_ref(live_inputs_array_val);
4351 bt_value_put_ref(run_args);
4352 bt_value_put_ref(all_names);
4353 destroy_glist_of_gstring(source_names);
4354 destroy_glist_of_gstring(filter_names);
4355 destroy_glist_of_gstring(sink_names);
4356 bt_value_put_ref(non_opt_params);
4357 bt_value_put_ref(non_opt_loglevels);
4358 bt_value_put_ref(non_opts);
4359 finalize_implicit_component_args(&implicit_ctf_output_args);
4360 finalize_implicit_component_args(&implicit_lttng_live_args);
4361 finalize_implicit_component_args(&implicit_dummy_args);
4362 finalize_implicit_component_args(&implicit_text_args);
4363 finalize_implicit_component_args(&implicit_debug_info_args);
4364 finalize_implicit_component_args(&implicit_muxer_args);
4365 finalize_implicit_component_args(&implicit_trimmer_args);
4366 bt_common_destroy_lttng_live_url_parts(&lttng_live_url_parts);
4367 auto_source_discovery_fini(&auto_disc);
4368
4369 if (discovered_source_args) {
4370 g_ptr_array_free(discovered_source_args, TRUE);
4371 }
4372
4373 g_free(ctf_fs_source_clock_class_offset_arg);
4374 g_free(ctf_fs_source_clock_class_offset_ns_arg);
4375
4376 if (auto_disc_comp_name) {
4377 g_string_free(auto_disc_comp_name, TRUE);
4378 }
4379
4380 return cfg;
4381 }
4382
4383 /*
4384 * Prints the Babeltrace 2.x general usage.
4385 */
4386 static
4387 void print_gen_usage(FILE *fp)
4388 {
4389 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS]\n");
4390 fprintf(fp, "\n");
4391 fprintf(fp, "General options:\n");
4392 fprintf(fp, "\n");
4393 fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=T)\n");
4394 fprintf(fp, " -h, --help Show this help and quit\n");
4395 fprintf(fp, " -l, --log-level=LVL Set the default log level to LVL (`N`, `T`, `D`,\n");
4396 fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n");
4397 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
4398 fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n");
4399 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
4400 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
4401 fprintf(fp, " dynamic plugins can be loaded\n");
4402 fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n");
4403 fprintf(fp, " -V, --version Show version and quit\n");
4404 fprintf(fp, "\n");
4405 fprintf(fp, "Available commands:\n");
4406 fprintf(fp, "\n");
4407 fprintf(fp, " convert Convert and trim traces (default)\n");
4408 fprintf(fp, " help Get help for a plugin or a component class\n");
4409 fprintf(fp, " list-plugins List available plugins and their content\n");
4410 fprintf(fp, " query Query objects from a component class\n");
4411 fprintf(fp, " run Build a processing graph and run it\n");
4412 fprintf(fp, "\n");
4413 fprintf(fp, "Use `babeltrace2 COMMAND --help` to show the help of COMMAND.\n");
4414 }
4415
4416 struct bt_config *bt_config_cli_args_create(int argc, const char *argv[],
4417 int *retcode, bool omit_system_plugin_path,
4418 bool omit_home_plugin_path,
4419 const bt_value *initial_plugin_paths,
4420 const bt_interrupter *interrupter)
4421 {
4422 struct bt_config *config = NULL;
4423 int i;
4424 int top_level_argc;
4425 const char **top_level_argv;
4426 int command_argc = -1;
4427 const char **command_argv = NULL;
4428 const char *command_name = NULL;
4429 int default_log_level = -1;
4430 struct argpar_parse_ret argpar_parse_ret = { 0 };
4431 bt_value *plugin_paths = NULL;
4432
4433 /* Top-level option descriptions. */
4434 static const struct argpar_opt_descr descrs[] = {
4435 { OPT_DEBUG, 'd', "debug", false },
4436 { OPT_HELP, 'h', "help", false },
4437 { OPT_LOG_LEVEL, 'l', "log-level", true },
4438 { OPT_VERBOSE, 'v', "verbose", false },
4439 { OPT_VERSION, 'V', "version", false},
4440 { OPT_OMIT_HOME_PLUGIN_PATH, '\0', "omit-home-plugin-path", false },
4441 { OPT_OMIT_SYSTEM_PLUGIN_PATH, '\0', "omit-system-plugin-path", false },
4442 { OPT_PLUGIN_PATH, '\0', "plugin-path", true },
4443 ARGPAR_OPT_DESCR_SENTINEL
4444 };
4445
4446 enum command_type {
4447 COMMAND_TYPE_NONE = -1,
4448 COMMAND_TYPE_RUN = 0,
4449 COMMAND_TYPE_CONVERT,
4450 COMMAND_TYPE_LIST_PLUGINS,
4451 COMMAND_TYPE_HELP,
4452 COMMAND_TYPE_QUERY,
4453 } command_type = COMMAND_TYPE_NONE;
4454
4455 *retcode = -1;
4456
4457 if (!initial_plugin_paths) {
4458 plugin_paths = bt_value_array_create();
4459 if (!plugin_paths) {
4460 goto error;
4461 }
4462 } else {
4463 bt_value_copy_status copy_status = bt_value_copy(
4464 initial_plugin_paths, &plugin_paths);
4465 if (copy_status) {
4466 goto error;
4467 }
4468 }
4469
4470 BT_ASSERT(plugin_paths);
4471
4472 /*
4473 * The `BABELTRACE_PLUGIN_PATH` paths take precedence over the
4474 * `--plugin-path` option's paths, so append it now before
4475 * parsing the general options.
4476 */
4477 if (append_env_var_plugin_paths(plugin_paths)) {
4478 goto error;
4479 }
4480
4481 if (argc <= 1) {
4482 print_version();
4483 puts("");
4484 print_gen_usage(stdout);
4485 goto end;
4486 }
4487
4488 /* Skip first argument, the name of the program. */
4489 top_level_argc = argc - 1;
4490 top_level_argv = argv + 1;
4491 argpar_parse_ret = argpar_parse(top_level_argc, top_level_argv,
4492 descrs, false);
4493
4494 if (argpar_parse_ret.error) {
4495 BT_CLI_LOGE_APPEND_CAUSE(
4496 "While parsing command-line arguments: %s",
4497 argpar_parse_ret.error);
4498 goto error;
4499 }
4500
4501 for (i = 0; i < argpar_parse_ret.items->n_items; i++) {
4502 struct argpar_item *item;
4503
4504 item = argpar_parse_ret.items->items[i];
4505
4506 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
4507 struct argpar_item_opt *item_opt =
4508 (struct argpar_item_opt *) item;
4509
4510 switch (item_opt->descr->id) {
4511 case OPT_DEBUG:
4512 default_log_level =
4513 logging_level_min(default_log_level, BT_LOG_TRACE);
4514 break;
4515 case OPT_VERBOSE:
4516 default_log_level =
4517 logging_level_min(default_log_level, BT_LOG_INFO);
4518 break;
4519 case OPT_LOG_LEVEL:
4520 {
4521 int level = bt_log_get_level_from_string(item_opt->arg);
4522
4523 if (level < 0) {
4524 BT_CLI_LOGE_APPEND_CAUSE(
4525 "Invalid argument for --log-level option:\n %s",
4526 item_opt->arg);
4527 goto error;
4528 }
4529
4530 default_log_level =
4531 logging_level_min(default_log_level, level);
4532 break;
4533 }
4534 case OPT_PLUGIN_PATH:
4535 if (bt_config_append_plugin_paths_check_setuid_setgid(
4536 plugin_paths, item_opt->arg)) {
4537 goto error;
4538 }
4539 break;
4540 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
4541 omit_system_plugin_path = true;
4542 break;
4543 case OPT_OMIT_HOME_PLUGIN_PATH:
4544 omit_home_plugin_path = true;
4545 break;
4546 case OPT_VERSION:
4547 print_version();
4548 goto end;
4549 case OPT_HELP:
4550 print_gen_usage(stdout);
4551 goto end;
4552 }
4553 } else if (item->type == ARGPAR_ITEM_TYPE_NON_OPT) {
4554 struct argpar_item_non_opt *item_non_opt =
4555 (struct argpar_item_non_opt *) item;
4556 /*
4557 * First unknown argument: is it a known command
4558 * name?
4559 */
4560 command_argc =
4561 top_level_argc - item_non_opt->orig_index - 1;
4562 command_argv =
4563 &top_level_argv[item_non_opt->orig_index + 1];
4564
4565 if (strcmp(item_non_opt->arg, "convert") == 0) {
4566 command_type = COMMAND_TYPE_CONVERT;
4567 command_name = "convert";
4568 } else if (strcmp(item_non_opt->arg, "list-plugins") == 0) {
4569 command_type = COMMAND_TYPE_LIST_PLUGINS;
4570 command_name = "list-plugins";
4571 } else if (strcmp(item_non_opt->arg, "help") == 0) {
4572 command_type = COMMAND_TYPE_HELP;
4573 command_name = "help";
4574 } else if (strcmp(item_non_opt->arg, "query") == 0) {
4575 command_type = COMMAND_TYPE_QUERY;
4576 command_name = "query";
4577 } else if (strcmp(item_non_opt->arg, "run") == 0) {
4578 command_type = COMMAND_TYPE_RUN;
4579 command_name = "run";
4580 } else {
4581 /*
4582 * Non-option argument, but not a known
4583 * command name: assume the default
4584 * `convert` command.
4585 */
4586 command_type = COMMAND_TYPE_CONVERT;
4587 command_name = "convert";
4588 command_argc++;
4589 command_argv--;
4590 }
4591 break;
4592 }
4593 }
4594
4595 if (command_type == COMMAND_TYPE_NONE) {
4596 if (argpar_parse_ret.ingested_orig_args == top_level_argc) {
4597 /*
4598 * We only got non-help, non-version general options
4599 * like --verbose and --debug, without any other
4600 * arguments, so we can't do anything useful: print the
4601 * usage and quit.
4602 */
4603 print_gen_usage(stdout);
4604 goto end;
4605 }
4606
4607 /*
4608 * We stopped on an unknown option argument (and therefore
4609 * didn't see a command name). Assume `convert` command.
4610 */
4611 command_type = COMMAND_TYPE_CONVERT;
4612 command_name = "convert";
4613 command_argc =
4614 top_level_argc - argpar_parse_ret.ingested_orig_args;
4615 command_argv =
4616 &top_level_argv[argpar_parse_ret.ingested_orig_args];
4617 }
4618
4619 BT_ASSERT(command_argv);
4620 BT_ASSERT(command_argc >= 0);
4621
4622 /*
4623 * For all commands other than `convert`, we now know the log level to
4624 * use, so we can apply it with `set_auto_log_levels`.
4625 *
4626 * The convert command has `--debug` and `--verbose` arguments that are
4627 * equivalent to the top-level arguments of the same name. So after it
4628 * has parsed its arguments, `bt_config_convert_from_args` calls
4629 * `set_auto_log_levels` itself.
4630 */
4631 if (command_type != COMMAND_TYPE_CONVERT) {
4632 set_auto_log_levels(&default_log_level);
4633 }
4634
4635 /*
4636 * At this point, `plugin_paths` contains the initial plugin
4637 * paths, the paths from the `BABELTRACE_PLUGIN_PATH` paths, and
4638 * the paths from the `--plugin-path` option.
4639 *
4640 * Now append the user and system plugin paths.
4641 */
4642 if (append_home_and_system_plugin_paths(plugin_paths,
4643 omit_system_plugin_path, omit_home_plugin_path)) {
4644 goto error;
4645 }
4646
4647 switch (command_type) {
4648 case COMMAND_TYPE_RUN:
4649 config = bt_config_run_from_args(command_argc, command_argv,
4650 retcode, plugin_paths,
4651 default_log_level);
4652 break;
4653 case COMMAND_TYPE_CONVERT:
4654 config = bt_config_convert_from_args(command_argc, command_argv,
4655 retcode, plugin_paths, &default_log_level, interrupter);
4656 break;
4657 case COMMAND_TYPE_LIST_PLUGINS:
4658 config = bt_config_list_plugins_from_args(command_argc,
4659 command_argv, retcode, plugin_paths);
4660 break;
4661 case COMMAND_TYPE_HELP:
4662 config = bt_config_help_from_args(command_argc,
4663 command_argv, retcode, plugin_paths,
4664 default_log_level);
4665 break;
4666 case COMMAND_TYPE_QUERY:
4667 config = bt_config_query_from_args(command_argc,
4668 command_argv, retcode, plugin_paths,
4669 default_log_level);
4670 break;
4671 default:
4672 bt_common_abort();
4673 }
4674
4675 if (config) {
4676 BT_ASSERT(default_log_level >= BT_LOG_TRACE);
4677 config->log_level = default_log_level;
4678 config->command_name = command_name;
4679 }
4680
4681 goto end;
4682
4683 error:
4684 *retcode = 1;
4685
4686 end:
4687 argpar_parse_ret_fini(&argpar_parse_ret);
4688 bt_value_put_ref(plugin_paths);
4689 return config;
4690 }
This page took 0.204435 seconds and 4 git commands to generate.