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