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