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