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