lib: value.c: remove BT_VALUE_FROM_CONCRETE() macro
[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
b4565e8b
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>
f6ccaed9 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"
7845e95f 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) */
b19ff26f 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;
b19ff26f
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
c7a2dd99
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
ae48e492 201 if (int_val > (((uint64_t) INT64_MAX) + 1)) {
c7a2dd99
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
b25da386
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
76749bd1
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);
b25da386
SM
309 } else if (state->scanner->value.v_char == '[') {
310 /* Array */
311 value = ini_parse_array(state);
76749bd1
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
ae48e492 321 if (int_val > INT64_MAX) {
76749bd1
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;
b19ff26f 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
05e21286
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 {
76749bd1 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) {
05e21286 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:
c5b9b441 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
b19ff26f 507bt_value *bt_value_from_ini(const char *arg,
da91b29a 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
05e21286 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:
c5b9b441 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
b19ff26f 633bt_value *bt_value_from_arg(const char *arg)
9009cc24 634{
b19ff26f 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 */
05e21286 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 }
da91b29a 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
e5c7cd9b
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,
4cdfc5e8 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
f6ccaed9
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') {
7b1e06a1 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{
7845e95f
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
b19ff26f 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
c5b9b441 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(
4cdfc5e8 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
3fea54f6
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 */
05e21286 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:
65300d60 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;
4cdfc5e8 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:
65300d60 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
b19ff26f 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
c5b9b441 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:
65300d60 979 BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.help.cfg_component);
9009cc24
PP
980 break;
981 case BT_CONFIG_COMMAND_QUERY:
65300d60 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);
c327e427
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);
c327e427
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
b19ff26f 1087bt_value *names_from_arg(const char *arg)
9009cc24
PP
1088{
1089 GScanner *scanner = NULL;
b19ff26f 1090 bt_value *names = NULL;
9009cc24
PP
1091 bool found_all = false, found_none = false, found_item = false;
1092
05e21286 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;
05e21286 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;
05e21286 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;
05e21286 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;
05e21286 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;
05e21286 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) {
05e21286 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:
c5b9b441 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
b19ff26f 1199bt_value *fields_from_arg(const char *arg)
9009cc24
PP
1200{
1201 GScanner *scanner = NULL;
b19ff26f 1202 bt_value *fields;
9009cc24 1203
05e21286 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")) {
05e21286 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:
c5b9b441 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{
f6ccaed9
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,
b19ff26f 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
07208d85 1317 for (i = 0; i < bt_value_array_get_size(names_array); i++) {
b19ff26f 1318 const bt_value *str_obj =
05e21286
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
601b0d3c 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,
9a16feea 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,
9009cc24
PP
1399 OPT_LIST,
1400 OPT_NAME,
1401 OPT_NAMES,
9009cc24
PP
1402 OPT_NO_DELTA,
1403 OPT_OMIT_HOME_PLUGIN_PATH,
1404 OPT_OMIT_SYSTEM_PLUGIN_PATH,
9009cc24 1405 OPT_OUTPUT,
fd5f8053 1406 OPT_OUTPUT_FORMAT,
9009cc24
PP
1407 OPT_PARAMS,
1408 OPT_PATH,
1409 OPT_PLUGIN_PATH,
1410 OPT_RESET_BASE_PARAMS,
1411 OPT_RETRY_DURATION,
1412 OPT_RUN_ARGS,
1413 OPT_RUN_ARGS_0,
9009cc24
PP
1414 OPT_STREAM_INTERSECTION,
1415 OPT_TIMERANGE,
1416 OPT_URL,
9009cc24
PP
1417 OPT_VERBOSE,
1418};
1419
1420enum bt_config_component_dest {
0a011c88 1421 BT_CONFIG_COMPONENT_DEST_UNKNOWN = -1,
9009cc24
PP
1422 BT_CONFIG_COMPONENT_DEST_SOURCE,
1423 BT_CONFIG_COMPONENT_DEST_FILTER,
1424 BT_CONFIG_COMPONENT_DEST_SINK,
1425};
1426
1427/*
1428 * Adds a configuration component to the appropriate configuration
1429 * array depending on the destination.
1430 */
1431static
1432void add_run_cfg_comp(struct bt_config *cfg,
1433 struct bt_config_component *cfg_comp,
1434 enum bt_config_component_dest dest)
1435{
65300d60 1436 bt_object_get_ref(cfg_comp);
9009cc24
PP
1437
1438 switch (dest) {
1439 case BT_CONFIG_COMPONENT_DEST_SOURCE:
1440 g_ptr_array_add(cfg->cmd_data.run.sources, cfg_comp);
1441 break;
1442 case BT_CONFIG_COMPONENT_DEST_FILTER:
1443 g_ptr_array_add(cfg->cmd_data.run.filters, cfg_comp);
1444 break;
1445 case BT_CONFIG_COMPONENT_DEST_SINK:
1446 g_ptr_array_add(cfg->cmd_data.run.sinks, cfg_comp);
1447 break;
1448 default:
0fbb9a9f 1449 abort();
9009cc24
PP
1450 }
1451}
1452
1453static
1454int add_run_cfg_comp_check_name(struct bt_config *cfg,
1455 struct bt_config_component *cfg_comp,
1456 enum bt_config_component_dest dest,
b19ff26f 1457 bt_value *instance_names)
9009cc24
PP
1458{
1459 int ret = 0;
1460
1461 if (cfg_comp->instance_name->len == 0) {
1462 printf_err("Found an unnamed component\n");
1463 ret = -1;
1464 goto end;
1465 }
1466
05e21286
PP
1467 if (bt_value_map_has_entry(instance_names,
1468 cfg_comp->instance_name->str)) {
9009cc24
PP
1469 printf_err("Duplicate component instance name:\n %s\n",
1470 cfg_comp->instance_name->str);
1471 ret = -1;
1472 goto end;
1473 }
1474
05e21286 1475 if (bt_value_map_insert_entry(instance_names,
9009cc24
PP
1476 cfg_comp->instance_name->str, bt_value_null)) {
1477 print_err_oom();
1478 ret = -1;
1479 goto end;
1480 }
1481
1482 add_run_cfg_comp(cfg, cfg_comp, dest);
1483
1484end:
1485 return ret;
1486}
1487
1488static
b19ff26f 1489int append_env_var_plugin_paths(bt_value *plugin_paths)
9009cc24
PP
1490{
1491 int ret = 0;
1492 const char *envvar;
1493
1494 if (bt_common_is_setuid_setgid()) {
b4565e8b 1495 BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary.");
9009cc24
PP
1496 goto end;
1497 }
1498
1499 envvar = getenv("BABELTRACE_PLUGIN_PATH");
1500 if (!envvar) {
1501 goto end;
1502 }
1503
1504 ret = bt_config_append_plugin_paths(plugin_paths, envvar);
1505
1506end:
1507 if (ret) {
1508 printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n");
1509 }
1510
1511 return ret;
1512}
1513
1514static
b19ff26f 1515int append_home_and_system_plugin_paths(bt_value *plugin_paths,
9009cc24
PP
1516 bool omit_system_plugin_path, bool omit_home_plugin_path)
1517{
1518 int ret;
1519
1520 if (!omit_home_plugin_path) {
1521 if (bt_common_is_setuid_setgid()) {
b4565e8b 1522 BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary.");
9009cc24
PP
1523 } else {
1524 char *home_plugin_dir =
1525 bt_common_get_home_plugin_path();
1526
1527 if (home_plugin_dir) {
1528 ret = bt_config_append_plugin_paths(
1529 plugin_paths, home_plugin_dir);
1530 free(home_plugin_dir);
1531
1532 if (ret) {
1533 printf_err("Invalid home plugin path\n");
1534 goto error;
1535 }
1536 }
1537 }
1538 }
1539
1540 if (!omit_system_plugin_path) {
1541 if (bt_config_append_plugin_paths(plugin_paths,
1542 bt_common_get_system_plugin_path())) {
1543 printf_err("Invalid system plugin path\n");
1544 goto error;
1545 }
1546 }
1547 return 0;
1548error:
1549 printf_err("Cannot append home and system plugin paths\n");
1550 return -1;
1551}
1552
1553static
1554int append_home_and_system_plugin_paths_cfg(struct bt_config *cfg)
1555{
1556 return append_home_and_system_plugin_paths(cfg->plugin_paths,
1557 cfg->omit_system_plugin_path, cfg->omit_home_plugin_path);
1558}
1559
1560static
1561struct bt_config *bt_config_base_create(enum bt_config_command command,
b19ff26f 1562 const bt_value *initial_plugin_paths,
da91b29a 1563 bool needs_plugins)
9009cc24
PP
1564{
1565 struct bt_config *cfg;
1566
1567 /* Create config */
1568 cfg = g_new0(struct bt_config, 1);
1569 if (!cfg) {
1570 print_err_oom();
1571 goto error;
1572 }
1573
3fea54f6 1574 bt_object_init_shared(&cfg->base, bt_config_destroy);
9009cc24
PP
1575 cfg->command = command;
1576 cfg->command_needs_plugins = needs_plugins;
1577
1578 if (initial_plugin_paths) {
b19ff26f 1579 bt_value *initial_plugin_paths_copy;
05e21286 1580
6be5a99e
PP
1581 (void) bt_value_copy(initial_plugin_paths,
1582 &initial_plugin_paths_copy);
05e21286 1583 cfg->plugin_paths = initial_plugin_paths_copy;
9009cc24 1584 } else {
05e21286 1585 cfg->plugin_paths = bt_value_array_create();
9009cc24
PP
1586 if (!cfg->plugin_paths) {
1587 print_err_oom();
1588 goto error;
1589 }
1590 }
1591
1592 goto end;
1593
1594error:
65300d60 1595 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
1596
1597end:
1598 return cfg;
1599}
1600
1601static
1602struct bt_config *bt_config_run_create(
b19ff26f 1603 const bt_value *initial_plugin_paths)
9009cc24
PP
1604{
1605 struct bt_config *cfg;
1606
1607 /* Create config */
1608 cfg = bt_config_base_create(BT_CONFIG_COMMAND_RUN,
1609 initial_plugin_paths, true);
1610 if (!cfg) {
1611 goto error;
1612 }
1613
1614 cfg->cmd_data.run.sources = g_ptr_array_new_with_free_func(
65300d60 1615 (GDestroyNotify) bt_object_put_ref);
9009cc24
PP
1616 if (!cfg->cmd_data.run.sources) {
1617 print_err_oom();
1618 goto error;
1619 }
1620
1621 cfg->cmd_data.run.filters = g_ptr_array_new_with_free_func(
65300d60 1622 (GDestroyNotify) bt_object_put_ref);
9009cc24
PP
1623 if (!cfg->cmd_data.run.filters) {
1624 print_err_oom();
1625 goto error;
1626 }
1627
1628 cfg->cmd_data.run.sinks = g_ptr_array_new_with_free_func(
65300d60 1629 (GDestroyNotify) bt_object_put_ref);
9009cc24
PP
1630 if (!cfg->cmd_data.run.sinks) {
1631 print_err_oom();
1632 goto error;
1633 }
1634
1635 cfg->cmd_data.run.connections = g_ptr_array_new_with_free_func(
1636 (GDestroyNotify) bt_config_connection_destroy);
1637 if (!cfg->cmd_data.run.connections) {
1638 print_err_oom();
1639 goto error;
1640 }
1641
1642 goto end;
1643
1644error:
65300d60 1645 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
1646
1647end:
1648 return cfg;
1649}
1650
1651static
1652struct bt_config *bt_config_list_plugins_create(
b19ff26f 1653 const bt_value *initial_plugin_paths)
9009cc24
PP
1654{
1655 struct bt_config *cfg;
1656
1657 /* Create config */
1658 cfg = bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS,
1659 initial_plugin_paths, true);
1660 if (!cfg) {
1661 goto error;
1662 }
1663
1664 goto end;
1665
1666error:
65300d60 1667 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
1668
1669end:
1670 return cfg;
1671}
1672
1673static
1674struct bt_config *bt_config_help_create(
b19ff26f 1675 const bt_value *initial_plugin_paths)
9009cc24
PP
1676{
1677 struct bt_config *cfg;
1678
1679 /* Create config */
1680 cfg = bt_config_base_create(BT_CONFIG_COMMAND_HELP,
1681 initial_plugin_paths, true);
1682 if (!cfg) {
1683 goto error;
1684 }
1685
1686 cfg->cmd_data.help.cfg_component =
d94d92ac 1687 bt_config_component_create(-1, NULL, NULL);
9009cc24
PP
1688 if (!cfg->cmd_data.help.cfg_component) {
1689 goto error;
1690 }
1691
1692 goto end;
1693
1694error:
65300d60 1695 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
1696
1697end:
1698 return cfg;
1699}
1700
1701static
1702struct bt_config *bt_config_query_create(
b19ff26f 1703 const bt_value *initial_plugin_paths)
9009cc24
PP
1704{
1705 struct bt_config *cfg;
1706
1707 /* Create config */
1708 cfg = bt_config_base_create(BT_CONFIG_COMMAND_QUERY,
1709 initial_plugin_paths, true);
1710 if (!cfg) {
1711 goto error;
1712 }
1713
1714 cfg->cmd_data.query.object = g_string_new(NULL);
1715 if (!cfg->cmd_data.query.object) {
1716 print_err_oom();
1717 goto error;
1718 }
1719
1720 goto end;
1721
1722error:
65300d60 1723 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
1724
1725end:
1726 return cfg;
1727}
1728
1729static
1730struct bt_config *bt_config_print_ctf_metadata_create(
b19ff26f 1731 const bt_value *initial_plugin_paths)
9009cc24
PP
1732{
1733 struct bt_config *cfg;
1734
1735 /* Create config */
1736 cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_CTF_METADATA,
1737 initial_plugin_paths, true);
1738 if (!cfg) {
1739 goto error;
1740 }
1741
1742 cfg->cmd_data.print_ctf_metadata.path = g_string_new(NULL);
1743 if (!cfg->cmd_data.print_ctf_metadata.path) {
1744 print_err_oom();
1745 goto error;
1746 }
1747
c327e427
PP
1748 cfg->cmd_data.print_ctf_metadata.output_path = g_string_new(NULL);
1749 if (!cfg->cmd_data.print_ctf_metadata.output_path) {
1750 print_err_oom();
1751 goto error;
1752 }
1753
9009cc24
PP
1754 goto end;
1755
1756error:
65300d60 1757 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
1758
1759end:
1760 return cfg;
1761}
1762
1763static
1764struct bt_config *bt_config_print_lttng_live_sessions_create(
b19ff26f 1765 const bt_value *initial_plugin_paths)
9009cc24
PP
1766{
1767 struct bt_config *cfg;
1768
1769 /* Create config */
1770 cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS,
1771 initial_plugin_paths, true);
1772 if (!cfg) {
1773 goto error;
1774 }
1775
1776 cfg->cmd_data.print_lttng_live_sessions.url = g_string_new(NULL);
1777 if (!cfg->cmd_data.print_lttng_live_sessions.url) {
1778 print_err_oom();
1779 goto error;
1780 }
1781
c327e427
PP
1782 cfg->cmd_data.print_lttng_live_sessions.output_path =
1783 g_string_new(NULL);
1784 if (!cfg->cmd_data.print_lttng_live_sessions.output_path) {
1785 print_err_oom();
1786 goto error;
1787 }
1788
9009cc24
PP
1789 goto end;
1790
1791error:
65300d60 1792 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
1793
1794end:
1795 return cfg;
1796}
1797
1798static
1799int bt_config_append_plugin_paths_check_setuid_setgid(
b19ff26f 1800 bt_value *plugin_paths, const char *arg)
9009cc24
PP
1801{
1802 int ret = 0;
1803
1804 if (bt_common_is_setuid_setgid()) {
b4565e8b 1805 BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary.");
9009cc24
PP
1806 goto end;
1807 }
1808
1809 if (bt_config_append_plugin_paths(plugin_paths, arg)) {
1810 printf_err("Invalid --plugin-path option's argument:\n %s\n",
1811 arg);
1812 ret = -1;
1813 goto end;
1814 }
1815
1816end:
1817 return ret;
1818}
1819
1820/*
1821 * Prints the expected format for a --params option.
1822 */
1823static
1824void print_expected_params_format(FILE *fp)
1825{
1826 fprintf(fp, "Expected format of PARAMS\n");
1827 fprintf(fp, "-------------------------\n");
1828 fprintf(fp, "\n");
1829 fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n");
1830 fprintf(fp, "\n");
1831 fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n");
1832 fprintf(fp, "where PARAM is the parameter name (C identifier plus the [:.-] characters),\n");
1833 fprintf(fp, "and VALUE can be one of:\n");
1834 fprintf(fp, "\n");
1835 fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n");
1836 fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n");
1837 fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n");
1838 fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n");
1839 fprintf(fp, " (`0x` prefix) signed 64-bit integer.\n");
1840 fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n");
1841 fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n");
1842 fprintf(fp, " the null and boolean value symbols above.\n");
1843 fprintf(fp, "* Double-quoted string (accepts escape characters).\n");
b25da386
SM
1844 fprintf(fp, "* Array, formatted as an opening `[`, a list of comma-separated values\n");
1845 fprintf(fp, " (as described by the current list) and a closing `]`.\n");
9009cc24
PP
1846 fprintf(fp, "\n");
1847 fprintf(fp, "You can put whitespaces allowed around individual `=` and `,` symbols.\n");
1848 fprintf(fp, "\n");
1849 fprintf(fp, "Example:\n");
1850 fprintf(fp, "\n");
1851 fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n");
1852 fprintf(fp, " observe=3.14, simple=beef, needs-quotes=\"some string\",\n");
b25da386
SM
1853 fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\",\n");
1854 fprintf(fp, " things=[1, \"2\", 3]\n");
9009cc24
PP
1855 fprintf(fp, "\n");
1856 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n");
1857 fprintf(fp, "babeltrace from a shell.\n");
1858}
1859
1860
1861/*
1862 * Prints the help command usage.
1863 */
1864static
1865void print_help_usage(FILE *fp)
1866{
1867 fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] help [OPTIONS] PLUGIN\n");
e5c7cd9b 1868 fprintf(fp, " babeltrace [GENERAL OPTIONS] help [OPTIONS] TYPE.PLUGIN.CLS\n");
9009cc24
PP
1869 fprintf(fp, "\n");
1870 fprintf(fp, "Options:\n");
1871 fprintf(fp, "\n");
9009cc24
PP
1872 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
1873 fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n");
1874 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
1875 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
1876 fprintf(fp, " dynamic plugins can be loaded\n");
3efa3052 1877 fprintf(fp, " -h, --help Show this help and quit\n");
9009cc24
PP
1878 fprintf(fp, "\n");
1879 fprintf(fp, "See `babeltrace --help` for the list of general options.\n");
1880 fprintf(fp, "\n");
1881 fprintf(fp, "Use `babeltrace list-plugins` to show the list of available plugins.\n");
1882}
1883
1884static
1885struct poptOption help_long_options[] = {
1886 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
9009cc24
PP
1887 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
1888 { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL },
1889 { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL },
1890 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
9009cc24
PP
1891 { NULL, 0, '\0', NULL, 0, NULL, NULL },
1892};
1893
1894/*
1895 * Creates a Babeltrace config object from the arguments of a help
1896 * command.
1897 *
1898 * *retcode is set to the appropriate exit code to use.
1899 */
1900static
1901struct bt_config *bt_config_help_from_args(int argc, const char *argv[],
1902 int *retcode, bool force_omit_system_plugin_path,
1903 bool force_omit_home_plugin_path,
b19ff26f 1904 const bt_value *initial_plugin_paths)
9009cc24
PP
1905{
1906 poptContext pc = NULL;
1907 char *arg = NULL;
1908 int opt;
1909 int ret;
1910 struct bt_config *cfg = NULL;
1911 const char *leftover;
1912 char *plugin_name = NULL, *comp_cls_name = NULL;
9009cc24
PP
1913
1914 *retcode = 0;
1915 cfg = bt_config_help_create(initial_plugin_paths);
1916 if (!cfg) {
1917 goto error;
1918 }
1919
1920 cfg->omit_system_plugin_path = force_omit_system_plugin_path;
1921 cfg->omit_home_plugin_path = force_omit_home_plugin_path;
1922 ret = append_env_var_plugin_paths(cfg->plugin_paths);
1923 if (ret) {
1924 goto error;
1925 }
1926
1927 /* Parse options */
1928 pc = poptGetContext(NULL, argc, (const char **) argv,
1929 help_long_options, 0);
1930 if (!pc) {
1931 printf_err("Cannot get popt context\n");
1932 goto error;
1933 }
1934
1935 poptReadDefaultConfig(pc, 0);
1936
1937 while ((opt = poptGetNextOpt(pc)) > 0) {
1938 arg = poptGetOptArg(pc);
1939
1940 switch (opt) {
1941 case OPT_PLUGIN_PATH:
1942 if (bt_config_append_plugin_paths_check_setuid_setgid(
1943 cfg->plugin_paths, arg)) {
1944 goto error;
1945 }
1946 break;
1947 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
1948 cfg->omit_system_plugin_path = true;
1949 break;
1950 case OPT_OMIT_HOME_PLUGIN_PATH:
1951 cfg->omit_home_plugin_path = true;
1952 break;
9009cc24
PP
1953 case OPT_HELP:
1954 print_help_usage(stdout);
1955 *retcode = -1;
65300d60 1956 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
1957 goto end;
1958 default:
1959 printf_err("Unknown command-line option specified (option code %d)\n",
1960 opt);
1961 goto error;
1962 }
1963
1964 free(arg);
1965 arg = NULL;
1966 }
1967
1968 /* Check for option parsing error */
1969 if (opt < -1) {
1970 printf_err("While parsing command-line options, at option %s: %s\n",
1971 poptBadOption(pc, 0), poptStrerror(opt));
1972 goto error;
1973 }
1974
1975 leftover = poptGetArg(pc);
1976 if (leftover) {
e5c7cd9b 1977 plugin_comp_cls_names(leftover, NULL,
fd5f8053
PP
1978 &plugin_name, &comp_cls_name,
1979 &cfg->cmd_data.help.cfg_component->type);
9009cc24 1980 if (plugin_name && comp_cls_name) {
e5c7cd9b
PP
1981 /* Component class help */
1982 g_string_assign(
1983 cfg->cmd_data.help.cfg_component->plugin_name,
9009cc24 1984 plugin_name);
e5c7cd9b
PP
1985 g_string_assign(
1986 cfg->cmd_data.help.cfg_component->comp_cls_name,
9009cc24
PP
1987 comp_cls_name);
1988 } else {
e5c7cd9b 1989 /* Fall back to plugin help */
e5c7cd9b
PP
1990 g_string_assign(
1991 cfg->cmd_data.help.cfg_component->plugin_name,
1992 leftover);
9009cc24 1993 }
e5c7cd9b
PP
1994 } else {
1995 print_help_usage(stdout);
1996 *retcode = -1;
65300d60 1997 BT_OBJECT_PUT_REF_AND_RESET(cfg);
e5c7cd9b 1998 goto end;
9009cc24
PP
1999 }
2000
2001 if (append_home_and_system_plugin_paths_cfg(cfg)) {
2002 goto error;
2003 }
2004
2005 goto end;
2006
2007error:
2008 *retcode = 1;
65300d60 2009 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
2010
2011end:
9009cc24
PP
2012 g_free(plugin_name);
2013 g_free(comp_cls_name);
2014
2015 if (pc) {
2016 poptFreeContext(pc);
2017 }
2018
2019 free(arg);
2020 return cfg;
2021}
2022
2023/*
2024 * Prints the help command usage.
2025 */
2026static
2027void print_query_usage(FILE *fp)
2028{
7b1e06a1 2029 fprintf(fp, "Usage: babeltrace [GEN OPTS] query [OPTS] TYPE.PLUGIN.CLS OBJECT\n");
9009cc24
PP
2030 fprintf(fp, "\n");
2031 fprintf(fp, "Options:\n");
2032 fprintf(fp, "\n");
9009cc24
PP
2033 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
2034 fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n");
2035 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
2036 fprintf(fp, " -p, --params=PARAMS Set the query parameters to PARAMS\n");
2037 fprintf(fp, " (see the expected format of PARAMS below)\n");
2038 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
2039 fprintf(fp, " dynamic plugins can be loaded\n");
3efa3052 2040 fprintf(fp, " -h, --help Show this help and quit\n");
9009cc24
PP
2041 fprintf(fp, "\n\n");
2042 print_expected_params_format(fp);
2043}
2044
2045static
2046struct poptOption query_long_options[] = {
2047 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
9009cc24
PP
2048 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
2049 { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL },
2050 { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL },
2051 { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL },
2052 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
9009cc24
PP
2053 { NULL, 0, '\0', NULL, 0, NULL, NULL },
2054};
2055
2056/*
2057 * Creates a Babeltrace config object from the arguments of a query
2058 * command.
2059 *
2060 * *retcode is set to the appropriate exit code to use.
2061 */
2062static
2063struct bt_config *bt_config_query_from_args(int argc, const char *argv[],
2064 int *retcode, bool force_omit_system_plugin_path,
2065 bool force_omit_home_plugin_path,
b19ff26f 2066 const bt_value *initial_plugin_paths)
9009cc24
PP
2067{
2068 poptContext pc = NULL;
2069 char *arg = NULL;
2070 int opt;
2071 int ret;
2072 struct bt_config *cfg = NULL;
2073 const char *leftover;
ac72582c
SM
2074 bt_value *params;
2075
2076 params = bt_value_null;
2077 bt_value_get_ref(bt_value_null);
9009cc24
PP
2078
2079 *retcode = 0;
2080 cfg = bt_config_query_create(initial_plugin_paths);
2081 if (!cfg) {
2082 goto error;
2083 }
2084
2085 cfg->omit_system_plugin_path = force_omit_system_plugin_path;
2086 cfg->omit_home_plugin_path = force_omit_home_plugin_path;
2087 ret = append_env_var_plugin_paths(cfg->plugin_paths);
2088 if (ret) {
2089 goto error;
2090 }
2091
2092 /* Parse options */
2093 pc = poptGetContext(NULL, argc, (const char **) argv,
2094 query_long_options, 0);
2095 if (!pc) {
2096 printf_err("Cannot get popt context\n");
2097 goto error;
2098 }
2099
2100 poptReadDefaultConfig(pc, 0);
2101
2102 while ((opt = poptGetNextOpt(pc)) > 0) {
2103 arg = poptGetOptArg(pc);
2104
2105 switch (opt) {
2106 case OPT_PLUGIN_PATH:
2107 if (bt_config_append_plugin_paths_check_setuid_setgid(
2108 cfg->plugin_paths, arg)) {
2109 goto error;
2110 }
2111 break;
2112 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
2113 cfg->omit_system_plugin_path = true;
2114 break;
2115 case OPT_OMIT_HOME_PLUGIN_PATH:
2116 cfg->omit_home_plugin_path = true;
2117 break;
9009cc24
PP
2118 case OPT_PARAMS:
2119 {
c5b9b441 2120 bt_value_put_ref(params);
05e21286 2121 params = bt_value_from_arg(arg);
9009cc24
PP
2122 if (!params) {
2123 printf_err("Invalid format for --params option's argument:\n %s\n",
2124 arg);
2125 goto error;
2126 }
2127 break;
2128 }
2129 case OPT_HELP:
2130 print_query_usage(stdout);
2131 *retcode = -1;
65300d60 2132 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
2133 goto end;
2134 default:
2135 printf_err("Unknown command-line option specified (option code %d)\n",
2136 opt);
2137 goto error;
2138 }
2139
2140 free(arg);
2141 arg = NULL;
2142 }
2143
9009cc24
PP
2144 /* Check for option parsing error */
2145 if (opt < -1) {
2146 printf_err("While parsing command-line options, at option %s: %s\n",
2147 poptBadOption(pc, 0), poptStrerror(opt));
2148 goto error;
2149 }
2150
2151 /*
7b1e06a1
PP
2152 * We need exactly two leftover arguments which are the
2153 * mandatory component class specification and query object.
9009cc24 2154 */
7b1e06a1
PP
2155 leftover = poptGetArg(pc);
2156 if (leftover) {
2157 cfg->cmd_data.query.cfg_component =
2158 bt_config_component_from_arg(leftover);
2159 if (!cfg->cmd_data.query.cfg_component) {
2160 printf_err("Invalid format for component class specification:\n %s\n",
2161 leftover);
2162 goto error;
2163 }
2164
f6ccaed9 2165 BT_ASSERT(params);
da91b29a
PP
2166 BT_OBJECT_MOVE_REF(cfg->cmd_data.query.cfg_component->params,
2167 params);
7b1e06a1
PP
2168 } else {
2169 print_query_usage(stdout);
2170 *retcode = -1;
65300d60 2171 BT_OBJECT_PUT_REF_AND_RESET(cfg);
7b1e06a1
PP
2172 goto end;
2173 }
2174
9009cc24
PP
2175 leftover = poptGetArg(pc);
2176 if (leftover) {
2177 if (strlen(leftover) == 0) {
2178 printf_err("Invalid empty object\n");
2179 goto error;
2180 }
2181
2182 g_string_assign(cfg->cmd_data.query.object, leftover);
2183 } else {
2184 print_query_usage(stdout);
2185 *retcode = -1;
65300d60 2186 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
2187 goto end;
2188 }
2189
2190 leftover = poptGetArg(pc);
2191 if (leftover) {
2192 printf_err("Unexpected argument: %s\n", leftover);
2193 goto error;
2194 }
2195
2196 if (append_home_and_system_plugin_paths_cfg(cfg)) {
2197 goto error;
2198 }
2199
2200 goto end;
2201
2202error:
2203 *retcode = 1;
65300d60 2204 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
2205
2206end:
2207 if (pc) {
2208 poptFreeContext(pc);
2209 }
2210
c5b9b441 2211 bt_value_put_ref(params);
9009cc24
PP
2212 free(arg);
2213 return cfg;
2214}
2215
2216/*
2217 * Prints the list-plugins command usage.
2218 */
2219static
2220void print_list_plugins_usage(FILE *fp)
2221{
2222 fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] list-plugins [OPTIONS]\n");
2223 fprintf(fp, "\n");
2224 fprintf(fp, "Options:\n");
2225 fprintf(fp, "\n");
2226 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
2227 fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n");
2228 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
2229 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
2230 fprintf(fp, " dynamic plugins can be loaded\n");
3efa3052 2231 fprintf(fp, " -h, --help Show this help and quit\n");
9009cc24
PP
2232 fprintf(fp, "\n");
2233 fprintf(fp, "See `babeltrace --help` for the list of general options.\n");
2234 fprintf(fp, "\n");
2235 fprintf(fp, "Use `babeltrace help` to get help for a specific plugin or component class.\n");
2236}
2237
2238static
2239struct poptOption list_plugins_long_options[] = {
2240 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
2241 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
2242 { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL },
2243 { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL },
2244 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
2245 { NULL, 0, '\0', NULL, 0, NULL, NULL },
2246};
2247
2248/*
2249 * Creates a Babeltrace config object from the arguments of a
2250 * list-plugins command.
2251 *
2252 * *retcode is set to the appropriate exit code to use.
2253 */
2254static
2255struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[],
2256 int *retcode, bool force_omit_system_plugin_path,
2257 bool force_omit_home_plugin_path,
b19ff26f 2258 const bt_value *initial_plugin_paths)
9009cc24
PP
2259{
2260 poptContext pc = NULL;
2261 char *arg = NULL;
2262 int opt;
2263 int ret;
2264 struct bt_config *cfg = NULL;
2265 const char *leftover;
2266
2267 *retcode = 0;
2268 cfg = bt_config_list_plugins_create(initial_plugin_paths);
2269 if (!cfg) {
2270 goto error;
2271 }
2272
2273 cfg->omit_system_plugin_path = force_omit_system_plugin_path;
2274 cfg->omit_home_plugin_path = force_omit_home_plugin_path;
2275 ret = append_env_var_plugin_paths(cfg->plugin_paths);
2276 if (ret) {
2277 goto error;
2278 }
2279
2280 /* Parse options */
2281 pc = poptGetContext(NULL, argc, (const char **) argv,
2282 list_plugins_long_options, 0);
2283 if (!pc) {
2284 printf_err("Cannot get popt context\n");
2285 goto error;
2286 }
2287
2288 poptReadDefaultConfig(pc, 0);
2289
2290 while ((opt = poptGetNextOpt(pc)) > 0) {
2291 arg = poptGetOptArg(pc);
2292
2293 switch (opt) {
2294 case OPT_PLUGIN_PATH:
2295 if (bt_config_append_plugin_paths_check_setuid_setgid(
2296 cfg->plugin_paths, arg)) {
2297 goto error;
2298 }
2299 break;
2300 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
2301 cfg->omit_system_plugin_path = true;
2302 break;
2303 case OPT_OMIT_HOME_PLUGIN_PATH:
2304 cfg->omit_home_plugin_path = true;
2305 break;
2306 case OPT_HELP:
2307 print_list_plugins_usage(stdout);
2308 *retcode = -1;
65300d60 2309 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
2310 goto end;
2311 default:
2312 printf_err("Unknown command-line option specified (option code %d)\n",
2313 opt);
2314 goto error;
2315 }
2316
2317 free(arg);
2318 arg = NULL;
2319 }
2320
2321 /* Check for option parsing error */
2322 if (opt < -1) {
2323 printf_err("While parsing command-line options, at option %s: %s\n",
2324 poptBadOption(pc, 0), poptStrerror(opt));
2325 goto error;
2326 }
2327
2328 leftover = poptGetArg(pc);
2329 if (leftover) {
2330 printf_err("Unexpected argument: %s\n", leftover);
2331 goto error;
2332 }
2333
2334 if (append_home_and_system_plugin_paths_cfg(cfg)) {
2335 goto error;
2336 }
2337
2338 goto end;
2339
2340error:
2341 *retcode = 1;
65300d60 2342 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
2343
2344end:
2345 if (pc) {
2346 poptFreeContext(pc);
2347 }
2348
2349 free(arg);
2350 return cfg;
2351}
2352
2353/*
2354 * Prints the run command usage.
2355 */
2356static
2357void print_run_usage(FILE *fp)
2358{
2359 fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] run [OPTIONS]\n");
2360 fprintf(fp, "\n");
2361 fprintf(fp, "Options:\n");
2362 fprintf(fp, "\n");
2363 fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n");
2364 fprintf(fp, " for all the following components until\n");
2365 fprintf(fp, " --reset-base-params is encountered\n");
2366 fprintf(fp, " (see the expected format of PARAMS below)\n");
fd5f8053
PP
2367 fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n");
2368 fprintf(fp, " Instantiate the component class CLS of type\n");
2369 fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n");
2370 fprintf(fp, " in the plugin PLUGIN, add it to the graph,\n");
2371 fprintf(fp, " and optionally name it NAME (you can also\n");
2372 fprintf(fp, " specify the name with --name)\n");
b87236ec 2373 fprintf(fp, " -x, --connect=CONNECTION Connect two created components (see the\n");
9009cc24 2374 fprintf(fp, " expected format of CONNECTION below)\n");
9009cc24
PP
2375 fprintf(fp, " -n, --name=NAME Set the name of the current component\n");
2376 fprintf(fp, " to NAME (must be unique amongst all the\n");
2377 fprintf(fp, " names of the created components)\n");
2378 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
2379 fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n");
2380 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
2381 fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n");
2382 fprintf(fp, " current component (see the expected format\n");
2383 fprintf(fp, " of PARAMS below)\n");
2384 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
2385 fprintf(fp, " dynamic plugins can be loaded\n");
2386 fprintf(fp, " -r, --reset-base-params Reset the current base parameters to an\n");
2387 fprintf(fp, " empty map\n");
2388 fprintf(fp, " --retry-duration=DUR When babeltrace(1) needs to retry to run\n");
2389 fprintf(fp, " the graph later, retry in DUR µs\n");
2390 fprintf(fp, " (default: 100000)\n");
3efa3052 2391 fprintf(fp, " -h, --help Show this help and quit\n");
9009cc24
PP
2392 fprintf(fp, "\n");
2393 fprintf(fp, "See `babeltrace --help` for the list of general options.\n");
2394 fprintf(fp, "\n\n");
2395 fprintf(fp, "Expected format of CONNECTION\n");
2396 fprintf(fp, "-----------------------------\n");
2397 fprintf(fp, "\n");
2398 fprintf(fp, " UPSTREAM[.UPSTREAM-PORT]:DOWNSTREAM[.DOWNSTREAM-PORT]\n");
2399 fprintf(fp, "\n");
2400 fprintf(fp, "UPSTREAM and DOWNSTREAM are names of the upstream and downstream\n");
2401 fprintf(fp, "components to connect together. You must escape the following characters\n\n");
2402 fprintf(fp, "with `\\`: `\\`, `.`, and `:`. You can set the name of the current\n");
2403 fprintf(fp, "component with the --name option.\n");
2404 fprintf(fp, "\n");
2405 fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are optional globbing patterns to\n");
2406 fprintf(fp, "identify the upstream and downstream ports to use for the connection.\n");
2407 fprintf(fp, "When the port is not specified, `*` is used.\n");
2408 fprintf(fp, "\n");
2409 fprintf(fp, "When a component named UPSTREAM has an available port which matches the\n");
2410 fprintf(fp, "UPSTREAM-PORT globbing pattern, it is connected to the first port which\n");
2411 fprintf(fp, "matches the DOWNSTREAM-PORT globbing pattern of the component named\n");
2412 fprintf(fp, "DOWNSTREAM.\n");
2413 fprintf(fp, "\n");
2414 fprintf(fp, "The only special character in UPSTREAM-PORT and DOWNSTREAM-PORT is `*`\n");
2415 fprintf(fp, "which matches anything. You must escape the following characters\n");
2416 fprintf(fp, "with `\\`: `\\`, `*`, `?`, `[`, `.`, and `:`.\n");
2417 fprintf(fp, "\n");
2418 fprintf(fp, "You can connect a source component to a filter or sink component. You\n");
2419 fprintf(fp, "can connect a filter component to a sink component.\n");
2420 fprintf(fp, "\n");
2421 fprintf(fp, "Examples:\n");
2422 fprintf(fp, "\n");
2423 fprintf(fp, " my-src:my-sink\n");
2424 fprintf(fp, " ctf-fs.*stream*:utils-muxer:*\n");
2425 fprintf(fp, "\n");
2426 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n");
2427 fprintf(fp, "babeltrace from a shell.\n");
2428 fprintf(fp, "\n\n");
2429 print_expected_params_format(fp);
2430}
2431
2432/*
2433 * Creates a Babeltrace config object from the arguments of a run
2434 * command.
2435 *
2436 * *retcode is set to the appropriate exit code to use.
2437 */
2438static
2439struct bt_config *bt_config_run_from_args(int argc, const char *argv[],
2440 int *retcode, bool force_omit_system_plugin_path,
2441 bool force_omit_home_plugin_path,
b19ff26f 2442 const bt_value *initial_plugin_paths)
9009cc24
PP
2443{
2444 poptContext pc = NULL;
2445 char *arg = NULL;
2446 struct bt_config_component *cur_cfg_comp = NULL;
0a011c88
JG
2447 enum bt_config_component_dest cur_cfg_comp_dest =
2448 BT_CONFIG_COMPONENT_DEST_UNKNOWN;
b19ff26f 2449 bt_value *cur_base_params = NULL;
9009cc24
PP
2450 int opt, ret = 0;
2451 struct bt_config *cfg = NULL;
b19ff26f
PP
2452 bt_value *instance_names = NULL;
2453 bt_value *connection_args = NULL;
9009cc24 2454 char error_buf[256] = { 0 };
c0521175 2455 long retry_duration = -1;
4cdfc5e8 2456 bt_value_status status;
9009cc24
PP
2457 struct poptOption run_long_options[] = {
2458 { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL },
fd5f8053 2459 { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL },
b87236ec 2460 { "connect", 'x', POPT_ARG_STRING, NULL, OPT_CONNECT, NULL, NULL },
9009cc24 2461 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
9009cc24
PP
2462 { "name", 'n', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL },
2463 { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL },
2464 { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL },
2465 { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL },
2466 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
2467 { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL },
c0521175 2468 { "retry-duration", '\0', POPT_ARG_LONG, &retry_duration, OPT_RETRY_DURATION, NULL, NULL },
9009cc24
PP
2469 { NULL, 0, '\0', NULL, 0, NULL, NULL },
2470 };
2471
2472 *retcode = 0;
9009cc24
PP
2473
2474 if (argc <= 1) {
2475 print_run_usage(stdout);
2476 *retcode = -1;
2477 goto end;
2478 }
2479
2480 cfg = bt_config_run_create(initial_plugin_paths);
2481 if (!cfg) {
2482 goto error;
2483 }
2484
2485 cfg->cmd_data.run.retry_duration_us = 100000;
2486 cfg->omit_system_plugin_path = force_omit_system_plugin_path;
2487 cfg->omit_home_plugin_path = force_omit_home_plugin_path;
05e21286 2488 cur_base_params = bt_value_map_create();
9009cc24
PP
2489 if (!cur_base_params) {
2490 print_err_oom();
2491 goto error;
2492 }
2493
05e21286 2494 instance_names = bt_value_map_create();
9009cc24
PP
2495 if (!instance_names) {
2496 print_err_oom();
2497 goto error;
2498 }
2499
05e21286 2500 connection_args = bt_value_array_create();
9009cc24
PP
2501 if (!connection_args) {
2502 print_err_oom();
2503 goto error;
2504 }
2505
2506 ret = append_env_var_plugin_paths(cfg->plugin_paths);
2507 if (ret) {
2508 goto error;
2509 }
2510
2511 /* Parse options */
2512 pc = poptGetContext(NULL, argc, (const char **) argv,
2513 run_long_options, 0);
2514 if (!pc) {
2515 printf_err("Cannot get popt context\n");
2516 goto error;
2517 }
2518
2519 poptReadDefaultConfig(pc, 0);
2520
2521 while ((opt = poptGetNextOpt(pc)) > 0) {
2522 arg = poptGetOptArg(pc);
2523
2524 switch (opt) {
2525 case OPT_PLUGIN_PATH:
2526 if (bt_config_append_plugin_paths_check_setuid_setgid(
2527 cfg->plugin_paths, arg)) {
2528 goto error;
2529 }
2530 break;
2531 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
2532 cfg->omit_system_plugin_path = true;
2533 break;
2534 case OPT_OMIT_HOME_PLUGIN_PATH:
2535 cfg->omit_home_plugin_path = true;
2536 break;
fd5f8053 2537 case OPT_COMPONENT:
9009cc24 2538 {
9009cc24 2539 enum bt_config_component_dest new_dest;
9009cc24
PP
2540
2541 if (cur_cfg_comp) {
2542 ret = add_run_cfg_comp_check_name(cfg,
2543 cur_cfg_comp, cur_cfg_comp_dest,
2544 instance_names);
65300d60 2545 BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp);
9009cc24
PP
2546 if (ret) {
2547 goto error;
2548 }
2549 }
2550
fd5f8053 2551 cur_cfg_comp = bt_config_component_from_arg(arg);
9009cc24 2552 if (!cur_cfg_comp) {
fd5f8053
PP
2553 printf_err("Invalid format for --component option's argument:\n %s\n",
2554 arg);
9009cc24
PP
2555 goto error;
2556 }
2557
fd5f8053
PP
2558 switch (cur_cfg_comp->type) {
2559 case BT_COMPONENT_CLASS_TYPE_SOURCE:
2560 new_dest = BT_CONFIG_COMPONENT_DEST_SOURCE;
2561 break;
2562 case BT_COMPONENT_CLASS_TYPE_FILTER:
2563 new_dest = BT_CONFIG_COMPONENT_DEST_FILTER;
2564 break;
2565 case BT_COMPONENT_CLASS_TYPE_SINK:
2566 new_dest = BT_CONFIG_COMPONENT_DEST_SINK;
2567 break;
2568 default:
2569 abort();
2570 }
2571
f6ccaed9 2572 BT_ASSERT(cur_base_params);
c5b9b441 2573 bt_value_put_ref(cur_cfg_comp->params);
6be5a99e
PP
2574 status = bt_value_copy(cur_base_params,
2575 &cur_cfg_comp->params);
601b0d3c 2576 if (status != BT_VALUE_STATUS_OK) {
9009cc24
PP
2577 print_err_oom();
2578 goto error;
2579 }
2580
2581 cur_cfg_comp_dest = new_dest;
2582 break;
2583 }
2584 case OPT_PARAMS:
2585 {
b19ff26f
PP
2586 bt_value *params;
2587 bt_value *params_to_set;
9009cc24
PP
2588
2589 if (!cur_cfg_comp) {
2590 printf_err("Cannot add parameters to unavailable component:\n %s\n",
2591 arg);
2592 goto error;
2593 }
2594
05e21286 2595 params = bt_value_from_arg(arg);
9009cc24
PP
2596 if (!params) {
2597 printf_err("Invalid format for --params option's argument:\n %s\n",
2598 arg);
2599 goto error;
2600 }
2601
35294e83
PP
2602 status = bt_value_map_extend(cur_cfg_comp->params,
2603 params, &params_to_set);
c5b9b441 2604 BT_VALUE_PUT_REF_AND_RESET(params);
601b0d3c 2605 if (status != BT_VALUE_STATUS_OK) {
9009cc24
PP
2606 printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n",
2607 arg);
2608 goto error;
2609 }
2610
65300d60 2611 BT_OBJECT_MOVE_REF(cur_cfg_comp->params, params_to_set);
9009cc24
PP
2612 break;
2613 }
9009cc24
PP
2614 case OPT_NAME:
2615 if (!cur_cfg_comp) {
2616 printf_err("Cannot set the name of unavailable component:\n %s\n",
2617 arg);
2618 goto error;
2619 }
2620
2621 g_string_assign(cur_cfg_comp->instance_name, arg);
2622 break;
2623 case OPT_BASE_PARAMS:
2624 {
b19ff26f 2625 bt_value *params =
05e21286 2626 bt_value_from_arg(arg);
9009cc24
PP
2627
2628 if (!params) {
2629 printf_err("Invalid format for --base-params option's argument:\n %s\n",
2630 arg);
2631 goto error;
2632 }
2633
65300d60 2634 BT_OBJECT_MOVE_REF(cur_base_params, params);
9009cc24
PP
2635 break;
2636 }
2637 case OPT_RESET_BASE_PARAMS:
c5b9b441 2638 BT_VALUE_PUT_REF_AND_RESET(cur_base_params);
05e21286 2639 cur_base_params = bt_value_map_create();
9009cc24
PP
2640 if (!cur_base_params) {
2641 print_err_oom();
2642 goto error;
2643 }
2644 break;
2645 case OPT_CONNECT:
05e21286 2646 if (bt_value_array_append_string_element(
07208d85 2647 connection_args, arg)) {
9009cc24
PP
2648 print_err_oom();
2649 goto error;
2650 }
2651 break;
2652 case OPT_RETRY_DURATION:
2653 if (retry_duration < 0) {
5b70610d 2654 printf_err("--retry-duration option's argument must be positive or 0: %ld\n",
9009cc24
PP
2655 retry_duration);
2656 goto error;
2657 }
2658
2659 cfg->cmd_data.run.retry_duration_us =
2660 (uint64_t) retry_duration;
2661 break;
2662 case OPT_HELP:
2663 print_run_usage(stdout);
2664 *retcode = -1;
65300d60 2665 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
2666 goto end;
2667 default:
2668 printf_err("Unknown command-line option specified (option code %d)\n",
2669 opt);
2670 goto error;
2671 }
2672
2673 free(arg);
2674 arg = NULL;
2675 }
2676
2677 /* Check for option parsing error */
2678 if (opt < -1) {
2679 printf_err("While parsing command-line options, at option %s: %s\n",
2680 poptBadOption(pc, 0), poptStrerror(opt));
2681 goto error;
2682 }
2683
2684 /* This command does not accept leftover arguments */
2685 if (poptPeekArg(pc)) {
2686 printf_err("Unexpected argument: %s\n", poptPeekArg(pc));
2687 goto error;
2688 }
2689
2690 /* Add current component */
2691 if (cur_cfg_comp) {
2692 ret = add_run_cfg_comp_check_name(cfg, cur_cfg_comp,
2693 cur_cfg_comp_dest, instance_names);
65300d60 2694 BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp);
9009cc24
PP
2695 if (ret) {
2696 goto error;
2697 }
2698 }
2699
2700 if (cfg->cmd_data.run.sources->len == 0) {
2701 printf_err("Incomplete graph: no source component\n");
2702 goto error;
2703 }
2704
2705 if (cfg->cmd_data.run.sinks->len == 0) {
2706 printf_err("Incomplete graph: no sink component\n");
2707 goto error;
2708 }
2709
2710 if (append_home_and_system_plugin_paths_cfg(cfg)) {
2711 goto error;
2712 }
2713
da91b29a 2714 ret = bt_config_cli_args_create_connections(cfg,
05e21286 2715 connection_args,
9009cc24
PP
2716 error_buf, 256);
2717 if (ret) {
2718 printf_err("Cannot creation connections:\n%s", error_buf);
2719 goto error;
2720 }
2721
2722 goto end;
2723
2724error:
2725 *retcode = 1;
65300d60 2726 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
2727
2728end:
2729 if (pc) {
2730 poptFreeContext(pc);
2731 }
2732
9009cc24 2733 free(arg);
65300d60 2734 BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp);
c5b9b441
PP
2735 BT_VALUE_PUT_REF_AND_RESET(cur_base_params);
2736 BT_VALUE_PUT_REF_AND_RESET(instance_names);
2737 BT_VALUE_PUT_REF_AND_RESET(connection_args);
9009cc24
PP
2738 return cfg;
2739}
2740
2741static
b19ff26f 2742struct bt_config *bt_config_run_from_args_array(const bt_value *run_args,
9009cc24
PP
2743 int *retcode, bool force_omit_system_plugin_path,
2744 bool force_omit_home_plugin_path,
b19ff26f 2745 const bt_value *initial_plugin_paths)
9009cc24
PP
2746{
2747 struct bt_config *cfg = NULL;
2748 const char **argv;
0ca8409d 2749 int64_t i, len;
07208d85 2750 const size_t argc = bt_value_array_get_size(run_args) + 1;
9009cc24
PP
2751
2752 argv = calloc(argc, sizeof(*argv));
2753 if (!argv) {
2754 print_err_oom();
2755 goto end;
2756 }
2757
2758 argv[0] = "run";
2759
07208d85 2760 len = bt_value_array_get_size(run_args);
0ca8409d
MD
2761 if (len < 0) {
2762 printf_err("Invalid executable arguments\n");
2763 goto end;
2764 }
2765 for (i = 0; i < len; i++) {
b19ff26f 2766 const bt_value *arg_value =
05e21286
PP
2767 bt_value_array_borrow_element_by_index_const(run_args,
2768 i);
9009cc24
PP
2769 const char *arg;
2770
f6ccaed9 2771 BT_ASSERT(arg_value);
601b0d3c 2772 arg = bt_value_string_get(arg_value);
f6ccaed9 2773 BT_ASSERT(arg);
9009cc24 2774 argv[i + 1] = arg;
9009cc24
PP
2775 }
2776
2777 cfg = bt_config_run_from_args(argc, argv, retcode,
2778 force_omit_system_plugin_path, force_omit_home_plugin_path,
2779 initial_plugin_paths);
2780
2781end:
2782 free(argv);
2783 return cfg;
2784}
2785
2786/*
2787 * Prints the convert command usage.
2788 */
2789static
2790void print_convert_usage(FILE *fp)
2791{
2792 fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] [convert] [OPTIONS] [PATH/URL]\n");
2793 fprintf(fp, "\n");
2794 fprintf(fp, "Options:\n");
2795 fprintf(fp, "\n");
fd5f8053
PP
2796 fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n");
2797 fprintf(fp, " Instantiate the component class CLS of type\n");
2798 fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n");
2799 fprintf(fp, " in the plugin PLUGIN, add it to the\n");
2800 fprintf(fp, " conversion graph, and optionally name it\n");
2801 fprintf(fp, " NAME (you can also specify the name with\n");
2802 fprintf(fp, " --name)\n");
9009cc24
PP
2803 fprintf(fp, " --name=NAME Set the name of the current component\n");
2804 fprintf(fp, " to NAME (must be unique amongst all the\n");
2805 fprintf(fp, " names of the created components)\n");
2806 fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n");
2807 fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n");
2808 fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n");
2809 fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n");
2810 fprintf(fp, " current component (see the expected format\n");
2811 fprintf(fp, " of PARAMS below)\n");
2812 fprintf(fp, " -P, --path=PATH Set the `path` string parameter of the\n");
2813 fprintf(fp, " current component to PATH\n");
2814 fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n");
2815 fprintf(fp, " --retry-duration=DUR When babeltrace(1) needs to retry to run\n");
2816 fprintf(fp, " the graph later, retry in DUR µs\n");
2817 fprintf(fp, " (default: 100000)\n");
2818 fprintf(fp, " dynamic plugins can be loaded\n");
2819 fprintf(fp, " --run-args Print the equivalent arguments for the\n");
2820 fprintf(fp, " `run` command to the standard output,\n");
2821 fprintf(fp, " formatted for a shell, and quit\n");
2822 fprintf(fp, " --run-args-0 Print the equivalent arguments for the\n");
2823 fprintf(fp, " `run` command to the standard output,\n");
2824 fprintf(fp, " formatted for `xargs -0`, and quit\n");
8ed0bf10
PP
2825 fprintf(fp, " --stream-intersection Only process events when all streams\n");
2826 fprintf(fp, " are active\n");
9009cc24
PP
2827 fprintf(fp, " -u, --url=URL Set the `url` string parameter of the\n");
2828 fprintf(fp, " current component to URL\n");
3efa3052 2829 fprintf(fp, " -h, --help Show this help and quit\n");
9009cc24 2830 fprintf(fp, "\n");
e7ad156c 2831 fprintf(fp, "Implicit `source.ctf.fs` component options:\n");
9009cc24
PP
2832 fprintf(fp, "\n");
2833 fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n");
2834 fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS ns\n");
9009cc24 2835 fprintf(fp, "\n");
e7ad156c 2836 fprintf(fp, "Implicit `sink.text.pretty` component options:\n");
9009cc24
PP
2837 fprintf(fp, "\n");
2838 fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n");
2839 fprintf(fp, " --clock-date Print timestamp dates\n");
2840 fprintf(fp, " --clock-gmt Print and parse timestamps in the GMT\n");
2841 fprintf(fp, " time zone instead of the local time zone\n");
2842 fprintf(fp, " --clock-seconds Print the timestamps as `SEC.NS` instead\n");
2843 fprintf(fp, " of `hh:mm:ss.nnnnnnnnn`\n");
2844 fprintf(fp, " --color=(never | auto | always)\n");
2845 fprintf(fp, " Never, automatically, or always emit\n");
2846 fprintf(fp, " console color codes\n");
2847 fprintf(fp, " -f, --fields=FIELD[,FIELD]... Print additional fields; FIELD can be:\n");
2848 fprintf(fp, " `all`, `trace`, `trace:hostname`,\n");
2849 fprintf(fp, " `trace:domain`, `trace:procname`,\n");
2850 fprintf(fp, " `trace:vpid`, `loglevel`, `emf`\n");
2851 fprintf(fp, " -n, --names=NAME[,NAME]... Print field names; NAME can be:\n");
2852 fprintf(fp, " `payload` (or `arg` or `args`), `none`,\n");
2853 fprintf(fp, " `all`, `scope`, `header`, `context`\n");
2854 fprintf(fp, " (or `ctx`)\n");
2855 fprintf(fp, " --no-delta Do not print time delta between\n");
2856 fprintf(fp, " consecutive events\n");
2857 fprintf(fp, " -w, --output=PATH Write output text to PATH instead of\n");
2858 fprintf(fp, " the standard output\n");
2859 fprintf(fp, "\n");
e7ad156c 2860 fprintf(fp, "Implicit `filter.utils.muxer` component options:\n");
9009cc24
PP
2861 fprintf(fp, "\n");
2862 fprintf(fp, " --clock-force-correlate Assume that clocks are inherently\n");
2863 fprintf(fp, " correlated across traces\n");
2864 fprintf(fp, "\n");
e7ad156c 2865 fprintf(fp, "Implicit `filter.utils.trimmer` component options:\n");
9009cc24
PP
2866 fprintf(fp, "\n");
2867 fprintf(fp, " -b, --begin=BEGIN Set the beginning time of the conversion\n");
2868 fprintf(fp, " time range to BEGIN (see the format of\n");
2869 fprintf(fp, " BEGIN below)\n");
2870 fprintf(fp, " -e, --end=END Set the end time of the conversion time\n");
2871 fprintf(fp, " range to END (see the format of END below)\n");
2872 fprintf(fp, " -t, --timerange=TIMERANGE Set conversion time range to TIMERANGE:\n");
2873 fprintf(fp, " BEGIN,END or [BEGIN,END] (literally `[` and\n");
2874 fprintf(fp, " `]`) (see the format of BEGIN/END below)\n");
2875 fprintf(fp, "\n");
e7ad156c 2876 fprintf(fp, "Implicit `filter.lttng-utils.debug-info` component options:\n");
9009cc24 2877 fprintf(fp, "\n");
9a16feea
PP
2878 fprintf(fp, " --debug-info Create an implicit\n");
2879 fprintf(fp, " `filter.lttng-utils.debug-info` component\n");
9009cc24
PP
2880 fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n");
2881 fprintf(fp, " instead of `/usr/lib/debug`\n");
2882 fprintf(fp, " --debug-info-full-path Show full debug info source and\n");
2883 fprintf(fp, " binary paths instead of just names\n");
2884 fprintf(fp, " --debug-info-target-prefix=DIR\n");
2885 fprintf(fp, " Use directory DIR as a prefix when\n");
2886 fprintf(fp, " looking up executables during debug\n");
2887 fprintf(fp, " info analysis\n");
9009cc24
PP
2888 fprintf(fp, "\n");
2889 fprintf(fp, "Legacy options that still work:\n");
2890 fprintf(fp, "\n");
2891 fprintf(fp, " -i, --input-format=(ctf | lttng-live)\n");
2892 fprintf(fp, " `ctf`:\n");
fd5f8053 2893 fprintf(fp, " Create an implicit `source.ctf.fs`\n");
9009cc24
PP
2894 fprintf(fp, " component\n");
2895 fprintf(fp, " `lttng-live`:\n");
fd5f8053
PP
2896 fprintf(fp, " Create an implicit `source.ctf.lttng-live`\n");
2897 fprintf(fp, " component\n");
e7ad156c 2898 fprintf(fp, " -o, --output-format=(text | ctf | dummy | ctf-metadata)\n");
9009cc24 2899 fprintf(fp, " `text`:\n");
fd5f8053 2900 fprintf(fp, " Create an implicit `sink.text.pretty`\n");
9009cc24 2901 fprintf(fp, " component\n");
dd46f7ea 2902 fprintf(fp, " `ctf`:\n");
e7ad156c
PP
2903 fprintf(fp, " Create an implicit `sink.ctf.fs`\n");
2904 fprintf(fp, " component\n");
9009cc24 2905 fprintf(fp, " `dummy`:\n");
fd5f8053 2906 fprintf(fp, " Create an implicit `sink.utils.dummy`\n");
9009cc24
PP
2907 fprintf(fp, " component\n");
2908 fprintf(fp, " `ctf-metadata`:\n");
fd5f8053
PP
2909 fprintf(fp, " Query the `source.ctf.fs` component class\n");
2910 fprintf(fp, " for metadata text and quit\n");
9009cc24
PP
2911 fprintf(fp, "\n");
2912 fprintf(fp, "See `babeltrace --help` for the list of general options.\n");
2913 fprintf(fp, "\n\n");
2914 fprintf(fp, "Format of BEGIN and END\n");
2915 fprintf(fp, "-----------------------\n");
2916 fprintf(fp, "\n");
2917 fprintf(fp, " [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n");
2918 fprintf(fp, "\n\n");
2919 print_expected_params_format(fp);
2920}
2921
2922static
2923struct poptOption convert_long_options[] = {
2924 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
2925 { "begin", 'b', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL },
2926 { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL },
2927 { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL },
2928 { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL },
2929 { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL },
2930 { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL },
2931 { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL },
2932 { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL },
2933 { "color", '\0', POPT_ARG_STRING, NULL, OPT_COLOR, NULL, NULL },
fd5f8053 2934 { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL },
9009cc24
PP
2935 { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL },
2936 { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL },
2937 { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL },
2938 { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL },
2939 { "end", 'e', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL },
2940 { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL },
9009cc24
PP
2941 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
2942 { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL },
2943 { "name", '\0', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL },
2944 { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL },
9a16feea 2945 { "debug-info", '\0', POPT_ARG_NONE, NULL, OPT_DEBUG_INFO, NULL, NULL },
9009cc24
PP
2946 { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL },
2947 { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL },
2948 { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL },
2949 { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT, NULL, NULL },
2950 { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL },
2951 { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL },
2952 { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL },
2953 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
2954 { "retry-duration", '\0', POPT_ARG_STRING, NULL, OPT_RETRY_DURATION, NULL, NULL },
2955 { "run-args", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS, NULL, NULL },
2956 { "run-args-0", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS_0, NULL, NULL },
9009cc24
PP
2957 { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL },
2958 { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL },
2959 { "url", 'u', POPT_ARG_STRING, NULL, OPT_URL, NULL, NULL },
2960 { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL },
2961 { NULL, 0, '\0', NULL, 0, NULL, NULL },
2962};
2963
2964static
2965GString *get_component_auto_name(const char *prefix,
b19ff26f 2966 const bt_value *existing_names)
9009cc24
PP
2967{
2968 unsigned int i = 0;
2969 GString *auto_name = g_string_new(NULL);
2970
2971 if (!auto_name) {
2972 print_err_oom();
2973 goto end;
2974 }
2975
07208d85 2976 if (!bt_value_map_has_entry(existing_names, prefix)) {
9009cc24
PP
2977 g_string_assign(auto_name, prefix);
2978 goto end;
2979 }
2980
2981 do {
2982 g_string_printf(auto_name, "%s-%d", prefix, i);
2983 i++;
07208d85 2984 } while (bt_value_map_has_entry(existing_names, auto_name->str));
9009cc24
PP
2985
2986end:
2987 return auto_name;
2988}
2989
2990struct implicit_component_args {
2991 bool exists;
fd5f8053 2992 GString *comp_arg;
9009cc24
PP
2993 GString *name_arg;
2994 GString *params_arg;
b19ff26f 2995 bt_value *extra_params;
9009cc24
PP
2996};
2997
2998static
2999int assign_name_to_implicit_component(struct implicit_component_args *args,
b19ff26f 3000 const char *prefix, bt_value *existing_names,
9009cc24
PP
3001 GList **comp_names, bool append_to_comp_names)
3002{
3003 int ret = 0;
3004 GString *name = NULL;
3005
3006 if (!args->exists) {
3007 goto end;
3008 }
3009
da91b29a 3010 name = get_component_auto_name(prefix,
05e21286 3011 existing_names);
9009cc24
PP
3012
3013 if (!name) {
3014 ret = -1;
3015 goto end;
3016 }
3017
3018 g_string_assign(args->name_arg, name->str);
3019
05e21286 3020 if (bt_value_map_insert_entry(existing_names, name->str,
9009cc24
PP
3021 bt_value_null)) {
3022 print_err_oom();
3023 ret = -1;
3024 goto end;
3025 }
3026
3027 if (append_to_comp_names) {
3028 *comp_names = g_list_append(*comp_names, name);
3029 name = NULL;
3030 }
3031
3032end:
3033 if (name) {
3034 g_string_free(name, TRUE);
3035 }
3036
3037 return ret;
3038}
3039
3040static
3041int append_run_args_for_implicit_component(
9009cc24 3042 struct implicit_component_args *impl_args,
b19ff26f 3043 bt_value *run_args)
9009cc24
PP
3044{
3045 int ret = 0;
3046 size_t i;
3047
3048 if (!impl_args->exists) {
3049 goto end;
3050 }
3051
05e21286 3052 if (bt_value_array_append_string_element(run_args, "--component")) {
fd5f8053
PP
3053 print_err_oom();
3054 goto error;
9009cc24
PP
3055 }
3056
05e21286 3057 if (bt_value_array_append_string_element(run_args, impl_args->comp_arg->str)) {
9009cc24
PP
3058 print_err_oom();
3059 goto error;
3060 }
3061
05e21286 3062 if (bt_value_array_append_string_element(run_args, "--name")) {
9009cc24
PP
3063 print_err_oom();
3064 goto error;
3065 }
3066
05e21286 3067 if (bt_value_array_append_string_element(run_args, impl_args->name_arg->str)) {
9009cc24
PP
3068 print_err_oom();
3069 goto error;
3070 }
3071
3072 if (impl_args->params_arg->len > 0) {
05e21286 3073 if (bt_value_array_append_string_element(run_args, "--params")) {
9009cc24
PP
3074 print_err_oom();
3075 goto error;
3076 }
3077
05e21286 3078 if (bt_value_array_append_string_element(run_args,
9009cc24
PP
3079 impl_args->params_arg->str)) {
3080 print_err_oom();
3081 goto error;
3082 }
3083 }
3084
05e21286 3085 for (i = 0; i < bt_value_array_get_size(impl_args->extra_params);
da91b29a 3086 i++) {
b19ff26f 3087 const bt_value *elem;
9009cc24
PP
3088 const char *arg;
3089
05e21286
PP
3090 elem = bt_value_array_borrow_element_by_index(impl_args->extra_params,
3091 i);
9009cc24
PP
3092 if (!elem) {
3093 goto error;
3094 }
3095
f6ccaed9 3096 BT_ASSERT(bt_value_is_string(elem));
601b0d3c 3097 arg = bt_value_string_get(elem);
05e21286 3098 ret = bt_value_array_append_string_element(run_args, arg);
9009cc24
PP
3099 if (ret) {
3100 print_err_oom();
3101 goto error;
3102 }
3103 }
3104
3105 goto end;
3106
3107error:
3108 ret = -1;
3109
3110end:
3111 return ret;
3112}
3113
3114static
94023a1c 3115void finalize_implicit_component_args(struct implicit_component_args *args)
9009cc24 3116{
f6ccaed9 3117 BT_ASSERT(args);
9009cc24 3118
fd5f8053
PP
3119 if (args->comp_arg) {
3120 g_string_free(args->comp_arg, TRUE);
9009cc24
PP
3121 }
3122
3123 if (args->name_arg) {
3124 g_string_free(args->name_arg, TRUE);
3125 }
3126
3127 if (args->params_arg) {
3128 g_string_free(args->params_arg, TRUE);
3129 }
3130
c5b9b441 3131 bt_value_put_ref(args->extra_params);
9009cc24
PP
3132}
3133
3134static
3135int init_implicit_component_args(struct implicit_component_args *args,
fd5f8053 3136 const char *comp_arg, bool exists)
9009cc24
PP
3137{
3138 int ret = 0;
3139
3140 args->exists = exists;
fd5f8053 3141 args->comp_arg = g_string_new(comp_arg);
9009cc24
PP
3142 args->name_arg = g_string_new(NULL);
3143 args->params_arg = g_string_new(NULL);
05e21286 3144 args->extra_params = bt_value_array_create();
9009cc24 3145
fd5f8053 3146 if (!args->comp_arg || !args->name_arg ||
e7ad156c 3147 !args->params_arg || !args->extra_params) {
9009cc24 3148 ret = -1;
94023a1c 3149 finalize_implicit_component_args(args);
9009cc24
PP
3150 print_err_oom();
3151 goto end;
3152 }
3153
3154end:
3155 return ret;
3156}
3157
3158static
3159void append_implicit_component_param(struct implicit_component_args *args,
3160 const char *key, const char *value)
3161{
f6ccaed9
PP
3162 BT_ASSERT(args);
3163 BT_ASSERT(key);
3164 BT_ASSERT(value);
9009cc24
PP
3165 append_param_arg(args->params_arg, key, value);
3166}
3167
c7b0cd78 3168/* Escape value to make it suitable to use as a string parameter value. */
9009cc24 3169static
c7b0cd78 3170gchar *escape_string_value(const char *value)
9009cc24 3171{
c7b0cd78
SM
3172 GString *ret;
3173 const char *in;
3174
3175 ret = g_string_new(NULL);
3176 if (!ret) {
3177 print_err_oom();
3178 goto end;
3179 }
3180
3181 in = value;
3182 while (*in) {
3183 switch (*in) {
3184 case '"':
3185 case '\\':
3186 g_string_append_c(ret, '\\');
3187 break;
3188 }
3189
3190 g_string_append_c(ret, *in);
3191
3192 in++;
3193 }
3194
3195end:
3196 return g_string_free(ret, FALSE);
3197}
9009cc24 3198
77957ab5 3199static
f280892e 3200int bt_value_to_cli_param_value_append(const bt_value *value, GString *buf)
77957ab5 3201{
f280892e 3202 BT_ASSERT(buf);
77957ab5 3203
f280892e 3204 int ret = -1;
77957ab5
SM
3205
3206 switch (bt_value_get_type(value)) {
3207 case BT_VALUE_TYPE_STRING:
3208 {
3209 const char *str_value = bt_value_string_get(value);
3210 gchar *escaped_str_value;
3211
3212 escaped_str_value = escape_string_value(str_value);
3213 if (!escaped_str_value) {
f280892e 3214 goto end;
77957ab5
SM
3215 }
3216
f280892e 3217 g_string_append_printf(buf, "\"%s\"", escaped_str_value);
77957ab5
SM
3218
3219 g_free(escaped_str_value);
3220 break;
3221 }
f280892e
SM
3222 case BT_VALUE_TYPE_ARRAY: {
3223 g_string_append_c(buf, '[');
3224 uint64_t sz = bt_value_array_get_size(value);
3225 for (uint64_t i = 0; i < sz; i++) {
bea5b968
PP
3226 const bt_value *item;
3227 int ret;
3228
f280892e 3229 if (i > 0) {
bea5b968 3230 g_string_append(buf, ", ");
f280892e 3231 }
bea5b968
PP
3232
3233 item = bt_value_array_borrow_element_by_index_const(
3234 value, i);
3235 ret = bt_value_to_cli_param_value_append(item, buf);
3236
f280892e
SM
3237 if (ret) {
3238 goto end;
3239 }
3240 }
3241 g_string_append_c(buf, ']');
3242 break;
3243 }
77957ab5
SM
3244 default:
3245 abort();
3246 }
3247
f280892e
SM
3248 ret = 0;
3249
3250end:
3251 return ret;
3252}
3253
3254/*
3255 * Convert `value` to its equivalent representation as a command line parameter
3256 * value.
3257 */
3258
3259static
3260gchar *bt_value_to_cli_param_value(bt_value *value)
3261{
3262 GString *buf;
3263 gchar *result = NULL;
3264
3265 buf = g_string_new(NULL);
3266 if (!buf) {
3267 print_err_oom();
3268 goto error;
3269 }
3270
3271 if (bt_value_to_cli_param_value_append(value, buf)) {
3272 goto error;
3273 }
3274
77957ab5
SM
3275 result = g_string_free(buf, FALSE);
3276 buf = NULL;
3277
3278 goto end;
3279
3280error:
3281 if (buf) {
3282 g_string_free(buf, TRUE);
3283 }
3284
3285end:
3286 return result;
3287}
3288
c7b0cd78 3289static
77957ab5 3290int append_parameter_to_args(bt_value *args, const char *key, bt_value *value)
c7b0cd78 3291{
f6ccaed9 3292 BT_ASSERT(args);
c7b0cd78 3293 BT_ASSERT(bt_value_get_type(args) == BT_VALUE_TYPE_ARRAY);
f6ccaed9
PP
3294 BT_ASSERT(key);
3295 BT_ASSERT(value);
9009cc24 3296
c7b0cd78 3297 int ret = 0;
77957ab5 3298 gchar *str_value = NULL;
c7b0cd78
SM
3299 GString *parameter = NULL;
3300
3301 if (bt_value_array_append_string_element(args, "--params")) {
9009cc24
PP
3302 print_err_oom();
3303 ret = -1;
3304 goto end;
3305 }
3306
77957ab5
SM
3307 str_value = bt_value_to_cli_param_value(value);
3308 if (!str_value) {
9009cc24
PP
3309 ret = -1;
3310 goto end;
3311 }
3312
c7b0cd78
SM
3313 parameter = g_string_new(NULL);
3314 if (!parameter) {
9009cc24
PP
3315 print_err_oom();
3316 ret = -1;
3317 goto end;
3318 }
3319
77957ab5 3320 g_string_printf(parameter, "%s=%s", key, str_value);
c7b0cd78
SM
3321
3322 if (bt_value_array_append_string_element(args, parameter->str)) {
9009cc24
PP
3323 print_err_oom();
3324 ret = -1;
3325 goto end;
3326 }
3327
3328end:
c7b0cd78
SM
3329 if (parameter) {
3330 g_string_free(parameter, TRUE);
3331 parameter = NULL;
3332 }
3333
77957ab5
SM
3334 if (str_value) {
3335 g_free(str_value);
3336 str_value = NULL;
3337 }
3338
3339 return ret;
3340}
3341
3342static
3343int append_string_parameter_to_args(bt_value *args, const char *key, const char *value)
3344{
3345 bt_value *str_value;
3346 int ret;
3347
3348 str_value = bt_value_string_create_init(value);
3349
3350 if (!str_value) {
3351 print_err_oom();
3352 ret = -1;
3353 goto end;
3354 }
3355
3356 ret = append_parameter_to_args(args, key, str_value);
3357
3358end:
3359 BT_VALUE_PUT_REF_AND_RESET(str_value);
9009cc24
PP
3360 return ret;
3361}
3362
c7b0cd78
SM
3363static
3364int append_implicit_component_extra_param(struct implicit_component_args *args,
3365 const char *key, const char *value)
3366{
77957ab5 3367 return append_string_parameter_to_args(args->extra_params, key, value);
c7b0cd78
SM
3368}
3369
9009cc24
PP
3370static
3371int convert_append_name_param(enum bt_config_component_dest dest,
3372 GString *cur_name, GString *cur_name_prefix,
b19ff26f
PP
3373 bt_value *run_args,
3374 bt_value *all_names,
9009cc24
PP
3375 GList **source_names, GList **filter_names,
3376 GList **sink_names)
3377{
3378 int ret = 0;
3379
3380 if (cur_name_prefix->len > 0) {
fd5f8053 3381 /* We're after a --component option */
9009cc24
PP
3382 GString *name = NULL;
3383 bool append_name_opt = false;
3384
3385 if (cur_name->len == 0) {
3386 /*
3387 * No explicit name was provided for the user
3388 * component.
3389 */
fd5f8053 3390 name = get_component_auto_name(cur_name_prefix->str,
05e21286 3391 all_names);
9009cc24
PP
3392 append_name_opt = true;
3393 } else {
3394 /*
3395 * An explicit name was provided for the user
3396 * component.
3397 */
05e21286
PP
3398 if (bt_value_map_has_entry(all_names,
3399 cur_name->str)) {
9009cc24
PP
3400 printf_err("Duplicate component instance name:\n %s\n",
3401 cur_name->str);
3402 goto error;
3403 }
3404
3405 name = g_string_new(cur_name->str);
3406 }
3407
3408 if (!name) {
3409 print_err_oom();
3410 goto error;
3411 }
3412
3413 /*
3414 * Remember this name globally, for the uniqueness of
3415 * all component names.
3416 */
05e21286 3417 if (bt_value_map_insert_entry(all_names, name->str, bt_value_null)) {
9009cc24
PP
3418 print_err_oom();
3419 goto error;
3420 }
3421
3422 /*
3423 * Append the --name option if necessary.
3424 */
3425 if (append_name_opt) {
05e21286 3426 if (bt_value_array_append_string_element(run_args, "--name")) {
9009cc24
PP
3427 print_err_oom();
3428 goto error;
3429 }
3430
05e21286 3431 if (bt_value_array_append_string_element(run_args, name->str)) {
9009cc24
PP
3432 print_err_oom();
3433 goto error;
3434 }
3435 }
3436
3437 /*
3438 * Remember this name specifically for the type of the
3439 * component. This is to create connection arguments.
3440 */
3441 switch (dest) {
3442 case BT_CONFIG_COMPONENT_DEST_SOURCE:
3443 *source_names = g_list_append(*source_names, name);
3444 break;
3445 case BT_CONFIG_COMPONENT_DEST_FILTER:
3446 *filter_names = g_list_append(*filter_names, name);
3447 break;
3448 case BT_CONFIG_COMPONENT_DEST_SINK:
3449 *sink_names = g_list_append(*sink_names, name);
3450 break;
3451 default:
0fbb9a9f 3452 abort();
9009cc24
PP
3453 }
3454
3455 g_string_assign(cur_name_prefix, "");
3456 }
3457
3458 goto end;
3459
3460error:
3461 ret = -1;
3462
3463end:
3464 return ret;
3465}
3466
3467/*
3468 * Escapes `.`, `:`, and `\` of `input` with `\`.
3469 */
3470static
3471GString *escape_dot_colon(const char *input)
3472{
3473 GString *output = g_string_new(NULL);
3474 const char *ch;
3475
3476 if (!output) {
3477 print_err_oom();
3478 goto end;
3479 }
3480
3481 for (ch = input; *ch != '\0'; ch++) {
3482 if (*ch == '\\' || *ch == '.' || *ch == ':') {
3483 g_string_append_c(output, '\\');
3484 }
3485
3486 g_string_append_c(output, *ch);
3487 }
3488
3489end:
3490 return output;
3491}
3492
3493/*
3494 * Appends a --connect option to a list of arguments. `upstream_name`
3495 * and `downstream_name` are escaped with escape_dot_colon() in this
3496 * function.
3497 */
3498static
b19ff26f 3499int append_connect_arg(bt_value *run_args,
9009cc24
PP
3500 const char *upstream_name, const char *downstream_name)
3501{
3502 int ret = 0;
3503 GString *e_upstream_name = escape_dot_colon(upstream_name);
3504 GString *e_downstream_name = escape_dot_colon(downstream_name);
3505 GString *arg = g_string_new(NULL);
3506
3507 if (!e_upstream_name || !e_downstream_name || !arg) {
3508 print_err_oom();
3509 ret = -1;
3510 goto end;
3511 }
3512
05e21286 3513 ret = bt_value_array_append_string_element(run_args, "--connect");
9009cc24
PP
3514 if (ret) {
3515 print_err_oom();
3516 ret = -1;
3517 goto end;
3518 }
3519
3520 g_string_append(arg, e_upstream_name->str);
3521 g_string_append_c(arg, ':');
3522 g_string_append(arg, e_downstream_name->str);
05e21286 3523 ret = bt_value_array_append_string_element(run_args, arg->str);
9009cc24
PP
3524 if (ret) {
3525 print_err_oom();
3526 ret = -1;
3527 goto end;
3528 }
3529
3530end:
3531 if (arg) {
3532 g_string_free(arg, TRUE);
3533 }
3534
3535 if (e_upstream_name) {
3536 g_string_free(e_upstream_name, TRUE);
3537 }
3538
3539 if (e_downstream_name) {
3540 g_string_free(e_downstream_name, TRUE);
3541 }
3542
3543 return ret;
3544}
3545
3546/*
3547 * Appends the run command's --connect options for the convert command.
3548 */
3549static
b19ff26f 3550int convert_auto_connect(bt_value *run_args,
9009cc24
PP
3551 GList *source_names, GList *filter_names,
3552 GList *sink_names)
3553{
3554 int ret = 0;
3555 GList *source_at = source_names;
3556 GList *filter_at = filter_names;
3557 GList *filter_prev;
3558 GList *sink_at = sink_names;
3559
f6ccaed9
PP
3560 BT_ASSERT(source_names);
3561 BT_ASSERT(filter_names);
3562 BT_ASSERT(sink_names);
9009cc24
PP
3563
3564 /* Connect all sources to the first filter */
3565 for (source_at = source_names; source_at != NULL; source_at = g_list_next(source_at)) {
3566 GString *source_name = source_at->data;
3567 GString *filter_name = filter_at->data;
3568
3569 ret = append_connect_arg(run_args, source_name->str,
3570 filter_name->str);
3571 if (ret) {
3572 goto error;
3573 }
3574 }
3575
3576 filter_prev = filter_at;
3577 filter_at = g_list_next(filter_at);
3578
3579 /* Connect remaining filters */
3580 for (; filter_at != NULL; filter_prev = filter_at, filter_at = g_list_next(filter_at)) {
3581 GString *filter_name = filter_at->data;
3582 GString *filter_prev_name = filter_prev->data;
3583
3584 ret = append_connect_arg(run_args, filter_prev_name->str,
3585 filter_name->str);
3586 if (ret) {
3587 goto error;
3588 }
3589 }
3590
3591 /* Connect last filter to all sinks */
3592 for (sink_at = sink_names; sink_at != NULL; sink_at = g_list_next(sink_at)) {
3593 GString *filter_name = filter_prev->data;
3594 GString *sink_name = sink_at->data;
3595
3596 ret = append_connect_arg(run_args, filter_name->str,
3597 sink_name->str);
3598 if (ret) {
3599 goto error;
3600 }
3601 }
3602
3603 goto end;
3604
3605error:
3606 ret = -1;
3607
3608end:
3609 return ret;
3610}
3611
3612static
3613int split_timerange(const char *arg, char **begin, char **end)
3614{
3615 int ret = 0;
3616 const char *ch = arg;
3617 size_t end_pos;
3618 GString *g_begin = NULL;
3619 GString *g_end = NULL;
3620
f6ccaed9 3621 BT_ASSERT(arg);
9009cc24
PP
3622
3623 if (*ch == '[') {
3624 ch++;
3625 }
3626
3627 g_begin = bt_common_string_until(ch, "", ",", &end_pos);
3628 if (!g_begin || ch[end_pos] != ',' || g_begin->len == 0) {
3629 goto error;
3630 }
3631
3632 ch += end_pos + 1;
3633
3634 g_end = bt_common_string_until(ch, "", "]", &end_pos);
3635 if (!g_end || g_end->len == 0) {
3636 goto error;
3637 }
3638
f6ccaed9
PP
3639 BT_ASSERT(begin);
3640 BT_ASSERT(end);
9009cc24
PP
3641 *begin = g_begin->str;
3642 *end = g_end->str;
3643 g_string_free(g_begin, FALSE);
3644 g_string_free(g_end, FALSE);
3645 g_begin = NULL;
3646 g_end = NULL;
3647 goto end;
3648
3649error:
3650 ret = -1;
3651
3652end:
3653 if (g_begin) {
3654 g_string_free(g_begin, TRUE);
3655 }
3656
3657 if (g_end) {
3658 g_string_free(g_end, TRUE);
3659 }
3660
3661 return ret;
3662}
3663
3664static
3665int g_list_prepend_gstring(GList **list, const char *string)
3666{
3667 int ret = 0;
3668 GString *gs = g_string_new(string);
3669
f6ccaed9 3670 BT_ASSERT(list);
9009cc24
PP
3671
3672 if (!gs) {
3673 print_err_oom();
3674 goto end;
3675 }
3676
3677 *list = g_list_prepend(*list, gs);
3678
3679end:
3680 return ret;
3681}
3682
3683/*
3684 * Creates a Babeltrace config object from the arguments of a convert
3685 * command.
3686 *
3687 * *retcode is set to the appropriate exit code to use.
3688 */
3689static
3690struct bt_config *bt_config_convert_from_args(int argc, const char *argv[],
3691 int *retcode, bool force_omit_system_plugin_path,
9a16feea 3692 bool force_omit_home_plugin_path,
b19ff26f 3693 const bt_value *initial_plugin_paths, char *log_level)
9009cc24
PP
3694{
3695 poptContext pc = NULL;
3696 char *arg = NULL;
0a011c88
JG
3697 enum bt_config_component_dest cur_comp_dest =
3698 BT_CONFIG_COMPONENT_DEST_UNKNOWN;
9009cc24
PP
3699 int opt, ret = 0;
3700 struct bt_config *cfg = NULL;
9009cc24
PP
3701 bool got_input_format_opt = false;
3702 bool got_output_format_opt = false;
3703 bool trimmer_has_begin = false;
3704 bool trimmer_has_end = false;
75a2cb9b 3705 bool stream_intersection_mode = false;
9009cc24
PP
3706 GString *cur_name = NULL;
3707 GString *cur_name_prefix = NULL;
3708 const char *leftover = NULL;
3709 bool print_run_args = false;
3710 bool print_run_args_0 = false;
3711 bool print_ctf_metadata = false;
b19ff26f
PP
3712 bt_value *run_args = NULL;
3713 bt_value *all_names = NULL;
9009cc24
PP
3714 GList *source_names = NULL;
3715 GList *filter_names = NULL;
3716 GList *sink_names = NULL;
f280892e
SM
3717 bt_value *leftovers = NULL;
3718 struct implicit_component_args implicit_ctf_input_args = { 0 };
e7ad156c 3719 struct implicit_component_args implicit_ctf_output_args = { 0 };
9009cc24
PP
3720 struct implicit_component_args implicit_lttng_live_args = { 0 };
3721 struct implicit_component_args implicit_dummy_args = { 0 };
3722 struct implicit_component_args implicit_text_args = { 0 };
3723 struct implicit_component_args implicit_debug_info_args = { 0 };
3724 struct implicit_component_args implicit_muxer_args = { 0 };
3725 struct implicit_component_args implicit_trimmer_args = { 0 };
b19ff26f 3726 bt_value *plugin_paths;
9009cc24
PP
3727 char error_buf[256] = { 0 };
3728 size_t i;
3729 struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 };
e7ad156c 3730 char *output = NULL;
9009cc24 3731
6be5a99e 3732 (void) bt_value_copy(initial_plugin_paths, &plugin_paths);
398454ed 3733
9009cc24
PP
3734 *retcode = 0;
3735
3736 if (argc <= 1) {
3737 print_convert_usage(stdout);
3738 *retcode = -1;
3739 goto end;
3740 }
3741
f280892e 3742 if (init_implicit_component_args(&implicit_ctf_input_args,
fd5f8053 3743 "source.ctf.fs", false)) {
9009cc24
PP
3744 goto error;
3745 }
3746
e7ad156c
PP
3747 if (init_implicit_component_args(&implicit_ctf_output_args,
3748 "sink.ctf.fs", false)) {
3749 goto error;
3750 }
3751
9009cc24 3752 if (init_implicit_component_args(&implicit_lttng_live_args,
fd5f8053 3753 "source.ctf.lttng-live", false)) {
9009cc24
PP
3754 goto error;
3755 }
3756
fd5f8053
PP
3757 if (init_implicit_component_args(&implicit_text_args,
3758 "sink.text.pretty", false)) {
9009cc24
PP
3759 goto error;
3760 }
3761
fd5f8053
PP
3762 if (init_implicit_component_args(&implicit_dummy_args,
3763 "sink.utils.dummy", false)) {
9009cc24
PP
3764 goto error;
3765 }
3766
3767 if (init_implicit_component_args(&implicit_debug_info_args,
9a16feea 3768 "filter.lttng-utils.debug-info", false)) {
9009cc24
PP
3769 goto error;
3770 }
3771
fd5f8053
PP
3772 if (init_implicit_component_args(&implicit_muxer_args,
3773 "filter.utils.muxer", true)) {
9009cc24
PP
3774 goto error;
3775 }
3776
3777 if (init_implicit_component_args(&implicit_trimmer_args,
fd5f8053 3778 "filter.utils.trimmer", false)) {
9009cc24
PP
3779 goto error;
3780 }
3781
05e21286 3782 all_names = bt_value_map_create();
9009cc24
PP
3783 if (!all_names) {
3784 print_err_oom();
3785 goto error;
3786 }
3787
05e21286 3788 run_args = bt_value_array_create();
9009cc24
PP
3789 if (!run_args) {
3790 print_err_oom();
3791 goto error;
3792 }
3793
3794 cur_name = g_string_new(NULL);
3795 if (!cur_name) {
3796 print_err_oom();
3797 goto error;
3798 }
3799
3800 cur_name_prefix = g_string_new(NULL);
3801 if (!cur_name_prefix) {
3802 print_err_oom();
3803 goto error;
3804 }
3805
3806 ret = append_env_var_plugin_paths(plugin_paths);
3807 if (ret) {
3808 goto error;
3809 }
3810
f280892e
SM
3811 leftovers = bt_value_array_create();
3812 if (!leftovers) {
3813 print_err_oom();
3814 goto error;
3815 }
3816
9009cc24
PP
3817 /*
3818 * First pass: collect all arguments which need to be passed
3819 * as is to the run command. This pass can also add --name
3820 * arguments if needed to automatically name unnamed component
3821 * instances. Also it does the following transformations:
3822 *
c7b0cd78
SM
3823 * --path=PATH -> --params=path="PATH"
3824 * --url=URL -> --params=url="URL"
9009cc24
PP
3825 *
3826 * Also it appends the plugin paths of --plugin-path to
3827 * `plugin_paths`.
3828 */
3829 pc = poptGetContext(NULL, argc, (const char **) argv,
3830 convert_long_options, 0);
3831 if (!pc) {
3832 printf_err("Cannot get popt context\n");
3833 goto error;
3834 }
3835
3836 poptReadDefaultConfig(pc, 0);
3837
3838 while ((opt = poptGetNextOpt(pc)) > 0) {
3839 char *name = NULL;
3840 char *plugin_name = NULL;
3841 char *comp_cls_name = NULL;
3842
3843 arg = poptGetOptArg(pc);
3844
3845 switch (opt) {
fd5f8053
PP
3846 case OPT_COMPONENT:
3847 {
4cdfc5e8 3848 bt_component_class_type type;
fd5f8053
PP
3849 const char *type_prefix;
3850
9009cc24
PP
3851 /* Append current component's name if needed */
3852 ret = convert_append_name_param(cur_comp_dest, cur_name,
3853 cur_name_prefix, run_args, all_names,
3854 &source_names, &filter_names, &sink_names);
3855 if (ret) {
3856 goto error;
3857 }
3858
3859 /* Parse the argument */
3860 plugin_comp_cls_names(arg, &name, &plugin_name,
fd5f8053 3861 &comp_cls_name, &type);
9009cc24 3862 if (!plugin_name || !comp_cls_name) {
fd5f8053 3863 printf_err("Invalid format for --component option's argument:\n %s\n",
9009cc24
PP
3864 arg);
3865 goto error;
3866 }
3867
3868 if (name) {
3869 g_string_assign(cur_name, name);
3870 } else {
3871 g_string_assign(cur_name, "");
3872 }
3873
fd5f8053
PP
3874 switch (type) {
3875 case BT_COMPONENT_CLASS_TYPE_SOURCE:
3876 cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE;
3877 type_prefix = "source";
9009cc24 3878 break;
fd5f8053
PP
3879 case BT_COMPONENT_CLASS_TYPE_FILTER:
3880 cur_comp_dest = BT_CONFIG_COMPONENT_DEST_FILTER;
3881 type_prefix = "filter";
9009cc24 3882 break;
fd5f8053
PP
3883 case BT_COMPONENT_CLASS_TYPE_SINK:
3884 cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK;
3885 type_prefix = "sink";
9009cc24
PP
3886 break;
3887 default:
0fbb9a9f 3888 abort();
9009cc24
PP
3889 }
3890
05e21286 3891 if (bt_value_array_append_string_element(run_args,
fd5f8053
PP
3892 "--component")) {
3893 print_err_oom();
3894 goto error;
3895 }
3896
05e21286 3897 if (bt_value_array_append_string_element(run_args, arg)) {
9009cc24
PP
3898 print_err_oom();
3899 goto error;
3900 }
3901
3902 g_string_assign(cur_name_prefix, "");
fd5f8053
PP
3903 g_string_append_printf(cur_name_prefix, "%s.%s.%s",
3904 type_prefix, plugin_name, comp_cls_name);
9009cc24
PP
3905 free(name);
3906 free(plugin_name);
3907 free(comp_cls_name);
3908 name = NULL;
3909 plugin_name = NULL;
3910 comp_cls_name = NULL;
3911 break;
fd5f8053 3912 }
9009cc24
PP
3913 case OPT_PARAMS:
3914 if (cur_name_prefix->len == 0) {
3915 printf_err("No current component of which to set parameters:\n %s\n",
3916 arg);
3917 goto error;
3918 }
3919
05e21286 3920 if (bt_value_array_append_string_element(run_args,
9009cc24
PP
3921 "--params")) {
3922 print_err_oom();
3923 goto error;
3924 }
3925
05e21286 3926 if (bt_value_array_append_string_element(run_args, arg)) {
9009cc24
PP
3927 print_err_oom();
3928 goto error;
3929 }
3930 break;
3931 case OPT_PATH:
3932 if (cur_name_prefix->len == 0) {
3933 printf_err("No current component of which to set `path` parameter:\n %s\n",
3934 arg);
3935 goto error;
3936 }
3937
77957ab5 3938 if (append_string_parameter_to_args(run_args, "path", arg)) {
9009cc24
PP
3939 goto error;
3940 }
3941 break;
3942 case OPT_URL:
3943 if (cur_name_prefix->len == 0) {
3944 printf_err("No current component of which to set `url` parameter:\n %s\n",
3945 arg);
3946 goto error;
3947 }
3948
9009cc24 3949
77957ab5 3950 if (append_string_parameter_to_args(run_args, "url", arg)) {
9009cc24
PP
3951 goto error;
3952 }
3953 break;
3954 case OPT_NAME:
3955 if (cur_name_prefix->len == 0) {
3956 printf_err("No current component to name:\n %s\n",
3957 arg);
3958 goto error;
3959 }
3960
05e21286 3961 if (bt_value_array_append_string_element(run_args, "--name")) {
9009cc24
PP
3962 print_err_oom();
3963 goto error;
3964 }
3965
05e21286 3966 if (bt_value_array_append_string_element(run_args, arg)) {
9009cc24
PP
3967 print_err_oom();
3968 goto error;
3969 }
3970
3971 g_string_assign(cur_name, arg);
3972 break;
3973 case OPT_OMIT_HOME_PLUGIN_PATH:
3974 force_omit_home_plugin_path = true;
3975
05e21286 3976 if (bt_value_array_append_string_element(run_args,
9009cc24
PP
3977 "--omit-home-plugin-path")) {
3978 print_err_oom();
3979 goto error;
3980 }
3981 break;
3982 case OPT_RETRY_DURATION:
05e21286 3983 if (bt_value_array_append_string_element(run_args,
9009cc24
PP
3984 "--retry-duration")) {
3985 print_err_oom();
3986 goto error;
3987 }
3988
05e21286 3989 if (bt_value_array_append_string_element(run_args, arg)) {
9009cc24
PP
3990 print_err_oom();
3991 goto error;
3992 }
3993 break;
3994 case OPT_OMIT_SYSTEM_PLUGIN_PATH:
3995 force_omit_system_plugin_path = true;
3996
05e21286 3997 if (bt_value_array_append_string_element(run_args,
9009cc24
PP
3998 "--omit-system-plugin-path")) {
3999 print_err_oom();
4000 goto error;
4001 }
4002 break;
4003 case OPT_PLUGIN_PATH:
4004 if (bt_config_append_plugin_paths_check_setuid_setgid(
4005 plugin_paths, arg)) {
4006 goto error;
4007 }
4008
05e21286 4009 if (bt_value_array_append_string_element(run_args,
9009cc24
PP
4010 "--plugin-path")) {
4011 print_err_oom();
4012 goto error;
4013 }
4014
05e21286 4015 if (bt_value_array_append_string_element(run_args, arg)) {
9009cc24
PP
4016 print_err_oom();
4017 goto error;
4018 }
4019 break;
4020 case OPT_HELP:
4021 print_convert_usage(stdout);
4022 *retcode = -1;
65300d60 4023 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
4024 goto end;
4025 case OPT_BEGIN:
4026 case OPT_CLOCK_CYCLES:
4027 case OPT_CLOCK_DATE:
4028 case OPT_CLOCK_FORCE_CORRELATE:
4029 case OPT_CLOCK_GMT:
4030 case OPT_CLOCK_OFFSET:
4031 case OPT_CLOCK_OFFSET_NS:
4032 case OPT_CLOCK_SECONDS:
4033 case OPT_COLOR:
4034 case OPT_DEBUG:
9a16feea 4035 case OPT_DEBUG_INFO:
9009cc24
PP
4036 case OPT_DEBUG_INFO_DIR:
4037 case OPT_DEBUG_INFO_FULL_PATH:
4038 case OPT_DEBUG_INFO_TARGET_PREFIX:
4039 case OPT_END:
4040 case OPT_FIELDS:
4041 case OPT_INPUT_FORMAT:
4042 case OPT_NAMES:
9009cc24
PP
4043 case OPT_NO_DELTA:
4044 case OPT_OUTPUT_FORMAT:
4045 case OPT_OUTPUT:
4046 case OPT_RUN_ARGS:
4047 case OPT_RUN_ARGS_0:
4048 case OPT_STREAM_INTERSECTION:
4049 case OPT_TIMERANGE:
4050 case OPT_VERBOSE:
4051 /* Ignore in this pass */
4052 break;
4053 default:
4054 printf_err("Unknown command-line option specified (option code %d)\n",
4055 opt);
4056 goto error;
4057 }
4058
4059 free(arg);
4060 arg = NULL;
4061 }
4062
4063 /* Append current component's name if needed */
4064 ret = convert_append_name_param(cur_comp_dest, cur_name,
4065 cur_name_prefix, run_args, all_names, &source_names,
4066 &filter_names, &sink_names);
4067 if (ret) {
4068 goto error;
4069 }
4070
4071 /* Check for option parsing error */
4072 if (opt < -1) {
4073 printf_err("While parsing command-line options, at option %s: %s\n",
4074 poptBadOption(pc, 0), poptStrerror(opt));
4075 goto error;
4076 }
4077
4078 poptFreeContext(pc);
4079 free(arg);
4080 arg = NULL;
4081
4082 /*
4083 * Second pass: transform the convert-specific options and
4084 * arguments into implicit component instances for the run
4085 * command.
4086 */
4087 pc = poptGetContext(NULL, argc, (const char **) argv,
4088 convert_long_options, 0);
4089 if (!pc) {
4090 printf_err("Cannot get popt context\n");
4091 goto error;
4092 }
4093
4094 poptReadDefaultConfig(pc, 0);
4095
4096 while ((opt = poptGetNextOpt(pc)) > 0) {
4097 arg = poptGetOptArg(pc);
4098
4099 switch (opt) {
4100 case OPT_BEGIN:
4101 if (trimmer_has_begin) {
4102 printf("At --begin option: --begin or --timerange option already specified\n %s\n",
4103 arg);
4104 goto error;
4105 }
4106
4107 trimmer_has_begin = true;
e7ad156c 4108 ret = append_implicit_component_extra_param(
9009cc24
PP
4109 &implicit_trimmer_args, "begin", arg);
4110 implicit_trimmer_args.exists = true;
4111 if (ret) {
4112 goto error;
4113 }
4114 break;
4115 case OPT_END:
4116 if (trimmer_has_end) {
4117 printf("At --end option: --end or --timerange option already specified\n %s\n",
4118 arg);
4119 goto error;
4120 }
4121
4122 trimmer_has_end = true;
e7ad156c 4123 ret = append_implicit_component_extra_param(
9009cc24
PP
4124 &implicit_trimmer_args, "end", arg);
4125 implicit_trimmer_args.exists = true;
4126 if (ret) {
4127 goto error;
4128 }
4129 break;
4130 case OPT_TIMERANGE:
4131 {
4132 char *begin;
4133 char *end;
4134
4135 if (trimmer_has_begin || trimmer_has_end) {
4136 printf("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n",
4137 arg);
4138 goto error;
4139 }
4140
4141 ret = split_timerange(arg, &begin, &end);
4142 if (ret) {
4143 printf_err("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s\n",
4144 arg);
4145 goto error;
4146 }
4147
e7ad156c 4148 ret = append_implicit_component_extra_param(
9009cc24 4149 &implicit_trimmer_args, "begin", begin);
e7ad156c 4150 ret |= append_implicit_component_extra_param(
9009cc24
PP
4151 &implicit_trimmer_args, "end", end);
4152 implicit_trimmer_args.exists = true;
4153 free(begin);
4154 free(end);
4155 if (ret) {
4156 goto error;
4157 }
4158 break;
4159 }
4160 case OPT_CLOCK_CYCLES:
4161 append_implicit_component_param(
4162 &implicit_text_args, "clock-cycles", "yes");
4163 implicit_text_args.exists = true;
4164 break;
4165 case OPT_CLOCK_DATE:
4166 append_implicit_component_param(
4167 &implicit_text_args, "clock-date", "yes");
4168 implicit_text_args.exists = true;
4169 break;
4170 case OPT_CLOCK_FORCE_CORRELATE:
4171 append_implicit_component_param(
a2a54545
PP
4172 &implicit_muxer_args,
4173 "assume-absolute-clock-classes", "yes");
9009cc24
PP
4174 break;
4175 case OPT_CLOCK_GMT:
4176 append_implicit_component_param(
4177 &implicit_text_args, "clock-gmt", "yes");
eb01fbce 4178 append_implicit_component_param(
f855116d 4179 &implicit_trimmer_args, "gmt", "yes");
9009cc24
PP
4180 implicit_text_args.exists = true;
4181 break;
4182 case OPT_CLOCK_OFFSET:
f280892e 4183 implicit_ctf_input_args.exists = true;
a2a54545 4184 append_implicit_component_param(
f280892e 4185 &implicit_ctf_input_args,
a2a54545 4186 "clock-class-offset-s", arg);
9009cc24
PP
4187 break;
4188 case OPT_CLOCK_OFFSET_NS:
f280892e 4189 implicit_ctf_input_args.exists = true;
291a6dc5 4190 append_implicit_component_param(
f280892e 4191 &implicit_ctf_input_args,
291a6dc5 4192 "clock-class-offset-ns", arg);
9009cc24
PP
4193 break;
4194 case OPT_CLOCK_SECONDS:
4195 append_implicit_component_param(
4196 &implicit_text_args, "clock-seconds", "yes");
4197 implicit_text_args.exists = true;
4198 break;
4199 case OPT_COLOR:
9009cc24 4200 implicit_text_args.exists = true;
e7ad156c
PP
4201 ret = append_implicit_component_extra_param(
4202 &implicit_text_args, "color", arg);
9009cc24
PP
4203 if (ret) {
4204 goto error;
4205 }
4206 break;
9a16feea
PP
4207 case OPT_DEBUG_INFO:
4208 implicit_debug_info_args.exists = true;
9009cc24
PP
4209 break;
4210 case OPT_DEBUG_INFO_DIR:
e7ad156c
PP
4211 implicit_debug_info_args.exists = true;
4212 ret = append_implicit_component_extra_param(
4cd687b9 4213 &implicit_debug_info_args, "debug-info-dir", arg);
9009cc24
PP
4214 if (ret) {
4215 goto error;
4216 }
4217 break;
4218 case OPT_DEBUG_INFO_FULL_PATH:
e7ad156c 4219 implicit_debug_info_args.exists = true;
9009cc24
PP
4220 append_implicit_component_param(
4221 &implicit_debug_info_args, "full-path", "yes");
4222 break;
4223 case OPT_DEBUG_INFO_TARGET_PREFIX:
e7ad156c
PP
4224 implicit_debug_info_args.exists = true;
4225 ret = append_implicit_component_extra_param(
9009cc24
PP
4226 &implicit_debug_info_args,
4227 "target-prefix", arg);
4228 if (ret) {
4229 goto error;
4230 }
4231 break;
4232 case OPT_FIELDS:
4233 {
b19ff26f 4234 bt_value *fields = fields_from_arg(arg);
9009cc24
PP
4235
4236 if (!fields) {
4237 goto error;
4238 }
4239
e7ad156c 4240 implicit_text_args.exists = true;
9009cc24
PP
4241 ret = insert_flat_params_from_array(
4242 implicit_text_args.params_arg,
05e21286 4243 fields, "field");
c5b9b441 4244 bt_value_put_ref(fields);
9009cc24
PP
4245 if (ret) {
4246 goto error;
4247 }
4248 break;
4249 }
4250 case OPT_NAMES:
4251 {
b19ff26f 4252 bt_value *names = names_from_arg(arg);
9009cc24
PP
4253
4254 if (!names) {
4255 goto error;
4256 }
4257
e7ad156c 4258 implicit_text_args.exists = true;
9009cc24
PP
4259 ret = insert_flat_params_from_array(
4260 implicit_text_args.params_arg,
05e21286 4261 names, "name");
c5b9b441 4262 bt_value_put_ref(names);
9009cc24
PP
4263 if (ret) {
4264 goto error;
4265 }
4266 break;
4267 }
4268 case OPT_NO_DELTA:
4269 append_implicit_component_param(
4270 &implicit_text_args, "no-delta", "yes");
4271 implicit_text_args.exists = true;
4272 break;
4273 case OPT_INPUT_FORMAT:
4274 if (got_input_format_opt) {
4275 printf_err("Duplicate --input-format option\n");
4276 goto error;
4277 }
4278
4279 got_input_format_opt = true;
4280
4281 if (strcmp(arg, "ctf") == 0) {
f280892e 4282 implicit_ctf_input_args.exists = true;
9009cc24
PP
4283 } else if (strcmp(arg, "lttng-live") == 0) {
4284 implicit_lttng_live_args.exists = true;
4285 } else {
4286 printf_err("Unknown legacy input format:\n %s\n",
4287 arg);
4288 goto error;
4289 }
4290 break;
4291 case OPT_OUTPUT_FORMAT:
4292 if (got_output_format_opt) {
4293 printf_err("Duplicate --output-format option\n");
4294 goto error;
4295 }
4296
4297 got_output_format_opt = true;
4298
4299 if (strcmp(arg, "text") == 0) {
4300 implicit_text_args.exists = true;
e7ad156c
PP
4301 } else if (strcmp(arg, "ctf") == 0) {
4302 implicit_ctf_output_args.exists = true;
9009cc24
PP
4303 } else if (strcmp(arg, "dummy") == 0) {
4304 implicit_dummy_args.exists = true;
4305 } else if (strcmp(arg, "ctf-metadata") == 0) {
4306 print_ctf_metadata = true;
4307 } else {
4308 printf_err("Unknown legacy output format:\n %s\n",
4309 arg);
4310 goto error;
4311 }
4312 break;
4313 case OPT_OUTPUT:
e7ad156c
PP
4314 if (output) {
4315 printf_err("Duplicate --output option\n");
4316 goto error;
4317 }
4318
4319 output = strdup(arg);
4320 if (!output) {
4321 print_err_oom();
9009cc24
PP
4322 goto error;
4323 }
4324 break;
4325 case OPT_RUN_ARGS:
4326 if (print_run_args_0) {
4327 printf_err("Cannot specify --run-args and --run-args-0\n");
4328 goto error;
4329 }
4330
4331 print_run_args = true;
4332 break;
4333 case OPT_RUN_ARGS_0:
4334 if (print_run_args) {
4335 printf_err("Cannot specify --run-args and --run-args-0\n");
4336 goto error;
4337 }
4338
4339 print_run_args_0 = true;
4340 break;
4341 case OPT_STREAM_INTERSECTION:
75a2cb9b
JG
4342 /*
4343 * Applies to all traces implementing the trace-info
4344 * query.
4345 */
4346 stream_intersection_mode = true;
9009cc24
PP
4347 break;
4348 case OPT_VERBOSE:
3efa3052
PP
4349 if (*log_level != 'V' && *log_level != 'D') {
4350 *log_level = 'I';
9009cc24 4351 }
9009cc24
PP
4352 break;
4353 case OPT_DEBUG:
3efa3052 4354 *log_level = 'V';
9009cc24
PP
4355 break;
4356 }
4357
4358 free(arg);
4359 arg = NULL;
4360 }
4361
4362 /* Check for option parsing error */
4363 if (opt < -1) {
4364 printf_err("While parsing command-line options, at option %s: %s\n",
4365 poptBadOption(pc, 0), poptStrerror(opt));
4366 goto error;
4367 }
4368
3efa3052
PP
4369 /*
4370 * Legacy behaviour: --verbose used to make the `text` output
4371 * format print more information. --verbose is now equivalent to
4372 * the INFO log level, which is why we compare to 'I' here.
4373 */
4374 if (*log_level == 'I') {
4375 append_implicit_component_param(&implicit_text_args,
4376 "verbose", "yes");
4377 }
4378
9009cc24
PP
4379 /*
4380 * Append home and system plugin paths now that we possibly got
4381 * --plugin-path.
4382 */
4383 if (append_home_and_system_plugin_paths(plugin_paths,
4384 force_omit_system_plugin_path,
4385 force_omit_home_plugin_path)) {
4386 goto error;
4387 }
4388
94023a1c
PP
4389 /* Consume and keep leftover arguments */
4390 while ((leftover = poptGetArg(pc))) {
f280892e
SM
4391 bt_value_status status = bt_value_array_append_string_element(leftovers, leftover);
4392 if (status != BT_VALUE_STATUS_OK) {
94023a1c
PP
4393 print_err_oom();
4394 goto error;
4395 }
9009cc24
PP
4396 }
4397
4398 /* Print CTF metadata or print LTTng live sessions */
4399 if (print_ctf_metadata) {
f280892e 4400 const bt_value *bt_val_leftover;
94023a1c 4401
f280892e 4402 if (bt_value_array_is_empty(leftovers)) {
9009cc24
PP
4403 printf_err("--output-format=ctf-metadata specified without a path\n");
4404 goto error;
4405 }
4406
f280892e 4407 if (bt_value_array_get_size(leftovers) > 1) {
94023a1c
PP
4408 printf_err("Too many paths specified for --output-format=ctf-metadata\n");
4409 goto error;
4410 }
4411
9009cc24
PP
4412 cfg = bt_config_print_ctf_metadata_create(plugin_paths);
4413 if (!cfg) {
4414 goto error;
4415 }
4416
f280892e 4417 bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0);
9009cc24 4418 g_string_assign(cfg->cmd_data.print_ctf_metadata.path,
f280892e 4419 bt_value_string_get(bt_val_leftover));
c327e427
PP
4420
4421 if (output) {
4422 g_string_assign(
4423 cfg->cmd_data.print_ctf_metadata.output_path,
4424 output);
4425 }
4426
9009cc24
PP
4427 goto end;
4428 }
4429
4430 /*
e7ad156c
PP
4431 * If -o ctf was specified, make sure an output path (--output)
4432 * was also specified. --output does not imply -o ctf because
4433 * it's also used for the default, implicit -o text if -o ctf
4434 * is not specified.
4435 */
4436 if (implicit_ctf_output_args.exists) {
4437 if (!output) {
4438 printf_err("--output-format=ctf specified without --output (trace output path)\n");
4439 goto error;
4440 }
4441
4442 /*
4443 * At this point we know that -o ctf AND --output were
4444 * specified. Make sure that no options were specified
4445 * which would imply -o text because --output would be
4446 * ambiguous in this case. For example, this is wrong:
4447 *
4448 * babeltrace --names=all -o ctf --output=/tmp/path my-trace
4449 *
4450 * because --names=all implies -o text, and --output
4451 * could apply to both the sink.text.pretty and
4452 * sink.ctf.fs implicit components.
4453 */
4454 if (implicit_text_args.exists) {
4455 printf_err("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text\n");
4456 goto error;
4457 }
4458 }
4459
4460 /*
4461 * If -o dummy and -o ctf were not specified, and if there are
4462 * no explicit sink components, then use an implicit
4463 * `sink.text.pretty` component.
9009cc24 4464 */
e7ad156c
PP
4465 if (!implicit_dummy_args.exists && !implicit_ctf_output_args.exists &&
4466 !sink_names) {
9009cc24
PP
4467 implicit_text_args.exists = true;
4468 }
4469
e7ad156c
PP
4470 /*
4471 * Set implicit `sink.text.pretty` or `sink.ctf.fs` component's
4472 * `path` parameter if --output was specified.
4473 */
4474 if (output) {
4475 if (implicit_text_args.exists) {
4476 append_implicit_component_extra_param(&implicit_text_args,
4477 "path", output);
4478 } else if (implicit_ctf_output_args.exists) {
4479 append_implicit_component_extra_param(&implicit_ctf_output_args,
4480 "path", output);
4481 }
4482 }
4483
94023a1c 4484 /* Decide where the leftover argument(s) go */
f280892e 4485 if (bt_value_array_get_size(leftovers) > 0) {
9009cc24 4486 if (implicit_lttng_live_args.exists) {
f280892e 4487 const bt_value *bt_val_leftover;
94023a1c 4488
f280892e 4489 if (bt_value_array_get_size(leftovers) > 1) {
94023a1c
PP
4490 printf_err("Too many URLs specified for --output-format=lttng-live\n");
4491 goto error;
4492 }
4493
f280892e 4494 bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0);
9009cc24 4495 lttng_live_url_parts =
f280892e 4496 bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_leftover),
94b828f3 4497 error_buf, sizeof(error_buf));
9009cc24
PP
4498 if (!lttng_live_url_parts.proto) {
4499 printf_err("Invalid LTTng live URL format: %s\n",
4500 error_buf);
4501 goto error;
4502 }
4503
4504 if (!lttng_live_url_parts.session_name) {
4505 /* Print LTTng live sessions */
4506 cfg = bt_config_print_lttng_live_sessions_create(
4507 plugin_paths);
4508 if (!cfg) {
4509 goto error;
4510 }
4511
9009cc24 4512 g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url,
f280892e 4513 bt_value_string_get(bt_val_leftover));
c327e427
PP
4514
4515 if (output) {
4516 g_string_assign(
4517 cfg->cmd_data.print_lttng_live_sessions.output_path,
4518 output);
4519 }
4520
9009cc24
PP
4521 goto end;
4522 }
4523
e7ad156c 4524 ret = append_implicit_component_extra_param(
94023a1c 4525 &implicit_lttng_live_args, "url",
f280892e 4526 bt_value_string_get(bt_val_leftover));
9009cc24
PP
4527 if (ret) {
4528 goto error;
4529 }
dccb8b6f
FD
4530
4531 ret = append_implicit_component_extra_param(
4532 &implicit_lttng_live_args,
4533 "session-not-found-action", "end");
4534 if (ret) {
4535 goto error;
4536 }
9009cc24 4537 } else {
94023a1c 4538 /*
f280892e
SM
4539 * Create one source.ctf.fs component, pass it an array
4540 * with the leftovers.
4541 * Note that it still has to be named later.
94023a1c 4542 */
f280892e
SM
4543 implicit_ctf_input_args.exists = true;
4544 ret = append_parameter_to_args(implicit_ctf_input_args.extra_params,
4545 "paths", leftovers);
9009cc24
PP
4546 if (ret) {
4547 goto error;
4548 }
4549 }
4550 }
4551
4552 /*
fd5f8053
PP
4553 * Ensure mutual exclusion between implicit `source.ctf.fs` and
4554 * `source.ctf.lttng-live` components.
9009cc24 4555 */
f280892e 4556 if (implicit_ctf_input_args.exists && implicit_lttng_live_args.exists) {
fd5f8053 4557 printf_err("Cannot create both implicit `%s` and `%s` components\n",
f280892e 4558 implicit_ctf_input_args.comp_arg->str,
fd5f8053 4559 implicit_lttng_live_args.comp_arg->str);
9009cc24
PP
4560 goto error;
4561 }
4562
4563 /*
fd5f8053 4564 * If the implicit `source.ctf.fs` or `source.ctf.lttng-live`
94023a1c
PP
4565 * components exists, make sure there's at least one leftover
4566 * (which is the path or URL).
9009cc24 4567 */
f280892e 4568 if (implicit_ctf_input_args.exists && bt_value_array_is_empty(leftovers)) {
fd5f8053 4569 printf_err("Missing path for implicit `%s` component\n",
f280892e 4570 implicit_ctf_input_args.comp_arg->str);
9009cc24
PP
4571 goto error;
4572 }
4573
f280892e 4574 if (implicit_lttng_live_args.exists && bt_value_array_is_empty(leftovers)) {
fd5f8053
PP
4575 printf_err("Missing URL for implicit `%s` component\n",
4576 implicit_lttng_live_args.comp_arg->str);
9009cc24
PP
4577 goto error;
4578 }
4579
4580 /* Assign names to implicit components */
f280892e
SM
4581 ret = assign_name_to_implicit_component(&implicit_ctf_input_args,
4582 "source-ctf-fs", all_names, &source_names, true);
4583 if (ret) {
4584 goto error;
9009cc24
PP
4585 }
4586
4587 ret = assign_name_to_implicit_component(&implicit_lttng_live_args,
4588 "lttng-live", all_names, &source_names, true);
4589 if (ret) {
4590 goto error;
4591 }
4592
4593 ret = assign_name_to_implicit_component(&implicit_text_args,
4594 "pretty", all_names, &sink_names, true);
4595 if (ret) {
4596 goto error;
4597 }
4598
e7ad156c
PP
4599 ret = assign_name_to_implicit_component(&implicit_ctf_output_args,
4600 "sink-ctf-fs", all_names, &sink_names, true);
4601 if (ret) {
4602 goto error;
4603 }
4604
9009cc24
PP
4605 ret = assign_name_to_implicit_component(&implicit_dummy_args,
4606 "dummy", all_names, &sink_names, true);
4607 if (ret) {
4608 goto error;
4609 }
4610
4611 ret = assign_name_to_implicit_component(&implicit_muxer_args,
4612 "muxer", all_names, NULL, false);
4613 if (ret) {
4614 goto error;
4615 }
4616
4617 ret = assign_name_to_implicit_component(&implicit_trimmer_args,
4618 "trimmer", all_names, NULL, false);
4619 if (ret) {
4620 goto error;
4621 }
4622
4623 ret = assign_name_to_implicit_component(&implicit_debug_info_args,
4624 "debug-info", all_names, NULL, false);
4625 if (ret) {
4626 goto error;
4627 }
4628
4629 /* Make sure there's at least one source and one sink */
4630 if (!source_names) {
4631 printf_err("No source component\n");
4632 goto error;
4633 }
4634
4635 if (!sink_names) {
4636 printf_err("No sink component\n");
4637 goto error;
4638 }
4639
4640 /*
4641 * Prepend the muxer, the trimmer, and the debug info to the
4642 * filter chain so that we have:
4643 *
4644 * sources -> muxer -> [trimmer] -> [debug info] ->
4645 * [user filters] -> sinks
4646 */
4647 if (implicit_debug_info_args.exists) {
4648 if (g_list_prepend_gstring(&filter_names,
4649 implicit_debug_info_args.name_arg->str)) {
4650 goto error;
4651 }
4652 }
4653
4654 if (implicit_trimmer_args.exists) {
4655 if (g_list_prepend_gstring(&filter_names,
4656 implicit_trimmer_args.name_arg->str)) {
4657 goto error;
4658 }
4659 }
4660
4661 if (g_list_prepend_gstring(&filter_names,
4662 implicit_muxer_args.name_arg->str)) {
4663 goto error;
4664 }
4665
4666 /*
4667 * Append the equivalent run arguments for the implicit
4668 * components.
4669 */
f280892e
SM
4670 ret = append_run_args_for_implicit_component(&implicit_ctf_input_args, run_args);
4671 if (ret) {
4672 goto error;
9009cc24
PP
4673 }
4674
fd5f8053 4675 ret = append_run_args_for_implicit_component(&implicit_lttng_live_args,
9009cc24
PP
4676 run_args);
4677 if (ret) {
4678 goto error;
4679 }
4680
fd5f8053
PP
4681 ret = append_run_args_for_implicit_component(&implicit_text_args,
4682 run_args);
9009cc24
PP
4683 if (ret) {
4684 goto error;
4685 }
4686
e7ad156c
PP
4687 ret = append_run_args_for_implicit_component(&implicit_ctf_output_args,
4688 run_args);
4689 if (ret) {
4690 goto error;
4691 }
4692
fd5f8053
PP
4693 ret = append_run_args_for_implicit_component(&implicit_dummy_args,
4694 run_args);
9009cc24
PP
4695 if (ret) {
4696 goto error;
4697 }
4698
fd5f8053
PP
4699 ret = append_run_args_for_implicit_component(&implicit_muxer_args,
4700 run_args);
9009cc24
PP
4701 if (ret) {
4702 goto error;
4703 }
4704
fd5f8053 4705 ret = append_run_args_for_implicit_component(&implicit_trimmer_args,
9009cc24
PP
4706 run_args);
4707 if (ret) {
4708 goto error;
4709 }
4710
fd5f8053 4711 ret = append_run_args_for_implicit_component(&implicit_debug_info_args,
9009cc24
PP
4712 run_args);
4713 if (ret) {
4714 goto error;
4715 }
4716
4717 /* Auto-connect components */
4718 ret = convert_auto_connect(run_args, source_names, filter_names,
4719 sink_names);
4720 if (ret) {
4721 printf_err("Cannot auto-connect components\n");
4722 goto error;
4723 }
4724
4725 /*
4726 * We have all the run command arguments now. Depending on
4727 * --run-args, we pass this to the run command or print them
4728 * here.
4729 */
4730 if (print_run_args || print_run_args_0) {
c60cf081
PP
4731 if (stream_intersection_mode) {
4732 printf_err("Cannot specify --stream-intersection with --run-args or --run-args-0\n");
4733 goto error;
4734 }
4735
05e21286 4736 for (i = 0; i < bt_value_array_get_size(run_args); i++) {
b19ff26f 4737 const bt_value *arg_value =
05e21286
PP
4738 bt_value_array_borrow_element_by_index(run_args,
4739 i);
9009cc24
PP
4740 const char *arg;
4741 GString *quoted = NULL;
4742 const char *arg_to_print;
4743
f6ccaed9 4744 BT_ASSERT(arg_value);
601b0d3c 4745 arg = bt_value_string_get(arg_value);
9009cc24
PP
4746
4747 if (print_run_args) {
4748 quoted = bt_common_shell_quote(arg, true);
4749 if (!quoted) {
4750 goto error;
4751 }
4752
4753 arg_to_print = quoted->str;
4754 } else {
4755 arg_to_print = arg;
4756 }
4757
4758 printf("%s", arg_to_print);
4759
4760 if (quoted) {
4761 g_string_free(quoted, TRUE);
4762 }
4763
05e21286 4764 if (i < bt_value_array_get_size(run_args) - 1) {
9009cc24
PP
4765 if (print_run_args) {
4766 putchar(' ');
4767 } else {
4768 putchar('\0');
4769 }
4770 }
4771 }
4772
4773 *retcode = -1;
65300d60 4774 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
4775 goto end;
4776 }
4777
05e21286
PP
4778 cfg = bt_config_run_from_args_array(run_args, retcode,
4779 force_omit_system_plugin_path,
4780 force_omit_home_plugin_path,
4781 initial_plugin_paths);
fc11b6a6
PP
4782 if (!cfg) {
4783 goto error;
4784 }
4785
75a2cb9b 4786 cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode;
9009cc24
PP
4787 goto end;
4788
4789error:
4790 *retcode = 1;
65300d60 4791 BT_OBJECT_PUT_REF_AND_RESET(cfg);
9009cc24
PP
4792
4793end:
4794 if (pc) {
4795 poptFreeContext(pc);
4796 }
4797
4798 free(arg);
e7ad156c 4799 free(output);
9009cc24
PP
4800
4801 if (cur_name) {
4802 g_string_free(cur_name, TRUE);
4803 }
4804
4805 if (cur_name_prefix) {
4806 g_string_free(cur_name_prefix, TRUE);
4807 }
4808
c5b9b441
PP
4809 bt_value_put_ref(run_args);
4810 bt_value_put_ref(all_names);
9009cc24
PP
4811 destroy_glist_of_gstring(source_names);
4812 destroy_glist_of_gstring(filter_names);
4813 destroy_glist_of_gstring(sink_names);
f280892e
SM
4814 bt_value_put_ref(leftovers);
4815 finalize_implicit_component_args(&implicit_ctf_input_args);
94023a1c
PP
4816 finalize_implicit_component_args(&implicit_ctf_output_args);
4817 finalize_implicit_component_args(&implicit_lttng_live_args);
4818 finalize_implicit_component_args(&implicit_dummy_args);
4819 finalize_implicit_component_args(&implicit_text_args);
4820 finalize_implicit_component_args(&implicit_debug_info_args);
4821 finalize_implicit_component_args(&implicit_muxer_args);
4822 finalize_implicit_component_args(&implicit_trimmer_args);
c5b9b441 4823 bt_value_put_ref(plugin_paths);
9009cc24
PP
4824 bt_common_destroy_lttng_live_url_parts(&lttng_live_url_parts);
4825 return cfg;
4826}
4827
4828/*
4829 * Prints the Babeltrace 2.x general usage.
4830 */
4831static
4832void print_gen_usage(FILE *fp)
4833{
4834 fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS]\n");
4835 fprintf(fp, "\n");
4836 fprintf(fp, "General options:\n");
4837 fprintf(fp, "\n");
3efa3052
PP
4838 fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=V)\n");
4839 fprintf(fp, " -h, --help Show this help and quit\n");
5bd4da00 4840 fprintf(fp, " -l, --log-level=LVL Set all log levels to LVL (`N`, `V`, `D`,\n");
3efa3052
PP
4841 fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n");
4842 fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n");
4843 fprintf(fp, " -V, --version Show version and quit\n");
9009cc24
PP
4844 fprintf(fp, "\n");
4845 fprintf(fp, "Available commands:\n");
4846 fprintf(fp, "\n");
4847 fprintf(fp, " convert Convert and trim traces (default)\n");
4848 fprintf(fp, " help Get help for a plugin or a component class\n");
4849 fprintf(fp, " list-plugins List available plugins and their content\n");
4850 fprintf(fp, " query Query objects from a component class\n");
4851 fprintf(fp, " run Build a processing graph and run it\n");
4852 fprintf(fp, "\n");
4853 fprintf(fp, "Use `babeltrace COMMAND --help` to show the help of COMMAND.\n");
4854}
4855
3efa3052
PP
4856static
4857char log_level_from_arg(const char *arg)
4858{
4859 char level = 'U';
4860
4861 if (strcmp(arg, "VERBOSE") == 0 ||
4862 strcmp(arg, "V") == 0) {
4863 level = 'V';
4864 } else if (strcmp(arg, "DEBUG") == 0 ||
4865 strcmp(arg, "D") == 0) {
4866 level = 'D';
4867 } else if (strcmp(arg, "INFO") == 0 ||
4868 strcmp(arg, "I") == 0) {
4869 level = 'I';
4870 } else if (strcmp(arg, "WARN") == 0 ||
4871 strcmp(arg, "WARNING") == 0 ||
4872 strcmp(arg, "W") == 0) {
4873 level = 'W';
4874 } else if (strcmp(arg, "ERROR") == 0 ||
4875 strcmp(arg, "E") == 0) {
4876 level = 'E';
4877 } else if (strcmp(arg, "FATAL") == 0 ||
4878 strcmp(arg, "F") == 0) {
4879 level = 'F';
4880 } else if (strcmp(arg, "NONE") == 0 ||
4881 strcmp(arg, "N") == 0) {
4882 level = 'N';
4883 }
4884
4885 return level;
4886}
4887
9009cc24
PP
4888struct bt_config *bt_config_cli_args_create(int argc, const char *argv[],
4889 int *retcode, bool force_omit_system_plugin_path,
9a16feea 4890 bool force_omit_home_plugin_path,
b19ff26f 4891 const bt_value *initial_plugin_paths)
9009cc24
PP
4892{
4893 struct bt_config *config = NULL;
9009cc24
PP
4894 int i;
4895 const char **command_argv = NULL;
4896 int command_argc = -1;
4897 const char *command_name = NULL;
3efa3052 4898 char log_level = 'U';
9009cc24
PP
4899
4900 enum command_type {
4901 COMMAND_TYPE_NONE = -1,
4902 COMMAND_TYPE_RUN = 0,
4903 COMMAND_TYPE_CONVERT,
4904 COMMAND_TYPE_LIST_PLUGINS,
4905 COMMAND_TYPE_HELP,
4906 COMMAND_TYPE_QUERY,
4907 } command_type = COMMAND_TYPE_NONE;
4908
4909 *retcode = -1;
4910
4911 if (!initial_plugin_paths) {
05e21286 4912 initial_plugin_paths = bt_value_array_create();
9009cc24
PP
4913 if (!initial_plugin_paths) {
4914 *retcode = 1;
4915 goto end;
4916 }
4917 } else {
c5b9b441 4918 bt_value_get_ref(initial_plugin_paths);
9009cc24
PP
4919 }
4920
4921 if (argc <= 1) {
d878cbfd
PP
4922 print_version();
4923 puts("");
9009cc24
PP
4924 print_gen_usage(stdout);
4925 goto end;
4926 }
4927
4928 for (i = 1; i < argc; i++) {
4929 const char *cur_arg = argv[i];
3efa3052 4930 const char *next_arg = i == (argc - 1) ? NULL : argv[i + 1];
9009cc24
PP
4931
4932 if (strcmp(cur_arg, "-d") == 0 ||
4933 strcmp(cur_arg, "--debug") == 0) {
3efa3052 4934 log_level = 'V';
9009cc24
PP
4935 } else if (strcmp(cur_arg, "-v") == 0 ||
4936 strcmp(cur_arg, "--verbose") == 0) {
3efa3052
PP
4937 if (log_level != 'V' && log_level != 'D') {
4938 /*
4939 * Legacy: do not override a previous
4940 * --debug because --verbose and --debug
4941 * can be specified together (in this
4942 * case we want the lowest log level to
4943 * apply, VERBOSE).
4944 */
4945 log_level = 'I';
4946 }
5bd4da00
PP
4947 } else if (strcmp(cur_arg, "--log-level") == 0 ||
4948 strcmp(cur_arg, "-l") == 0) {
3efa3052
PP
4949 if (!next_arg) {
4950 printf_err("Missing log level value for --log-level option\n");
4951 *retcode = 1;
4952 goto end;
4953 }
4954
4955 log_level = log_level_from_arg(next_arg);
4956 if (log_level == 'U') {
4957 printf_err("Invalid argument for --log-level option:\n %s\n",
4958 next_arg);
4959 *retcode = 1;
4960 goto end;
4961 }
4962
4963 i++;
4964 } else if (strncmp(cur_arg, "--log-level=", 12) == 0) {
4965 const char *arg = &cur_arg[12];
4966
5bd4da00
PP
4967 log_level = log_level_from_arg(arg);
4968 if (log_level == 'U') {
4969 printf_err("Invalid argument for --log-level option:\n %s\n",
4970 arg);
4971 *retcode = 1;
4972 goto end;
4973 }
4974 } else if (strncmp(cur_arg, "-l", 2) == 0) {
4975 const char *arg = &cur_arg[2];
4976
3efa3052
PP
4977 log_level = log_level_from_arg(arg);
4978 if (log_level == 'U') {
4979 printf_err("Invalid argument for --log-level option:\n %s\n",
4980 arg);
4981 *retcode = 1;
4982 goto end;
4983 }
9009cc24
PP
4984 } else if (strcmp(cur_arg, "-V") == 0 ||
4985 strcmp(cur_arg, "--version") == 0) {
4986 print_version();
4987 goto end;
4988 } else if (strcmp(cur_arg, "-h") == 0 ||
4989 strcmp(cur_arg, "--help") == 0) {
4990 print_gen_usage(stdout);
4991 goto end;
4992 } else {
9009cc24
PP
4993 /*
4994 * First unknown argument: is it a known command
4995 * name?
4996 */
3efa3052
PP
4997 command_argv = &argv[i];
4998 command_argc = argc - i;
4999
9009cc24
PP
5000 if (strcmp(cur_arg, "convert") == 0) {
5001 command_type = COMMAND_TYPE_CONVERT;
5002 } else if (strcmp(cur_arg, "list-plugins") == 0) {
5003 command_type = COMMAND_TYPE_LIST_PLUGINS;
5004 } else if (strcmp(cur_arg, "help") == 0) {
5005 command_type = COMMAND_TYPE_HELP;
5006 } else if (strcmp(cur_arg, "query") == 0) {
5007 command_type = COMMAND_TYPE_QUERY;
5008 } else if (strcmp(cur_arg, "run") == 0) {
5009 command_type = COMMAND_TYPE_RUN;
5010 } else {
5011 /*
5012 * Unknown argument, but not a known
3efa3052
PP
5013 * command name: assume the default
5014 * `convert` command.
9009cc24
PP
5015 */
5016 command_type = COMMAND_TYPE_CONVERT;
3efa3052
PP
5017 command_name = "convert";
5018 command_argv = &argv[i - 1];
5019 command_argc = argc - i + 1;
9009cc24
PP
5020 }
5021 break;
5022 }
5023 }
5024
5025 if (command_type == COMMAND_TYPE_NONE) {
5026 /*
5027 * We only got non-help, non-version general options
5028 * like --verbose and --debug, without any other
5029 * arguments, so we can't do anything useful: print the
5030 * usage and quit.
5031 */
5032 print_gen_usage(stdout);
5033 goto end;
5034 }
5035
f6ccaed9
PP
5036 BT_ASSERT(command_argv);
5037 BT_ASSERT(command_argc >= 0);
9009cc24
PP
5038
5039 switch (command_type) {
5040 case COMMAND_TYPE_RUN:
5041 config = bt_config_run_from_args(command_argc, command_argv,
5042 retcode, force_omit_system_plugin_path,
5043 force_omit_home_plugin_path, initial_plugin_paths);
5044 break;
5045 case COMMAND_TYPE_CONVERT:
5046 config = bt_config_convert_from_args(command_argc, command_argv,
5047 retcode, force_omit_system_plugin_path,
9a16feea 5048 force_omit_home_plugin_path,
3efa3052 5049 initial_plugin_paths, &log_level);
9009cc24
PP
5050 break;
5051 case COMMAND_TYPE_LIST_PLUGINS:
5052 config = bt_config_list_plugins_from_args(command_argc,
5053 command_argv, retcode, force_omit_system_plugin_path,
5054 force_omit_home_plugin_path, initial_plugin_paths);
5055 break;
5056 case COMMAND_TYPE_HELP:
5057 config = bt_config_help_from_args(command_argc,
5058 command_argv, retcode, force_omit_system_plugin_path,
5059 force_omit_home_plugin_path, initial_plugin_paths);
5060 break;
5061 case COMMAND_TYPE_QUERY:
5062 config = bt_config_query_from_args(command_argc,
5063 command_argv, retcode, force_omit_system_plugin_path,
5064 force_omit_home_plugin_path, initial_plugin_paths);
5065 break;
5066 default:
0fbb9a9f 5067 abort();
9009cc24
PP
5068 }
5069
5070 if (config) {
3efa3052
PP
5071 if (log_level == 'U') {
5072 log_level = 'W';
9009cc24
PP
5073 }
5074
3efa3052 5075 config->log_level = log_level;
9009cc24
PP
5076 config->command_name = command_name;
5077 }
5078
5079end:
c5b9b441 5080 bt_value_put_ref(initial_plugin_paths);
9009cc24
PP
5081 return config;
5082}
This page took 0.271017 seconds and 4 git commands to generate.