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