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