d3072488e0643f414c2cfe6014542919e2ec1fb0
[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 {
2009 bool legacy_input = false;
2010 bool legacy_output = false;
2011
2012 /* Determine if the input and output should be legacy-style */
2013 if (*legacy_input_format != LEGACY_INPUT_FORMAT_NONE ||
2014 cfg->sources->len == 0 ||
2015 !bt_value_array_is_empty(legacy_input_paths) ||
2016 ctf_legacy_opts_is_any_set(ctf_legacy_opts)) {
2017 legacy_input = true;
2018 }
2019
2020 if (*legacy_output_format != LEGACY_OUTPUT_FORMAT_NONE ||
2021 cfg->sinks->len == 0 ||
2022 text_legacy_opts_is_any_set(text_legacy_opts)) {
2023 legacy_output = true;
2024 }
2025
2026 if (legacy_input) {
2027 /* If no legacy input format was specified, default to CTF */
2028 if (*legacy_input_format == LEGACY_INPUT_FORMAT_NONE) {
2029 *legacy_input_format = LEGACY_INPUT_FORMAT_CTF;
2030 }
2031
2032 /* Make sure at least one input path exists */
2033 if (bt_value_array_is_empty(legacy_input_paths)) {
2034 switch (*legacy_input_format) {
2035 case LEGACY_INPUT_FORMAT_CTF:
2036 printf_err("No input path specified for legacy `ctf` input format\n");
2037 break;
2038 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
2039 printf_err("No URL specified for legacy `lttng-live` input format\n");
2040 break;
2041 default:
2042 assert(false);
2043 }
2044 goto error;
2045 }
2046
2047 /* Make sure no non-legacy sources are specified */
2048 if (cfg->sources->len != 0) {
2049 print_input_legacy_to_sources(*legacy_input_format,
2050 legacy_input_paths, ctf_legacy_opts);
2051 goto error;
2052 }
2053 }
2054
2055 if (legacy_output) {
2056 /*
2057 * If no legacy output format was specified, default to
2058 * "text".
2059 */
2060 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_NONE) {
2061 *legacy_output_format = LEGACY_OUTPUT_FORMAT_TEXT;
2062 }
2063
2064 /*
2065 * If any "text" option was specified, the output must be
2066 * legacy "text".
2067 */
2068 if (text_legacy_opts_is_any_set(text_legacy_opts) &&
2069 *legacy_output_format !=
2070 LEGACY_OUTPUT_FORMAT_TEXT) {
2071 printf_err("Options for legacy `text` output format specified with a different legacy output format\n");
2072 goto error;
2073 }
2074
2075 /* Make sure no non-legacy sinks are specified */
2076 if (cfg->sinks->len != 0) {
2077 print_output_legacy_to_sinks(*legacy_output_format,
2078 text_legacy_opts);
2079 goto error;
2080 }
2081 }
2082
2083 /*
2084 * If the output is the legacy "ctf-metadata" format, then the
2085 * input should be the legacy "ctf" input format.
2086 */
2087 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_CTF_METADATA &&
2088 *legacy_input_format != LEGACY_INPUT_FORMAT_CTF) {
2089 printf_err("Legacy `ctf-metadata` output format requires using legacy `ctf` input format\n");
2090 goto error;
2091 }
2092
2093 return true;
2094
2095 error:
2096 return false;
2097 }
2098
2099 /*
2100 * Parses a 64-bit signed integer.
2101 *
2102 * Returns a negative value if anything goes wrong.
2103 */
2104 static
2105 int parse_int64(const char *arg, int64_t *val)
2106 {
2107 char *endptr;
2108
2109 errno = 0;
2110 *val = strtoll(arg, &endptr, 0);
2111 if (*endptr != '\0' || arg == endptr || errno != 0) {
2112 return -1;
2113 }
2114
2115 return 0;
2116 }
2117
2118 /* popt options */
2119 enum {
2120 OPT_NONE = 0,
2121 OPT_BASE_PARAMS,
2122 OPT_BEGIN,
2123 OPT_CLOCK_CYCLES,
2124 OPT_CLOCK_DATE,
2125 OPT_CLOCK_FORCE_CORRELATE,
2126 OPT_CLOCK_GMT,
2127 OPT_CLOCK_OFFSET,
2128 OPT_CLOCK_OFFSET_NS,
2129 OPT_CLOCK_SECONDS,
2130 OPT_DEBUG,
2131 OPT_DEBUG_INFO_DIR,
2132 OPT_DEBUG_INFO_FULL_PATH,
2133 OPT_DEBUG_INFO_TARGET_PREFIX,
2134 OPT_END,
2135 OPT_FIELDS,
2136 OPT_HELP,
2137 OPT_HELP_LEGACY,
2138 OPT_INPUT_FORMAT,
2139 OPT_LIST,
2140 OPT_NAMES,
2141 OPT_NO_DELTA,
2142 OPT_OUTPUT_FORMAT,
2143 OPT_OUTPUT_PATH,
2144 OPT_PATH,
2145 OPT_PARAMS,
2146 OPT_PLUGIN_PATH,
2147 OPT_RESET_BASE_PARAMS,
2148 OPT_SINK,
2149 OPT_SOURCE,
2150 OPT_STREAM_INTERSECTION,
2151 OPT_TIMERANGE,
2152 OPT_VERBOSE,
2153 OPT_VERSION,
2154 OPT_OMIT_SYSTEM_PLUGIN_PATH,
2155 OPT_OMIT_HOME_PLUGIN_PATH,
2156 };
2157
2158 /* popt long option descriptions */
2159 static struct poptOption long_options[] = {
2160 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
2161 { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL },
2162 { "begin", '\0', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL },
2163 { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL },
2164 { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL },
2165 { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL },
2166 { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL },
2167 { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL },
2168 { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL },
2169 { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL },
2170 { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL },
2171 { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL },
2172 { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL },
2173 { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL },
2174 { "end", '\0', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL },
2175 { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL },
2176 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
2177 { "help-legacy", '\0', POPT_ARG_NONE, NULL, OPT_HELP_LEGACY, NULL, NULL },
2178 { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL },
2179 { "list", 'l', POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL },
2180 { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL },
2181 { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL },
2182 { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT_PATH, NULL, NULL },
2183 { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL },
2184 { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL },
2185 { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL },
2186 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
2187 { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL },
2188 { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL },
2189 { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL },
2190 { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL },
2191 { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL },
2192 { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL },
2193 { "version", 'V', POPT_ARG_NONE, NULL, OPT_VERSION, NULL, NULL },
2194 { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL },
2195 { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL },
2196 { NULL, 0, 0, NULL, 0, NULL, NULL },
2197 };
2198
2199 /*
2200 * Sets the value of a given legacy offset option and marks it as set.
2201 */
2202 static void set_offset_value(struct offset_opt *offset_opt, int64_t value)
2203 {
2204 offset_opt->value = value;
2205 offset_opt->is_set = true;
2206 }
2207
2208 enum bt_config_component_dest {
2209 BT_CONFIG_COMPONENT_DEST_SOURCE,
2210 BT_CONFIG_COMPONENT_DEST_SINK,
2211 };
2212
2213 /*
2214 * Adds a configuration component to the appropriate configuration
2215 * array depending on the destination.
2216 */
2217 static void add_cfg_comp(struct bt_config *cfg,
2218 struct bt_config_component *cfg_comp,
2219 enum bt_config_component_dest dest)
2220 {
2221 if (dest == BT_CONFIG_COMPONENT_DEST_SOURCE) {
2222 g_ptr_array_add(cfg->sources, cfg_comp);
2223 } else {
2224 g_ptr_array_add(cfg->sinks, cfg_comp);
2225 }
2226 }
2227
2228 static int split_timerange(const char *arg, const char **begin, const char **end)
2229 {
2230 const char *c;
2231
2232 /* Try to match [begin,end] */
2233 c = strchr(arg, '[');
2234 if (!c)
2235 goto skip;
2236 *begin = ++c;
2237 c = strchr(c, ',');
2238 if (!c)
2239 goto skip;
2240 *end = ++c;
2241 c = strchr(c, ']');
2242 if (!c)
2243 goto skip;
2244 goto found;
2245
2246 skip:
2247 /* Try to match begin,end */
2248 c = arg;
2249 *begin = c;
2250 c = strchr(c, ',');
2251 if (!c)
2252 goto not_found;
2253 *end = ++c;
2254 /* fall-through */
2255 found:
2256 return 0;
2257 not_found:
2258 return -1;
2259 }
2260
2261 static char *bt_secure_getenv(const char *name)
2262 {
2263 if (is_setuid_setgid()) {
2264 printf_err("Disregarding %s environment variable for setuid/setgid binary", name);
2265 return NULL;
2266 }
2267 return getenv(name);
2268 }
2269
2270 static const char *get_home_dir(void)
2271 {
2272 char *val = NULL;
2273 struct passwd *pwd;
2274
2275 val = bt_secure_getenv(HOME_ENV_VAR);
2276 if (val) {
2277 goto end;
2278 }
2279 /* Fallback on password file. */
2280 pwd = getpwuid(getuid());
2281 if (!pwd) {
2282 goto end;
2283 }
2284 val = pwd->pw_dir;
2285 end:
2286 return val;
2287 }
2288
2289 static int add_internal_plugin_paths(struct bt_config *cfg)
2290 {
2291 if (!omit_home_plugin_path) {
2292 char path[PATH_MAX];
2293 const char *home_dir;
2294
2295 if (is_setuid_setgid()) {
2296 printf_debug("Skipping non-system plugin paths for setuid/setgid binary.");
2297 } else {
2298 home_dir = get_home_dir();
2299 if (home_dir) {
2300 if (strlen(home_dir) + strlen(HOME_SUBPATH) + 1
2301 >= PATH_MAX) {
2302 printf_err("Home directory path too long\n");
2303 goto error;
2304 }
2305 strcpy(path, home_dir);
2306 strcat(path, HOME_SUBPATH);
2307 if (plugin_paths_from_arg(cfg->plugin_paths, path)) {
2308 printf_err("Invalid home plugin path\n");
2309 goto error;
2310 }
2311 }
2312 }
2313 }
2314
2315 if (!omit_system_plugin_path) {
2316 if (plugin_paths_from_arg(cfg->plugin_paths,
2317 SYSTEM_PLUGIN_PATH)) {
2318 printf_err("Invalid system plugin path\n");
2319 goto error;
2320 }
2321 }
2322 return 0;
2323 error:
2324 return -1;
2325 }
2326
2327 /*
2328 * Returns a Babeltrace configuration, out of command-line arguments,
2329 * containing everything that is needed to instanciate specific
2330 * components with given parameters.
2331 *
2332 * *exit_code is set to the appropriate exit code to use as far as this
2333 * function goes.
2334 *
2335 * Return value is NULL on error, otherwise it's owned by the caller.
2336 */
2337 struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_code)
2338 {
2339 struct bt_config *cfg = NULL;
2340 poptContext pc = NULL;
2341 char *arg = NULL;
2342 struct ctf_legacy_opts ctf_legacy_opts;
2343 struct text_legacy_opts text_legacy_opts;
2344 enum legacy_input_format legacy_input_format = LEGACY_INPUT_FORMAT_NONE;
2345 enum legacy_output_format legacy_output_format =
2346 LEGACY_OUTPUT_FORMAT_NONE;
2347 struct bt_value *legacy_input_paths = NULL;
2348 struct bt_config_component *implicit_source_comp = NULL;
2349 struct bt_config_component *cur_cfg_comp = NULL;
2350 bool cur_is_implicit_source = false;
2351 bool use_implicit_source = false;
2352 enum bt_config_component_dest cur_cfg_comp_dest =
2353 BT_CONFIG_COMPONENT_DEST_SOURCE;
2354 struct bt_value *cur_base_params = NULL;
2355 int opt;
2356
2357 memset(&ctf_legacy_opts, 0, sizeof(ctf_legacy_opts));
2358 memset(&text_legacy_opts, 0, sizeof(text_legacy_opts));
2359 *exit_code = 0;
2360
2361 if (argc <= 1) {
2362 print_usage(stdout);
2363 goto end;
2364 }
2365
2366 text_legacy_opts.output = g_string_new(NULL);
2367 if (!text_legacy_opts.output) {
2368 print_err_oom();
2369 goto error;
2370 }
2371
2372 text_legacy_opts.dbg_info_dir = g_string_new(NULL);
2373 if (!text_legacy_opts.dbg_info_dir) {
2374 print_err_oom();
2375 goto error;
2376 }
2377
2378 text_legacy_opts.dbg_info_target_prefix = g_string_new(NULL);
2379 if (!text_legacy_opts.dbg_info_target_prefix) {
2380 print_err_oom();
2381 goto error;
2382 }
2383
2384 cur_base_params = bt_value_map_create();
2385 if (!cur_base_params) {
2386 print_err_oom();
2387 goto error;
2388 }
2389
2390 /* Create config */
2391 cfg = g_new0(struct bt_config, 1);
2392 if (!cfg) {
2393 print_err_oom();
2394 goto error;
2395 }
2396
2397 bt_object_init(cfg, bt_config_destroy);
2398 cfg->sources = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
2399 if (!cfg->sources) {
2400 print_err_oom();
2401 goto error;
2402 }
2403
2404 cfg->sinks = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
2405 if (!cfg->sinks) {
2406 print_err_oom();
2407 goto error;
2408 }
2409
2410 legacy_input_paths = bt_value_array_create();
2411 if (!legacy_input_paths) {
2412 print_err_oom();
2413 goto error;
2414 }
2415
2416 /* Note: implicit source never gets positional base params. */
2417 implicit_source_comp = bt_config_component_from_arg(DEFAULT_SOURCE_COMPONENT_NAME);
2418 if (implicit_source_comp) {
2419 cur_cfg_comp = implicit_source_comp;
2420 cur_is_implicit_source = true;
2421 use_implicit_source = true;
2422 } else {
2423 printf_debug("Cannot find implicit source plugin \"%s\"",
2424 DEFAULT_SOURCE_COMPONENT_NAME);
2425 }
2426
2427 cfg->plugin_paths = bt_value_array_create();
2428 if (!cfg->plugin_paths) {
2429 print_err_oom();
2430 goto error;
2431 }
2432
2433 /* Parse options */
2434 pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
2435 if (!pc) {
2436 printf_err("Cannot get popt context\n");
2437 goto error;
2438 }
2439
2440 poptReadDefaultConfig(pc, 0);
2441
2442 while ((opt = poptGetNextOpt(pc)) > 0) {
2443 arg = poptGetOptArg(pc);
2444
2445 switch (opt) {
2446 case OPT_PLUGIN_PATH:
2447 if (is_setuid_setgid()) {
2448 printf_debug("Skipping non-system plugin paths for setuid/setgid binary.");
2449 } else {
2450 if (plugin_paths_from_arg(cfg->plugin_paths, arg)) {
2451 printf_err("Invalid --plugin-path option's argument\n");
2452 goto error;
2453 }
2454 }
2455 break;
2456 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
2457 omit_system_plugin_path = true;
2458 break;
2459 case OPT_OMIT_HOME_PLUGIN_PATH:
2460 omit_home_plugin_path = true;
2461 break;
2462 case OPT_OUTPUT_PATH:
2463 if (text_legacy_opts.output->len > 0) {
2464 printf_err("Duplicate --output option\n");
2465 goto error;
2466 }
2467
2468 g_string_assign(text_legacy_opts.output, arg);
2469 break;
2470 case OPT_DEBUG_INFO_DIR:
2471 if (text_legacy_opts.dbg_info_dir->len > 0) {
2472 printf_err("Duplicate --debug-info-dir option\n");
2473 goto error;
2474 }
2475
2476 g_string_assign(text_legacy_opts.dbg_info_dir, arg);
2477 break;
2478 case OPT_DEBUG_INFO_TARGET_PREFIX:
2479 if (text_legacy_opts.dbg_info_target_prefix->len > 0) {
2480 printf_err("Duplicate --debug-info-target-prefix option\n");
2481 goto error;
2482 }
2483
2484 g_string_assign(text_legacy_opts.dbg_info_target_prefix, arg);
2485 break;
2486 case OPT_INPUT_FORMAT:
2487 case OPT_SOURCE:
2488 {
2489 if (opt == OPT_INPUT_FORMAT) {
2490 if (!strcmp(arg, "ctf")) {
2491 /* Legacy CTF input format */
2492 if (legacy_input_format) {
2493 print_err_dup_legacy_input();
2494 goto error;
2495 }
2496
2497 legacy_input_format =
2498 LEGACY_INPUT_FORMAT_CTF;
2499 break;
2500 } else if (!strcmp(arg, "lttng-live")) {
2501 /* Legacy LTTng-live input format */
2502 if (legacy_input_format) {
2503 print_err_dup_legacy_input();
2504 goto error;
2505 }
2506
2507 legacy_input_format =
2508 LEGACY_INPUT_FORMAT_LTTNG_LIVE;
2509 break;
2510 }
2511 }
2512
2513 use_implicit_source = false;
2514
2515 /* Non-legacy: try to create a component config */
2516 if (cur_cfg_comp && !cur_is_implicit_source) {
2517 add_cfg_comp(cfg, cur_cfg_comp,
2518 cur_cfg_comp_dest);
2519 }
2520
2521 cur_cfg_comp = bt_config_component_from_arg(arg);
2522 if (!cur_cfg_comp) {
2523 printf_err("Invalid format for --source option's argument:\n %s\n",
2524 arg);
2525 goto error;
2526 }
2527 cur_is_implicit_source = false;
2528
2529 assert(cur_base_params);
2530 bt_put(cur_cfg_comp->params);
2531 cur_cfg_comp->params = bt_value_copy(cur_base_params);
2532 if (!cur_cfg_comp) {
2533 print_err_oom();
2534 goto end;
2535 }
2536
2537 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE;
2538 break;
2539 }
2540 case OPT_OUTPUT_FORMAT:
2541 case OPT_SINK:
2542 {
2543 if (opt == OPT_OUTPUT_FORMAT) {
2544 if (!strcmp(arg, "text")) {
2545 /* Legacy CTF-text output format */
2546 if (legacy_output_format) {
2547 print_err_dup_legacy_output();
2548 goto error;
2549 }
2550
2551 legacy_output_format =
2552 LEGACY_OUTPUT_FORMAT_TEXT;
2553 break;
2554 } else if (!strcmp(arg, "dummy")) {
2555 /* Legacy dummy output format */
2556 if (legacy_output_format) {
2557 print_err_dup_legacy_output();
2558 goto error;
2559 }
2560
2561 legacy_output_format =
2562 LEGACY_OUTPUT_FORMAT_DUMMY;
2563 break;
2564 } else if (!strcmp(arg, "ctf-metadata")) {
2565 /* Legacy CTF-metadata output format */
2566 if (legacy_output_format) {
2567 print_err_dup_legacy_output();
2568 goto error;
2569 }
2570
2571 legacy_output_format =
2572 LEGACY_OUTPUT_FORMAT_CTF_METADATA;
2573 break;
2574 }
2575 }
2576
2577 /* Non-legacy: try to create a component config */
2578 if (cur_cfg_comp && !cur_is_implicit_source) {
2579 add_cfg_comp(cfg, cur_cfg_comp,
2580 cur_cfg_comp_dest);
2581 }
2582
2583 cur_cfg_comp = bt_config_component_from_arg(arg);
2584 if (!cur_cfg_comp) {
2585 printf_err("Invalid format for --sink option's argument:\n %s\n",
2586 arg);
2587 goto error;
2588 }
2589 cur_is_implicit_source = false;
2590
2591 assert(cur_base_params);
2592 bt_put(cur_cfg_comp->params);
2593 cur_cfg_comp->params = bt_value_copy(cur_base_params);
2594 if (!cur_cfg_comp) {
2595 print_err_oom();
2596 goto end;
2597 }
2598
2599 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK;
2600 break;
2601 }
2602 case OPT_PARAMS:
2603 {
2604 struct bt_value *params;
2605 struct bt_value *params_to_set;
2606
2607 if (!cur_cfg_comp) {
2608 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2609 DEFAULT_SOURCE_COMPONENT_NAME);
2610 goto error;
2611 }
2612
2613 params = bt_value_from_arg(arg);
2614 if (!params) {
2615 printf_err("Invalid format for --params option's argument:\n %s\n",
2616 arg);
2617 goto error;
2618 }
2619
2620 params_to_set = bt_value_map_extend(cur_cfg_comp->params,
2621 params);
2622 BT_PUT(params);
2623 if (!params_to_set) {
2624 printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n",
2625 arg);
2626 goto error;
2627 }
2628
2629 BT_MOVE(cur_cfg_comp->params, params_to_set);
2630 break;
2631 }
2632 case OPT_PATH:
2633 if (!cur_cfg_comp) {
2634 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2635 DEFAULT_SOURCE_COMPONENT_NAME);
2636 goto error;
2637 }
2638
2639 assert(cur_cfg_comp->params);
2640
2641 if (bt_value_map_insert_string(cur_cfg_comp->params,
2642 "path", arg)) {
2643 print_err_oom();
2644 goto error;
2645 }
2646 break;
2647 case OPT_BASE_PARAMS:
2648 {
2649 struct bt_value *params = bt_value_from_arg(arg);
2650
2651 if (!params) {
2652 printf_err("Invalid format for --base-params option's argument:\n %s\n",
2653 arg);
2654 goto error;
2655 }
2656
2657 BT_MOVE(cur_base_params, params);
2658 break;
2659 }
2660 case OPT_RESET_BASE_PARAMS:
2661 BT_PUT(cur_base_params);
2662 cur_base_params = bt_value_map_create();
2663 if (!cur_base_params) {
2664 print_err_oom();
2665 goto error;
2666 }
2667 break;
2668 case OPT_NAMES:
2669 if (text_legacy_opts.names) {
2670 printf_err("Duplicate --names option\n");
2671 goto error;
2672 }
2673
2674 text_legacy_opts.names = names_from_arg(arg);
2675 if (!text_legacy_opts.names) {
2676 printf_err("Invalid --names option's argument\n");
2677 goto error;
2678 }
2679 break;
2680 case OPT_FIELDS:
2681 if (text_legacy_opts.fields) {
2682 printf_err("Duplicate --fields option\n");
2683 goto error;
2684 }
2685
2686 text_legacy_opts.fields = fields_from_arg(arg);
2687 if (!text_legacy_opts.fields) {
2688 printf_err("Invalid --fields option's argument\n");
2689 goto error;
2690 }
2691 break;
2692 case OPT_NO_DELTA:
2693 text_legacy_opts.no_delta = true;
2694 break;
2695 case OPT_CLOCK_CYCLES:
2696 text_legacy_opts.clock_cycles = true;
2697 break;
2698 case OPT_CLOCK_SECONDS:
2699 text_legacy_opts.clock_seconds = true;
2700 break;
2701 case OPT_CLOCK_DATE:
2702 text_legacy_opts.clock_date = true;
2703 break;
2704 case OPT_CLOCK_GMT:
2705 text_legacy_opts.clock_gmt = true;
2706 break;
2707 case OPT_DEBUG_INFO_FULL_PATH:
2708 text_legacy_opts.dbg_info_full_path = true;
2709 break;
2710 case OPT_CLOCK_OFFSET:
2711 {
2712 int64_t val;
2713
2714 if (ctf_legacy_opts.offset_s.is_set) {
2715 printf_err("Duplicate --clock-offset option\n");
2716 goto error;
2717 }
2718
2719 if (parse_int64(arg, &val)) {
2720 printf_err("Invalid --clock-offset option's argument\n");
2721 goto error;
2722 }
2723
2724 set_offset_value(&ctf_legacy_opts.offset_s, val);
2725 break;
2726 }
2727 case OPT_CLOCK_OFFSET_NS:
2728 {
2729 int64_t val;
2730
2731 if (ctf_legacy_opts.offset_ns.is_set) {
2732 printf_err("Duplicate --clock-offset-ns option\n");
2733 goto error;
2734 }
2735
2736 if (parse_int64(arg, &val)) {
2737 printf_err("Invalid --clock-offset-ns option's argument\n");
2738 goto error;
2739 }
2740
2741 set_offset_value(&ctf_legacy_opts.offset_ns, val);
2742 break;
2743 }
2744 case OPT_STREAM_INTERSECTION:
2745 ctf_legacy_opts.stream_intersection = true;
2746 break;
2747 case OPT_CLOCK_FORCE_CORRELATE:
2748 cfg->force_correlate = true;
2749 break;
2750 case OPT_BEGIN:
2751 if (!cur_cfg_comp) {
2752 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2753 DEFAULT_SOURCE_COMPONENT_NAME);
2754 goto error;
2755 }
2756 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2757 printf_err("--begin option must follow a --source option\n");
2758 goto error;
2759 }
2760 if (bt_value_map_insert_string(cur_cfg_comp->params,
2761 "begin", arg)) {
2762 print_err_oom();
2763 goto error;
2764 }
2765 break;
2766 case OPT_END:
2767 if (!cur_cfg_comp) {
2768 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2769 DEFAULT_SOURCE_COMPONENT_NAME);
2770 goto error;
2771 }
2772 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2773 printf_err("--end option must follow a --source option\n");
2774 goto error;
2775 }
2776 if (bt_value_map_insert_string(cur_cfg_comp->params,
2777 "end", arg)) {
2778 print_err_oom();
2779 goto error;
2780 }
2781 break;
2782 case OPT_TIMERANGE:
2783 {
2784 const char *begin, *end;
2785
2786 if (!cur_cfg_comp) {
2787 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2788 DEFAULT_SOURCE_COMPONENT_NAME);
2789 goto error;
2790 }
2791 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2792 printf_err("--timerange option must follow a --source option\n");
2793 goto error;
2794 }
2795 if (split_timerange(arg, &begin, &end)) {
2796 printf_err("Invalid --timerange format, expecting: begin,end or [begin,end] (where [] are actual brackets)\n");
2797 goto error;
2798 }
2799 if (bt_value_map_insert_string(cur_cfg_comp->params,
2800 "begin", begin)) {
2801 print_err_oom();
2802 goto error;
2803 }
2804 if (bt_value_map_insert_string(cur_cfg_comp->params,
2805 "end", end)) {
2806 print_err_oom();
2807 goto error;
2808 }
2809 break;
2810 }
2811 case OPT_HELP:
2812 BT_PUT(cfg);
2813 print_usage(stdout);
2814 goto end;
2815 case OPT_HELP_LEGACY:
2816 BT_PUT(cfg);
2817 print_legacy_usage(stdout);
2818 goto end;
2819 case OPT_VERSION:
2820 BT_PUT(cfg);
2821 print_version();
2822 goto end;
2823 case OPT_LIST:
2824 cfg->do_list = true;
2825 goto end;
2826 case OPT_VERBOSE:
2827 cfg->verbose = true;
2828 break;
2829 case OPT_DEBUG:
2830 cfg->debug = true;
2831 break;
2832 default:
2833 printf_err("Unknown command-line option specified (option code %d)\n",
2834 opt);
2835 goto error;
2836 }
2837
2838 free(arg);
2839 arg = NULL;
2840 }
2841
2842 if (add_internal_plugin_paths(cfg)) {
2843 goto error;
2844 }
2845
2846 /* Append current component configuration, if any */
2847 if (cur_cfg_comp && !cur_is_implicit_source) {
2848 add_cfg_comp(cfg, cur_cfg_comp, cur_cfg_comp_dest);
2849 }
2850 cur_cfg_comp = NULL;
2851
2852 if (use_implicit_source) {
2853 add_cfg_comp(cfg, implicit_source_comp,
2854 BT_CONFIG_COMPONENT_DEST_SOURCE);
2855 implicit_source_comp = NULL;
2856 } else {
2857 if (implicit_source_comp
2858 && !bt_value_map_is_empty(implicit_source_comp->params)) {
2859 printf_err("Arguments specified for implicit source, but an explicit source has been specified, overriding it\n");
2860 goto error;
2861 }
2862 }
2863
2864 /* Check for option parsing error */
2865 if (opt < -1) {
2866 printf_err("While parsing command-line options, at option %s: %s\n",
2867 poptBadOption(pc, 0), poptStrerror(opt));
2868 goto error;
2869 }
2870
2871 /* Consume leftover arguments as legacy input paths */
2872 while (true) {
2873 const char *input_path = poptGetArg(pc);
2874
2875 if (!input_path) {
2876 break;
2877 }
2878
2879 if (bt_value_array_append_string(legacy_input_paths,
2880 input_path)) {
2881 print_err_oom();
2882 goto error;
2883 }
2884 }
2885
2886 /* Validate legacy/non-legacy options */
2887 if (!validate_cfg(cfg, &legacy_input_format, &legacy_output_format,
2888 legacy_input_paths, &ctf_legacy_opts,
2889 &text_legacy_opts)) {
2890 printf_err("Command-line options form an invalid configuration\n");
2891 goto error;
2892 }
2893
2894 /*
2895 * If there's a legacy input format, convert it to source
2896 * component configurations.
2897 */
2898 if (legacy_input_format) {
2899 if (append_sources_from_legacy_opts(cfg->sources,
2900 legacy_input_format, &ctf_legacy_opts,
2901 legacy_input_paths)) {
2902 printf_err("Cannot convert legacy input format options to source component instance(s)\n");
2903 goto error;
2904 }
2905 }
2906
2907 /*
2908 * If there's a legacy output format, convert it to sink
2909 * component configurations.
2910 */
2911 if (legacy_output_format) {
2912 if (append_sinks_from_legacy_opts(cfg->sinks,
2913 legacy_output_format, &text_legacy_opts)) {
2914 printf_err("Cannot convert legacy output format options to sink component instance(s)\n");
2915 goto error;
2916 }
2917 }
2918
2919 goto end;
2920
2921 error:
2922 BT_PUT(cfg);
2923 cfg = NULL;
2924 *exit_code = 1;
2925
2926 end:
2927 if (pc) {
2928 poptFreeContext(pc);
2929 }
2930
2931 if (text_legacy_opts.output) {
2932 g_string_free(text_legacy_opts.output, TRUE);
2933 }
2934
2935 if (text_legacy_opts.dbg_info_dir) {
2936 g_string_free(text_legacy_opts.dbg_info_dir, TRUE);
2937 }
2938
2939 if (text_legacy_opts.dbg_info_target_prefix) {
2940 g_string_free(text_legacy_opts.dbg_info_target_prefix, TRUE);
2941 }
2942
2943 free(arg);
2944 BT_PUT(implicit_source_comp);
2945 BT_PUT(cur_cfg_comp);
2946 BT_PUT(cur_base_params);
2947 BT_PUT(text_legacy_opts.names);
2948 BT_PUT(text_legacy_opts.fields);
2949 BT_PUT(legacy_input_paths);
2950 return cfg;
2951 }
This page took 0.089208 seconds and 3 git commands to generate.