babeltrace-cfg.c: improve error messages
[babeltrace.git] / converter / babeltrace-cfg.c
CommitLineData
c42c79ea
PP
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#include <errno.h>
26#include <stdlib.h>
27#include <string.h>
28#include <assert.h>
29#include <stdio.h>
30#include <stdbool.h>
31#include <inttypes.h>
32#include <babeltrace/babeltrace.h>
1670bffd 33#include <babeltrace/common-internal.h>
c42c79ea
PP
34#include <babeltrace/values.h>
35#include <popt.h>
36#include <glib.h>
98ecef32
MD
37#include <sys/types.h>
38#include <pwd.h>
c42c79ea
PP
39#include "babeltrace-cfg.h"
40
65582340 41#define DEFAULT_SOURCE_COMPONENT_NAME "ctf.fs"
bd9f0fa2 42#define DEFAULT_SINK_COMPONENT_NAME "text.text"
65582340 43
c42c79ea
PP
44/*
45 * Error printf() macro which prepends "Error: " the first time it's
46 * called. This gives a nicer feel than having a bunch of error prefixes
47 * (since the following lines usually describe the error and possible
48 * solutions), or the error prefix just at the end.
49 */
50#define printf_err(fmt, args...) \
51 do { \
52 if (is_first_error) { \
53 fprintf(stderr, "Error: "); \
54 is_first_error = false; \
55 } \
56 fprintf(stderr, fmt, ##args); \
57 } while (0)
58
59static bool is_first_error = true;
60
61/* INI-style parsing FSM states */
62enum ini_parsing_fsm_state {
63 /* Expect a map key (identifier) */
64 INI_EXPECT_MAP_KEY,
65
66 /* Expect an equal character ('=') */
67 INI_EXPECT_EQUAL,
68
69 /* Expect a value */
70 INI_EXPECT_VALUE,
71
72 /* Expect a negative number value */
73 INI_EXPECT_VALUE_NUMBER_NEG,
74
75 /* Expect a comma character (',') */
76 INI_EXPECT_COMMA,
77};
78
79/* INI-style parsing state variables */
80struct ini_parsing_state {
81 /* Lexical scanner (owned by this) */
82 GScanner *scanner;
83
84 /* Output map value object being filled (owned by this) */
85 struct bt_value *params;
86
87 /* Next expected FSM state */
88 enum ini_parsing_fsm_state expecting;
89
90 /* Last decoded map key (owned by this) */
91 char *last_map_key;
92
93 /* Complete INI-style string to parse (not owned by this) */
94 const char *arg;
95
96 /* Error buffer (not owned by this) */
97 GString *ini_error;
98};
99
100/* Offset option with "is set" boolean */
101struct offset_opt {
102 int64_t value;
103 bool is_set;
104};
105
106/* Legacy "ctf"/"lttng-live" format options */
107struct ctf_legacy_opts {
108 struct offset_opt offset_s;
109 struct offset_opt offset_ns;
110 bool stream_intersection;
111};
112
113/* Legacy "text" format options */
114struct text_legacy_opts {
115 /*
116 * output, dbg_info_dir, dbg_info_target_prefix, names,
117 * and fields are owned by this.
118 */
119 GString *output;
120 GString *dbg_info_dir;
121 GString *dbg_info_target_prefix;
122 struct bt_value *names;
123 struct bt_value *fields;
124
125 /* Flags */
126 bool no_delta;
127 bool clock_cycles;
128 bool clock_seconds;
129 bool clock_date;
130 bool clock_gmt;
131 bool dbg_info_full_path;
132};
133
134/* Legacy input format format */
135enum legacy_input_format {
136 LEGACY_INPUT_FORMAT_NONE = 0,
137 LEGACY_INPUT_FORMAT_CTF,
138 LEGACY_INPUT_FORMAT_LTTNG_LIVE,
139};
140
141/* Legacy output format format */
142enum legacy_output_format {
143 LEGACY_OUTPUT_FORMAT_NONE = 0,
144 LEGACY_OUTPUT_FORMAT_TEXT,
145 LEGACY_OUTPUT_FORMAT_CTF_METADATA,
146 LEGACY_OUTPUT_FORMAT_DUMMY,
147};
148
149/*
150 * Prints the "out of memory" error.
151 */
152static
153void print_err_oom(void)
154{
155 printf_err("Out of memory\n");
156}
157
158/*
159 * Prints duplicate legacy output format error.
160 */
161static
162void print_err_dup_legacy_output(void)
163{
164 printf_err("More than one legacy output format specified\n");
165}
166
167/*
168 * Prints duplicate legacy input format error.
169 */
170static
171void print_err_dup_legacy_input(void)
172{
173 printf_err("More than one legacy input format specified\n");
174}
175
176/*
177 * Checks if any of the "text" legacy options is set.
178 */
179static
180bool text_legacy_opts_is_any_set(struct text_legacy_opts *opts)
181{
182 return (opts->output && opts->output->len > 0) ||
183 (opts->dbg_info_dir && opts->dbg_info_dir->len > 0) ||
184 (opts->dbg_info_target_prefix &&
185 opts->dbg_info_target_prefix->len > 0) ||
186 bt_value_array_size(opts->names) > 0 ||
187 bt_value_array_size(opts->fields) > 0 ||
188 opts->no_delta || opts->clock_cycles || opts->clock_seconds ||
189 opts->clock_date || opts->clock_gmt ||
190 opts->dbg_info_full_path;
191}
192
193/*
194 * Checks if any of the "ctf" legacy options is set.
195 */
196static
197bool ctf_legacy_opts_is_any_set(struct ctf_legacy_opts *opts)
198{
199 return opts->offset_s.is_set || opts->offset_ns.is_set ||
200 opts->stream_intersection;
201}
202
203/*
204 * Appends an "expecting token" error to the INI-style parsing state's
205 * error buffer.
206 */
207static
208void ini_append_error_expecting(struct ini_parsing_state *state,
209 GScanner *scanner, const char *expecting)
210{
211 size_t i;
212 size_t pos;
213
214 g_string_append_printf(state->ini_error, "Expecting %s:\n", expecting);
215
216 /* Only print error if there's one line */
217 if (strchr(state->arg, '\n') != NULL || strlen(state->arg) == 0) {
218 return;
219 }
220
221 g_string_append_printf(state->ini_error, "\n %s\n", state->arg);
222 pos = g_scanner_cur_position(scanner) + 4;
223
224 if (!g_scanner_eof(scanner)) {
225 pos--;
226 }
227
228 for (i = 0; i < pos; ++i) {
229 g_string_append_printf(state->ini_error, " ");
230 }
231
232 g_string_append_printf(state->ini_error, "^\n\n");
233}
234
235static
236int ini_handle_state(struct ini_parsing_state *state)
237{
238 int ret = 0;
239 GTokenType token_type;
240 struct bt_value *value = NULL;
241
242 token_type = g_scanner_get_next_token(state->scanner);
243 if (token_type == G_TOKEN_EOF) {
244 if (state->expecting != INI_EXPECT_COMMA) {
245 switch (state->expecting) {
246 case INI_EXPECT_EQUAL:
247 ini_append_error_expecting(state,
248 state->scanner, "'='");
249 break;
250 case INI_EXPECT_VALUE:
251 case INI_EXPECT_VALUE_NUMBER_NEG:
252 ini_append_error_expecting(state,
253 state->scanner, "value");
254 break;
255 case INI_EXPECT_MAP_KEY:
256 ini_append_error_expecting(state,
257 state->scanner, "unquoted map key");
258 break;
259 default:
260 break;
261 }
262 goto error;
263 }
264
265 /* We're done! */
266 ret = 1;
267 goto success;
268 }
269
270 switch (state->expecting) {
271 case INI_EXPECT_MAP_KEY:
272 if (token_type != G_TOKEN_IDENTIFIER) {
273 ini_append_error_expecting(state, state->scanner,
274 "unquoted map key");
275 goto error;
276 }
277
278 free(state->last_map_key);
279 state->last_map_key =
280 strdup(state->scanner->value.v_identifier);
281 if (!state->last_map_key) {
282 g_string_append(state->ini_error,
283 "Out of memory\n");
284 goto error;
285 }
286
287 if (bt_value_map_has_key(state->params, state->last_map_key)) {
288 g_string_append_printf(state->ini_error,
6d1d5711 289 "Duplicate parameter key: `%s`\n",
c42c79ea
PP
290 state->last_map_key);
291 goto error;
292 }
293
294 state->expecting = INI_EXPECT_EQUAL;
295 goto success;
296 case INI_EXPECT_EQUAL:
297 if (token_type != G_TOKEN_CHAR) {
298 ini_append_error_expecting(state,
299 state->scanner, "'='");
300 goto error;
301 }
302
303 if (state->scanner->value.v_char != '=') {
304 ini_append_error_expecting(state,
305 state->scanner, "'='");
306 goto error;
307 }
308
309 state->expecting = INI_EXPECT_VALUE;
310 goto success;
311 case INI_EXPECT_VALUE:
312 {
313 switch (token_type) {
314 case G_TOKEN_CHAR:
315 if (state->scanner->value.v_char == '-') {
316 /* Negative number */
317 state->expecting =
318 INI_EXPECT_VALUE_NUMBER_NEG;
319 goto success;
320 } else {
321 ini_append_error_expecting(state,
322 state->scanner, "value");
323 goto error;
324 }
325 break;
326 case G_TOKEN_INT:
327 {
328 /* Positive integer */
329 uint64_t int_val = state->scanner->value.v_int64;
330
331 if (int_val > (1ULL << 63) - 1) {
332 g_string_append_printf(state->ini_error,
333 "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n",
334 int_val);
335 goto error;
336 }
337
338 value = bt_value_integer_create_init(
339 (int64_t) int_val);
340 break;
341 }
342 case G_TOKEN_FLOAT:
343 /* Positive floating point number */
344 value = bt_value_float_create_init(
345 state->scanner->value.v_float);
346 break;
347 case G_TOKEN_STRING:
348 /* Quoted string */
349 value = bt_value_string_create_init(
350 state->scanner->value.v_string);
351 break;
352 case G_TOKEN_IDENTIFIER:
353 {
354 /*
355 * Using symbols would be appropriate here,
356 * but said symbols are allowed as map key,
357 * so it's easier to consider everything an
358 * identifier.
359 *
360 * If one of the known symbols is not
361 * recognized here, then fall back to creating
362 * a string value.
363 */
364 const char *id = state->scanner->value.v_identifier;
365
366 if (!strcmp(id, "null") || !strcmp(id, "NULL") ||
367 !strcmp(id, "nul")) {
368 value = bt_value_null;
369 } else if (!strcmp(id, "true") || !strcmp(id, "TRUE") ||
370 !strcmp(id, "yes") ||
371 !strcmp(id, "YES")) {
372 value = bt_value_bool_create_init(true);
373 } else if (!strcmp(id, "false") ||
374 !strcmp(id, "FALSE") ||
375 !strcmp(id, "no") ||
376 !strcmp(id, "NO")) {
377 value = bt_value_bool_create_init(false);
378 } else {
379 value = bt_value_string_create_init(id);
380 }
381 break;
382 }
383 default:
384 /* Unset value variable will trigger the error */
385 break;
386 }
387
388 if (!value) {
389 ini_append_error_expecting(state,
390 state->scanner, "value");
391 goto error;
392 }
393
394 state->expecting = INI_EXPECT_COMMA;
395 goto success;
396 }
397 case INI_EXPECT_VALUE_NUMBER_NEG:
398 {
399 switch (token_type) {
400 case G_TOKEN_INT:
401 {
402 /* Negative integer */
403 uint64_t int_val = state->scanner->value.v_int64;
404
405 if (int_val > (1ULL << 63) - 1) {
406 g_string_append_printf(state->ini_error,
407 "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n",
408 int_val);
409 goto error;
410 }
411
412 value = bt_value_integer_create_init(
413 -((int64_t) int_val));
414 break;
415 }
416 case G_TOKEN_FLOAT:
417 /* Negative floating point number */
418 value = bt_value_float_create_init(
419 -state->scanner->value.v_float);
420 break;
421 default:
422 /* Unset value variable will trigger the error */
423 break;
424 }
425
426 if (!value) {
427 ini_append_error_expecting(state,
428 state->scanner, "value");
429 goto error;
430 }
431
432 state->expecting = INI_EXPECT_COMMA;
433 goto success;
434 }
435 case INI_EXPECT_COMMA:
436 if (token_type != G_TOKEN_CHAR) {
437 ini_append_error_expecting(state,
438 state->scanner, "','");
439 goto error;
440 }
441
442 if (state->scanner->value.v_char != ',') {
443 ini_append_error_expecting(state,
444 state->scanner, "','");
445 goto error;
446 }
447
448 state->expecting = INI_EXPECT_MAP_KEY;
449 goto success;
450 default:
451 assert(false);
452 }
453
454error:
455 ret = -1;
456 goto end;
457
458success:
459 if (value) {
460 if (bt_value_map_insert(state->params,
461 state->last_map_key, value)) {
462 /* Only override return value on error */
463 ret = -1;
464 }
465 }
466
467end:
468 BT_PUT(value);
469 return ret;
470}
471
472/*
473 * Converts an INI-style argument to an equivalent map value object.
474 *
475 * Return value is owned by the caller.
476 */
477static
478struct bt_value *bt_value_from_ini(const char *arg, GString *ini_error)
479{
480 /* Lexical scanner configuration */
481 GScannerConfig scanner_config = {
482 /* Skip whitespaces */
483 .cset_skip_characters = " \t\n",
484
485 /* Identifier syntax is: [a-zA-Z_][a-zA-Z0-9_.:-]* */
486 .cset_identifier_first =
487 G_CSET_a_2_z
488 "_"
489 G_CSET_A_2_Z,
490 .cset_identifier_nth =
491 G_CSET_a_2_z
492 "_0123456789-.:"
493 G_CSET_A_2_Z,
494
495 /* "hello" and "Hello" two different keys */
496 .case_sensitive = TRUE,
497
498 /* No comments */
499 .cpair_comment_single = NULL,
500 .skip_comment_multi = TRUE,
501 .skip_comment_single = TRUE,
502 .scan_comment_multi = FALSE,
503
504 /*
505 * Do scan identifiers, including 1-char identifiers,
506 * but NULL is a normal identifier.
507 */
508 .scan_identifier = TRUE,
509 .scan_identifier_1char = TRUE,
510 .scan_identifier_NULL = FALSE,
511
512 /*
513 * No specific symbols: null and boolean "symbols" are
514 * scanned as plain identifiers.
515 */
516 .scan_symbols = FALSE,
517 .symbol_2_token = FALSE,
518 .scope_0_fallback = FALSE,
519
520 /*
521 * Scan "0b"-, "0"-, and "0x"-prefixed integers, but not
522 * integers prefixed with "$".
523 */
524 .scan_binary = TRUE,
525 .scan_octal = TRUE,
526 .scan_float = TRUE,
527 .scan_hex = TRUE,
528 .scan_hex_dollar = FALSE,
529
530 /* Convert scanned numbers to integer tokens */
531 .numbers_2_int = TRUE,
532
533 /* Support both integers and floating-point numbers */
534 .int_2_float = FALSE,
535
536 /* Scan integers as 64-bit signed integers */
537 .store_int64 = TRUE,
538
539 /* Only scan double-quoted strings */
540 .scan_string_sq = FALSE,
541 .scan_string_dq = TRUE,
542
543 /* Do not converter identifiers to string tokens */
544 .identifier_2_string = FALSE,
545
546 /* Scan characters as G_TOKEN_CHAR token */
547 .char_2_token = FALSE,
548 };
549 struct ini_parsing_state state = {
550 .scanner = NULL,
551 .params = NULL,
552 .expecting = INI_EXPECT_MAP_KEY,
553 .arg = arg,
554 .ini_error = ini_error,
555 };
556
557 state.params = bt_value_map_create();
558 if (!state.params) {
559 goto error;
560 }
561
562 state.scanner = g_scanner_new(&scanner_config);
563 if (!state.scanner) {
564 goto error;
565 }
566
567 /* Let the scan begin */
568 g_scanner_input_text(state.scanner, arg, strlen(arg));
569
570 while (true) {
571 int ret = ini_handle_state(&state);
572
573 if (ret < 0) {
574 /* Error */
575 goto error;
576 } else if (ret > 0) {
577 /* Done */
578 break;
579 }
580 }
581
582 goto end;
583
584error:
585 BT_PUT(state.params);
586
587end:
588 if (state.scanner) {
589 g_scanner_destroy(state.scanner);
590 }
591
592 free(state.last_map_key);
593 return state.params;
594}
595
596/*
597 * Returns the parameters map value object from a command-line
bdc61c70 598 * parameter option's argument.
c42c79ea
PP
599 *
600 * Return value is owned by the caller.
601 */
602static
603struct bt_value *bt_value_from_arg(const char *arg)
604{
605 struct bt_value *params = NULL;
c42c79ea
PP
606 GString *ini_error = NULL;
607
c42c79ea
PP
608 ini_error = g_string_new(NULL);
609 if (!ini_error) {
610 print_err_oom();
611 goto end;
612 }
613
614 /* Try INI-style parsing */
bdc61c70 615 params = bt_value_from_ini(arg, ini_error);
c42c79ea
PP
616 if (!params) {
617 printf_err("%s", ini_error->str);
618 goto end;
619 }
620
621end:
622 if (ini_error) {
623 g_string_free(ini_error, TRUE);
624 }
625 return params;
626}
627
628/*
629 * Returns the plugin and component names from a command-line
bdc61c70
PP
630 * source/sink option's argument. arg must have the following format:
631 *
632 * PLUGIN.COMPONENT
633 *
634 * where PLUGIN is the plugin name, and COMPONENT is the component
635 * name.
c42c79ea
PP
636 *
637 * On success, both *plugin and *component are not NULL. *plugin
638 * and *component are owned by the caller.
639 */
640static
641void plugin_component_names_from_arg(const char *arg, char **plugin,
642 char **component)
643{
644 const char *dot;
bdc61c70 645 const char *end;
c42c79ea
PP
646 size_t plugin_len;
647 size_t component_len;
648
bdc61c70 649 /* Initialize both return values to NULL: not found */
c42c79ea
PP
650 *plugin = NULL;
651 *component = NULL;
652
653 dot = strchr(arg, '.');
bdc61c70
PP
654 if (!dot) {
655 /* No dot */
c42c79ea
PP
656 goto end;
657 }
658
bdc61c70 659 end = arg + strlen(arg);
c42c79ea 660 plugin_len = dot - arg;
bdc61c70 661 component_len = end - dot - 1;
c42c79ea
PP
662 if (plugin_len == 0 || component_len == 0) {
663 goto end;
664 }
665
bdc61c70 666 *plugin = g_malloc0(plugin_len + 1);
c42c79ea
PP
667 if (!*plugin) {
668 print_err_oom();
669 goto end;
670 }
671
bdc61c70
PP
672 g_strlcpy(*plugin, arg, plugin_len + 1);
673 *component = g_malloc0(component_len + 1);
c42c79ea
PP
674 if (!*component) {
675 print_err_oom();
676 goto end;
677 }
678
bdc61c70 679 g_strlcpy(*component, dot + 1, component_len + 1);
c42c79ea
PP
680
681end:
682 return;
683}
684
685/*
686 * Prints the Babeltrace version.
687 */
688static
689void print_version(void)
690{
691 puts("Babeltrace " VERSION);
692}
693
c42c79ea
PP
694/*
695 * Destroys a component configuration.
696 */
697static
698void bt_config_component_destroy(struct bt_object *obj)
699{
700 struct bt_config_component *bt_config_component =
701 container_of(obj, struct bt_config_component, base);
702
703 if (!obj) {
704 goto end;
705 }
706
707 if (bt_config_component->plugin_name) {
708 g_string_free(bt_config_component->plugin_name, TRUE);
709 }
710
711 if (bt_config_component->component_name) {
712 g_string_free(bt_config_component->component_name, TRUE);
713 }
714
715 BT_PUT(bt_config_component->params);
716 g_free(bt_config_component);
717
718end:
719 return;
720}
721
722/*
bdc61c70
PP
723 * Creates a component configuration using the given plugin name and
724 * component name. plugin_name and component_name are copied (belong to
725 * the return value).
c42c79ea
PP
726 *
727 * Return value is owned by the caller.
728 */
729static
730struct bt_config_component *bt_config_component_create(const char *plugin_name,
bdc61c70 731 const char *component_name)
c42c79ea
PP
732{
733 struct bt_config_component *cfg_component = NULL;
734
735 cfg_component = g_new0(struct bt_config_component, 1);
736 if (!cfg_component) {
737 print_err_oom();
738 goto error;
739 }
740
741 bt_object_init(cfg_component, bt_config_component_destroy);
742 cfg_component->plugin_name = g_string_new(plugin_name);
743 if (!cfg_component->plugin_name) {
744 print_err_oom();
745 goto error;
746 }
747
748 cfg_component->component_name = g_string_new(component_name);
749 if (!cfg_component->component_name) {
750 print_err_oom();
751 goto error;
752 }
753
bdc61c70
PP
754 /* Start with empty parameters */
755 cfg_component->params = bt_value_map_create();
756 if (!cfg_component->params) {
757 print_err_oom();
758 goto error;
759 }
760
c42c79ea
PP
761 goto end;
762
763error:
764 BT_PUT(cfg_component);
765
766end:
767 return cfg_component;
768}
769
770/*
771 * Creates a component configuration from a command-line source/sink
bdc61c70 772 * option's argument.
c42c79ea
PP
773 */
774static
775struct bt_config_component *bt_config_component_from_arg(const char *arg)
776{
777 struct bt_config_component *bt_config_component = NULL;
778 char *plugin_name;
779 char *component_name;
c42c79ea
PP
780
781 plugin_component_names_from_arg(arg, &plugin_name, &component_name);
782 if (!plugin_name || !component_name) {
49849a47 783 printf_err("Cannot get plugin or component class name\n");
c42c79ea
PP
784 goto error;
785 }
786
c42c79ea 787 bt_config_component = bt_config_component_create(plugin_name,
bdc61c70 788 component_name);
c42c79ea
PP
789 if (!bt_config_component) {
790 goto error;
791 }
792
793 goto end;
794
795error:
796 BT_PUT(bt_config_component);
797
798end:
bdc61c70
PP
799 g_free(plugin_name);
800 g_free(component_name);
c42c79ea
PP
801 return bt_config_component;
802}
803
804/*
805 * Destroys a configuration.
806 */
807static
808void bt_config_destroy(struct bt_object *obj)
809{
290725f7 810 struct bt_config *cfg =
c42c79ea
PP
811 container_of(obj, struct bt_config, base);
812
813 if (!obj) {
814 goto end;
815 }
816
290725f7
PP
817 switch (cfg->command) {
818 case BT_CONFIG_COMMAND_CONVERT:
819 if (cfg->cmd_data.convert.sources) {
820 g_ptr_array_free(cfg->cmd_data.convert.sources, TRUE);
821 }
c42c79ea 822
290725f7
PP
823 if (cfg->cmd_data.convert.sinks) {
824 g_ptr_array_free(cfg->cmd_data.convert.sinks, TRUE);
825 }
826
827 BT_PUT(cfg->cmd_data.convert.plugin_paths);
828 break;
829
830 case BT_CONFIG_COMMAND_LIST_PLUGINS:
831 BT_PUT(cfg->cmd_data.list_plugins.plugin_paths);
832 break;
833
834 default:
835 assert(false);
c42c79ea
PP
836 }
837
290725f7 838 g_free(cfg);
c42c79ea
PP
839
840end:
841 return;
842}
843
1670bffd
PP
844static void destroy_gstring(void *data)
845{
846 g_string_free(data, TRUE);
847}
848
c42c79ea
PP
849/*
850 * Extracts the various paths from the string arg, delimited by ':',
5a3ee633 851 * and appends them to the array value object plugin_paths.
c42c79ea 852 */
5a3ee633
PP
853enum bt_value_status bt_config_append_plugin_paths(
854 struct bt_value *plugin_paths, const char *arg)
c42c79ea 855{
1670bffd
PP
856 enum bt_value_status status = BT_VALUE_STATUS_OK;
857 GPtrArray *dirs = g_ptr_array_new_with_free_func(destroy_gstring);
858 int ret;
859 size_t i;
c42c79ea 860
1670bffd
PP
861 if (!dirs) {
862 status = BT_VALUE_STATUS_ERROR;
863 goto end;
864 }
c42c79ea 865
1670bffd
PP
866 ret = bt_common_append_plugin_path_dirs(arg, dirs);
867 if (ret) {
868 status = BT_VALUE_STATUS_ERROR;
869 goto end;
870 }
c42c79ea 871
1670bffd
PP
872 for (i = 0; i < dirs->len; i++) {
873 GString *dir = g_ptr_array_index(dirs, i);
c42c79ea 874
1670bffd 875 bt_value_array_append_string(plugin_paths, dir->str);
c42c79ea
PP
876 }
877
1670bffd
PP
878end:
879 g_ptr_array_free(dirs, TRUE);
880 return status;
c42c79ea
PP
881}
882
883/*
884 * Creates a simple lexical scanner for parsing comma-delimited names
885 * and fields.
886 *
887 * Return value is owned by the caller.
888 */
889static
890GScanner *create_csv_identifiers_scanner(void)
891{
892 GScannerConfig scanner_config = {
893 .cset_skip_characters = " \t\n",
894 .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "_",
895 .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ":_-",
896 .case_sensitive = TRUE,
897 .cpair_comment_single = NULL,
898 .skip_comment_multi = TRUE,
899 .skip_comment_single = TRUE,
900 .scan_comment_multi = FALSE,
901 .scan_identifier = TRUE,
902 .scan_identifier_1char = TRUE,
903 .scan_identifier_NULL = FALSE,
904 .scan_symbols = FALSE,
905 .symbol_2_token = FALSE,
906 .scope_0_fallback = FALSE,
907 .scan_binary = FALSE,
908 .scan_octal = FALSE,
909 .scan_float = FALSE,
910 .scan_hex = FALSE,
911 .scan_hex_dollar = FALSE,
912 .numbers_2_int = FALSE,
913 .int_2_float = FALSE,
914 .store_int64 = FALSE,
915 .scan_string_sq = FALSE,
916 .scan_string_dq = FALSE,
917 .identifier_2_string = FALSE,
918 .char_2_token = TRUE,
919 };
920
921 return g_scanner_new(&scanner_config);
922}
923
6e1bc0df
MD
924/*
925 * Inserts a string (if exists and not empty) or null to a map value
926 * object.
927 */
928static
929enum bt_value_status map_insert_string_or_null(struct bt_value *map,
930 const char *key, GString *string)
931{
932 enum bt_value_status ret;
933
934 if (string && string->len > 0) {
935 ret = bt_value_map_insert_string(map, key, string->str);
936 } else {
937 ret = bt_value_map_insert(map, key, bt_value_null);
938 }
939 return ret;
940}
941
c42c79ea
PP
942/*
943 * Converts a comma-delimited list of known names (--names option) to
944 * an array value object containing those names as string value objects.
945 *
946 * Return value is owned by the caller.
947 */
948static
949struct bt_value *names_from_arg(const char *arg)
950{
951 GScanner *scanner = NULL;
952 struct bt_value *names = NULL;
6e1bc0df 953 bool found_all = false, found_none = false, found_item = false;
c42c79ea
PP
954
955 names = bt_value_array_create();
956 if (!names) {
957 print_err_oom();
958 goto error;
959 }
960
961 scanner = create_csv_identifiers_scanner();
962 if (!scanner) {
963 print_err_oom();
964 goto error;
965 }
966
967 g_scanner_input_text(scanner, arg, strlen(arg));
968
969 while (true) {
970 GTokenType token_type = g_scanner_get_next_token(scanner);
971
972 switch (token_type) {
973 case G_TOKEN_IDENTIFIER:
974 {
975 const char *identifier = scanner->value.v_identifier;
976
977 if (!strcmp(identifier, "payload") ||
978 !strcmp(identifier, "args") ||
979 !strcmp(identifier, "arg")) {
6e1bc0df 980 found_item = true;
c42c79ea
PP
981 if (bt_value_array_append_string(names,
982 "payload")) {
983 goto error;
984 }
985 } else if (!strcmp(identifier, "context") ||
986 !strcmp(identifier, "ctx")) {
6e1bc0df 987 found_item = true;
c42c79ea
PP
988 if (bt_value_array_append_string(names,
989 "context")) {
990 goto error;
991 }
992 } else if (!strcmp(identifier, "scope") ||
993 !strcmp(identifier, "header")) {
6e1bc0df 994 found_item = true;
c42c79ea
PP
995 if (bt_value_array_append_string(names,
996 identifier)) {
997 goto error;
998 }
6e1bc0df
MD
999 } else if (!strcmp(identifier, "all")) {
1000 found_all = true;
1001 if (bt_value_array_append_string(names,
1002 identifier)) {
c42c79ea
PP
1003 goto error;
1004 }
6e1bc0df
MD
1005 } else if (!strcmp(identifier, "none")) {
1006 found_none = true;
c42c79ea
PP
1007 if (bt_value_array_append_string(names,
1008 identifier)) {
1009 goto error;
1010 }
c42c79ea 1011 } else {
6d1d5711 1012 printf_err("Unknown field name: `%s`\n",
c42c79ea
PP
1013 identifier);
1014 goto error;
1015 }
1016 break;
1017 }
1018 case G_TOKEN_COMMA:
1019 continue;
1020 case G_TOKEN_EOF:
1021 goto end;
1022 default:
1023 goto error;
1024 }
1025 }
1026
6e1bc0df
MD
1027end:
1028 if (found_none && found_all) {
6d1d5711 1029 printf_err("Only either `all` or `none` can be specified in the list given to the --names option, but not both.\n");
6e1bc0df
MD
1030 goto error;
1031 }
1032 /*
1033 * Legacy behavior is to clear the defaults (show none) when at
1034 * least one item is specified.
1035 */
1036 if (found_item && !found_none && !found_all) {
1037 if (bt_value_array_append_string(names, "none")) {
1038 goto error;
1039 }
1040 }
1041 if (scanner) {
1042 g_scanner_destroy(scanner);
1043 }
1044 return names;
c42c79ea
PP
1045
1046error:
1047 BT_PUT(names);
c42c79ea
PP
1048 if (scanner) {
1049 g_scanner_destroy(scanner);
1050 }
1051 return names;
1052}
1053
1054
1055/*
1056 * Converts a comma-delimited list of known fields (--fields option) to
1057 * an array value object containing those fields as string
1058 * value objects.
1059 *
1060 * Return value is owned by the caller.
1061 */
1062static
1063struct bt_value *fields_from_arg(const char *arg)
1064{
1065 GScanner *scanner = NULL;
1066 struct bt_value *fields;
1067
1068 fields = bt_value_array_create();
1069 if (!fields) {
1070 print_err_oom();
1071 goto error;
1072 }
1073
1074 scanner = create_csv_identifiers_scanner();
1075 if (!scanner) {
1076 print_err_oom();
1077 goto error;
1078 }
1079
1080 g_scanner_input_text(scanner, arg, strlen(arg));
1081
1082 while (true) {
1083 GTokenType token_type = g_scanner_get_next_token(scanner);
1084
1085 switch (token_type) {
1086 case G_TOKEN_IDENTIFIER:
1087 {
1088 const char *identifier = scanner->value.v_identifier;
1089
1090 if (!strcmp(identifier, "trace") ||
1091 !strcmp(identifier, "trace:hostname") ||
1092 !strcmp(identifier, "trace:domain") ||
1093 !strcmp(identifier, "trace:procname") ||
1094 !strcmp(identifier, "trace:vpid") ||
1095 !strcmp(identifier, "loglevel") ||
1096 !strcmp(identifier, "emf") ||
6e1bc0df
MD
1097 !strcmp(identifier, "callsite") ||
1098 !strcmp(identifier, "all")) {
c42c79ea
PP
1099 if (bt_value_array_append_string(fields,
1100 identifier)) {
1101 goto error;
1102 }
c42c79ea 1103 } else {
6d1d5711 1104 printf_err("Unknown field name: `%s`\n",
c42c79ea
PP
1105 identifier);
1106 goto error;
1107 }
1108 break;
1109 }
1110 case G_TOKEN_COMMA:
1111 continue;
1112 case G_TOKEN_EOF:
1113 goto end;
1114 default:
1115 goto error;
1116 }
1117 }
1118
1119 goto end;
1120
1121error:
1122 BT_PUT(fields);
1123
1124end:
1125 if (scanner) {
1126 g_scanner_destroy(scanner);
1127 }
1128 return fields;
1129}
1130
1131/*
1132 * Inserts the equivalent "prefix-name" true boolean value objects into
1133 * map_obj where the names are in array_obj.
1134 */
1135static
1136int insert_flat_names_fields_from_array(struct bt_value *map_obj,
1137 struct bt_value *array_obj, const char *prefix)
1138{
1139 int ret = 0;
1140 int i;
6e1bc0df 1141 GString *tmpstr = NULL, *default_value = NULL;
c42c79ea
PP
1142
1143 /*
1144 * array_obj may be NULL if no CLI options were specified to
1145 * trigger its creation.
1146 */
1147 if (!array_obj) {
1148 goto end;
1149 }
1150
1151 tmpstr = g_string_new(NULL);
1152 if (!tmpstr) {
1153 print_err_oom();
1154 ret = -1;
1155 goto end;
1156 }
1157
6e1bc0df
MD
1158 default_value = g_string_new(NULL);
1159 if (!default_value) {
1160 print_err_oom();
1161 ret = -1;
1162 goto end;
1163 }
1164
c42c79ea
PP
1165 for (i = 0; i < bt_value_array_size(array_obj); i++) {
1166 struct bt_value *str_obj = bt_value_array_get(array_obj, i);
1167 const char *suffix;
6e1bc0df 1168 bool is_default = false;
c42c79ea
PP
1169
1170 if (!str_obj) {
1171 printf_err("Unexpected error\n");
1172 ret = -1;
1173 goto end;
1174 }
1175
1176 ret = bt_value_string_get(str_obj, &suffix);
1177 BT_PUT(str_obj);
1178 if (ret) {
1179 printf_err("Unexpected error\n");
1180 goto end;
1181 }
1182
1183 g_string_assign(tmpstr, prefix);
1184 g_string_append(tmpstr, "-");
6e1bc0df
MD
1185
1186 /* Special-case for "all" and "none". */
1187 if (!strcmp(suffix, "all")) {
1188 is_default = true;
1189 g_string_assign(default_value, "show");
1190 } else if (!strcmp(suffix, "none")) {
1191 is_default = true;
1192 g_string_assign(default_value, "hide");
1193 }
1194 if (is_default) {
1195 g_string_append(tmpstr, "default");
1196 ret = map_insert_string_or_null(map_obj,
1197 tmpstr->str,
1198 default_value);
1199 if (ret) {
1200 print_err_oom();
1201 goto end;
1202 }
1203 } else {
1204 g_string_append(tmpstr, suffix);
1205 ret = bt_value_map_insert_bool(map_obj, tmpstr->str,
1206 true);
1207 if (ret) {
1208 print_err_oom();
1209 goto end;
1210 }
c42c79ea
PP
1211 }
1212 }
1213
1214end:
6e1bc0df
MD
1215 if (default_value) {
1216 g_string_free(default_value, TRUE);
1217 }
c42c79ea
PP
1218 if (tmpstr) {
1219 g_string_free(tmpstr, TRUE);
1220 }
1221
1222 return ret;
1223}
1224
c42c79ea
PP
1225/*
1226 * Returns the parameters (map value object) corresponding to the
1227 * legacy text format options.
1228 *
1229 * Return value is owned by the caller.
1230 */
1231static
1232struct bt_value *params_from_text_legacy_opts(
1233 struct text_legacy_opts *text_legacy_opts)
1234{
1235 struct bt_value *params;
1236
1237 params = bt_value_map_create();
1238 if (!params) {
1239 print_err_oom();
1240 goto error;
1241 }
1242
1243 if (map_insert_string_or_null(params, "output-path",
1244 text_legacy_opts->output)) {
1245 print_err_oom();
1246 goto error;
1247 }
1248
1249 if (map_insert_string_or_null(params, "debug-info-dir",
1250 text_legacy_opts->dbg_info_dir)) {
1251 print_err_oom();
1252 goto error;
1253 }
1254
1255 if (map_insert_string_or_null(params, "debug-info-target-prefix",
1256 text_legacy_opts->dbg_info_target_prefix)) {
1257 print_err_oom();
1258 goto error;
1259 }
1260
1261 if (bt_value_map_insert_bool(params, "debug-info-full-path",
1262 text_legacy_opts->dbg_info_full_path)) {
1263 print_err_oom();
1264 goto error;
1265 }
1266
1267 if (bt_value_map_insert_bool(params, "no-delta",
1268 text_legacy_opts->no_delta)) {
1269 print_err_oom();
1270 goto error;
1271 }
1272
1273 if (bt_value_map_insert_bool(params, "clock-cycles",
1274 text_legacy_opts->clock_cycles)) {
1275 print_err_oom();
1276 goto error;
1277 }
1278
1279 if (bt_value_map_insert_bool(params, "clock-seconds",
1280 text_legacy_opts->clock_seconds)) {
1281 print_err_oom();
1282 goto error;
1283 }
1284
1285 if (bt_value_map_insert_bool(params, "clock-date",
1286 text_legacy_opts->clock_date)) {
1287 print_err_oom();
1288 goto error;
1289 }
1290
1291 if (bt_value_map_insert_bool(params, "clock-gmt",
1292 text_legacy_opts->clock_gmt)) {
1293 print_err_oom();
1294 goto error;
1295 }
1296
1297 if (insert_flat_names_fields_from_array(params,
1298 text_legacy_opts->names, "name")) {
1299 goto error;
1300 }
1301
1302 if (insert_flat_names_fields_from_array(params,
1303 text_legacy_opts->fields, "field")) {
1304 goto error;
1305 }
1306
1307 goto end;
1308
1309error:
1310 BT_PUT(params);
1311
1312end:
1313 return params;
1314}
1315
1316static
1317int append_sinks_from_legacy_opts(GPtrArray *sinks,
1318 enum legacy_output_format legacy_output_format,
1319 struct text_legacy_opts *text_legacy_opts)
1320{
1321 int ret = 0;
1322 struct bt_value *params = NULL;
1323 const char *plugin_name;
1324 const char *component_name;
1325 struct bt_config_component *bt_config_component = NULL;
1326
1327 switch (legacy_output_format) {
1328 case LEGACY_OUTPUT_FORMAT_TEXT:
1329 plugin_name = "text";
1330 component_name = "text";
1331 break;
1332 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1333 plugin_name = "ctf";
1334 component_name = "metadata-text";
1335 break;
1336 case LEGACY_OUTPUT_FORMAT_DUMMY:
1337 plugin_name = "dummy";
1338 component_name = "dummy";
1339 break;
1340 default:
1341 assert(false);
1342 break;
1343 }
1344
1345 if (legacy_output_format == LEGACY_OUTPUT_FORMAT_TEXT) {
1346 /* Legacy "text" output format has parameters */
1347 params = params_from_text_legacy_opts(text_legacy_opts);
1348 if (!params) {
1349 goto error;
1350 }
1351 } else {
1352 /*
1353 * Legacy "dummy" and "ctf-metadata" output formats do
1354 * not have parameters.
1355 */
1356 params = bt_value_map_create();
1357 if (!params) {
1358 print_err_oom();
1359 goto error;
1360 }
1361 }
1362
1363 /* Create a component configuration */
1364 bt_config_component = bt_config_component_create(plugin_name,
bdc61c70 1365 component_name);
c42c79ea
PP
1366 if (!bt_config_component) {
1367 goto error;
1368 }
1369
bdc61c70
PP
1370 BT_MOVE(bt_config_component->params, params);
1371
c42c79ea
PP
1372 /* Move created component configuration to the array */
1373 g_ptr_array_add(sinks, bt_config_component);
1374
1375 goto end;
1376
1377error:
1378 ret = -1;
1379
1380end:
1381 BT_PUT(params);
1382
1383 return ret;
1384}
1385
1386/*
1387 * Returns the parameters (map value object) corresponding to the
1388 * given legacy CTF format options.
1389 *
1390 * Return value is owned by the caller.
1391 */
1392static
1393struct bt_value *params_from_ctf_legacy_opts(
1394 struct ctf_legacy_opts *ctf_legacy_opts)
1395{
1396 struct bt_value *params;
1397
1398 params = bt_value_map_create();
1399 if (!params) {
1400 print_err_oom();
1401 goto error;
1402 }
1403
1404 if (bt_value_map_insert_integer(params, "offset-s",
1405 ctf_legacy_opts->offset_s.value)) {
1406 print_err_oom();
1407 goto error;
1408 }
1409
1410 if (bt_value_map_insert_integer(params, "offset-ns",
1411 ctf_legacy_opts->offset_ns.value)) {
1412 print_err_oom();
1413 goto error;
1414 }
1415
1416 if (bt_value_map_insert_bool(params, "stream-intersection",
1417 ctf_legacy_opts->stream_intersection)) {
1418 print_err_oom();
1419 goto error;
1420 }
1421
1422 goto end;
1423
1424error:
1425 BT_PUT(params);
1426
1427end:
1428 return params;
1429}
1430
1431static
1432int append_sources_from_legacy_opts(GPtrArray *sources,
1433 enum legacy_input_format legacy_input_format,
1434 struct ctf_legacy_opts *ctf_legacy_opts,
528debdf 1435 struct bt_value *legacy_input_paths)
c42c79ea
PP
1436{
1437 int ret = 0;
1438 int i;
1439 struct bt_value *base_params;
1440 struct bt_value *params = NULL;
1441 struct bt_value *input_path = NULL;
1442 struct bt_value *input_path_copy = NULL;
1443 const char *input_key;
1444 const char *component_name;
1445
1446 switch (legacy_input_format) {
1447 case LEGACY_INPUT_FORMAT_CTF:
1448 input_key = "path";
1449 component_name = "fs";
1450 break;
1451 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
1452 input_key = "url";
1453 component_name = "lttng-live";
1454 break;
1455 default:
1456 assert(false);
1457 break;
1458 }
1459
1460 base_params = params_from_ctf_legacy_opts(ctf_legacy_opts);
1461 if (!base_params) {
1462 goto error;
1463 }
1464
1465 for (i = 0; i < bt_value_array_size(legacy_input_paths); i++) {
1466 struct bt_config_component *bt_config_component = NULL;
1467
1468 /* Copy base parameters as current parameters */
1469 params = bt_value_copy(base_params);
1470 if (!params) {
1471 goto error;
1472 }
1473
1474 /* Get current input path string value object */
1475 input_path = bt_value_array_get(legacy_input_paths, i);
1476 if (!input_path) {
1477 goto error;
1478 }
1479
1480 /* Copy current input path value object */
1481 input_path_copy = bt_value_copy(input_path);
1482 if (!input_path_copy) {
1483 goto error;
1484 }
1485
1486 /* Insert input path value object into current parameters */
1487 ret = bt_value_map_insert(params, input_key, input_path_copy);
1488 if (ret) {
1489 goto error;
1490 }
1491
1492 /* Create a component configuration */
1493 bt_config_component = bt_config_component_create("ctf",
bdc61c70 1494 component_name);
c42c79ea
PP
1495 if (!bt_config_component) {
1496 goto error;
1497 }
1498
bdc61c70
PP
1499 BT_MOVE(bt_config_component->params, params);
1500
c42c79ea
PP
1501 /* Move created component configuration to the array */
1502 g_ptr_array_add(sources, bt_config_component);
1503
1504 /* Put current stuff */
1505 BT_PUT(input_path);
1506 BT_PUT(input_path_copy);
c42c79ea
PP
1507 }
1508
1509 goto end;
1510
1511error:
1512 ret = -1;
1513
1514end:
1515 BT_PUT(base_params);
1516 BT_PUT(params);
1517 BT_PUT(input_path);
1518 BT_PUT(input_path_copy);
1519 return ret;
1520}
1521
1522/*
1523 * Escapes a string for the shell. The string is escaped knowing that
1524 * it's a parameter string value (double-quoted), and that it will be
1525 * entered between single quotes in the shell.
1526 *
1527 * Return value is owned by the caller.
1528 */
1529static
1530char *str_shell_escape(const char *input)
1531{
1532 char *ret = NULL;
1533 const char *at = input;
1534 GString *str = g_string_new(NULL);
1535
1536 if (!str) {
1537 goto end;
1538 }
1539
1540 while (*at != '\0') {
1541 switch (*at) {
1542 case '\\':
1543 g_string_append(str, "\\\\");
1544 break;
1545 case '"':
1546 g_string_append(str, "\\\"");
1547 break;
1548 case '\'':
1549 g_string_append(str, "'\"'\"'");
1550 break;
1551 case '\n':
1552 g_string_append(str, "\\n");
1553 break;
1554 case '\t':
1555 g_string_append(str, "\\t");
1556 break;
1557 default:
1558 g_string_append_c(str, *at);
1559 break;
1560 }
1561
1562 at++;
1563 }
1564
1565end:
1566 if (str) {
1567 ret = str->str;
1568 g_string_free(str, FALSE);
1569 }
1570
1571 return ret;
1572}
1573
1574static
1575int append_prefixed_flag_params(GString *str, struct bt_value *flags,
1576 const char *prefix)
1577{
1578 int ret = 0;
1579 int i;
1580
1581 if (!flags) {
1582 goto end;
1583 }
1584
1585 for (i = 0; i < bt_value_array_size(flags); i++) {
1586 struct bt_value *value = bt_value_array_get(flags, i);
1587 const char *flag;
1588
1589 if (!value) {
1590 ret = -1;
1591 goto end;
1592 }
1593
1594 if (bt_value_string_get(value, &flag)) {
1595 BT_PUT(value);
1596 ret = -1;
1597 goto end;
1598 }
1599
1600 g_string_append_printf(str, ",%s-%s=true", prefix, flag);
1601 BT_PUT(value);
1602 }
1603
1604end:
1605 return ret;
1606}
1607
1608/*
1609 * Appends a boolean parameter string.
1610 */
1611static
1612void g_string_append_bool_param(GString *str, const char *name, bool value)
1613{
1614 g_string_append_printf(str, ",%s=%s", name, value ? "true" : "false");
1615}
1616
1617/*
1618 * Appends a path parameter string, or null if it's empty.
1619 */
1620static
1621int g_string_append_string_path_param(GString *str, const char *name,
1622 GString *path)
1623{
1624 int ret = 0;
1625
1626 if (path->len > 0) {
1627 char *escaped_path = str_shell_escape(path->str);
1628
1629 if (!escaped_path) {
1630 print_err_oom();
1631 goto error;
1632 }
1633
1634 g_string_append_printf(str, "%s=\"%s\"", name, escaped_path);
1635 free(escaped_path);
1636 } else {
1637 g_string_append_printf(str, "%s=null", name);
1638 }
1639
1640 goto end;
1641
1642error:
1643 ret = -1;
1644
1645end:
1646 return ret;
1647}
1648
1649/*
1650 * Prints the non-legacy sink options equivalent to the specified
1651 * legacy output format options.
1652 */
1653static
1654void print_output_legacy_to_sinks(
1655 enum legacy_output_format legacy_output_format,
1656 struct text_legacy_opts *text_legacy_opts)
1657{
1658 const char *input_format;
1659 GString *str = NULL;
1660
1661 str = g_string_new(" ");
1662 if (!str) {
1663 print_err_oom();
1664 goto end;
1665 }
1666
1667 switch (legacy_output_format) {
1668 case LEGACY_OUTPUT_FORMAT_TEXT:
1669 input_format = "text";
1670 break;
1671 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1672 input_format = "ctf-metadata";
1673 break;
1674 case LEGACY_OUTPUT_FORMAT_DUMMY:
1675 input_format = "dummy";
1676 break;
1677 default:
1678 assert(false);
1679 }
1680
49849a47 1681 printf_err("Both `%s` legacy output format and non-legacy sink component\ninstances(s) specified.\n\n",
c42c79ea 1682 input_format);
49849a47 1683 printf_err("Specify the following non-legacy sink component instance instead of the\nlegacy `%s` output format options:\n\n",
c42c79ea
PP
1684 input_format);
1685 g_string_append(str, "-o ");
1686
1687 switch (legacy_output_format) {
1688 case LEGACY_OUTPUT_FORMAT_TEXT:
1689 g_string_append(str, "text.text");
1690 break;
1691 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1692 g_string_append(str, "ctf.metadata-text");
1693 break;
1694 case LEGACY_OUTPUT_FORMAT_DUMMY:
1695 g_string_append(str, "dummy.dummy");
1696 break;
1697 default:
1698 assert(false);
1699 }
1700
1701 if (legacy_output_format == LEGACY_OUTPUT_FORMAT_TEXT &&
1702 text_legacy_opts_is_any_set(text_legacy_opts)) {
1703 int ret;
1704
bdc61c70 1705 g_string_append(str, " -p '");
c42c79ea
PP
1706
1707 if (g_string_append_string_path_param(str, "output-path",
1708 text_legacy_opts->output)) {
1709 goto end;
1710 }
1711
1712 g_string_append(str, ",");
1713
1714 if (g_string_append_string_path_param(str, "debug-info-dir",
1715 text_legacy_opts->dbg_info_dir)) {
1716 goto end;
1717 }
1718
1719 g_string_append(str, ",");
1720
1721 if (g_string_append_string_path_param(str,
1722 "debug-info-target-prefix",
1723 text_legacy_opts->dbg_info_target_prefix)) {
1724 goto end;
1725 }
1726
1727 g_string_append_bool_param(str, "no-delta",
1728 text_legacy_opts->no_delta);
1729 g_string_append_bool_param(str, "clock-cycles",
1730 text_legacy_opts->clock_cycles);
1731 g_string_append_bool_param(str, "clock-seconds",
1732 text_legacy_opts->clock_seconds);
1733 g_string_append_bool_param(str, "clock-date",
1734 text_legacy_opts->clock_date);
1735 g_string_append_bool_param(str, "clock-gmt",
1736 text_legacy_opts->clock_gmt);
1737 ret = append_prefixed_flag_params(str, text_legacy_opts->names,
1738 "name");
1739 if (ret) {
1740 goto end;
1741 }
1742
1743 ret = append_prefixed_flag_params(str, text_legacy_opts->fields,
1744 "field");
1745 if (ret) {
1746 goto end;
1747 }
1748
1749 /* Remove last comma and close single quote */
1750 g_string_append(str, "'");
1751 }
1752
1753 printf_err("%s\n\n", str->str);
1754
1755end:
1756 if (str) {
1757 g_string_free(str, TRUE);
1758 }
1759 return;
1760}
1761
1762/*
1763 * Prints the non-legacy source options equivalent to the specified
1764 * legacy input format options.
1765 */
1766static
1767void print_input_legacy_to_sources(enum legacy_input_format legacy_input_format,
1768 struct bt_value *legacy_input_paths,
1769 struct ctf_legacy_opts *ctf_legacy_opts)
1770{
1771 const char *input_format;
1772 GString *str = NULL;
1773 int i;
1774
1775 str = g_string_new(" ");
1776 if (!str) {
1777 print_err_oom();
1778 goto end;
1779 }
1780
1781 switch (legacy_input_format) {
1782 case LEGACY_INPUT_FORMAT_CTF:
1783 input_format = "ctf";
1784 break;
1785 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
1786 input_format = "lttng-live";
1787 break;
1788 default:
1789 assert(false);
1790 }
1791
49849a47 1792 printf_err("Both `%s` legacy input format and non-legacy source component\ninstance(s) specified.\n\n",
c42c79ea 1793 input_format);
49849a47 1794 printf_err("Specify the following non-legacy source component instance(s) instead of the\nlegacy `%s` input format options and positional arguments:\n\n",
c42c79ea
PP
1795 input_format);
1796
1797 for (i = 0; i < bt_value_array_size(legacy_input_paths); i++) {
1798 struct bt_value *input_value =
1799 bt_value_array_get(legacy_input_paths, i);
1800 const char *input = NULL;
1801 char *escaped_input;
1802 int ret;
1803
1804 assert(input_value);
1805 ret = bt_value_string_get(input_value, &input);
1806 BT_PUT(input_value);
1807 assert(!ret && input);
1808 escaped_input = str_shell_escape(input);
1809 if (!escaped_input) {
1810 print_err_oom();
1811 goto end;
1812 }
1813
1814 g_string_append(str, "-i ctf.");
1815
1816 switch (legacy_input_format) {
1817 case LEGACY_INPUT_FORMAT_CTF:
bdc61c70 1818 g_string_append(str, "fs -p 'path=\"");
c42c79ea
PP
1819 break;
1820 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
bdc61c70 1821 g_string_append(str, "lttng-live -p 'url=\"");
c42c79ea
PP
1822 break;
1823 default:
1824 assert(false);
1825 }
1826
1827 g_string_append(str, escaped_input);
1828 g_string_append(str, "\"");
1829 g_string_append_printf(str, ",offset-s=%" PRId64,
1830 ctf_legacy_opts->offset_s.value);
1831 g_string_append_printf(str, ",offset-ns=%" PRId64,
1832 ctf_legacy_opts->offset_ns.value);
1833 g_string_append_bool_param(str, "stream-intersection",
1834 ctf_legacy_opts->stream_intersection);
1835 g_string_append(str, "' ");
1836 g_free(escaped_input);
1837 }
1838
1839 printf_err("%s\n\n", str->str);
1840
1841end:
1842 if (str) {
1843 g_string_free(str, TRUE);
1844 }
1845 return;
1846}
1847
1848/*
1849 * Validates a given configuration, with optional legacy input and
1850 * output formats options. Prints useful error messages if anything
1851 * is wrong.
1852 *
1853 * Returns true when the configuration is valid.
1854 */
1855static
1856bool validate_cfg(struct bt_config *cfg,
1857 enum legacy_input_format *legacy_input_format,
1858 enum legacy_output_format *legacy_output_format,
1859 struct bt_value *legacy_input_paths,
1860 struct ctf_legacy_opts *ctf_legacy_opts,
bd9f0fa2 1861 struct text_legacy_opts *text_legacy_opts)
c42c79ea
PP
1862{
1863 bool legacy_input = false;
1864 bool legacy_output = false;
1865
1866 /* Determine if the input and output should be legacy-style */
1867 if (*legacy_input_format != LEGACY_INPUT_FORMAT_NONE ||
c42c79ea
PP
1868 !bt_value_array_is_empty(legacy_input_paths) ||
1869 ctf_legacy_opts_is_any_set(ctf_legacy_opts)) {
1870 legacy_input = true;
1871 }
1872
1873 if (*legacy_output_format != LEGACY_OUTPUT_FORMAT_NONE ||
c42c79ea
PP
1874 text_legacy_opts_is_any_set(text_legacy_opts)) {
1875 legacy_output = true;
1876 }
1877
1878 if (legacy_input) {
1879 /* If no legacy input format was specified, default to CTF */
1880 if (*legacy_input_format == LEGACY_INPUT_FORMAT_NONE) {
1881 *legacy_input_format = LEGACY_INPUT_FORMAT_CTF;
1882 }
1883
1884 /* Make sure at least one input path exists */
1885 if (bt_value_array_is_empty(legacy_input_paths)) {
1886 switch (*legacy_input_format) {
1887 case LEGACY_INPUT_FORMAT_CTF:
6d1d5711 1888 printf_err("No input path specified for legacy `ctf` input format\n");
c42c79ea
PP
1889 break;
1890 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
6d1d5711 1891 printf_err("No URL specified for legacy `lttng-live` input format\n");
c42c79ea
PP
1892 break;
1893 default:
1894 assert(false);
1895 }
1896 goto error;
1897 }
1898
1899 /* Make sure no non-legacy sources are specified */
290725f7 1900 if (cfg->cmd_data.convert.sources->len != 0) {
c42c79ea
PP
1901 print_input_legacy_to_sources(*legacy_input_format,
1902 legacy_input_paths, ctf_legacy_opts);
1903 goto error;
1904 }
1905 }
1906
1907 if (legacy_output) {
1908 /*
1909 * If no legacy output format was specified, default to
1910 * "text".
1911 */
1912 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_NONE) {
1913 *legacy_output_format = LEGACY_OUTPUT_FORMAT_TEXT;
1914 }
1915
1916 /*
1917 * If any "text" option was specified, the output must be
1918 * legacy "text".
1919 */
1920 if (text_legacy_opts_is_any_set(text_legacy_opts) &&
1921 *legacy_output_format !=
1922 LEGACY_OUTPUT_FORMAT_TEXT) {
6d1d5711 1923 printf_err("Options for legacy `text` output format specified with a different legacy output format\n");
c42c79ea
PP
1924 goto error;
1925 }
1926
1927 /* Make sure no non-legacy sinks are specified */
290725f7 1928 if (cfg->cmd_data.convert.sinks->len != 0) {
c42c79ea
PP
1929 print_output_legacy_to_sinks(*legacy_output_format,
1930 text_legacy_opts);
1931 goto error;
1932 }
1933 }
1934
1935 /*
1936 * If the output is the legacy "ctf-metadata" format, then the
1937 * input should be the legacy "ctf" input format.
1938 */
1939 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_CTF_METADATA &&
1940 *legacy_input_format != LEGACY_INPUT_FORMAT_CTF) {
6d1d5711 1941 printf_err("Legacy `ctf-metadata` output format requires using legacy `ctf` input format\n");
c42c79ea
PP
1942 goto error;
1943 }
1944
1945 return true;
1946
1947error:
1948 return false;
1949}
1950
1951/*
1952 * Parses a 64-bit signed integer.
1953 *
1954 * Returns a negative value if anything goes wrong.
1955 */
1956static
1957int parse_int64(const char *arg, int64_t *val)
1958{
1959 char *endptr;
1960
1961 errno = 0;
1962 *val = strtoll(arg, &endptr, 0);
1963 if (*endptr != '\0' || arg == endptr || errno != 0) {
1964 return -1;
1965 }
1966
1967 return 0;
1968}
1969
1970/* popt options */
1971enum {
1972 OPT_NONE = 0,
b07ffa28 1973 OPT_BASE_PARAMS,
0f9915c6 1974 OPT_BEGIN,
c42c79ea
PP
1975 OPT_CLOCK_CYCLES,
1976 OPT_CLOCK_DATE,
1977 OPT_CLOCK_FORCE_CORRELATE,
1978 OPT_CLOCK_GMT,
1979 OPT_CLOCK_OFFSET,
1980 OPT_CLOCK_OFFSET_NS,
1981 OPT_CLOCK_SECONDS,
1982 OPT_DEBUG,
1983 OPT_DEBUG_INFO_DIR,
1984 OPT_DEBUG_INFO_FULL_PATH,
1985 OPT_DEBUG_INFO_TARGET_PREFIX,
0f9915c6 1986 OPT_END,
c42c79ea
PP
1987 OPT_FIELDS,
1988 OPT_HELP,
c42c79ea
PP
1989 OPT_INPUT_FORMAT,
1990 OPT_LIST,
1991 OPT_NAMES,
1992 OPT_NO_DELTA,
290725f7
PP
1993 OPT_OMIT_HOME_PLUGIN_PATH,
1994 OPT_OMIT_SYSTEM_PLUGIN_PATH,
c42c79ea
PP
1995 OPT_OUTPUT_FORMAT,
1996 OPT_OUTPUT_PATH,
bdc61c70 1997 OPT_PARAMS,
290725f7 1998 OPT_PATH,
c42c79ea 1999 OPT_PLUGIN_PATH,
b07ffa28 2000 OPT_RESET_BASE_PARAMS,
c42c79ea
PP
2001 OPT_SINK,
2002 OPT_SOURCE,
2003 OPT_STREAM_INTERSECTION,
0f9915c6 2004 OPT_TIMERANGE,
c42c79ea 2005 OPT_VERBOSE,
c42c79ea
PP
2006};
2007
2008/*
2009 * Sets the value of a given legacy offset option and marks it as set.
2010 */
2011static void set_offset_value(struct offset_opt *offset_opt, int64_t value)
2012{
2013 offset_opt->value = value;
2014 offset_opt->is_set = true;
2015}
2016
bdc61c70
PP
2017enum bt_config_component_dest {
2018 BT_CONFIG_COMPONENT_DEST_SOURCE,
2019 BT_CONFIG_COMPONENT_DEST_SINK,
2020};
2021
2022/*
2023 * Adds a configuration component to the appropriate configuration
2024 * array depending on the destination.
2025 */
2026static void add_cfg_comp(struct bt_config *cfg,
2027 struct bt_config_component *cfg_comp,
2028 enum bt_config_component_dest dest)
2029{
2030 if (dest == BT_CONFIG_COMPONENT_DEST_SOURCE) {
290725f7 2031 g_ptr_array_add(cfg->cmd_data.convert.sources, cfg_comp);
bdc61c70 2032 } else {
290725f7 2033 g_ptr_array_add(cfg->cmd_data.convert.sinks, cfg_comp);
bdc61c70
PP
2034 }
2035}
2036
0f9915c6
MD
2037static int split_timerange(const char *arg, const char **begin, const char **end)
2038{
2039 const char *c;
2040
2041 /* Try to match [begin,end] */
2042 c = strchr(arg, '[');
2043 if (!c)
2044 goto skip;
2045 *begin = ++c;
2046 c = strchr(c, ',');
2047 if (!c)
2048 goto skip;
2049 *end = ++c;
2050 c = strchr(c, ']');
2051 if (!c)
2052 goto skip;
2053 goto found;
2054
2055skip:
2056 /* Try to match begin,end */
2057 c = arg;
2058 *begin = c;
2059 c = strchr(c, ',');
2060 if (!c)
2061 goto not_found;
2062 *end = ++c;
2063 /* fall-through */
2064found:
2065 return 0;
2066not_found:
2067 return -1;
2068}
2069
290725f7 2070static int append_env_var_plugin_paths(struct bt_value *plugin_paths)
5a3ee633
PP
2071{
2072 int ret = 0;
2073 const char *envvar;
2074
2075 if (bt_common_is_setuid_setgid()) {
2076 printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n");
2077 goto end;
2078 }
2079
2080 envvar = getenv("BABELTRACE_PLUGIN_PATH");
2081 if (!envvar) {
2082 goto end;
2083 }
2084
290725f7 2085 ret = bt_config_append_plugin_paths(plugin_paths, envvar);
5a3ee633
PP
2086
2087end:
2088 return ret;
2089}
2090
290725f7
PP
2091static int append_home_and_system_plugin_paths(struct bt_value *plugin_paths,
2092 bool omit_system_plugin_path, bool omit_home_plugin_path)
98ecef32 2093{
1670bffd 2094 int ret;
98ecef32 2095
290725f7 2096 if (!omit_home_plugin_path) {
1670bffd 2097 if (bt_common_is_setuid_setgid()) {
5a3ee633 2098 printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n");
98ecef32 2099 } else {
1670bffd
PP
2100 char *home_plugin_dir =
2101 bt_common_get_home_plugin_path();
2102
2103 if (home_plugin_dir) {
290725f7
PP
2104 ret = bt_config_append_plugin_paths(
2105 plugin_paths,
1670bffd
PP
2106 home_plugin_dir);
2107 free(home_plugin_dir);
2108
2109 if (ret) {
98ecef32
MD
2110 printf_err("Invalid home plugin path\n");
2111 goto error;
2112 }
2113 }
2114 }
2115 }
2116
290725f7
PP
2117 if (!omit_system_plugin_path) {
2118 if (bt_config_append_plugin_paths(plugin_paths,
1670bffd 2119 bt_common_get_system_plugin_path())) {
98ecef32
MD
2120 printf_err("Invalid system plugin path\n");
2121 goto error;
2122 }
2123 }
2124 return 0;
2125error:
2126 return -1;
2127}
2128
bd9f0fa2
MD
2129static int append_sources_from_implicit_params(GPtrArray *sources,
2130 struct bt_config_component *implicit_source_comp)
2131{
2132 size_t i;
2133 size_t len = sources->len;
2134
2135 for (i = 0; i < len; i++) {
2136 struct bt_config_component *comp;
2137 struct bt_value *params_to_set;
2138
2139 comp = g_ptr_array_index(sources, i);
2140 params_to_set = bt_value_map_extend(comp->params,
2141 implicit_source_comp->params);
2142 if (!params_to_set) {
2143 printf_err("Cannot extend legacy component parameters with non-legacy parameters\n");
2144 goto error;
2145 }
2146 BT_MOVE(comp->params, params_to_set);
2147 }
2148 return 0;
2149error:
2150 return -1;
2151}
2152
290725f7 2153static struct bt_config *bt_config_base_create(enum bt_config_command command)
c1870f57
JG
2154{
2155 struct bt_config *cfg;
2156
2157 /* Create config */
2158 cfg = g_new0(struct bt_config, 1);
2159 if (!cfg) {
2160 print_err_oom();
2161 goto error;
2162 }
2163
2164 bt_object_init(cfg, bt_config_destroy);
290725f7
PP
2165 cfg->command = command;
2166 goto end;
c1870f57 2167
290725f7
PP
2168error:
2169 BT_PUT(cfg);
c1870f57 2170
c1870f57
JG
2171end:
2172 return cfg;
c1870f57
JG
2173}
2174
290725f7
PP
2175static struct bt_config *bt_config_convert_create(
2176 struct bt_value *initial_plugin_paths)
c42c79ea 2177{
290725f7 2178 struct bt_config *cfg;
c42c79ea 2179
290725f7
PP
2180 /* Create config */
2181 cfg = bt_config_base_create(BT_CONFIG_COMMAND_CONVERT);
2182 if (!cfg) {
c42c79ea
PP
2183 print_err_oom();
2184 goto error;
2185 }
2186
290725f7
PP
2187 cfg->cmd_data.convert.sources = g_ptr_array_new_with_free_func(
2188 (GDestroyNotify) bt_put);
2189 if (!cfg->cmd_data.convert.sources) {
c42c79ea
PP
2190 print_err_oom();
2191 goto error;
2192 }
2193
290725f7
PP
2194 cfg->cmd_data.convert.sinks = g_ptr_array_new_with_free_func(
2195 (GDestroyNotify) bt_put);
2196 if (!cfg->cmd_data.convert.sinks) {
c42c79ea
PP
2197 print_err_oom();
2198 goto error;
2199 }
2200
290725f7
PP
2201 if (initial_plugin_paths) {
2202 cfg->cmd_data.convert.plugin_paths =
2203 bt_get(initial_plugin_paths);
2204 } else {
2205 cfg->cmd_data.convert.plugin_paths = bt_value_array_create();
2206 if (!cfg->cmd_data.convert.plugin_paths) {
2207 print_err_oom();
2208 goto error;
2209 }
b07ffa28
PP
2210 }
2211
290725f7 2212 goto end;
c42c79ea 2213
290725f7
PP
2214error:
2215 BT_PUT(cfg);
2216
2217end:
2218 return cfg;
2219}
2220
2221static struct bt_config *bt_config_list_plugins_create(
2222 struct bt_value *initial_plugin_paths)
2223{
2224 struct bt_config *cfg;
2225
2226 /* Create config */
2227 cfg = bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS);
2228 if (!cfg) {
2229 print_err_oom();
5a3ee633
PP
2230 goto error;
2231 }
2232
290725f7
PP
2233 if (initial_plugin_paths) {
2234 cfg->cmd_data.list_plugins.plugin_paths =
2235 bt_get(initial_plugin_paths);
65582340 2236 } else {
290725f7
PP
2237 cfg->cmd_data.list_plugins.plugin_paths =
2238 bt_value_array_create();
2239 if (!cfg->cmd_data.list_plugins.plugin_paths) {
2240 print_err_oom();
2241 goto error;
2242 }
c42c79ea
PP
2243 }
2244
290725f7
PP
2245 goto end;
2246
2247error:
2248 BT_PUT(cfg);
2249
2250end:
2251 return cfg;
2252}
2253
2254/*
2255 * Prints the list-plugins command usage.
2256 */
2257static
2258void print_list_plugins_usage(FILE *fp)
2259{
2260 fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] list-plugins [OPTIONS]\n");
2261 fprintf(fp, "\n");
2262 fprintf(fp, "Options:\n");
2263 fprintf(fp, "\n");
2264 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
2265 fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n");
2266 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
2267 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
2268 fprintf(fp, " dynamic plugins can be loaded\n");
2269 fprintf(fp, " -h --help Show this help and quit\n");
2270 fprintf(fp, "\n");
2271 fprintf(fp, "See `babeltrace --help` for the list of general options.\n");
2272}
2273
2274static struct poptOption list_plugins_long_options[] = {
2275 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
2276 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
2277 { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL },
2278 { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL },
2279 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
2280 { NULL, 0, 0, NULL, 0, NULL, NULL },
2281};
2282
2283/*
2284 * Creates a Babeltrace config object from the arguments of a
2285 * list-plugins command.
2286 *
2287 * *retcode is set to the appropriate exit code to use.
2288 */
2289struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[],
2290 int *retcode, bool omit_system_plugin_path,
2291 bool omit_home_plugin_path,
2292 struct bt_value *initial_plugin_paths)
2293{
2294 poptContext pc = NULL;
2295 char *arg = NULL;
2296 int opt;
2297 int ret;
2298 struct bt_config *cfg = NULL;
2299 const char *leftover;
2300
2301 *retcode = 0;
2302 cfg = bt_config_list_plugins_create(initial_plugin_paths);
2303 if (!cfg) {
2304 print_err_oom();
2305 goto error;
2306 }
2307
2308 cfg->cmd_data.list_plugins.omit_system_plugin_path = omit_system_plugin_path;
2309 cfg->cmd_data.list_plugins.omit_home_plugin_path = omit_home_plugin_path;
2310 ret = append_env_var_plugin_paths(
2311 cfg->cmd_data.list_plugins.plugin_paths);
2312 if (ret) {
2313 printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n");
2314 goto error;
2315 }
2316
2317 /* Parse options */
2318 pc = poptGetContext(NULL, argc, (const char **) argv,
2319 list_plugins_long_options, 0);
2320 if (!pc) {
2321 printf_err("Cannot get popt context\n");
2322 goto error;
2323 }
2324
2325 poptReadDefaultConfig(pc, 0);
c42c79ea
PP
2326
2327 while ((opt = poptGetNextOpt(pc)) > 0) {
2328 arg = poptGetOptArg(pc);
2329
2330 switch (opt) {
2331 case OPT_PLUGIN_PATH:
5a3ee633
PP
2332 if (bt_common_is_setuid_setgid()) {
2333 printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n");
98ecef32 2334 } else {
290725f7
PP
2335 if (bt_config_append_plugin_paths(
2336 cfg->cmd_data.list_plugins.plugin_paths,
2337 arg)) {
2703153b
PP
2338 printf_err("Invalid --plugin-path option's argument:\n %s\n",
2339 arg);
98ecef32
MD
2340 goto error;
2341 }
c42c79ea
PP
2342 }
2343 break;
98ecef32 2344 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
290725f7 2345 cfg->cmd_data.list_plugins.omit_system_plugin_path = true;
98ecef32
MD
2346 break;
2347 case OPT_OMIT_HOME_PLUGIN_PATH:
290725f7
PP
2348 cfg->cmd_data.list_plugins.omit_home_plugin_path = true;
2349 break;
2350 case OPT_HELP:
2351 print_list_plugins_usage(stdout);
2352 *retcode = -1;
2353 BT_PUT(cfg);
2354 goto end;
2355 default:
2356 printf_err("Unknown command-line option specified (option code %d)\n",
2357 opt);
2358 goto error;
2359 }
2360
2361 free(arg);
2362 arg = NULL;
2363 }
2364
2365 /* Check for option parsing error */
2366 if (opt < -1) {
2367 printf_err("While parsing command-line options, at option %s: %s\n",
2368 poptBadOption(pc, 0), poptStrerror(opt));
2369 goto error;
2370 }
2371
2372 leftover = poptGetArg(pc);
2373 if (leftover) {
2374 printf_err("Invalid argument: %s\n", leftover);
2375 goto error;
2376 }
2377
2378 if (append_home_and_system_plugin_paths(
2379 cfg->cmd_data.list_plugins.plugin_paths,
2380 cfg->cmd_data.list_plugins.omit_system_plugin_path,
2381 cfg->cmd_data.list_plugins.omit_home_plugin_path)) {
2382 printf_err("Cannot append home and system plugin paths\n");
2383 goto error;
2384 }
2385
2386 goto end;
2387
2388error:
2389 *retcode = 1;
2390 BT_PUT(cfg);
2391
2392end:
2393 if (pc) {
2394 poptFreeContext(pc);
2395 }
2396
2397 free(arg);
2398 return cfg;
2399}
2400
2401
2402/*
2403 * Prints the legacy, Babeltrace 1.x command usage. Those options are
2404 * still compatible in Babeltrace 2.x, but it is recommended to use
2405 * the more generic plugin/component parameters instead of those
2406 * hard-coded option names.
2407 */
2408static
2409void print_legacy_usage(FILE *fp)
2410{
2411 fprintf(fp, "Usage: babeltrace [OPTIONS] INPUT...\n");
2412 fprintf(fp, "\n");
2413 fprintf(fp, "The following options are compatible with the Babeltrace 1.x options:\n");
2414 fprintf(fp, "\n");
2415 fprintf(fp, " --clock-force-correlate Assume that clocks are inherently correlated\n");
2416 fprintf(fp, " across traces\n");
2417 fprintf(fp, " -d, --debug Enable debug mode\n");
2418 fprintf(fp, " -i, --input-format=FORMAT Input trace format (default: ctf)\n");
2419 fprintf(fp, " -l, --list List available formats\n");
2420 fprintf(fp, " -o, --output-format=FORMAT Output trace format (default: text)\n");
2421 fprintf(fp, " -v, --verbose Enable verbose output\n");
2422 fprintf(fp, " --help-legacy Show this help and quit\n");
2423 fprintf(fp, " -V, --version Show version and quit\n");
2424 fprintf(fp, "\n");
2425 fprintf(fp, " Available input formats: ctf, lttng-live, ctf-metadata\n");
2426 fprintf(fp, " Available output formats: text, dummy\n");
2427 fprintf(fp, "\n");
2428 fprintf(fp, "Input formats specific options:\n");
2429 fprintf(fp, "\n");
2430 fprintf(fp, " INPUT... Input trace file(s), directory(ies), or URLs\n");
2431 fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n");
2432 fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS nanoseconds\n");
2433 fprintf(fp, " --stream-intersection Only process events when all streams are active\n");
2434 fprintf(fp, "\n");
2435 fprintf(fp, "text output format specific options:\n");
2436 fprintf(fp, " \n");
2437 fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n");
2438 fprintf(fp, " --clock-date Print timestamp dates\n");
2439 fprintf(fp, " --clock-gmt Print and parse timestamps in GMT time zone\n");
2440 fprintf(fp, " (default: local time zone)\n");
2441 fprintf(fp, " --clock-seconds Print the timestamps as [SEC.NS]\n");
2442 fprintf(fp, " (default format: [HH:MM:SS.NS])\n");
2443 fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n");
2444 fprintf(fp, " (default: `/usr/lib/debug`)\n");
2445 fprintf(fp, " --debug-info-full-path Show full debug info source and binary paths\n");
2446 fprintf(fp, " --debug-info-target-prefix=DIR Use directory DIR as a prefix when looking\n");
2447 fprintf(fp, " up executables during debug info analysis\n");
2448 fprintf(fp, " (default: `/usr/lib/debug`)\n");
2449 fprintf(fp, " -f, --fields=NAME[,NAME]... Print additional fields:\n");
2450 fprintf(fp, " all, trace, trace:hostname, trace:domain,\n");
2451 fprintf(fp, " trace:procname, trace:vpid, loglevel, emf\n");
2452 fprintf(fp, " (default: trace:hostname, trace:procname,\n");
2453 fprintf(fp, " trace:vpid)\n");
2454 fprintf(fp, " -n, --names=NAME[,NAME]... Print field names:\n");
2455 fprintf(fp, " payload (or arg or args)\n");
2456 fprintf(fp, " none, all, scope, header, context (or ctx)\n");
2457 fprintf(fp, " (default: payload, context)\n");
2458 fprintf(fp, " --no-delta Do not print time delta between consecutive\n");
2459 fprintf(fp, " events\n");
2460 fprintf(fp, " -w, --output=PATH Write output to PATH (default: standard output)\n");
2461}
2462
2463/*
2464 * Prints the convert command usage.
2465 */
2466static
2467void print_convert_usage(FILE *fp)
2468{
2469 fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] convert [OPTIONS]\n");
2470 fprintf(fp, "\n");
2471 fprintf(fp, "Options:\n");
2472 fprintf(fp, "\n");
2473 fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n");
2474 fprintf(fp, " for the following component instances\n");
2475 fprintf(fp, " (see the expected format of PARAMS below)\n");
2476 fprintf(fp, " --begin=BEGIN Set the `begin` parameter of the latest\n");
2477 fprintf(fp, " source component instance to BEGIN\n");
2478 fprintf(fp, " (see the suggested format of BEGIN below)\n");
2479 fprintf(fp, " -d, --debug Enable debug mode\n");
2480 fprintf(fp, " --end=END Set the `end` parameter of the latest\n");
2481 fprintf(fp, " source component instance to END\n");
2482 fprintf(fp, " (see the suggested format of BEGIN below)\n");
2483 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
2484 fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n");
2485 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
2486 fprintf(fp, " -p, --params=PARAMS Set the parameters of the latest component\n");
2487 fprintf(fp, " instance (in command-line order) to PARAMS\n");
2488 fprintf(fp, " (see the expected format of PARAMS below)\n");
2489 fprintf(fp, " -P, --path=PATH Set the `path` parameter of the latest\n");
2490 fprintf(fp, " component instance to PATH\n");
2491 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
2492 fprintf(fp, " dynamic plugins can be loaded\n");
2493 fprintf(fp, " -r, --reset-base-params Reset the current base parameters of the\n");
2494 fprintf(fp, " following source and sink component\n");
2495 fprintf(fp, " instances to an empty map\n");
2496 fprintf(fp, " -o, --sink=PLUGIN.COMPCLS Instantiate a sink component from plugin\n");
2497 fprintf(fp, " PLUGIN and component class COMPCLS (may be\n");
2498 fprintf(fp, " repeated)\n");
2499 fprintf(fp, " -i, --source=PLUGIN.COMPCLS Instantiate a source component from plugin\n");
2500 fprintf(fp, " PLUGIN and component class COMPCLS (may be\n");
2501 fprintf(fp, " repeated)\n");
2502 fprintf(fp, " --timerange=TIMERANGE Set time range to TIMERANGE: BEGIN,END or\n");
2503 fprintf(fp, " [BEGIN,END] (literally `[` and `]`)\n");
2504 fprintf(fp, " (suggested format of BEGIN/END below)\n");
2505 fprintf(fp, " -v, --verbose Enable verbose output\n");
2506 fprintf(fp, " -h --help Show this help and quit\n");
2507 fprintf(fp, "\n");
2508 fprintf(fp, "See `babeltrace --help` for the list of general options.\n");
2509 fprintf(fp, "\n\n");
2510 fprintf(fp, "Suggested format of BEGIN and END\n");
2511 fprintf(fp, "---------------------------------\n");
2512 fprintf(fp, "\n");
2513 fprintf(fp, " [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n");
2514 fprintf(fp, "\n\n");
2515 fprintf(fp, "Expected format of PARAMS\n");
2516 fprintf(fp, "-------------------------\n");
2517 fprintf(fp, "\n");
2518 fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n");
2519 fprintf(fp, "\n");
2520 fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n");
2521 fprintf(fp, "where PARAM is the parameter name (C identifier plus [:.-] characters), and\n");
2522 fprintf(fp, "VALUE can be one of:\n");
2523 fprintf(fp, "\n");
2524 fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n");
2525 fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n");
2526 fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n");
2527 fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n");
2528 fprintf(fp, " (`0x` prefix) signed 64-bit integer.\n");
2529 fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n");
2530 fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n");
2531 fprintf(fp, " the null and boolean value symbols above.\n");
2532 fprintf(fp, "* Double-quoted string (accepts escape characters).\n");
2533 fprintf(fp, "\n");
2534 fprintf(fp, "Whitespaces are allowed around individual `=` and `,` tokens.\n");
2535 fprintf(fp, "\n");
2536 fprintf(fp, "Example:\n");
2537 fprintf(fp, "\n");
2538 fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n");
2539 fprintf(fp, " observe=3.14, simple=beef, needs-quotes=\"some string\",\n");
2540 fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\"\n");
2541 fprintf(fp, "\n");
2542 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run babeltrace\n");
2543 fprintf(fp, "from a shell.\n");
2544}
2545
2546static struct poptOption convert_long_options[] = {
2547 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
2548 { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL },
2549 { "begin", '\0', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL },
2550 { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL },
2551 { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL },
2552 { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL },
2553 { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL },
2554 { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL },
2555 { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL },
2556 { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL },
2557 { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL },
2558 { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL },
2559 { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL },
2560 { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL },
2561 { "end", '\0', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL },
2562 { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL },
2563 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
2564 { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL },
2565 { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL },
2566 { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL },
2567 { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL },
2568 { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL },
2569 { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT_PATH, NULL, NULL },
2570 { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL },
2571 { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL },
2572 { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL },
2573 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
2574 { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL },
2575 { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL },
2576 { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL },
2577 { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL },
2578 { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL },
2579 { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL },
2580 { NULL, 0, 0, NULL, 0, NULL, NULL },
2581};
2582
2583/*
2584 * Creates a Babeltrace config object from the arguments of a convert
2585 * command.
2586 *
2587 * *retcode is set to the appropriate exit code to use.
2588 */
2589struct bt_config *bt_config_convert_from_args(int argc, const char *argv[],
2590 int *retcode, bool omit_system_plugin_path,
2591 bool omit_home_plugin_path,
2592 struct bt_value *initial_plugin_paths)
2593{
2594 poptContext pc = NULL;
2595 char *arg = NULL;
2596 struct ctf_legacy_opts ctf_legacy_opts;
2597 struct text_legacy_opts text_legacy_opts;
2598 enum legacy_input_format legacy_input_format = LEGACY_INPUT_FORMAT_NONE;
2599 enum legacy_output_format legacy_output_format =
2600 LEGACY_OUTPUT_FORMAT_NONE;
2601 struct bt_value *legacy_input_paths = NULL;
2602 struct bt_config_component *implicit_source_comp = NULL;
2603 struct bt_config_component *cur_cfg_comp = NULL;
2604 bool cur_is_implicit_source = false;
2605 bool use_implicit_source = false;
2606 enum bt_config_component_dest cur_cfg_comp_dest =
2607 BT_CONFIG_COMPONENT_DEST_SOURCE;
2608 struct bt_value *cur_base_params = NULL;
2609 int opt, ret = 0;
2610 struct bt_config *cfg = NULL;
2611
2612 *retcode = 0;
2613 memset(&ctf_legacy_opts, 0, sizeof(ctf_legacy_opts));
2614 memset(&text_legacy_opts, 0, sizeof(text_legacy_opts));
2615
2616 if (argc <= 1) {
2617 print_convert_usage(stdout);
2618 *retcode = -1;
2619 goto end;
2620 }
2621
2622 cfg = bt_config_convert_create(initial_plugin_paths);
2623 if (!cfg) {
2624 print_err_oom();
2625 goto error;
2626 }
2627
2628 cfg->cmd_data.convert.omit_system_plugin_path = omit_system_plugin_path;
2629 cfg->cmd_data.convert.omit_home_plugin_path = omit_home_plugin_path;
2630 text_legacy_opts.output = g_string_new(NULL);
2631 if (!text_legacy_opts.output) {
2632 print_err_oom();
2633 goto error;
2634 }
2635
2636 text_legacy_opts.dbg_info_dir = g_string_new(NULL);
2637 if (!text_legacy_opts.dbg_info_dir) {
2638 print_err_oom();
2639 goto error;
2640 }
2641
2642 text_legacy_opts.dbg_info_target_prefix = g_string_new(NULL);
2643 if (!text_legacy_opts.dbg_info_target_prefix) {
2644 print_err_oom();
2645 goto error;
2646 }
2647
2648 cur_base_params = bt_value_map_create();
2649 if (!cur_base_params) {
2650 print_err_oom();
2651 goto error;
2652 }
2653
2654 legacy_input_paths = bt_value_array_create();
2655 if (!legacy_input_paths) {
2656 print_err_oom();
2657 goto error;
2658 }
2659
2660 ret = append_env_var_plugin_paths(cfg->cmd_data.convert.plugin_paths);
2661 if (ret) {
2662 printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n");
2663 goto error;
2664 }
2665
2666 /* Note: implicit source never gets positional base params. */
2667 implicit_source_comp = bt_config_component_from_arg(DEFAULT_SOURCE_COMPONENT_NAME);
2668 if (implicit_source_comp) {
2669 cur_cfg_comp = implicit_source_comp;
2670 cur_is_implicit_source = true;
2671 use_implicit_source = true;
2672 } else {
2703153b 2673 printf_debug("Cannot find implicit source plugin `%s`",
290725f7
PP
2674 DEFAULT_SOURCE_COMPONENT_NAME);
2675 }
2676
2677 /* Parse options */
2678 pc = poptGetContext(NULL, argc, (const char **) argv,
2679 convert_long_options, 0);
2680 if (!pc) {
2681 printf_err("Cannot get popt context\n");
2682 goto error;
2683 }
2684
2685 poptReadDefaultConfig(pc, 0);
2686
2687 while ((opt = poptGetNextOpt(pc)) > 0) {
2688 arg = poptGetOptArg(pc);
2689
2690 switch (opt) {
2691 case OPT_PLUGIN_PATH:
2692 if (bt_common_is_setuid_setgid()) {
2693 printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n");
2694 } else {
2695 if (bt_config_append_plugin_paths(
2696 cfg->cmd_data.convert.plugin_paths,
2697 arg)) {
2703153b
PP
2698 printf_err("Invalid --plugin-path option's argument:\n %s\n",
2699 arg);
290725f7
PP
2700 goto error;
2701 }
2702 }
2703 break;
2704 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
2705 cfg->cmd_data.convert.omit_system_plugin_path = true;
2706 break;
2707 case OPT_OMIT_HOME_PLUGIN_PATH:
2708 cfg->cmd_data.convert.omit_home_plugin_path = true;
98ecef32 2709 break;
c42c79ea
PP
2710 case OPT_OUTPUT_PATH:
2711 if (text_legacy_opts.output->len > 0) {
2712 printf_err("Duplicate --output option\n");
2713 goto error;
2714 }
2715
2716 g_string_assign(text_legacy_opts.output, arg);
2717 break;
2718 case OPT_DEBUG_INFO_DIR:
2719 if (text_legacy_opts.dbg_info_dir->len > 0) {
2720 printf_err("Duplicate --debug-info-dir option\n");
2721 goto error;
2722 }
2723
2724 g_string_assign(text_legacy_opts.dbg_info_dir, arg);
2725 break;
2726 case OPT_DEBUG_INFO_TARGET_PREFIX:
2727 if (text_legacy_opts.dbg_info_target_prefix->len > 0) {
2728 printf_err("Duplicate --debug-info-target-prefix option\n");
2729 goto error;
2730 }
2731
2732 g_string_assign(text_legacy_opts.dbg_info_target_prefix, arg);
2733 break;
2734 case OPT_INPUT_FORMAT:
2735 case OPT_SOURCE:
2736 {
c42c79ea
PP
2737 if (opt == OPT_INPUT_FORMAT) {
2738 if (!strcmp(arg, "ctf")) {
2739 /* Legacy CTF input format */
2740 if (legacy_input_format) {
2741 print_err_dup_legacy_input();
2742 goto error;
2743 }
2744
2745 legacy_input_format =
2746 LEGACY_INPUT_FORMAT_CTF;
2747 break;
2748 } else if (!strcmp(arg, "lttng-live")) {
2749 /* Legacy LTTng-live input format */
2750 if (legacy_input_format) {
2751 print_err_dup_legacy_input();
2752 goto error;
2753 }
2754
2755 legacy_input_format =
2756 LEGACY_INPUT_FORMAT_LTTNG_LIVE;
2757 break;
2758 }
2759 }
2760
65582340
MD
2761 use_implicit_source = false;
2762
c42c79ea 2763 /* Non-legacy: try to create a component config */
65582340 2764 if (cur_cfg_comp && !cur_is_implicit_source) {
bdc61c70
PP
2765 add_cfg_comp(cfg, cur_cfg_comp,
2766 cur_cfg_comp_dest);
2767 }
2768
2769 cur_cfg_comp = bt_config_component_from_arg(arg);
2770 if (!cur_cfg_comp) {
49849a47 2771 printf_err("Invalid format for --source option's argument:\n %s\n",
c42c79ea
PP
2772 arg);
2773 goto error;
2774 }
65582340 2775 cur_is_implicit_source = false;
c42c79ea 2776
b07ffa28
PP
2777 assert(cur_base_params);
2778 bt_put(cur_cfg_comp->params);
c9313318 2779 cur_cfg_comp->params = bt_value_copy(cur_base_params);
bd9f0fa2 2780 if (!cur_cfg_comp->params) {
c9313318 2781 print_err_oom();
290725f7 2782 goto error;
c9313318
PP
2783 }
2784
bdc61c70 2785 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE;
c42c79ea
PP
2786 break;
2787 }
2788 case OPT_OUTPUT_FORMAT:
2789 case OPT_SINK:
2790 {
c42c79ea
PP
2791 if (opt == OPT_OUTPUT_FORMAT) {
2792 if (!strcmp(arg, "text")) {
2793 /* Legacy CTF-text output format */
2794 if (legacy_output_format) {
2795 print_err_dup_legacy_output();
2796 goto error;
2797 }
2798
2799 legacy_output_format =
2800 LEGACY_OUTPUT_FORMAT_TEXT;
2801 break;
2802 } else if (!strcmp(arg, "dummy")) {
2803 /* Legacy dummy output format */
2804 if (legacy_output_format) {
2805 print_err_dup_legacy_output();
2806 goto error;
2807 }
2808
2809 legacy_output_format =
2810 LEGACY_OUTPUT_FORMAT_DUMMY;
2811 break;
2812 } else if (!strcmp(arg, "ctf-metadata")) {
2813 /* Legacy CTF-metadata output format */
2814 if (legacy_output_format) {
2815 print_err_dup_legacy_output();
2816 goto error;
2817 }
2818
2819 legacy_output_format =
2820 LEGACY_OUTPUT_FORMAT_CTF_METADATA;
2821 break;
2822 }
2823 }
2824
2825 /* Non-legacy: try to create a component config */
65582340 2826 if (cur_cfg_comp && !cur_is_implicit_source) {
bdc61c70
PP
2827 add_cfg_comp(cfg, cur_cfg_comp,
2828 cur_cfg_comp_dest);
2829 }
2830
2831 cur_cfg_comp = bt_config_component_from_arg(arg);
2832 if (!cur_cfg_comp) {
49849a47 2833 printf_err("Invalid format for --sink option's argument:\n %s\n",
c42c79ea
PP
2834 arg);
2835 goto error;
2836 }
65582340 2837 cur_is_implicit_source = false;
c42c79ea 2838
b07ffa28
PP
2839 assert(cur_base_params);
2840 bt_put(cur_cfg_comp->params);
c9313318 2841 cur_cfg_comp->params = bt_value_copy(cur_base_params);
bd9f0fa2 2842 if (!cur_cfg_comp->params) {
c9313318 2843 print_err_oom();
290725f7 2844 goto error;
c9313318
PP
2845 }
2846
bdc61c70 2847 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK;
bdc61c70
PP
2848 break;
2849 }
2850 case OPT_PARAMS:
2851 {
2852 struct bt_value *params;
b07ffa28 2853 struct bt_value *params_to_set;
bdc61c70
PP
2854
2855 if (!cur_cfg_comp) {
2703153b
PP
2856 printf_err("Cannot add parameters to unavailable default source component `%s`:\n %s\n",
2857 DEFAULT_SOURCE_COMPONENT_NAME, arg);
bdc61c70
PP
2858 goto error;
2859 }
2860
bdc61c70
PP
2861 params = bt_value_from_arg(arg);
2862 if (!params) {
2863 printf_err("Invalid format for --params option's argument:\n %s\n",
2864 arg);
2865 goto error;
2866 }
2867
560ff91c 2868 params_to_set = bt_value_map_extend(cur_cfg_comp->params,
b07ffa28
PP
2869 params);
2870 BT_PUT(params);
2871 if (!params_to_set) {
560ff91c 2872 printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n",
b07ffa28
PP
2873 arg);
2874 goto error;
2875 }
2876
2877 BT_MOVE(cur_cfg_comp->params, params_to_set);
c42c79ea
PP
2878 break;
2879 }
ad6a19bd
PP
2880 case OPT_PATH:
2881 if (!cur_cfg_comp) {
2703153b
PP
2882 printf_err("Cannot add `path` parameter to unavailable default source component `%s`:\n %s\n",
2883 DEFAULT_SOURCE_COMPONENT_NAME, arg);
ad6a19bd
PP
2884 goto error;
2885 }
2886
2887 assert(cur_cfg_comp->params);
2888
2889 if (bt_value_map_insert_string(cur_cfg_comp->params,
2890 "path", arg)) {
2891 print_err_oom();
2892 goto error;
2893 }
2894 break;
b07ffa28
PP
2895 case OPT_BASE_PARAMS:
2896 {
2897 struct bt_value *params = bt_value_from_arg(arg);
2898
2899 if (!params) {
2900 printf_err("Invalid format for --base-params option's argument:\n %s\n",
2901 arg);
2902 goto error;
2903 }
2904
2905 BT_MOVE(cur_base_params, params);
2906 break;
2907 }
2908 case OPT_RESET_BASE_PARAMS:
2909 BT_PUT(cur_base_params);
2910 cur_base_params = bt_value_map_create();
2911 if (!cur_base_params) {
2912 print_err_oom();
2913 goto error;
2914 }
2915 break;
c42c79ea
PP
2916 case OPT_NAMES:
2917 if (text_legacy_opts.names) {
2918 printf_err("Duplicate --names option\n");
2919 goto error;
2920 }
2921
2922 text_legacy_opts.names = names_from_arg(arg);
2923 if (!text_legacy_opts.names) {
2703153b
PP
2924 printf_err("Invalid --names option's argument:\n %s\n",
2925 arg);
c42c79ea
PP
2926 goto error;
2927 }
2928 break;
2929 case OPT_FIELDS:
2930 if (text_legacy_opts.fields) {
2931 printf_err("Duplicate --fields option\n");
2932 goto error;
2933 }
2934
2935 text_legacy_opts.fields = fields_from_arg(arg);
2936 if (!text_legacy_opts.fields) {
2703153b
PP
2937 printf_err("Invalid --fields option's argument:\n %s\n",
2938 arg);
c42c79ea
PP
2939 goto error;
2940 }
2941 break;
2942 case OPT_NO_DELTA:
2943 text_legacy_opts.no_delta = true;
2944 break;
2945 case OPT_CLOCK_CYCLES:
2946 text_legacy_opts.clock_cycles = true;
2947 break;
2948 case OPT_CLOCK_SECONDS:
2949 text_legacy_opts.clock_seconds = true;
2950 break;
2951 case OPT_CLOCK_DATE:
2952 text_legacy_opts.clock_date = true;
2953 break;
2954 case OPT_CLOCK_GMT:
2955 text_legacy_opts.clock_gmt = true;
2956 break;
2957 case OPT_DEBUG_INFO_FULL_PATH:
2958 text_legacy_opts.dbg_info_full_path = true;
2959 break;
2960 case OPT_CLOCK_OFFSET:
2961 {
2962 int64_t val;
2963
2964 if (ctf_legacy_opts.offset_s.is_set) {
2965 printf_err("Duplicate --clock-offset option\n");
2966 goto error;
2967 }
2968
2969 if (parse_int64(arg, &val)) {
2703153b
PP
2970 printf_err("Invalid --clock-offset option's argument:\n %s\n",
2971 arg);
c42c79ea
PP
2972 goto error;
2973 }
2974
2975 set_offset_value(&ctf_legacy_opts.offset_s, val);
2976 break;
2977 }
2978 case OPT_CLOCK_OFFSET_NS:
2979 {
2980 int64_t val;
2981
2982 if (ctf_legacy_opts.offset_ns.is_set) {
2983 printf_err("Duplicate --clock-offset-ns option\n");
2984 goto error;
2985 }
2986
2987 if (parse_int64(arg, &val)) {
2703153b
PP
2988 printf_err("Invalid --clock-offset-ns option's argument:\n %s\n",
2989 arg);
c42c79ea
PP
2990 goto error;
2991 }
2992
2993 set_offset_value(&ctf_legacy_opts.offset_ns, val);
2994 break;
2995 }
2996 case OPT_STREAM_INTERSECTION:
2997 ctf_legacy_opts.stream_intersection = true;
2998 break;
2999 case OPT_CLOCK_FORCE_CORRELATE:
290725f7 3000 cfg->cmd_data.convert.force_correlate = true;
c42c79ea 3001 break;
0f9915c6
MD
3002 case OPT_BEGIN:
3003 if (!cur_cfg_comp) {
2703153b
PP
3004 printf_err("Cannot add `begin` parameter to unavailable default source component `%s`:\n %s\n",
3005 DEFAULT_SOURCE_COMPONENT_NAME, arg);
0f9915c6
MD
3006 goto error;
3007 }
3008 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2703153b
PP
3009 printf_err("--begin option must follow a --source option:\n %s\n",
3010 arg);
0f9915c6
MD
3011 goto error;
3012 }
3013 if (bt_value_map_insert_string(cur_cfg_comp->params,
3014 "begin", arg)) {
3015 print_err_oom();
3016 goto error;
3017 }
3018 break;
3019 case OPT_END:
3020 if (!cur_cfg_comp) {
2703153b
PP
3021 printf_err("Cannot add `end` parameter to unavailable default source component `%s`:\n %s\n",
3022 DEFAULT_SOURCE_COMPONENT_NAME, arg);
0f9915c6
MD
3023 goto error;
3024 }
3025 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2703153b
PP
3026 printf_err("--end option must follow a --source option:\n %s\n",
3027 arg);
0f9915c6
MD
3028 goto error;
3029 }
3030 if (bt_value_map_insert_string(cur_cfg_comp->params,
3031 "end", arg)) {
3032 print_err_oom();
3033 goto error;
3034 }
3035 break;
3036 case OPT_TIMERANGE:
3037 {
3038 const char *begin, *end;
3039
3040 if (!cur_cfg_comp) {
2703153b
PP
3041 printf_err("Cannot add `begin` and `end` parameters to unavailable default source component `%s`:\n %s\n",
3042 DEFAULT_SOURCE_COMPONENT_NAME, arg);
0f9915c6
MD
3043 goto error;
3044 }
3045 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2703153b
PP
3046 printf_err("--timerange option must follow a --source option:\n %s\n",
3047 arg);
0f9915c6
MD
3048 goto error;
3049 }
3050 if (split_timerange(arg, &begin, &end)) {
2703153b
PP
3051 printf_err("Invalid --timerange format: expecting BEGIN,END or [BEGIN,END]:\n %s\n",
3052 arg);
0f9915c6
MD
3053 goto error;
3054 }
3055 if (bt_value_map_insert_string(cur_cfg_comp->params,
3056 "begin", begin)) {
3057 print_err_oom();
3058 goto error;
3059 }
3060 if (bt_value_map_insert_string(cur_cfg_comp->params,
3061 "end", end)) {
3062 print_err_oom();
3063 goto error;
3064 }
3065 break;
3066 }
c42c79ea 3067 case OPT_HELP:
290725f7
PP
3068 print_convert_usage(stdout);
3069 *retcode = -1;
3070 BT_PUT(cfg);
c42c79ea
PP
3071 goto end;
3072 case OPT_VERBOSE:
3073 cfg->verbose = true;
3074 break;
3075 case OPT_DEBUG:
3076 cfg->debug = true;
3077 break;
3078 default:
3079 printf_err("Unknown command-line option specified (option code %d)\n",
3080 opt);
3081 goto error;
3082 }
3083
3084 free(arg);
3085 arg = NULL;
3086 }
3087
3088 /* Check for option parsing error */
3089 if (opt < -1) {
3090 printf_err("While parsing command-line options, at option %s: %s\n",
3091 poptBadOption(pc, 0), poptStrerror(opt));
3092 goto error;
3093 }
3094
3095 /* Consume leftover arguments as legacy input paths */
3096 while (true) {
3097 const char *input_path = poptGetArg(pc);
3098
3099 if (!input_path) {
3100 break;
3101 }
3102
3103 if (bt_value_array_append_string(legacy_input_paths,
3104 input_path)) {
3105 print_err_oom();
3106 goto error;
3107 }
3108 }
3109
290725f7
PP
3110 if (append_home_and_system_plugin_paths(
3111 cfg->cmd_data.convert.plugin_paths,
3112 cfg->cmd_data.convert.omit_system_plugin_path,
3113 cfg->cmd_data.convert.omit_home_plugin_path)) {
3114 printf_err("Cannot append home and system plugin paths\n");
6cf24c61
MD
3115 goto error;
3116 }
3117
3118 /* Append current component configuration, if any */
3119 if (cur_cfg_comp && !cur_is_implicit_source) {
3120 add_cfg_comp(cfg, cur_cfg_comp, cur_cfg_comp_dest);
3121 }
3122 cur_cfg_comp = NULL;
3123
c42c79ea
PP
3124 /* Validate legacy/non-legacy options */
3125 if (!validate_cfg(cfg, &legacy_input_format, &legacy_output_format,
3126 legacy_input_paths, &ctf_legacy_opts,
bd9f0fa2 3127 &text_legacy_opts)) {
c42c79ea
PP
3128 printf_err("Command-line options form an invalid configuration\n");
3129 goto error;
3130 }
3131
3132 /*
3133 * If there's a legacy input format, convert it to source
3134 * component configurations.
3135 */
3136 if (legacy_input_format) {
290725f7
PP
3137 if (append_sources_from_legacy_opts(
3138 cfg->cmd_data.convert.sources,
c42c79ea 3139 legacy_input_format, &ctf_legacy_opts,
528debdf 3140 legacy_input_paths)) {
49849a47 3141 printf_err("Cannot convert legacy input format options to source component instance(s)\n");
c42c79ea
PP
3142 goto error;
3143 }
290725f7
PP
3144 if (append_sources_from_implicit_params(
3145 cfg->cmd_data.convert.sources,
bd9f0fa2
MD
3146 implicit_source_comp)) {
3147 printf_err("Cannot initialize legacy component parameters\n");
3148 goto error;
3149 }
3150 use_implicit_source = false;
3151 } else {
3152 if (use_implicit_source) {
3153 add_cfg_comp(cfg, implicit_source_comp,
3154 BT_CONFIG_COMPONENT_DEST_SOURCE);
3155 implicit_source_comp = NULL;
3156 } else {
3157 if (implicit_source_comp
3158 && !bt_value_map_is_empty(implicit_source_comp->params)) {
3159 printf_err("Arguments specified for implicit source, but an explicit source has been specified, overriding it\n");
3160 goto error;
3161 }
3162 }
c42c79ea
PP
3163 }
3164
3165 /*
3166 * If there's a legacy output format, convert it to sink
3167 * component configurations.
3168 */
3169 if (legacy_output_format) {
290725f7 3170 if (append_sinks_from_legacy_opts(cfg->cmd_data.convert.sinks,
c42c79ea 3171 legacy_output_format, &text_legacy_opts)) {
49849a47 3172 printf_err("Cannot convert legacy output format options to sink component instance(s)\n");
c42c79ea
PP
3173 goto error;
3174 }
3175 }
3176
290725f7 3177 if (cfg->cmd_data.convert.sinks->len == 0) {
bd9f0fa2
MD
3178 /* Use implicit sink as default. */
3179 cur_cfg_comp = bt_config_component_from_arg(DEFAULT_SINK_COMPONENT_NAME);
3180 if (!cur_cfg_comp) {
2703153b 3181 printf_error("Cannot find implicit sink plugin `%s`\n",
bd9f0fa2
MD
3182 DEFAULT_SINK_COMPONENT_NAME);
3183 }
3184 add_cfg_comp(cfg, cur_cfg_comp,
3185 BT_CONFIG_COMPONENT_DEST_SINK);
3186 cur_cfg_comp = NULL;
3187 }
3188
c42c79ea
PP
3189 goto end;
3190
3191error:
290725f7
PP
3192 *retcode = 1;
3193 BT_PUT(cfg);
3194
c42c79ea
PP
3195end:
3196 if (pc) {
3197 poptFreeContext(pc);
3198 }
3199
3200 if (text_legacy_opts.output) {
3201 g_string_free(text_legacy_opts.output, TRUE);
3202 }
3203
3204 if (text_legacy_opts.dbg_info_dir) {
3205 g_string_free(text_legacy_opts.dbg_info_dir, TRUE);
3206 }
3207
3208 if (text_legacy_opts.dbg_info_target_prefix) {
3209 g_string_free(text_legacy_opts.dbg_info_target_prefix, TRUE);
3210 }
3211
3212 free(arg);
65582340 3213 BT_PUT(implicit_source_comp);
bdc61c70 3214 BT_PUT(cur_cfg_comp);
b07ffa28 3215 BT_PUT(cur_base_params);
c42c79ea
PP
3216 BT_PUT(text_legacy_opts.names);
3217 BT_PUT(text_legacy_opts.fields);
3218 BT_PUT(legacy_input_paths);
290725f7
PP
3219 return cfg;
3220}
3221
3222/*
3223 * Prints the Babeltrace 2.x general usage.
3224 */
3225static
3226void print_gen_usage(FILE *fp)
3227{
3228 fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] [COMMAND] [COMMAND OPTIONS]\n");
3229 fprintf(fp, "\n");
3230 fprintf(fp, "General options:\n");
3231 fprintf(fp, "\n");
3232 fprintf(fp, " -d, --debug Enable debug mode\n");
3233 fprintf(fp, " -h --help Show this help and quit\n");
3234 fprintf(fp, " --help-legacy Show Babeltrace 1.x legacy help and quit\n");
3235 fprintf(fp, " -v, --verbose Enable verbose output\n");
3236 fprintf(fp, " -V, --version Show version and quit\n");
3237 fprintf(fp, "\n");
3238 fprintf(fp, "Available commands:\n");
3239 fprintf(fp, "\n");
3240 fprintf(fp, " convert Build a trace conversion graph and run it (default)\n");
3241 fprintf(fp, " list-plugins List available plugins and their content\n");
3242 fprintf(fp, "\n");
3243 fprintf(fp, "Use `babeltrace COMMAND --help` to show the help of COMMAND.\n");
3244}
3245
3246struct bt_config *bt_config_from_args(int argc, const char *argv[],
3247 int *retcode, bool omit_system_plugin_path,
3248 bool omit_home_plugin_path,
3249 struct bt_value *initial_plugin_paths)
3250{
3251 struct bt_config *config = NULL;
3252 bool verbose = false;
3253 bool debug = false;
3254 int i;
3255 enum bt_config_command command = -1;
3256 const char **command_argv = NULL;
3257 int command_argc = -1;
3258 const char *command_name = NULL;
3259
3260 *retcode = -1;
3261
3262 if (argc <= 1) {
3263 print_gen_usage(stdout);
3264 goto end;
3265 }
3266
3267 for (i = 1; i < argc; i++) {
3268 const char *cur_arg = argv[i];
3269
3270 if (strcmp(cur_arg, "-d") == 0 ||
3271 strcmp(cur_arg, "--debug") == 0) {
3272 debug = true;
3273 } else if (strcmp(cur_arg, "-v") == 0 ||
3274 strcmp(cur_arg, "--verbose") == 0) {
3275 verbose = true;
3276 } else if (strcmp(cur_arg, "-V") == 0 ||
3277 strcmp(cur_arg, "--version") == 0) {
3278 print_version();
3279 goto end;
3280 } else if (strcmp(cur_arg, "-h") == 0 ||
3281 strcmp(cur_arg, "--help") == 0) {
3282 print_gen_usage(stdout);
3283 goto end;
3284 } else if (strcmp(cur_arg, "--help-legacy") == 0) {
3285 print_legacy_usage(stdout);
3286 goto end;
3287 } else {
3288 /*
3289 * First unknown argument: is it a known command
3290 * name?
3291 */
3292 if (strcmp(cur_arg, "convert") == 0) {
3293 command = BT_CONFIG_COMMAND_CONVERT;
3294 command_argv = &argv[i];
3295 command_argc = argc - i;
3296 command_name = cur_arg;
3297 } else if (strcmp(cur_arg, "list-plugins") == 0) {
3298 command = BT_CONFIG_COMMAND_LIST_PLUGINS;
3299 command_argv = &argv[i];
3300 command_argc = argc - i;
3301 command_name = cur_arg;
3302 } else {
3303 /*
3304 * Unknown argument, but not a known
3305 * command name: assume the whole
3306 * arguments are for the default convert
3307 * command.
3308 */
3309 command = BT_CONFIG_COMMAND_CONVERT;
3310 command_argv = argv;
3311 command_argc = argc;
3312 }
3313 break;
3314 }
3315 }
3316
3317 if ((int) command < 0) {
3318 /*
3319 * We only got non-help, non-version general options
3320 * like --verbose and --debug, without any other
3321 * arguments, so we can't do anything useful: print the
3322 * usage and quit.
3323 */
3324 print_gen_usage(stdout);
3325 goto end;
3326 }
3327
3328 assert(command_argv);
3329 assert(command_argc >= 0);
3330
3331 switch (command) {
3332 case BT_CONFIG_COMMAND_CONVERT:
3333 config = bt_config_convert_from_args(command_argc, command_argv,
3334 retcode, omit_system_plugin_path,
3335 omit_home_plugin_path, initial_plugin_paths);
3336 break;
3337 case BT_CONFIG_COMMAND_LIST_PLUGINS:
3338 config = bt_config_list_plugins_from_args(command_argc,
3339 command_argv, retcode, omit_system_plugin_path,
3340 omit_home_plugin_path, initial_plugin_paths);
3341 break;
3342 default:
3343 assert(false);
3344 }
3345
3346 if (config) {
3347 if (verbose) {
3348 config->verbose = true;
3349 }
3350
3351 if (debug) {
3352 config->debug = true;
3353 }
3354
3355 config->command_name = command_name;
3356 }
3357
3358end:
3359 return config;
c42c79ea 3360}
This page took 0.163874 seconds and 4 git commands to generate.