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