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