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