cli: remove `run` and `convert` commands' --name option
[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
3182 static
3183 void create_implicit_component_args_from_auto_discovered_sources(
3184 const struct auto_source_discovery *auto_disc, GPtrArray *component_args)
3185 {
3186 gchar *cc_name = NULL;
3187 struct implicit_component_args *comp = NULL;
3188 int status;
3189 guint i, len;
3190
3191 len = auto_disc->results->len;
3192
3193 for (i = 0; i < len; i++) {
3194 struct auto_source_discovery_result *res =
3195 g_ptr_array_index(auto_disc->results, i);
3196
3197 g_free(cc_name);
3198 cc_name = g_strdup_printf("source.%s.%s", res->plugin_name, res->source_cc_name);
3199 if (!cc_name) {
3200 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3201 goto end;
3202 }
3203
3204 comp = create_implicit_component_args(cc_name);
3205 if (!comp) {
3206 goto end;
3207 }
3208
3209 status = append_parameter_to_args(comp->extra_params, "inputs", res->inputs);
3210 if (status != 0) {
3211 goto end;
3212 }
3213
3214 g_ptr_array_add(component_args, comp);
3215 comp = NULL;
3216 }
3217
3218 end:
3219 g_free(cc_name);
3220
3221 if (comp) {
3222 destroy_implicit_component_args(comp);
3223 }
3224 }
3225
3226 /*
3227 * As we iterate the arguments to the convert command, this tracks what is the
3228 * type of the current item, to which some contextual options (e.g. --params)
3229 * apply to.
3230 */
3231 enum convert_current_item_type {
3232 /* There is no current item. */
3233 CONVERT_CURRENT_ITEM_TYPE_NONE,
3234
3235 /* Current item is a component. */
3236 CONVERT_CURRENT_ITEM_TYPE_COMPONENT,
3237 };
3238
3239 /*
3240 * Creates a Babeltrace config object from the arguments of a convert
3241 * command.
3242 *
3243 * *retcode is set to the appropriate exit code to use.
3244 */
3245 static
3246 struct bt_config *bt_config_convert_from_args(int argc, const char *argv[],
3247 int *retcode, bool force_omit_system_plugin_path,
3248 bool force_omit_home_plugin_path,
3249 const bt_value *initial_plugin_paths, int *default_log_level)
3250 {
3251 enum convert_current_item_type current_item_type =
3252 CONVERT_CURRENT_ITEM_TYPE_NONE;
3253 int ret = 0;
3254 struct bt_config *cfg = NULL;
3255 bool got_input_format_opt = false;
3256 bool got_output_format_opt = false;
3257 bool trimmer_has_begin = false;
3258 bool trimmer_has_end = false;
3259 bool stream_intersection_mode = false;
3260 bool print_run_args = false;
3261 bool print_run_args_0 = false;
3262 bool print_ctf_metadata = false;
3263 bt_value *run_args = NULL;
3264 bt_value *all_names = NULL;
3265 GList *source_names = NULL;
3266 GList *filter_names = NULL;
3267 GList *sink_names = NULL;
3268 bt_value *leftovers = NULL;
3269 struct implicit_component_args implicit_ctf_output_args = { 0 };
3270 struct implicit_component_args implicit_lttng_live_args = { 0 };
3271 struct implicit_component_args implicit_dummy_args = { 0 };
3272 struct implicit_component_args implicit_text_args = { 0 };
3273 struct implicit_component_args implicit_debug_info_args = { 0 };
3274 struct implicit_component_args implicit_muxer_args = { 0 };
3275 struct implicit_component_args implicit_trimmer_args = { 0 };
3276 bt_value *plugin_paths;
3277 char error_buf[256] = { 0 };
3278 size_t i;
3279 struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 };
3280 char *output = NULL;
3281 struct auto_source_discovery auto_disc = { NULL };
3282 GString *auto_disc_comp_name = NULL;
3283 struct bt_argpar_parse_ret argpar_parse_ret = { 0 };
3284 GString *name_gstr = NULL;
3285 GString *component_arg_for_run = NULL;
3286
3287 /*
3288 * Array of `struct implicit_component_args *` created for the sources
3289 * we have auto-discovered.
3290 */
3291 GPtrArray *discovered_source_args = NULL;
3292
3293 /*
3294 * If set, restrict automatic source discovery to this component class
3295 * of this plugin.
3296 */
3297 const char *auto_source_discovery_restrict_plugin_name = NULL;
3298 const char *auto_source_discovery_restrict_component_class_name = NULL;
3299
3300 gchar *ctf_fs_source_clock_class_offset_arg = NULL;
3301 gchar *ctf_fs_source_clock_class_offset_ns_arg = NULL;
3302
3303 (void) bt_value_copy(initial_plugin_paths, &plugin_paths);
3304
3305 *retcode = 0;
3306
3307 if (argc < 1) {
3308 print_convert_usage(stdout);
3309 *retcode = -1;
3310 goto end;
3311 }
3312
3313 if (init_implicit_component_args(&implicit_ctf_output_args,
3314 "sink.ctf.fs", false)) {
3315 goto error;
3316 }
3317
3318 if (init_implicit_component_args(&implicit_lttng_live_args,
3319 "source.ctf.lttng-live", false)) {
3320 goto error;
3321 }
3322
3323 if (init_implicit_component_args(&implicit_text_args,
3324 "sink.text.pretty", false)) {
3325 goto error;
3326 }
3327
3328 if (init_implicit_component_args(&implicit_dummy_args,
3329 "sink.utils.dummy", false)) {
3330 goto error;
3331 }
3332
3333 if (init_implicit_component_args(&implicit_debug_info_args,
3334 "filter.lttng-utils.debug-info", false)) {
3335 goto error;
3336 }
3337
3338 if (init_implicit_component_args(&implicit_muxer_args,
3339 "filter.utils.muxer", true)) {
3340 goto error;
3341 }
3342
3343 if (init_implicit_component_args(&implicit_trimmer_args,
3344 "filter.utils.trimmer", false)) {
3345 goto error;
3346 }
3347
3348 all_names = bt_value_map_create();
3349 if (!all_names) {
3350 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3351 goto error;
3352 }
3353
3354 run_args = bt_value_array_create();
3355 if (!run_args) {
3356 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3357 goto error;
3358 }
3359
3360 component_arg_for_run = g_string_new(NULL);
3361 if (!component_arg_for_run) {
3362 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3363 goto error;
3364 }
3365
3366 ret = append_env_var_plugin_paths(plugin_paths);
3367 if (ret) {
3368 goto error;
3369 }
3370
3371 leftovers = bt_value_array_create();
3372 if (!leftovers) {
3373 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3374 goto error;
3375 }
3376
3377 if (auto_source_discovery_init(&auto_disc) != 0) {
3378 goto error;
3379 }
3380
3381 discovered_source_args =
3382 g_ptr_array_new_with_free_func((GDestroyNotify) destroy_implicit_component_args);
3383 if (!discovered_source_args) {
3384 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3385 goto error;
3386 }
3387
3388 auto_disc_comp_name = g_string_new(NULL);
3389 if (!auto_disc_comp_name) {
3390 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3391 goto error;
3392 }
3393
3394 /*
3395 * First pass: collect all arguments which need to be passed
3396 * as is to the run command. This pass can also add --name
3397 * arguments if needed to automatically name unnamed component
3398 * instances.
3399 *
3400 * Also it appends the plugin paths of --plugin-path to
3401 * `plugin_paths`.
3402 */
3403 argpar_parse_ret = bt_argpar_parse(argc, argv, convert_options, true);
3404 if (argpar_parse_ret.error) {
3405 BT_CLI_LOGE_APPEND_CAUSE(
3406 "While parsing `convert` command's command-line arguments: %s",
3407 argpar_parse_ret.error->str);
3408 goto error;
3409 }
3410
3411 if (help_option_is_specified(&argpar_parse_ret)) {
3412 print_convert_usage(stdout);
3413 *retcode = -1;
3414 BT_OBJECT_PUT_REF_AND_RESET(cfg);
3415 goto end;
3416 }
3417
3418 for (i = 0; i < argpar_parse_ret.items->len; i++) {
3419 struct bt_argpar_item *argpar_item =
3420 g_ptr_array_index(argpar_parse_ret.items, i);
3421 struct bt_argpar_item_opt *argpar_item_opt;
3422 char *name = NULL;
3423 char *plugin_name = NULL;
3424 char *comp_cls_name = NULL;
3425 const char *arg;
3426
3427 if (argpar_item->type != BT_ARGPAR_ITEM_TYPE_OPT) {
3428 continue;
3429 }
3430
3431 argpar_item_opt = (struct bt_argpar_item_opt *) argpar_item;
3432 arg = argpar_item_opt->arg;
3433
3434 switch (argpar_item_opt->descr->id) {
3435 case OPT_COMPONENT:
3436 {
3437 bt_component_class_type type;
3438
3439 current_item_type = CONVERT_CURRENT_ITEM_TYPE_COMPONENT;
3440
3441 /* Parse the argument */
3442 plugin_comp_cls_names(arg, &name, &plugin_name,
3443 &comp_cls_name, &type);
3444 if (!plugin_name || !comp_cls_name) {
3445 BT_CLI_LOGE_APPEND_CAUSE(
3446 "Invalid format for --component option's argument:\n %s",
3447 arg);
3448 goto error;
3449 }
3450
3451 if (name) {
3452 /*
3453 * Name was given by the user, verify it isn't
3454 * taken.
3455 */
3456 if (bt_value_map_has_entry(all_names, name)) {
3457 BT_CLI_LOGE_APPEND_CAUSE(
3458 "Duplicate component instance name:\n %s",
3459 name);
3460 goto error;
3461 }
3462
3463 name_gstr = g_string_new(name);
3464 if (!name_gstr) {
3465 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3466 goto error;
3467 }
3468
3469 g_string_assign(component_arg_for_run, arg);
3470 } else {
3471 /* Name not given by user, generate one. */
3472 name_gstr = get_component_auto_name(arg, all_names);
3473 if (!name_gstr) {
3474 goto error;
3475 }
3476
3477 g_string_printf(component_arg_for_run, "%s:%s",
3478 name_gstr->str, arg);
3479 }
3480
3481 if (bt_value_array_append_string_element(run_args,
3482 "--component")) {
3483 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3484 goto error;
3485 }
3486
3487 if (bt_value_array_append_string_element(run_args,
3488 component_arg_for_run->str)) {
3489 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3490 goto error;
3491 }
3492
3493 /*
3494 * Remember this name globally, for the uniqueness of
3495 * all component names.
3496 */
3497 if (bt_value_map_insert_entry(all_names,
3498 name_gstr->str, bt_value_null)) {
3499 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3500 goto error;
3501 }
3502
3503 /*
3504 * Remember this name specifically for the type of the
3505 * component. This is to create connection arguments.
3506 *
3507 * The list takes ownership of `name_gstr`.
3508 */
3509 switch (type) {
3510 case BT_COMPONENT_CLASS_TYPE_SOURCE:
3511 source_names = g_list_append(source_names, name_gstr);
3512 break;
3513 case BT_COMPONENT_CLASS_TYPE_FILTER:
3514 filter_names = g_list_append(filter_names, name_gstr);
3515 break;
3516 case BT_COMPONENT_CLASS_TYPE_SINK:
3517 sink_names = g_list_append(sink_names, name_gstr);
3518 break;
3519 default:
3520 abort();
3521 }
3522 name_gstr = NULL;
3523
3524 free(name);
3525 free(plugin_name);
3526 free(comp_cls_name);
3527 name = NULL;
3528 plugin_name = NULL;
3529 comp_cls_name = NULL;
3530 break;
3531 }
3532 case OPT_PARAMS:
3533 if (current_item_type != CONVERT_CURRENT_ITEM_TYPE_COMPONENT) {
3534 BT_CLI_LOGE_APPEND_CAUSE("No current component of which to set parameters:\n %s",
3535 arg);
3536 goto error;
3537 }
3538
3539 if (bt_value_array_append_string_element(run_args,
3540 "--params")) {
3541 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3542 goto error;
3543 }
3544
3545 if (bt_value_array_append_string_element(run_args, arg)) {
3546 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3547 goto error;
3548 }
3549 break;
3550 case OPT_LOG_LEVEL:
3551 if (current_item_type != CONVERT_CURRENT_ITEM_TYPE_COMPONENT) {
3552 BT_CLI_LOGE_APPEND_CAUSE("No current component to assign a log level to:\n %s",
3553 arg);
3554 goto error;
3555 }
3556
3557 if (bt_value_array_append_string_element(run_args, "--log-level")) {
3558 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3559 goto error;
3560 }
3561
3562 if (bt_value_array_append_string_element(run_args, arg)) {
3563 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3564 goto error;
3565 }
3566
3567 break;
3568 case OPT_OMIT_HOME_PLUGIN_PATH:
3569 force_omit_home_plugin_path = true;
3570
3571 if (bt_value_array_append_string_element(run_args,
3572 "--omit-home-plugin-path")) {
3573 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3574 goto error;
3575 }
3576 break;
3577 case OPT_RETRY_DURATION:
3578 if (bt_value_array_append_string_element(run_args,
3579 "--retry-duration")) {
3580 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3581 goto error;
3582 }
3583
3584 if (bt_value_array_append_string_element(run_args, arg)) {
3585 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3586 goto error;
3587 }
3588 break;
3589 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
3590 force_omit_system_plugin_path = true;
3591
3592 if (bt_value_array_append_string_element(run_args,
3593 "--omit-system-plugin-path")) {
3594 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3595 goto error;
3596 }
3597 break;
3598 case OPT_PLUGIN_PATH:
3599 if (bt_config_append_plugin_paths_check_setuid_setgid(
3600 plugin_paths, arg)) {
3601 goto error;
3602 }
3603
3604 if (bt_value_array_append_string_element(run_args,
3605 "--plugin-path")) {
3606 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3607 goto error;
3608 }
3609
3610 if (bt_value_array_append_string_element(run_args, arg)) {
3611 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3612 goto error;
3613 }
3614 break;
3615 case OPT_BEGIN:
3616 case OPT_CLOCK_CYCLES:
3617 case OPT_CLOCK_DATE:
3618 case OPT_CLOCK_FORCE_CORRELATE:
3619 case OPT_CLOCK_GMT:
3620 case OPT_CLOCK_OFFSET:
3621 case OPT_CLOCK_OFFSET_NS:
3622 case OPT_CLOCK_SECONDS:
3623 case OPT_COLOR:
3624 case OPT_DEBUG:
3625 case OPT_DEBUG_INFO:
3626 case OPT_DEBUG_INFO_DIR:
3627 case OPT_DEBUG_INFO_FULL_PATH:
3628 case OPT_DEBUG_INFO_TARGET_PREFIX:
3629 case OPT_END:
3630 case OPT_FIELDS:
3631 case OPT_INPUT_FORMAT:
3632 case OPT_NAMES:
3633 case OPT_NO_DELTA:
3634 case OPT_OUTPUT_FORMAT:
3635 case OPT_OUTPUT:
3636 case OPT_RUN_ARGS:
3637 case OPT_RUN_ARGS_0:
3638 case OPT_STREAM_INTERSECTION:
3639 case OPT_TIMERANGE:
3640 case OPT_VERBOSE:
3641 /* Ignore in this pass */
3642 break;
3643 default:
3644 BT_CLI_LOGE_APPEND_CAUSE("Unknown command-line option specified (option code %d).",
3645 argpar_item_opt->descr->id);
3646 goto error;
3647 }
3648 }
3649
3650 /*
3651 * Second pass: transform the convert-specific options and
3652 * arguments into implicit component instances for the run
3653 * command.
3654 */
3655 for (i = 0; i < argpar_parse_ret.items->len; i++) {
3656 struct bt_argpar_item *argpar_item =
3657 g_ptr_array_index(argpar_parse_ret.items, i);
3658 struct bt_argpar_item_opt *argpar_item_opt;
3659 const char *arg;
3660
3661 if (argpar_item->type != BT_ARGPAR_ITEM_TYPE_OPT) {
3662 continue;
3663 }
3664
3665 argpar_item_opt = (struct bt_argpar_item_opt *) argpar_item;
3666 arg = argpar_item_opt->arg;
3667
3668 switch (argpar_item_opt->descr->id) {
3669 case OPT_BEGIN:
3670 if (trimmer_has_begin) {
3671 printf("At --begin option: --begin or --timerange option already specified\n %s\n",
3672 arg);
3673 goto error;
3674 }
3675
3676 trimmer_has_begin = true;
3677 ret = append_implicit_component_extra_param(
3678 &implicit_trimmer_args, "begin", arg);
3679 implicit_trimmer_args.exists = true;
3680 if (ret) {
3681 goto error;
3682 }
3683 break;
3684 case OPT_END:
3685 if (trimmer_has_end) {
3686 printf("At --end option: --end or --timerange option already specified\n %s\n",
3687 arg);
3688 goto error;
3689 }
3690
3691 trimmer_has_end = true;
3692 ret = append_implicit_component_extra_param(
3693 &implicit_trimmer_args, "end", arg);
3694 implicit_trimmer_args.exists = true;
3695 if (ret) {
3696 goto error;
3697 }
3698 break;
3699 case OPT_TIMERANGE:
3700 {
3701 char *begin;
3702 char *end;
3703
3704 if (trimmer_has_begin || trimmer_has_end) {
3705 printf("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n",
3706 arg);
3707 goto error;
3708 }
3709
3710 ret = split_timerange(arg, &begin, &end);
3711 if (ret) {
3712 BT_CLI_LOGE_APPEND_CAUSE("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s",
3713 arg);
3714 goto error;
3715 }
3716
3717 ret = append_implicit_component_extra_param(
3718 &implicit_trimmer_args, "begin", begin);
3719 ret |= append_implicit_component_extra_param(
3720 &implicit_trimmer_args, "end", end);
3721 implicit_trimmer_args.exists = true;
3722 free(begin);
3723 free(end);
3724 if (ret) {
3725 goto error;
3726 }
3727 break;
3728 }
3729 case OPT_CLOCK_CYCLES:
3730 append_implicit_component_param(
3731 &implicit_text_args, "clock-cycles", "yes");
3732 implicit_text_args.exists = true;
3733 break;
3734 case OPT_CLOCK_DATE:
3735 append_implicit_component_param(
3736 &implicit_text_args, "clock-date", "yes");
3737 implicit_text_args.exists = true;
3738 break;
3739 case OPT_CLOCK_FORCE_CORRELATE:
3740 append_implicit_component_param(
3741 &implicit_muxer_args,
3742 "assume-absolute-clock-classes", "yes");
3743 break;
3744 case OPT_CLOCK_GMT:
3745 append_implicit_component_param(
3746 &implicit_text_args, "clock-gmt", "yes");
3747 append_implicit_component_param(
3748 &implicit_trimmer_args, "gmt", "yes");
3749 implicit_text_args.exists = true;
3750 break;
3751 case OPT_CLOCK_OFFSET:
3752 if (ctf_fs_source_clock_class_offset_arg) {
3753 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset option\n");
3754 goto error;
3755 }
3756
3757 ctf_fs_source_clock_class_offset_arg = g_strdup(arg);
3758 if (!ctf_fs_source_clock_class_offset_arg) {
3759 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3760 goto error;
3761 }
3762 break;
3763 case OPT_CLOCK_OFFSET_NS:
3764 if (ctf_fs_source_clock_class_offset_ns_arg) {
3765 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset-ns option\n");
3766 goto error;
3767 }
3768
3769 ctf_fs_source_clock_class_offset_ns_arg = g_strdup(arg);
3770 if (!ctf_fs_source_clock_class_offset_ns_arg) {
3771 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3772 goto error;
3773 }
3774 break;
3775 case OPT_CLOCK_SECONDS:
3776 append_implicit_component_param(
3777 &implicit_text_args, "clock-seconds", "yes");
3778 implicit_text_args.exists = true;
3779 break;
3780 case OPT_COLOR:
3781 implicit_text_args.exists = true;
3782 ret = append_implicit_component_extra_param(
3783 &implicit_text_args, "color", arg);
3784 if (ret) {
3785 goto error;
3786 }
3787 break;
3788 case OPT_DEBUG_INFO:
3789 implicit_debug_info_args.exists = true;
3790 break;
3791 case OPT_DEBUG_INFO_DIR:
3792 implicit_debug_info_args.exists = true;
3793 ret = append_implicit_component_extra_param(
3794 &implicit_debug_info_args, "debug-info-dir", arg);
3795 if (ret) {
3796 goto error;
3797 }
3798 break;
3799 case OPT_DEBUG_INFO_FULL_PATH:
3800 implicit_debug_info_args.exists = true;
3801 append_implicit_component_param(
3802 &implicit_debug_info_args, "full-path", "yes");
3803 break;
3804 case OPT_DEBUG_INFO_TARGET_PREFIX:
3805 implicit_debug_info_args.exists = true;
3806 ret = append_implicit_component_extra_param(
3807 &implicit_debug_info_args,
3808 "target-prefix", arg);
3809 if (ret) {
3810 goto error;
3811 }
3812 break;
3813 case OPT_FIELDS:
3814 {
3815 bt_value *fields = fields_from_arg(arg);
3816
3817 if (!fields) {
3818 goto error;
3819 }
3820
3821 implicit_text_args.exists = true;
3822 ret = insert_flat_params_from_array(
3823 implicit_text_args.params_arg,
3824 fields, "field");
3825 bt_value_put_ref(fields);
3826 if (ret) {
3827 goto error;
3828 }
3829 break;
3830 }
3831 case OPT_NAMES:
3832 {
3833 bt_value *names = names_from_arg(arg);
3834
3835 if (!names) {
3836 goto error;
3837 }
3838
3839 implicit_text_args.exists = true;
3840 ret = insert_flat_params_from_array(
3841 implicit_text_args.params_arg,
3842 names, "name");
3843 bt_value_put_ref(names);
3844 if (ret) {
3845 goto error;
3846 }
3847 break;
3848 }
3849 case OPT_NO_DELTA:
3850 append_implicit_component_param(
3851 &implicit_text_args, "no-delta", "yes");
3852 implicit_text_args.exists = true;
3853 break;
3854 case OPT_INPUT_FORMAT:
3855 if (got_input_format_opt) {
3856 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --input-format option.");
3857 goto error;
3858 }
3859
3860 got_input_format_opt = true;
3861
3862 if (strcmp(arg, "ctf") == 0) {
3863 auto_source_discovery_restrict_plugin_name = "ctf";
3864 auto_source_discovery_restrict_component_class_name = "fs";
3865 } else if (strcmp(arg, "lttng-live") == 0) {
3866 auto_source_discovery_restrict_plugin_name = "ctf";
3867 auto_source_discovery_restrict_component_class_name = "lttng-live";
3868 implicit_lttng_live_args.exists = true;
3869 } else {
3870 BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy input format:\n %s",
3871 arg);
3872 goto error;
3873 }
3874 break;
3875 case OPT_OUTPUT_FORMAT:
3876 if (got_output_format_opt) {
3877 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output-format option.");
3878 goto error;
3879 }
3880
3881 got_output_format_opt = true;
3882
3883 if (strcmp(arg, "text") == 0) {
3884 implicit_text_args.exists = true;
3885 } else if (strcmp(arg, "ctf") == 0) {
3886 implicit_ctf_output_args.exists = true;
3887 } else if (strcmp(arg, "dummy") == 0) {
3888 implicit_dummy_args.exists = true;
3889 } else if (strcmp(arg, "ctf-metadata") == 0) {
3890 print_ctf_metadata = true;
3891 } else {
3892 BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy output format:\n %s",
3893 arg);
3894 goto error;
3895 }
3896 break;
3897 case OPT_OUTPUT:
3898 if (output) {
3899 BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output option");
3900 goto error;
3901 }
3902
3903 output = strdup(arg);
3904 if (!output) {
3905 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3906 goto error;
3907 }
3908 break;
3909 case OPT_RUN_ARGS:
3910 if (print_run_args_0) {
3911 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0.");
3912 goto error;
3913 }
3914
3915 print_run_args = true;
3916 break;
3917 case OPT_RUN_ARGS_0:
3918 if (print_run_args) {
3919 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0.");
3920 goto error;
3921 }
3922
3923 print_run_args_0 = true;
3924 break;
3925 case OPT_STREAM_INTERSECTION:
3926 /*
3927 * Applies to all traces implementing the
3928 * babeltrace.trace-info query.
3929 */
3930 stream_intersection_mode = true;
3931 break;
3932 case OPT_VERBOSE:
3933 if (*default_log_level != BT_LOG_TRACE &&
3934 *default_log_level != BT_LOG_DEBUG) {
3935 *default_log_level = BT_LOG_INFO;
3936 }
3937 break;
3938 case OPT_DEBUG:
3939 *default_log_level = BT_LOG_TRACE;
3940 break;
3941 default:
3942 break;
3943 }
3944 }
3945
3946 /*
3947 * Legacy behaviour: --verbose used to make the `text` output
3948 * format print more information. --verbose is now equivalent to
3949 * the INFO log level, which is why we compare to `BT_LOG_INFO`
3950 * here.
3951 */
3952 if (*default_log_level == BT_LOG_INFO) {
3953 append_implicit_component_param(&implicit_text_args,
3954 "verbose", "yes");
3955 }
3956
3957 /*
3958 * Append home and system plugin paths now that we possibly got
3959 * --plugin-path.
3960 */
3961 if (append_home_and_system_plugin_paths(plugin_paths,
3962 force_omit_system_plugin_path,
3963 force_omit_home_plugin_path)) {
3964 goto error;
3965 }
3966
3967 /* Consume and keep leftover arguments */
3968 for (i = 0; i < argpar_parse_ret.items->len; i++) {
3969 struct bt_argpar_item *argpar_item =
3970 g_ptr_array_index(argpar_parse_ret.items, i);
3971 struct bt_argpar_item_non_opt *argpar_item_non_opt;
3972
3973 if (argpar_item->type != BT_ARGPAR_ITEM_TYPE_NON_OPT) {
3974 continue;
3975 }
3976
3977 argpar_item_non_opt = (struct bt_argpar_item_non_opt *) argpar_item;
3978
3979 if (bt_value_array_append_string_element(leftovers, argpar_item_non_opt->arg) !=
3980 BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
3981 BT_CLI_LOGE_APPEND_CAUSE_OOM();
3982 goto error;
3983 }
3984 }
3985
3986 /* Print CTF metadata or print LTTng live sessions */
3987 if (print_ctf_metadata) {
3988 const bt_value *bt_val_leftover;
3989
3990 if (bt_value_array_is_empty(leftovers)) {
3991 BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf-metadata specified without a path.");
3992 goto error;
3993 }
3994
3995 if (bt_value_array_get_size(leftovers) > 1) {
3996 BT_CLI_LOGE_APPEND_CAUSE("Too many paths specified for --output-format=ctf-metadata.");
3997 goto error;
3998 }
3999
4000 cfg = bt_config_print_ctf_metadata_create(plugin_paths);
4001 if (!cfg) {
4002 goto error;
4003 }
4004
4005 bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0);
4006 g_string_assign(cfg->cmd_data.print_ctf_metadata.path,
4007 bt_value_string_get(bt_val_leftover));
4008
4009 if (output) {
4010 g_string_assign(
4011 cfg->cmd_data.print_ctf_metadata.output_path,
4012 output);
4013 }
4014
4015 goto end;
4016 }
4017
4018 /*
4019 * If -o ctf was specified, make sure an output path (--output)
4020 * was also specified. --output does not imply -o ctf because
4021 * it's also used for the default, implicit -o text if -o ctf
4022 * is not specified.
4023 */
4024 if (implicit_ctf_output_args.exists) {
4025 if (!output) {
4026 BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf specified without --output (trace output path).");
4027 goto error;
4028 }
4029
4030 /*
4031 * At this point we know that -o ctf AND --output were
4032 * specified. Make sure that no options were specified
4033 * which would imply -o text because --output would be
4034 * ambiguous in this case. For example, this is wrong:
4035 *
4036 * babeltrace2 --names=all -o ctf --output=/tmp/path my-trace
4037 *
4038 * because --names=all implies -o text, and --output
4039 * could apply to both the sink.text.pretty and
4040 * sink.ctf.fs implicit components.
4041 */
4042 if (implicit_text_args.exists) {
4043 BT_CLI_LOGE_APPEND_CAUSE("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text.");
4044 goto error;
4045 }
4046 }
4047
4048 /*
4049 * If -o dummy and -o ctf were not specified, and if there are
4050 * no explicit sink components, then use an implicit
4051 * `sink.text.pretty` component.
4052 */
4053 if (!implicit_dummy_args.exists && !implicit_ctf_output_args.exists &&
4054 !sink_names) {
4055 implicit_text_args.exists = true;
4056 }
4057
4058 /*
4059 * Set implicit `sink.text.pretty` or `sink.ctf.fs` component's
4060 * `path` parameter if --output was specified.
4061 */
4062 if (output) {
4063 if (implicit_text_args.exists) {
4064 append_implicit_component_extra_param(&implicit_text_args,
4065 "path", output);
4066 } else if (implicit_ctf_output_args.exists) {
4067 append_implicit_component_extra_param(&implicit_ctf_output_args,
4068 "path", output);
4069 }
4070 }
4071
4072 /* Decide where the leftover argument(s) go */
4073 if (bt_value_array_get_size(leftovers) > 0) {
4074 if (implicit_lttng_live_args.exists) {
4075 const bt_value *bt_val_leftover;
4076
4077 if (bt_value_array_get_size(leftovers) > 1) {
4078 BT_CLI_LOGE_APPEND_CAUSE("Too many URLs specified for --input-format=lttng-live.");
4079 goto error;
4080 }
4081
4082 bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0);
4083 lttng_live_url_parts =
4084 bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_leftover),
4085 error_buf, sizeof(error_buf));
4086 if (!lttng_live_url_parts.proto) {
4087 BT_CLI_LOGE_APPEND_CAUSE("Invalid LTTng live URL format: %s.",
4088 error_buf);
4089 goto error;
4090 }
4091
4092 if (!lttng_live_url_parts.session_name) {
4093 /* Print LTTng live sessions */
4094 cfg = bt_config_print_lttng_live_sessions_create(
4095 plugin_paths);
4096 if (!cfg) {
4097 goto error;
4098 }
4099
4100 g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url,
4101 bt_value_string_get(bt_val_leftover));
4102
4103 if (output) {
4104 g_string_assign(
4105 cfg->cmd_data.print_lttng_live_sessions.output_path,
4106 output);
4107 }
4108
4109 goto end;
4110 }
4111
4112 ret = append_implicit_component_extra_param(
4113 &implicit_lttng_live_args, "url",
4114 bt_value_string_get(bt_val_leftover));
4115 if (ret) {
4116 goto error;
4117 }
4118
4119 ret = append_implicit_component_extra_param(
4120 &implicit_lttng_live_args,
4121 "session-not-found-action", "end");
4122 if (ret) {
4123 goto error;
4124 }
4125 } else {
4126 int status;
4127
4128 status = auto_discover_source_components(plugin_paths, leftovers,
4129 auto_source_discovery_restrict_plugin_name,
4130 auto_source_discovery_restrict_component_class_name,
4131 *default_log_level >= 0 ? *default_log_level : cli_default_log_level,
4132 &auto_disc);
4133
4134 if (status != 0) {
4135 goto error;
4136 }
4137
4138 create_implicit_component_args_from_auto_discovered_sources(
4139 &auto_disc, discovered_source_args);
4140 }
4141 }
4142
4143 /* If --clock-offset was given, apply it to any src.ctf.fs component. */
4144 if (ctf_fs_source_clock_class_offset_arg) {
4145 int n;
4146
4147 n = append_multiple_implicit_components_param(
4148 discovered_source_args, "source.ctf.fs", "clock-class-offset-s",
4149 ctf_fs_source_clock_class_offset_arg);
4150
4151 if (n == 0) {
4152 BT_CLI_LOGE_APPEND_CAUSE("--clock-offset specified, but no source.ctf.fs component instantiated.");
4153 goto error;
4154 }
4155 }
4156
4157 /* If --clock-offset-ns was given, apply it to any src.ctf.fs component. */
4158 if (ctf_fs_source_clock_class_offset_ns_arg) {
4159 int n;
4160
4161 n = append_multiple_implicit_components_param(
4162 discovered_source_args, "source.ctf.fs", "clock-class-offset-ns",
4163 ctf_fs_source_clock_class_offset_ns_arg);
4164
4165 if (n == 0) {
4166 BT_CLI_LOGE_APPEND_CAUSE("--clock-offset-ns specified, but no source.ctf.fs component instantiated.");
4167 goto error;
4168 }
4169 }
4170
4171 /*
4172 * If the implicit `source.ctf.lttng-live` component exists, make sure
4173 * there's at least one leftover (which is the URL).
4174 */
4175 if (implicit_lttng_live_args.exists && bt_value_array_is_empty(leftovers)) {
4176 BT_CLI_LOGE_APPEND_CAUSE("Missing URL for implicit `%s` component.",
4177 implicit_lttng_live_args.comp_arg->str);
4178 goto error;
4179 }
4180
4181 /* Assign names to implicit components */
4182 for (i = 0; i < discovered_source_args->len; i++) {
4183 struct implicit_component_args *args;
4184 int j;
4185
4186 args = discovered_source_args->pdata[i];
4187
4188 g_string_printf(auto_disc_comp_name, "auto-disc-%s", args->comp_arg->str);
4189
4190 /* Give it a name like `auto-disc-src-ctf-fs`. */
4191 for (j = 0; j < auto_disc_comp_name->len; j++) {
4192 if (auto_disc_comp_name->str[j] == '.') {
4193 auto_disc_comp_name->str[j] = '-';
4194 }
4195 }
4196
4197 ret = assign_name_to_implicit_component(args,
4198 auto_disc_comp_name->str, all_names, &source_names, true);
4199 if (ret) {
4200 goto error;
4201 }
4202 }
4203
4204 ret = assign_name_to_implicit_component(&implicit_lttng_live_args,
4205 "lttng-live", all_names, &source_names, true);
4206 if (ret) {
4207 goto error;
4208 }
4209
4210 ret = assign_name_to_implicit_component(&implicit_text_args,
4211 "pretty", all_names, &sink_names, true);
4212 if (ret) {
4213 goto error;
4214 }
4215
4216 ret = assign_name_to_implicit_component(&implicit_ctf_output_args,
4217 "sink-ctf-fs", all_names, &sink_names, true);
4218 if (ret) {
4219 goto error;
4220 }
4221
4222 ret = assign_name_to_implicit_component(&implicit_dummy_args,
4223 "dummy", all_names, &sink_names, true);
4224 if (ret) {
4225 goto error;
4226 }
4227
4228 ret = assign_name_to_implicit_component(&implicit_muxer_args,
4229 "muxer", all_names, NULL, false);
4230 if (ret) {
4231 goto error;
4232 }
4233
4234 ret = assign_name_to_implicit_component(&implicit_trimmer_args,
4235 "trimmer", all_names, NULL, false);
4236 if (ret) {
4237 goto error;
4238 }
4239
4240 ret = assign_name_to_implicit_component(&implicit_debug_info_args,
4241 "debug-info", all_names, NULL, false);
4242 if (ret) {
4243 goto error;
4244 }
4245
4246 /* Make sure there's at least one source and one sink */
4247 if (!source_names) {
4248 BT_CLI_LOGE_APPEND_CAUSE("No source component.");
4249 goto error;
4250 }
4251
4252 if (!sink_names) {
4253 BT_CLI_LOGE_APPEND_CAUSE("No sink component.");
4254 goto error;
4255 }
4256
4257 /*
4258 * Prepend the muxer, the trimmer, and the debug info to the
4259 * filter chain so that we have:
4260 *
4261 * sources -> muxer -> [trimmer] -> [debug info] ->
4262 * [user filters] -> sinks
4263 */
4264 if (implicit_debug_info_args.exists) {
4265 if (g_list_prepend_gstring(&filter_names,
4266 implicit_debug_info_args.name_arg->str)) {
4267 goto error;
4268 }
4269 }
4270
4271 if (implicit_trimmer_args.exists) {
4272 if (g_list_prepend_gstring(&filter_names,
4273 implicit_trimmer_args.name_arg->str)) {
4274 goto error;
4275 }
4276 }
4277
4278 if (g_list_prepend_gstring(&filter_names,
4279 implicit_muxer_args.name_arg->str)) {
4280 goto error;
4281 }
4282
4283 /*
4284 * Append the equivalent run arguments for the implicit
4285 * components.
4286 */
4287 for (i = 0; i < discovered_source_args->len; i++) {
4288 struct implicit_component_args *args =
4289 discovered_source_args->pdata[i];
4290
4291 ret = append_run_args_for_implicit_component(args, run_args);
4292 if (ret) {
4293 goto error;
4294 }
4295 }
4296
4297 ret = append_run_args_for_implicit_component(&implicit_lttng_live_args,
4298 run_args);
4299 if (ret) {
4300 goto error;
4301 }
4302
4303 ret = append_run_args_for_implicit_component(&implicit_text_args,
4304 run_args);
4305 if (ret) {
4306 goto error;
4307 }
4308
4309 ret = append_run_args_for_implicit_component(&implicit_ctf_output_args,
4310 run_args);
4311 if (ret) {
4312 goto error;
4313 }
4314
4315 ret = append_run_args_for_implicit_component(&implicit_dummy_args,
4316 run_args);
4317 if (ret) {
4318 goto error;
4319 }
4320
4321 ret = append_run_args_for_implicit_component(&implicit_muxer_args,
4322 run_args);
4323 if (ret) {
4324 goto error;
4325 }
4326
4327 ret = append_run_args_for_implicit_component(&implicit_trimmer_args,
4328 run_args);
4329 if (ret) {
4330 goto error;
4331 }
4332
4333 ret = append_run_args_for_implicit_component(&implicit_debug_info_args,
4334 run_args);
4335 if (ret) {
4336 goto error;
4337 }
4338
4339 /* Auto-connect components */
4340 ret = convert_auto_connect(run_args, source_names, filter_names,
4341 sink_names);
4342 if (ret) {
4343 BT_CLI_LOGE_APPEND_CAUSE("Cannot auto-connect components.");
4344 goto error;
4345 }
4346
4347 /*
4348 * We have all the run command arguments now. Depending on
4349 * --run-args, we pass this to the run command or print them
4350 * here.
4351 */
4352 if (print_run_args || print_run_args_0) {
4353 if (stream_intersection_mode) {
4354 BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --stream-intersection with --run-args or --run-args-0.");
4355 goto error;
4356 }
4357
4358 for (i = 0; i < bt_value_array_get_size(run_args); i++) {
4359 const bt_value *arg_value =
4360 bt_value_array_borrow_element_by_index(run_args,
4361 i);
4362 const char *arg;
4363 GString *quoted = NULL;
4364 const char *arg_to_print;
4365
4366 BT_ASSERT(arg_value);
4367 arg = bt_value_string_get(arg_value);
4368
4369 if (print_run_args) {
4370 quoted = bt_common_shell_quote(arg, true);
4371 if (!quoted) {
4372 goto error;
4373 }
4374
4375 arg_to_print = quoted->str;
4376 } else {
4377 arg_to_print = arg;
4378 }
4379
4380 printf("%s", arg_to_print);
4381
4382 if (quoted) {
4383 g_string_free(quoted, TRUE);
4384 }
4385
4386 if (i < bt_value_array_get_size(run_args) - 1) {
4387 if (print_run_args) {
4388 putchar(' ');
4389 } else {
4390 putchar('\0');
4391 }
4392 }
4393 }
4394
4395 *retcode = -1;
4396 BT_OBJECT_PUT_REF_AND_RESET(cfg);
4397 goto end;
4398 }
4399
4400 /*
4401 * If the log level is still unset at this point, set it to
4402 * the program's default.
4403 */
4404 if (*default_log_level < 0) {
4405 *default_log_level = cli_default_log_level;
4406 }
4407
4408 cfg = bt_config_run_from_args_array(run_args, retcode,
4409 force_omit_system_plugin_path,
4410 force_omit_home_plugin_path,
4411 initial_plugin_paths, *default_log_level);
4412 if (!cfg) {
4413 goto error;
4414 }
4415
4416 cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode;
4417 goto end;
4418
4419 error:
4420 *retcode = 1;
4421 BT_OBJECT_PUT_REF_AND_RESET(cfg);
4422
4423 end:
4424 /*
4425 * If the log level is still unset at this point, set it to
4426 * the program's default.
4427 */
4428 if (*default_log_level < 0) {
4429 *default_log_level = cli_default_log_level;
4430 }
4431
4432 bt_argpar_parse_ret_fini(&argpar_parse_ret);
4433
4434 free(output);
4435
4436 if (component_arg_for_run) {
4437 g_string_free(component_arg_for_run, TRUE);
4438 }
4439
4440 if (name_gstr) {
4441 g_string_free(name_gstr, TRUE);
4442 }
4443
4444 bt_value_put_ref(run_args);
4445 bt_value_put_ref(all_names);
4446 destroy_glist_of_gstring(source_names);
4447 destroy_glist_of_gstring(filter_names);
4448 destroy_glist_of_gstring(sink_names);
4449 bt_value_put_ref(leftovers);
4450 finalize_implicit_component_args(&implicit_ctf_output_args);
4451 finalize_implicit_component_args(&implicit_lttng_live_args);
4452 finalize_implicit_component_args(&implicit_dummy_args);
4453 finalize_implicit_component_args(&implicit_text_args);
4454 finalize_implicit_component_args(&implicit_debug_info_args);
4455 finalize_implicit_component_args(&implicit_muxer_args);
4456 finalize_implicit_component_args(&implicit_trimmer_args);
4457 bt_value_put_ref(plugin_paths);
4458 bt_common_destroy_lttng_live_url_parts(&lttng_live_url_parts);
4459 auto_source_discovery_fini(&auto_disc);
4460
4461 if (discovered_source_args) {
4462 g_ptr_array_free(discovered_source_args, TRUE);
4463 }
4464
4465 g_free(ctf_fs_source_clock_class_offset_arg);
4466 g_free(ctf_fs_source_clock_class_offset_ns_arg);
4467
4468 if (auto_disc_comp_name) {
4469 g_string_free(auto_disc_comp_name, TRUE);
4470 }
4471
4472 return cfg;
4473 }
4474
4475 /*
4476 * Prints the Babeltrace 2.x general usage.
4477 */
4478 static
4479 void print_gen_usage(FILE *fp)
4480 {
4481 fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS]\n");
4482 fprintf(fp, "\n");
4483 fprintf(fp, "General options:\n");
4484 fprintf(fp, "\n");
4485 fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=V)\n");
4486 fprintf(fp, " -h, --help Show this help and quit\n");
4487 fprintf(fp, " -l, --log-level=LVL Set the default log level to LVL (`N`, `V`, `D`,\n");
4488 fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n");
4489 fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n");
4490 fprintf(fp, " -V, --version Show version and quit\n");
4491 fprintf(fp, "\n");
4492 fprintf(fp, "Available commands:\n");
4493 fprintf(fp, "\n");
4494 fprintf(fp, " convert Convert and trim traces (default)\n");
4495 fprintf(fp, " help Get help for a plugin or a component class\n");
4496 fprintf(fp, " list-plugins List available plugins and their content\n");
4497 fprintf(fp, " query Query objects from a component class\n");
4498 fprintf(fp, " run Build a processing graph and run it\n");
4499 fprintf(fp, "\n");
4500 fprintf(fp, "Use `babeltrace2 COMMAND --help` to show the help of COMMAND.\n");
4501 }
4502
4503 struct bt_config *bt_config_cli_args_create(int argc, const char *argv[],
4504 int *retcode, bool force_omit_system_plugin_path,
4505 bool force_omit_home_plugin_path,
4506 const bt_value *initial_plugin_paths)
4507 {
4508 struct bt_config *config = NULL;
4509 int i;
4510 int top_level_argc;
4511 const char **top_level_argv;
4512 int command_argc = -1;
4513 const char **command_argv = NULL;
4514 const char *command_name = NULL;
4515 int default_log_level = -1;
4516 struct bt_argpar_parse_ret argpar_parse_ret = { 0 };
4517
4518 /* Top-level option descriptions. */
4519 static const struct bt_argpar_opt_descr descrs[] = {
4520 { OPT_DEBUG, 'd', "debug", false },
4521 { OPT_HELP, 'h', "help", false },
4522 { OPT_LOG_LEVEL, 'l', "log-level", true },
4523 { OPT_VERBOSE, 'v', "verbose", false },
4524 { OPT_VERSION, 'V', "version", false},
4525 BT_ARGPAR_OPT_DESCR_SENTINEL
4526 };
4527
4528 enum command_type {
4529 COMMAND_TYPE_NONE = -1,
4530 COMMAND_TYPE_RUN = 0,
4531 COMMAND_TYPE_CONVERT,
4532 COMMAND_TYPE_LIST_PLUGINS,
4533 COMMAND_TYPE_HELP,
4534 COMMAND_TYPE_QUERY,
4535 } command_type = COMMAND_TYPE_NONE;
4536
4537 *retcode = -1;
4538
4539 if (!initial_plugin_paths) {
4540 initial_plugin_paths = bt_value_array_create();
4541 if (!initial_plugin_paths) {
4542 *retcode = 1;
4543 goto end;
4544 }
4545 } else {
4546 bt_value_get_ref(initial_plugin_paths);
4547 }
4548
4549 if (argc <= 1) {
4550 print_version();
4551 puts("");
4552 print_gen_usage(stdout);
4553 goto end;
4554 }
4555
4556 /* Skip first argument, the name of the program. */
4557 top_level_argc = argc - 1;
4558 top_level_argv = argv + 1;
4559 argpar_parse_ret = bt_argpar_parse(top_level_argc, top_level_argv,
4560 descrs, false);
4561
4562 if (argpar_parse_ret.error) {
4563 BT_CLI_LOGE_APPEND_CAUSE(
4564 "While parsing command-line arguments: %s",
4565 argpar_parse_ret.error->str);
4566 goto error;
4567 }
4568
4569 for (i = 0; i < argpar_parse_ret.items->len; i++) {
4570 struct bt_argpar_item *item;
4571
4572 item = g_ptr_array_index(argpar_parse_ret.items, i);
4573
4574 if (item->type == BT_ARGPAR_ITEM_TYPE_OPT) {
4575 struct bt_argpar_item_opt *item_opt =
4576 (struct bt_argpar_item_opt *) item;
4577
4578 switch (item_opt->descr->id) {
4579 case OPT_DEBUG:
4580 default_log_level = BT_LOG_TRACE;
4581 break;
4582 case OPT_VERBOSE:
4583 /*
4584 * Legacy: do not override a previous
4585 * --debug because --verbose and --debug
4586 * can be specified together (in this
4587 * case we want the lowest log level to
4588 * apply, TRACE).
4589 */
4590 default_log_level = BT_LOG_INFO;
4591 break;
4592 case OPT_LOG_LEVEL:
4593 default_log_level =
4594 bt_log_get_level_from_string(item_opt->arg);
4595 if (default_log_level < 0) {
4596 BT_CLI_LOGE_APPEND_CAUSE(
4597 "Invalid argument for --log-level option:\n %s",
4598 item_opt->arg);
4599 goto error;
4600 }
4601 break;
4602 case OPT_VERSION:
4603 print_version();
4604 goto end;
4605 case OPT_HELP:
4606 print_gen_usage(stdout);
4607 goto end;
4608 }
4609 } else if (item->type == BT_ARGPAR_ITEM_TYPE_NON_OPT) {
4610 struct bt_argpar_item_non_opt *item_non_opt =
4611 (struct bt_argpar_item_non_opt *) item;
4612 /*
4613 * First unknown argument: is it a known command
4614 * name?
4615 */
4616 command_argc =
4617 top_level_argc - item_non_opt->orig_index - 1;
4618 command_argv =
4619 &top_level_argv[item_non_opt->orig_index + 1];
4620
4621 if (strcmp(item_non_opt->arg, "convert") == 0) {
4622 command_type = COMMAND_TYPE_CONVERT;
4623 } else if (strcmp(item_non_opt->arg, "list-plugins") == 0) {
4624 command_type = COMMAND_TYPE_LIST_PLUGINS;
4625 } else if (strcmp(item_non_opt->arg, "help") == 0) {
4626 command_type = COMMAND_TYPE_HELP;
4627 } else if (strcmp(item_non_opt->arg, "query") == 0) {
4628 command_type = COMMAND_TYPE_QUERY;
4629 } else if (strcmp(item_non_opt->arg, "run") == 0) {
4630 command_type = COMMAND_TYPE_RUN;
4631 } else {
4632 /*
4633 * Non-option argument, but not a known
4634 * command name: assume the default
4635 * `convert` command.
4636 */
4637 command_type = COMMAND_TYPE_CONVERT;
4638 command_name = "convert";
4639 command_argc++;
4640 command_argv--;
4641 }
4642 break;
4643 }
4644 }
4645
4646 if (command_type == COMMAND_TYPE_NONE) {
4647 if (argpar_parse_ret.ingested_orig_args == top_level_argc) {
4648 /*
4649 * We only got non-help, non-version general options
4650 * like --verbose and --debug, without any other
4651 * arguments, so we can't do anything useful: print the
4652 * usage and quit.
4653 */
4654 print_gen_usage(stdout);
4655 goto end;
4656 }
4657
4658 /*
4659 * We stopped on an unknown option argument (and therefore
4660 * didn't see a command name). Assume `convert` command.
4661 */
4662 command_type = COMMAND_TYPE_CONVERT;
4663 command_name = "convert";
4664 command_argc =
4665 top_level_argc - argpar_parse_ret.ingested_orig_args;
4666 command_argv =
4667 &top_level_argv[argpar_parse_ret.ingested_orig_args];
4668 }
4669
4670 BT_ASSERT(command_argv);
4671 BT_ASSERT(command_argc >= 0);
4672
4673 /*
4674 * The convert command can set its own default log level for
4675 * backward compatibility reasons. It only does so if there's no
4676 * log level yet, so do not force one for this command.
4677 */
4678 if (command_type != COMMAND_TYPE_CONVERT && default_log_level < 0) {
4679 /* Default log level */
4680 default_log_level = cli_default_log_level;
4681 }
4682
4683 switch (command_type) {
4684 case COMMAND_TYPE_RUN:
4685 config = bt_config_run_from_args(command_argc, command_argv,
4686 retcode, force_omit_system_plugin_path,
4687 force_omit_home_plugin_path, initial_plugin_paths,
4688 default_log_level);
4689 break;
4690 case COMMAND_TYPE_CONVERT:
4691 config = bt_config_convert_from_args(command_argc, command_argv,
4692 retcode, force_omit_system_plugin_path,
4693 force_omit_home_plugin_path,
4694 initial_plugin_paths, &default_log_level);
4695 break;
4696 case COMMAND_TYPE_LIST_PLUGINS:
4697 config = bt_config_list_plugins_from_args(command_argc,
4698 command_argv, retcode, force_omit_system_plugin_path,
4699 force_omit_home_plugin_path, initial_plugin_paths);
4700 break;
4701 case COMMAND_TYPE_HELP:
4702 config = bt_config_help_from_args(command_argc,
4703 command_argv, retcode, force_omit_system_plugin_path,
4704 force_omit_home_plugin_path, initial_plugin_paths,
4705 default_log_level);
4706 break;
4707 case COMMAND_TYPE_QUERY:
4708 config = bt_config_query_from_args(command_argc,
4709 command_argv, retcode, force_omit_system_plugin_path,
4710 force_omit_home_plugin_path, initial_plugin_paths,
4711 default_log_level);
4712 break;
4713 default:
4714 abort();
4715 }
4716
4717 if (config) {
4718 BT_ASSERT(default_log_level >= BT_LOG_TRACE);
4719 config->log_level = default_log_level;
4720 config->command_name = command_name;
4721 }
4722
4723 goto end;
4724
4725 error:
4726 *retcode = 1;
4727
4728 end:
4729 bt_argpar_parse_ret_fini(&argpar_parse_ret);
4730 bt_value_put_ref(initial_plugin_paths);
4731 return config;
4732 }
This page took 0.191615 seconds and 4 git commands to generate.