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