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