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