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