X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=src%2Fcli%2Fbabeltrace2-cfg-cli-args.c;h=73d67d7785d50f42ff89788d28dd4a10a5a07dd5;hb=1f316595674936318a79ceb02911d0a5713fcde3;hp=c4cb1756ce7361aa8db1af36513634e150233477;hpb=cff899542956ec9207d44e6ce61264d74af0d2fc;p=babeltrace.git diff --git a/src/cli/babeltrace2-cfg-cli-args.c b/src/cli/babeltrace2-cfg-cli-args.c index c4cb1756..73d67d77 100644 --- a/src/cli/babeltrace2-cfg-cli-args.c +++ b/src/cli/babeltrace2-cfg-cli-args.c @@ -22,7 +22,7 @@ * SOFTWARE. */ -#define BT_LOG_TAG "CLI-CFG-CLI-ARGS" +#define BT_LOG_TAG "CLI/CFG-CLI-ARGS" #include "logging.h" #include @@ -34,653 +34,20 @@ #include #include #include "common/common.h" -#include #include #include +#include "argpar/argpar.h" #include "babeltrace2-cfg.h" #include "babeltrace2-cfg-cli-args.h" #include "babeltrace2-cfg-cli-args-connect.h" +#include "param-parse/param-parse.h" +#include "babeltrace2-log-level.h" +#include "babeltrace2-plugins.h" +#include "babeltrace2-query.h" +#include "autodisc/autodisc.h" #include "common/version.h" -/* - * Error printf() macro which prepends "Error: " the first time it's - * called. This gives a nicer feel than having a bunch of error prefixes - * (since the following lines usually describe the error and possible - * solutions), or the error prefix just at the end. - */ -#define printf_err(fmt, args...) \ - do { \ - if (is_first_error) { \ - fprintf(stderr, "Command line error: "); \ - is_first_error = false; \ - } \ - fprintf(stderr, fmt, ##args); \ - } while (0) - -static bool is_first_error = true; -static const int cli_default_log_level = BT_LOG_WARN; - -/* INI-style parsing FSM states */ -enum ini_parsing_fsm_state { - /* Expect a map key (identifier) */ - INI_EXPECT_MAP_KEY, - - /* Expect an equal character ('=') */ - INI_EXPECT_EQUAL, - - /* Expect a value */ - INI_EXPECT_VALUE, - - /* Expect a comma character (',') */ - INI_EXPECT_COMMA, -}; - -/* INI-style parsing state variables */ -struct ini_parsing_state { - /* Lexical scanner (owned by this) */ - GScanner *scanner; - - /* Output map value object being filled (owned by this) */ - bt_value *params; - - /* Next expected FSM state */ - enum ini_parsing_fsm_state expecting; - - /* Last decoded map key (owned by this) */ - char *last_map_key; - - /* Complete INI-style string to parse (not owned by this) */ - const char *arg; - - /* Error buffer (not owned by this) */ - GString *ini_error; -}; - -/* Offset option with "is set" boolean */ -struct offset_opt { - int64_t value; - bool is_set; -}; - -/* Legacy "ctf"/"lttng-live" format options */ -struct ctf_legacy_opts { - struct offset_opt offset_s; - struct offset_opt offset_ns; - bool stream_intersection; -}; - -/* Legacy "text" format options */ -struct text_legacy_opts { - /* - * output, dbg_info_dir, dbg_info_target_prefix, names, - * and fields are owned by this. - */ - GString *output; - GString *dbg_info_dir; - GString *dbg_info_target_prefix; - const bt_value *names; - const bt_value *fields; - - /* Flags */ - bool no_delta; - bool clock_cycles; - bool clock_seconds; - bool clock_date; - bool clock_gmt; - bool dbg_info_full_path; - bool verbose; -}; - -/* Legacy input format format */ -enum legacy_input_format { - LEGACY_INPUT_FORMAT_NONE = 0, - LEGACY_INPUT_FORMAT_CTF, - LEGACY_INPUT_FORMAT_LTTNG_LIVE, -}; - -/* Legacy output format format */ -enum legacy_output_format { - LEGACY_OUTPUT_FORMAT_NONE = 0, - LEGACY_OUTPUT_FORMAT_TEXT, - LEGACY_OUTPUT_FORMAT_DUMMY, -}; - -/* - * Prints the "out of memory" error. - */ -static -void print_err_oom(void) -{ - printf_err("Out of memory\n"); -} - -/* - * Appends an "expecting token" error to the INI-style parsing state's - * error buffer. - */ -static -void ini_append_error_expecting(struct ini_parsing_state *state, - GScanner *scanner, const char *expecting) -{ - size_t i; - size_t pos; - - g_string_append_printf(state->ini_error, "Expecting %s:\n", expecting); - - /* Only print error if there's one line */ - if (strchr(state->arg, '\n') != NULL || strlen(state->arg) == 0) { - return; - } - - g_string_append_printf(state->ini_error, "\n %s\n", state->arg); - pos = g_scanner_cur_position(scanner) + 4; - - if (!g_scanner_eof(scanner)) { - pos--; - } - - for (i = 0; i < pos; ++i) { - g_string_append_printf(state->ini_error, " "); - } - - g_string_append_printf(state->ini_error, "^\n\n"); -} - -/* Parse the next token as an unsigned integer. */ -static -bt_value *ini_parse_uint(struct ini_parsing_state *state) -{ - bt_value *value = NULL; - GTokenType token_type = g_scanner_get_next_token(state->scanner); - - if (token_type != G_TOKEN_INT) { - ini_append_error_expecting(state, state->scanner, - "integer value"); - goto end; - } - - value = bt_value_unsigned_integer_create_init( - state->scanner->value.v_int64); - -end: - return value; -} - -/* Parse the next token as a number and return its negation. */ -static -bt_value *ini_parse_neg_number(struct ini_parsing_state *state) -{ - bt_value *value = NULL; - GTokenType token_type = g_scanner_get_next_token(state->scanner); - - switch (token_type) { - case G_TOKEN_INT: - { - /* Negative integer */ - uint64_t int_val = state->scanner->value.v_int64; - - if (int_val > (((uint64_t) INT64_MAX) + 1)) { - g_string_append_printf(state->ini_error, - "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n", - int_val); - } else { - value = bt_value_signed_integer_create_init( - -((int64_t) int_val)); - } - - break; - } - case G_TOKEN_FLOAT: - /* Negative floating point number */ - value = bt_value_real_create_init(-state->scanner->value.v_float); - break; - default: - ini_append_error_expecting(state, state->scanner, "value"); - break; - } - - return value; -} - -static bt_value *ini_parse_value(struct ini_parsing_state *state); - -/* - * Parse the current and following tokens as an array. Arrays are formatted as - * an opening `[`, a list of comma-separated values and a closing `]`. For - * convenience we support an optional trailing comma, after the last value. - * - * The current token of the parser must be the opening square bracket of the - * array. - */ -static -bt_value *ini_parse_array(struct ini_parsing_state *state) -{ - /* The [ character must have already been ingested. */ - BT_ASSERT(g_scanner_cur_token(state->scanner) == G_TOKEN_CHAR); - BT_ASSERT(g_scanner_cur_value(state->scanner).v_char == '['); - - bt_value *array_value; - GTokenType token_type; - - array_value = bt_value_array_create (); - token_type = g_scanner_get_next_token(state->scanner); - - /* While the current token is not a ]... */ - while (!(token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ']')) { - /* Parse the item... */ - bt_value *item_value; - bt_value_status status; - - item_value = ini_parse_value(state); - if (!item_value) { - goto error; - } - - /* ... and add it to the result array. */ - status = bt_value_array_append_element(array_value, item_value); - BT_VALUE_PUT_REF_AND_RESET(item_value); - - if (status != BT_VALUE_STATUS_OK) { - goto error; - } - - /* - * Ingest the token following the value, it should be either a - * comma or closing square brace. - */ - token_type = g_scanner_get_next_token(state->scanner); - - if (token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ',') { - /* - * Ingest the token following the comma. If it happens - * to be a closing square bracket, we'll exit the loop - * and we are done (we allow trailing commas). - * Otherwise, we are ready for the next ini_parse_value call. - */ - token_type = g_scanner_get_next_token(state->scanner); - } else if (token_type != G_TOKEN_CHAR || g_scanner_cur_value(state->scanner).v_char != ']') { - ini_append_error_expecting(state, state->scanner, ", or ]"); - goto error; - } - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(array_value); - -end: - return array_value; -} - -/* - * Parse the current token (and the following ones if needed) as a value, return - * it as a bt_value. - */ -static -bt_value *ini_parse_value(struct ini_parsing_state *state) -{ - bt_value *value = NULL; - GTokenType token_type = state->scanner->token; - - switch (token_type) { - case G_TOKEN_CHAR: - if (state->scanner->value.v_char == '-') { - /* Negative number */ - value = ini_parse_neg_number(state); - } else if (state->scanner->value.v_char == '+') { - /* Unsigned integer */ - value = ini_parse_uint(state); - } else if (state->scanner->value.v_char == '[') { - /* Array */ - value = ini_parse_array(state); - } else { - ini_append_error_expecting(state, state->scanner, "value"); - } - break; - case G_TOKEN_INT: - { - /* Positive, signed integer */ - uint64_t int_val = state->scanner->value.v_int64; - - if (int_val > INT64_MAX) { - g_string_append_printf(state->ini_error, - "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n", - int_val); - } else { - value = bt_value_signed_integer_create_init( - (int64_t) int_val); - } - break; - } - case G_TOKEN_FLOAT: - /* Positive floating point number */ - value = bt_value_real_create_init(state->scanner->value.v_float); - break; - case G_TOKEN_STRING: - /* Quoted string */ - value = bt_value_string_create_init(state->scanner->value.v_string); - break; - case G_TOKEN_IDENTIFIER: - { - /* - * Using symbols would be appropriate here, - * but said symbols are allowed as map key, - * so it's easier to consider everything an - * identifier. - * - * If one of the known symbols is not - * recognized here, then fall back to creating - * a string value. - */ - const char *id = state->scanner->value.v_identifier; - - if (!strcmp(id, "null") || !strcmp(id, "NULL") || - !strcmp(id, "nul")) { - value = bt_value_null; - } else if (!strcmp(id, "true") || !strcmp(id, "TRUE") || - !strcmp(id, "yes") || - !strcmp(id, "YES")) { - value = bt_value_bool_create_init(true); - } else if (!strcmp(id, "false") || - !strcmp(id, "FALSE") || - !strcmp(id, "no") || - !strcmp(id, "NO")) { - value = bt_value_bool_create_init(false); - } else { - value = bt_value_string_create_init(id); - } - break; - } - default: - /* Unset value variable will trigger the error */ - ini_append_error_expecting(state, state->scanner, "value"); - break; - } - - return value; -} - -static -int ini_handle_state(struct ini_parsing_state *state) -{ - int ret = 0; - GTokenType token_type; - bt_value *value = NULL; - - token_type = g_scanner_get_next_token(state->scanner); - if (token_type == G_TOKEN_EOF) { - if (state->expecting != INI_EXPECT_COMMA) { - switch (state->expecting) { - case INI_EXPECT_EQUAL: - ini_append_error_expecting(state, - state->scanner, "'='"); - break; - case INI_EXPECT_VALUE: - ini_append_error_expecting(state, - state->scanner, "value"); - break; - case INI_EXPECT_MAP_KEY: - ini_append_error_expecting(state, - state->scanner, "unquoted map key"); - break; - default: - break; - } - goto error; - } - - /* We're done! */ - ret = 1; - goto success; - } - - switch (state->expecting) { - case INI_EXPECT_MAP_KEY: - if (token_type != G_TOKEN_IDENTIFIER) { - ini_append_error_expecting(state, state->scanner, - "unquoted map key"); - goto error; - } - - free(state->last_map_key); - state->last_map_key = - strdup(state->scanner->value.v_identifier); - if (!state->last_map_key) { - g_string_append(state->ini_error, - "Out of memory\n"); - goto error; - } - - if (bt_value_map_has_entry(state->params, - state->last_map_key)) { - g_string_append_printf(state->ini_error, - "Duplicate parameter key: `%s`\n", - state->last_map_key); - goto error; - } - - state->expecting = INI_EXPECT_EQUAL; - goto success; - case INI_EXPECT_EQUAL: - if (token_type != G_TOKEN_CHAR) { - ini_append_error_expecting(state, - state->scanner, "'='"); - goto error; - } - - if (state->scanner->value.v_char != '=') { - ini_append_error_expecting(state, - state->scanner, "'='"); - goto error; - } - - state->expecting = INI_EXPECT_VALUE; - goto success; - case INI_EXPECT_VALUE: - { - value = ini_parse_value(state); - if (!value) { - goto error; - } - - state->expecting = INI_EXPECT_COMMA; - goto success; - } - case INI_EXPECT_COMMA: - if (token_type != G_TOKEN_CHAR) { - ini_append_error_expecting(state, - state->scanner, "','"); - goto error; - } - - if (state->scanner->value.v_char != ',') { - ini_append_error_expecting(state, - state->scanner, "','"); - goto error; - } - - state->expecting = INI_EXPECT_MAP_KEY; - goto success; - default: - abort(); - } - -error: - ret = -1; - goto end; - -success: - if (value) { - if (bt_value_map_insert_entry(state->params, - state->last_map_key, value)) { - /* Only override return value on error */ - ret = -1; - } - } - -end: - BT_VALUE_PUT_REF_AND_RESET(value); - return ret; -} - -/* - * Converts an INI-style argument to an equivalent map value object. - * - * Return value is owned by the caller. - */ -static -bt_value *bt_value_from_ini(const char *arg, - GString *ini_error) -{ - /* Lexical scanner configuration */ - GScannerConfig scanner_config = { - /* Skip whitespaces */ - .cset_skip_characters = " \t\n", - - /* Identifier syntax is: [a-zA-Z_][a-zA-Z0-9_.:-]* */ - .cset_identifier_first = - G_CSET_a_2_z - "_" - G_CSET_A_2_Z, - .cset_identifier_nth = - G_CSET_a_2_z - "_0123456789-.:" - G_CSET_A_2_Z, - - /* "hello" and "Hello" two different keys */ - .case_sensitive = TRUE, - - /* No comments */ - .cpair_comment_single = NULL, - .skip_comment_multi = TRUE, - .skip_comment_single = TRUE, - .scan_comment_multi = FALSE, - - /* - * Do scan identifiers, including 1-char identifiers, - * but NULL is a normal identifier. - */ - .scan_identifier = TRUE, - .scan_identifier_1char = TRUE, - .scan_identifier_NULL = FALSE, - - /* - * No specific symbols: null and boolean "symbols" are - * scanned as plain identifiers. - */ - .scan_symbols = FALSE, - .symbol_2_token = FALSE, - .scope_0_fallback = FALSE, - - /* - * Scan "0b"-, "0"-, and "0x"-prefixed integers, but not - * integers prefixed with "$". - */ - .scan_binary = TRUE, - .scan_octal = TRUE, - .scan_float = TRUE, - .scan_hex = TRUE, - .scan_hex_dollar = FALSE, - - /* Convert scanned numbers to integer tokens */ - .numbers_2_int = TRUE, - - /* Support both integers and floating-point numbers */ - .int_2_float = FALSE, - - /* Scan integers as 64-bit signed integers */ - .store_int64 = TRUE, - - /* Only scan double-quoted strings */ - .scan_string_sq = FALSE, - .scan_string_dq = TRUE, - - /* Do not converter identifiers to string tokens */ - .identifier_2_string = FALSE, - - /* Scan characters as G_TOKEN_CHAR token */ - .char_2_token = FALSE, - }; - struct ini_parsing_state state = { - .scanner = NULL, - .params = NULL, - .expecting = INI_EXPECT_MAP_KEY, - .arg = arg, - .ini_error = ini_error, - }; - - state.params = bt_value_map_create(); - if (!state.params) { - goto error; - } - - state.scanner = g_scanner_new(&scanner_config); - if (!state.scanner) { - goto error; - } - - /* Let the scan begin */ - g_scanner_input_text(state.scanner, arg, strlen(arg)); - - while (true) { - int ret = ini_handle_state(&state); - - if (ret < 0) { - /* Error */ - goto error; - } else if (ret > 0) { - /* Done */ - break; - } - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(state.params); - -end: - if (state.scanner) { - g_scanner_destroy(state.scanner); - } - - free(state.last_map_key); - return state.params; -} - -/* - * Returns the parameters map value object from a command-line - * parameter option's argument. - * - * Return value is owned by the caller. - */ -static -bt_value *bt_value_from_arg(const char *arg) -{ - bt_value *params = NULL; - GString *ini_error = NULL; - - ini_error = g_string_new(NULL); - if (!ini_error) { - print_err_oom(); - goto end; - } - - /* Try INI-style parsing */ - params = bt_value_from_ini(arg, ini_error); - if (!params) { - printf_err("%s", ini_error->str); - goto end; - } - -end: - if (ini_error) { - g_string_free(ini_error, TRUE); - } - - return params; -} +#define BT_CLI_LOGE_APPEND_CAUSE_OOM() BT_CLI_LOGE_APPEND_CAUSE("Out of memory.") /* * Returns the plugin name, component class name, component class type, @@ -714,7 +81,7 @@ void plugin_comp_cls_names(const char *arg, char **name, char **plugin, BT_ASSERT(comp_cls_type); if (!bt_common_string_is_printable(arg)) { - printf_err("Argument contains a non-printable character\n"); + BT_CLI_LOGE_APPEND_CAUSE("Argument contains a non-printable character."); goto error; } @@ -734,7 +101,7 @@ void plugin_comp_cls_names(const char *arg, char **name, char **plugin, /* Parse the component class type */ gs_comp_cls_type = bt_common_string_until(at, ".:\\", ".", &end_pos); if (!gs_comp_cls_type || at[end_pos] == '\0') { - printf_err("Missing component class type (`source`, `filter`, or `sink`)\n"); + BT_CLI_LOGE_APPEND_CAUSE("Missing component class type (`source`, `filter`, or `sink`)."); goto error; } @@ -747,7 +114,7 @@ void plugin_comp_cls_names(const char *arg, char **name, char **plugin, } else if (strcmp(gs_comp_cls_type->str, "sink") == 0) { *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SINK; } else { - printf_err("Unknown component class type: `%s`\n", + BT_CLI_LOGE_APPEND_CAUSE("Unknown component class type: `%s`.", gs_comp_cls_type->str); goto error; } @@ -757,7 +124,7 @@ void plugin_comp_cls_names(const char *arg, char **name, char **plugin, /* Parse the plugin name */ gs_plugin = bt_common_string_until(at, ".:\\", ".", &end_pos); if (!gs_plugin || gs_plugin->len == 0 || at[end_pos] == '\0') { - printf_err("Missing plugin or component class name\n"); + BT_CLI_LOGE_APPEND_CAUSE("Missing plugin or component class name."); goto error; } @@ -766,7 +133,7 @@ void plugin_comp_cls_names(const char *arg, char **name, char **plugin, /* Parse the component class name */ gs_comp_cls = bt_common_string_until(at, ".:\\", ".", &end_pos); if (!gs_comp_cls || gs_comp_cls->len == 0) { - printf_err("Missing component class name\n"); + BT_CLI_LOGE_APPEND_CAUSE("Missing component class name."); goto error; } @@ -830,10 +197,10 @@ end: static void print_version(void) { - if (GIT_VERSION[0] == '\0') { + if (BT_VERSION_GIT[0] == '\0') { puts("Babeltrace " VERSION); } else { - puts("Babeltrace " VERSION " - " GIT_VERSION); + puts("Babeltrace " VERSION " - " BT_VERSION_GIT); } } @@ -886,7 +253,7 @@ struct bt_config_component *bt_config_component_create( cfg_component = g_new0(struct bt_config_component, 1); if (!cfg_component) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -895,19 +262,19 @@ struct bt_config_component *bt_config_component_create( cfg_component->type = type; cfg_component->plugin_name = g_string_new(plugin_name); if (!cfg_component->plugin_name) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } cfg_component->comp_cls_name = g_string_new(comp_cls_name); if (!cfg_component->comp_cls_name) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } cfg_component->instance_name = g_string_new(NULL); if (!cfg_component->instance_name) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -916,7 +283,7 @@ struct bt_config_component *bt_config_component_create( /* Start with empty parameters */ cfg_component->params = bt_value_map_create(); if (!cfg_component->params) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -1036,7 +403,7 @@ void bt_config_destroy(bt_object *obj) } break; default: - abort(); + bt_common_abort(); } g_free(cfg); @@ -1054,7 +421,7 @@ void destroy_glist_of_gstring(GList *list) return; } - for (at = list; at != NULL; at = g_list_next(at)) { + for (at = list; at; at = g_list_next(at)) { g_string_free(at->data, TRUE); } @@ -1072,9 +439,9 @@ GScanner *create_csv_identifiers_scanner(void) { GScanner *scanner; GScannerConfig scanner_config = { - .cset_skip_characters = " \t\n", - .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "_", - .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ":_-", + .cset_skip_characters = (gchar *) " \t\n", + .cset_identifier_first = (gchar *) G_CSET_a_2_z G_CSET_A_2_Z "_", + .cset_identifier_nth = (gchar *) G_CSET_a_2_z G_CSET_A_2_Z ":_-", .case_sensitive = TRUE, .cpair_comment_single = NULL, .skip_comment_multi = TRUE, @@ -1102,7 +469,7 @@ GScanner *create_csv_identifiers_scanner(void) scanner = g_scanner_new(&scanner_config); if (!scanner) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); } return scanner; @@ -1123,7 +490,7 @@ bt_value *names_from_arg(const char *arg) names = bt_value_array_create(); if (!names) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -1142,42 +509,42 @@ bt_value *names_from_arg(const char *arg) { const char *identifier = scanner->value.v_identifier; - if (!strcmp(identifier, "payload") || - !strcmp(identifier, "args") || - !strcmp(identifier, "arg")) { + if (strcmp(identifier, "payload") == 0 || + strcmp(identifier, "args") == 0 || + strcmp(identifier, "arg") == 0) { found_item = true; if (bt_value_array_append_string_element(names, "payload")) { goto error; } - } else if (!strcmp(identifier, "context") || - !strcmp(identifier, "ctx")) { + } else if (strcmp(identifier, "context") == 0 || + strcmp(identifier, "ctx") == 0) { found_item = true; if (bt_value_array_append_string_element(names, "context")) { goto error; } - } else if (!strcmp(identifier, "scope") || - !strcmp(identifier, "header")) { + } else if (strcmp(identifier, "scope") == 0 || + strcmp(identifier, "header") == 0) { found_item = true; if (bt_value_array_append_string_element(names, identifier)) { goto error; } - } else if (!strcmp(identifier, "all")) { + } else if (strcmp(identifier, "all") == 0) { found_all = true; if (bt_value_array_append_string_element(names, identifier)) { goto error; } - } else if (!strcmp(identifier, "none")) { + } else if (strcmp(identifier, "none") == 0) { found_none = true; if (bt_value_array_append_string_element(names, identifier)) { goto error; } } else { - printf_err("Unknown name: `%s`\n", + BT_CLI_LOGE_APPEND_CAUSE("Unknown name: `%s`.", identifier); goto error; } @@ -1194,7 +561,7 @@ bt_value *names_from_arg(const char *arg) end: if (found_none && found_all) { - printf_err("Only either `all` or `none` can be specified in the list given to the --names option, but not both.\n"); + BT_CLI_LOGE_APPEND_CAUSE("Only either `all` or `none` can be specified in the list given to the --names option, but not both."); goto error; } /* @@ -1234,7 +601,7 @@ bt_value *fields_from_arg(const char *arg) fields = bt_value_array_create(); if (!fields) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -1253,21 +620,21 @@ bt_value *fields_from_arg(const char *arg) { const char *identifier = scanner->value.v_identifier; - if (!strcmp(identifier, "trace") || - !strcmp(identifier, "trace:hostname") || - !strcmp(identifier, "trace:domain") || - !strcmp(identifier, "trace:procname") || - !strcmp(identifier, "trace:vpid") || - !strcmp(identifier, "loglevel") || - !strcmp(identifier, "emf") || - !strcmp(identifier, "callsite") || - !strcmp(identifier, "all")) { + if (strcmp(identifier, "trace") == 0 || + strcmp(identifier, "trace:hostname") == 0 || + strcmp(identifier, "trace:domain") == 0 || + strcmp(identifier, "trace:procname") == 0 || + strcmp(identifier, "trace:vpid") == 0 || + strcmp(identifier, "loglevel") == 0 || + strcmp(identifier, "emf") == 0 || + strcmp(identifier, "callsite") == 0 || + strcmp(identifier, "all") == 0) { if (bt_value_array_append_string_element(fields, identifier)) { goto error; } } else { - printf_err("Unknown field: `%s`\n", + BT_CLI_LOGE_APPEND_CAUSE("Unknown field: `%s`.", identifier); goto error; } @@ -1319,7 +686,7 @@ int insert_flat_params_from_array(GString *params_arg, const bt_value *names_array, const char *prefix) { int ret = 0; - int i; + uint64_t i; GString *tmpstr = NULL, *default_value = NULL; bool default_set = false, non_default_set = false; @@ -1333,41 +700,35 @@ int insert_flat_params_from_array(GString *params_arg, tmpstr = g_string_new(NULL); if (!tmpstr) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } default_value = g_string_new(NULL); if (!default_value) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } - for (i = 0; i < bt_value_array_get_size(names_array); i++) { + for (i = 0; i < bt_value_array_get_length(names_array); i++) { const bt_value *str_obj = bt_value_array_borrow_element_by_index_const(names_array, - i); + i); const char *suffix; bool is_default = false; - if (!str_obj) { - printf_err("Unexpected error\n"); - ret = -1; - goto end; - } - suffix = bt_value_string_get(str_obj); g_string_assign(tmpstr, prefix); g_string_append(tmpstr, "-"); /* Special-case for "all" and "none". */ - if (!strcmp(suffix, "all")) { + if (strcmp(suffix, "all") == 0) { is_default = true; g_string_assign(default_value, "show"); - } else if (!strcmp(suffix, "none")) { + } else if (strcmp(suffix, "none") == 0) { is_default = true; g_string_assign(default_value, "hide"); } @@ -1403,7 +764,7 @@ end: return ret; } -/* popt options */ +/* argpar options */ enum { OPT_NONE = 0, OPT_BASE_PARAMS, @@ -1429,7 +790,6 @@ enum { OPT_INPUT_FORMAT, OPT_LIST, OPT_LOG_LEVEL, - OPT_NAME, OPT_NAMES, OPT_NO_DELTA, OPT_OMIT_HOME_PLUGIN_PATH, @@ -1437,7 +797,6 @@ enum { OPT_OUTPUT, OPT_OUTPUT_FORMAT, OPT_PARAMS, - OPT_PATH, OPT_PLUGIN_PATH, OPT_RESET_BASE_PARAMS, OPT_RETRY_DURATION, @@ -1445,8 +804,8 @@ enum { OPT_RUN_ARGS_0, OPT_STREAM_INTERSECTION, OPT_TIMERANGE, - OPT_URL, OPT_VERBOSE, + OPT_VERSION, }; enum bt_config_component_dest { @@ -1478,7 +837,7 @@ void add_run_cfg_comp(struct bt_config *cfg, g_ptr_array_add(cfg->cmd_data.run.sinks, cfg_comp); break; default: - abort(); + bt_common_abort(); } } @@ -1491,14 +850,14 @@ int add_run_cfg_comp_check_name(struct bt_config *cfg, int ret = 0; if (cfg_comp->instance_name->len == 0) { - printf_err("Found an unnamed component\n"); + BT_CLI_LOGE_APPEND_CAUSE("Found an unnamed component."); ret = -1; goto end; } if (bt_value_map_has_entry(instance_names, cfg_comp->instance_name->str)) { - printf_err("Duplicate component instance name:\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Duplicate component instance name:\n %s", cfg_comp->instance_name->str); ret = -1; goto end; @@ -1506,7 +865,7 @@ int add_run_cfg_comp_check_name(struct bt_config *cfg, if (bt_value_map_insert_entry(instance_names, cfg_comp->instance_name->str, bt_value_null)) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } @@ -1537,7 +896,7 @@ int append_env_var_plugin_paths(bt_value *plugin_paths) end: if (ret) { - printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); + BT_CLI_LOGE_APPEND_CAUSE("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH."); } return ret; @@ -1562,7 +921,7 @@ int append_home_and_system_plugin_paths(bt_value *plugin_paths, free(home_plugin_dir); if (ret) { - printf_err("Invalid home plugin path\n"); + BT_CLI_LOGE_APPEND_CAUSE("Invalid home plugin path."); goto error; } } @@ -1572,34 +931,26 @@ int append_home_and_system_plugin_paths(bt_value *plugin_paths, if (!omit_system_plugin_path) { if (bt_config_append_plugin_paths(plugin_paths, bt_common_get_system_plugin_path())) { - printf_err("Invalid system plugin path\n"); + BT_CLI_LOGE_APPEND_CAUSE("Invalid system plugin path."); goto error; } } return 0; error: - printf_err("Cannot append home and system plugin paths\n"); + BT_CLI_LOGE_APPEND_CAUSE("Cannot append home and system plugin paths."); return -1; } -static -int append_home_and_system_plugin_paths_cfg(struct bt_config *cfg) -{ - return append_home_and_system_plugin_paths(cfg->plugin_paths, - cfg->omit_system_plugin_path, cfg->omit_home_plugin_path); -} - static struct bt_config *bt_config_base_create(enum bt_config_command command, - const bt_value *initial_plugin_paths, - bool needs_plugins) + const bt_value *plugin_paths, bool needs_plugins) { struct bt_config *cfg; /* Create config */ cfg = g_new0(struct bt_config, 1); if (!cfg) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -1607,16 +958,16 @@ struct bt_config *bt_config_base_create(enum bt_config_command command, cfg->command = command; cfg->command_needs_plugins = needs_plugins; - if (initial_plugin_paths) { - bt_value *initial_plugin_paths_copy; + if (plugin_paths) { + bt_value *plugin_paths_copy; - (void) bt_value_copy(initial_plugin_paths, - &initial_plugin_paths_copy); - cfg->plugin_paths = initial_plugin_paths_copy; + (void) bt_value_copy(plugin_paths, + &plugin_paths_copy); + cfg->plugin_paths = plugin_paths_copy; } else { cfg->plugin_paths = bt_value_array_create(); if (!cfg->plugin_paths) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } } @@ -1631,14 +982,13 @@ end: } static -struct bt_config *bt_config_run_create( - const bt_value *initial_plugin_paths) +struct bt_config *bt_config_run_create(const bt_value *plugin_paths) { struct bt_config *cfg; /* Create config */ cfg = bt_config_base_create(BT_CONFIG_COMMAND_RUN, - initial_plugin_paths, true); + plugin_paths, true); if (!cfg) { goto error; } @@ -1646,28 +996,28 @@ struct bt_config *bt_config_run_create( cfg->cmd_data.run.sources = g_ptr_array_new_with_free_func( (GDestroyNotify) bt_object_put_ref); if (!cfg->cmd_data.run.sources) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } cfg->cmd_data.run.filters = g_ptr_array_new_with_free_func( (GDestroyNotify) bt_object_put_ref); if (!cfg->cmd_data.run.filters) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } cfg->cmd_data.run.sinks = g_ptr_array_new_with_free_func( (GDestroyNotify) bt_object_put_ref); if (!cfg->cmd_data.run.sinks) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } cfg->cmd_data.run.connections = g_ptr_array_new_with_free_func( (GDestroyNotify) bt_config_connection_destroy); if (!cfg->cmd_data.run.connections) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -1681,37 +1031,21 @@ end: } static -struct bt_config *bt_config_list_plugins_create( - const bt_value *initial_plugin_paths) +struct bt_config *bt_config_list_plugins_create(const bt_value *plugin_paths) { - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; + return bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS, + plugin_paths, true); } static -struct bt_config *bt_config_help_create( - const bt_value *initial_plugin_paths, +struct bt_config *bt_config_help_create(const bt_value *plugin_paths, int default_log_level) { struct bt_config *cfg; /* Create config */ cfg = bt_config_base_create(BT_CONFIG_COMMAND_HELP, - initial_plugin_paths, true); + plugin_paths, true); if (!cfg) { goto error; } @@ -1732,21 +1066,20 @@ end: } static -struct bt_config *bt_config_query_create( - const bt_value *initial_plugin_paths) +struct bt_config *bt_config_query_create(const bt_value *plugin_paths) { struct bt_config *cfg; /* Create config */ cfg = bt_config_base_create(BT_CONFIG_COMMAND_QUERY, - initial_plugin_paths, true); + plugin_paths, true); if (!cfg) { goto error; } cfg->cmd_data.query.object = g_string_new(NULL); if (!cfg->cmd_data.query.object) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -1761,26 +1094,26 @@ end: static struct bt_config *bt_config_print_ctf_metadata_create( - const bt_value *initial_plugin_paths) + const bt_value *plugin_paths) { struct bt_config *cfg; /* Create config */ cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_CTF_METADATA, - initial_plugin_paths, true); + plugin_paths, true); if (!cfg) { goto error; } cfg->cmd_data.print_ctf_metadata.path = g_string_new(NULL); if (!cfg->cmd_data.print_ctf_metadata.path) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } cfg->cmd_data.print_ctf_metadata.output_path = g_string_new(NULL); if (!cfg->cmd_data.print_ctf_metadata.output_path) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -1795,27 +1128,27 @@ end: static struct bt_config *bt_config_print_lttng_live_sessions_create( - const bt_value *initial_plugin_paths) + const bt_value *plugin_paths) { struct bt_config *cfg; /* Create config */ cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, - initial_plugin_paths, true); + plugin_paths, true); if (!cfg) { goto error; } cfg->cmd_data.print_lttng_live_sessions.url = g_string_new(NULL); if (!cfg->cmd_data.print_lttng_live_sessions.url) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } cfg->cmd_data.print_lttng_live_sessions.output_path = g_string_new(NULL); if (!cfg->cmd_data.print_lttng_live_sessions.output_path) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -1840,7 +1173,7 @@ int bt_config_append_plugin_paths_check_setuid_setgid( } if (bt_config_append_plugin_paths(plugin_paths, arg)) { - printf_err("Invalid --plugin-path option's argument:\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Invalid --plugin-path option's argument:\n %s", arg); ret = -1; goto end; @@ -1876,6 +1209,8 @@ void print_expected_params_format(FILE *fp) fprintf(fp, "* Double-quoted string (accepts escape characters).\n"); fprintf(fp, "* Array, formatted as an opening `[`, a list of comma-separated values\n"); fprintf(fp, " (as described by the current list) and a closing `]`.\n"); + fprintf(fp, "* Map, formatted as an opening `{`, a comma-separated list of PARAM=VALUE\n"); + fprintf(fp, " assignments and a closing `}`.\n"); fprintf(fp, "\n"); fprintf(fp, "You can put whitespaces allowed around individual `=` and `,` symbols.\n"); fprintf(fp, "\n"); @@ -1890,6 +1225,31 @@ void print_expected_params_format(FILE *fp) fprintf(fp, "babeltrace2 from a shell.\n"); } +static +bool help_option_is_specified( + const struct argpar_parse_ret *argpar_parse_ret) +{ + int i; + bool specified = false; + + for (i = 0; i < argpar_parse_ret->items->n_items; i++) { + struct argpar_item *argpar_item = + argpar_parse_ret->items->items[i]; + struct argpar_item_opt *argpar_item_opt; + + if (argpar_item->type != ARGPAR_ITEM_TYPE_OPT) { + continue; + } + + argpar_item_opt = (struct argpar_item_opt *) argpar_item; + if (argpar_item_opt->descr->id == OPT_HELP) { + specified = true; + break; + } + } + + return specified; +} /* * Prints the help command usage. @@ -1902,12 +1262,7 @@ void print_help_usage(FILE *fp) fprintf(fp, "\n"); fprintf(fp, "Options:\n"); fprintf(fp, "\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); fprintf(fp, "\n"); fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); fprintf(fp, "\n"); @@ -1915,13 +1270,10 @@ void print_help_usage(FILE *fp) } static -struct poptOption help_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, +const struct argpar_opt_descr help_options[] = { + /* id, short_name, long_name, with_arg */ + { OPT_HELP, 'h', "help", false }, + ARGPAR_OPT_DESCR_SENTINEL }; /* @@ -1932,107 +1284,86 @@ struct poptOption help_long_options[] = { */ static struct bt_config *bt_config_help_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths, int default_log_level) + int *retcode, const bt_value *plugin_paths, + int default_log_level) { - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; struct bt_config *cfg = NULL; - const char *leftover; char *plugin_name = NULL, *comp_cls_name = NULL; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + struct argpar_item_non_opt *non_opt; + GString *substring = NULL; + size_t end_pos; *retcode = 0; - cfg = bt_config_help_create(initial_plugin_paths, default_log_level); + cfg = bt_config_help_create(plugin_paths, default_log_level); if (!cfg) { goto error; } - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - help_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); + argpar_parse_ret = argpar_parse(argc, argv, help_options, true); + if (argpar_parse_ret.error) { + BT_CLI_LOGE_APPEND_CAUSE( + "While parsing `help` command's command-line arguments: %s", + argpar_parse_ret.error); goto error; } - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_HELP: - print_help_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } + if (help_option_is_specified(&argpar_parse_ret)) { + print_help_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } - free(arg); - arg = NULL; + if (argpar_parse_ret.items->n_items == 0) { + BT_CLI_LOGE_APPEND_CAUSE( + "Missing plugin name or component class descriptor."); + goto error; + } else if (argpar_parse_ret.items->n_items > 1) { + /* + * At this point we know there are least two non-option + * arguments because we don't reach here with `--help`, + * the only option. + */ + non_opt = (struct argpar_item_non_opt *) argpar_parse_ret.items->items[1]; + BT_CLI_LOGE_APPEND_CAUSE( + "Extraneous command-line argument specified to `help` command: `%s`.", + non_opt->arg); + goto error; } - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); + non_opt = (struct argpar_item_non_opt *) argpar_parse_ret.items->items[0]; + + /* Look for unescaped dots in the argument. */ + substring = bt_common_string_until(non_opt->arg, ".\\", ".", &end_pos); + if (!substring) { + BT_CLI_LOGE_APPEND_CAUSE("Could not consume argument: arg=%s", + non_opt->arg); goto error; } - leftover = poptGetArg(pc); - if (leftover) { - plugin_comp_cls_names(leftover, NULL, - &plugin_name, &comp_cls_name, + if (end_pos == strlen(non_opt->arg)) { + /* Didn't find an unescaped dot, treat it as a plugin name. */ + g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name, + non_opt->arg); + } else { + /* + * Found an unescaped dot, treat it as a component class name. + */ + plugin_comp_cls_names(non_opt->arg, NULL, &plugin_name, &comp_cls_name, &cfg->cmd_data.help.cfg_component->type); - if (plugin_name && comp_cls_name) { - /* Component class help */ - g_string_assign( - cfg->cmd_data.help.cfg_component->plugin_name, - plugin_name); - g_string_assign( - cfg->cmd_data.help.cfg_component->comp_cls_name, - comp_cls_name); - } else { - /* Fall back to plugin help */ - g_string_assign( - cfg->cmd_data.help.cfg_component->plugin_name, - leftover); + if (!plugin_name || !comp_cls_name) { + BT_CLI_LOGE_APPEND_CAUSE( + "Could not parse argument as a component class name: arg=%s", + non_opt->arg); + goto error; } - } else { - print_help_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; + g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name, + plugin_name); + g_string_assign(cfg->cmd_data.help.cfg_component->comp_cls_name, + comp_cls_name); } goto end; @@ -2045,11 +1376,12 @@ end: g_free(plugin_name); g_free(comp_cls_name); - if (pc) { - poptFreeContext(pc); + if (substring) { + g_string_free(substring, TRUE); } - free(arg); + argpar_parse_ret_fini(&argpar_parse_ret); + return cfg; } @@ -2063,27 +1395,19 @@ void print_query_usage(FILE *fp) fprintf(fp, "\n"); fprintf(fp, "Options:\n"); fprintf(fp, "\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " -p, --params=PARAMS Set the query parameters to PARAMS\n"); - fprintf(fp, " (see the expected format of PARAMS below)\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, " -p, --params=PARAMS Set the query parameters to PARAMS (see the expected\n"); + fprintf(fp, " format of PARAMS below)\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); fprintf(fp, "\n\n"); print_expected_params_format(fp); } static -struct poptOption query_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, +const struct argpar_opt_descr query_options[] = { + /* id, short_name, long_name, with_arg */ + { OPT_HELP, 'h', "help", false }, + { OPT_PARAMS, 'p', "params", true }, + ARGPAR_OPT_DESCR_SENTINEL }; /* @@ -2094,144 +1418,130 @@ struct poptOption query_long_options[] = { */ static struct bt_config *bt_config_query_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths, + int *retcode, const bt_value *plugin_paths, int default_log_level) { - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; + int i; struct bt_config *cfg = NULL; - const char *leftover; - bt_value *params; + const char *component_class_spec = NULL; + const char *query_object = NULL; + GString *error_str = NULL; + struct argpar_parse_ret argpar_parse_ret = { 0 }; - params = bt_value_null; - bt_value_get_ref(bt_value_null); + bt_value *params = bt_value_map_create(); + if (!params) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } *retcode = 0; - cfg = bt_config_query_create(initial_plugin_paths); + cfg = bt_config_query_create(plugin_paths); if (!cfg) { goto error; } - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { + error_str = g_string_new(NULL); + if (!error_str) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - query_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); + argpar_parse_ret = argpar_parse(argc, argv, query_options, true); + if (argpar_parse_ret.error) { + BT_CLI_LOGE_APPEND_CAUSE( + "While parsing `query` command's command-line arguments: %s", + argpar_parse_ret.error); goto error; } - poptReadDefaultConfig(pc, 0); + if (help_option_is_specified(&argpar_parse_ret)) { + print_query_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *argpar_item = + argpar_parse_ret.items->items[i]; - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); + if (argpar_item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *argpar_item_opt = + (struct argpar_item_opt *) argpar_item; + const char *arg = argpar_item_opt->arg; - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; + switch (argpar_item_opt->descr->id) { + case OPT_PARAMS: + { + bt_value *parsed_params = bt_param_parse(arg, error_str); + bt_value_map_extend_status extend_status; + if (!parsed_params) { + BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --params option's argument:\n %s", + error_str->str); + goto error; + } + + extend_status = bt_value_map_extend(params, parsed_params); + BT_VALUE_PUT_REF_AND_RESET(parsed_params); + if (extend_status) { + BT_CLI_LOGE_APPEND_CAUSE("Cannot extend current parameters with --params option's argument:\n %s", + arg); + goto error; + } + break; } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_PARAMS: - { - bt_value_put_ref(params); - params = bt_value_from_arg(arg); - if (!params) { - printf_err("Invalid format for --params option's argument:\n %s\n", - arg); + default: + BT_CLI_LOGE_APPEND_CAUSE("Unknown command-line option specified (option code %d).", + argpar_item_opt->descr->id); goto error; } - break; - } - case OPT_HELP: - print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } + } else { + struct argpar_item_non_opt *argpar_item_non_opt + = (struct argpar_item_non_opt *) argpar_item; - /* - * We need exactly two leftover arguments which are the - * mandatory component class specification and query object. - */ - leftover = poptGetArg(pc); - if (leftover) { - cfg->cmd_data.query.cfg_component = - bt_config_component_from_arg(leftover, - default_log_level); - if (!cfg->cmd_data.query.cfg_component) { - printf_err("Invalid format for component class specification:\n %s\n", - leftover); - goto error; + /* + * We need exactly two non-option arguments + * which are the mandatory component class + * specification and query object. + */ + if (!component_class_spec) { + component_class_spec = argpar_item_non_opt->arg; + } else if (!query_object) { + query_object = argpar_item_non_opt->arg; + } else { + BT_CLI_LOGE_APPEND_CAUSE("Extraneous command-line argument specified to `query` command: `%s`.", + argpar_item_non_opt->arg); + goto error; + } } - - BT_ASSERT(params); - BT_OBJECT_MOVE_REF(cfg->cmd_data.query.cfg_component->params, - params); - } else { - print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; } - leftover = poptGetArg(pc); - if (leftover) { - if (strlen(leftover) == 0) { - printf_err("Invalid empty object\n"); - goto error; - } - - g_string_assign(cfg->cmd_data.query.object, leftover); - } else { + if (!component_class_spec || !query_object) { print_query_usage(stdout); *retcode = -1; BT_OBJECT_PUT_REF_AND_RESET(cfg); goto end; } - leftover = poptGetArg(pc); - if (leftover) { - printf_err("Unexpected argument: %s\n", leftover); + cfg->cmd_data.query.cfg_component = + bt_config_component_from_arg(component_class_spec, + default_log_level); + if (!cfg->cmd_data.query.cfg_component) { + BT_CLI_LOGE_APPEND_CAUSE("Invalid format for component class specification:\n %s", + component_class_spec); goto error; } - if (append_home_and_system_plugin_paths_cfg(cfg)) { + BT_ASSERT(params); + BT_OBJECT_MOVE_REF(cfg->cmd_data.query.cfg_component->params, params); + + if (strlen(query_object) == 0) { + BT_CLI_LOGE_APPEND_CAUSE("Invalid empty object."); goto error; } + g_string_assign(cfg->cmd_data.query.object, query_object); goto end; error: @@ -2239,12 +1549,13 @@ error: BT_OBJECT_PUT_REF_AND_RESET(cfg); end: - if (pc) { - poptFreeContext(pc); + argpar_parse_ret_fini(&argpar_parse_ret); + + if (error_str) { + g_string_free(error_str, TRUE); } bt_value_put_ref(params); - free(arg); return cfg; } @@ -2258,11 +1569,6 @@ void print_list_plugins_usage(FILE *fp) fprintf(fp, "\n"); fprintf(fp, "Options:\n"); fprintf(fp, "\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); fprintf(fp, " -h, --help Show this help and quit\n"); fprintf(fp, "\n"); fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); @@ -2271,13 +1577,10 @@ void print_list_plugins_usage(FILE *fp) } static -struct poptOption list_plugins_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, +const struct argpar_opt_descr list_plugins_options[] = { + /* id, short_name, long_name, with_arg */ + { OPT_HELP, 'h', "help", false }, + ARGPAR_OPT_DESCR_SENTINEL }; /* @@ -2288,85 +1591,45 @@ struct poptOption list_plugins_long_options[] = { */ static struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) + int *retcode, const bt_value *plugin_paths) { - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; struct bt_config *cfg = NULL; - const char *leftover; + struct argpar_parse_ret argpar_parse_ret = { 0 }; *retcode = 0; - cfg = bt_config_list_plugins_create(initial_plugin_paths); + cfg = bt_config_list_plugins_create(plugin_paths); if (!cfg) { goto error; } - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - list_plugins_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); + argpar_parse_ret = argpar_parse(argc, argv, list_plugins_options, true); + if (argpar_parse_ret.error) { + BT_CLI_LOGE_APPEND_CAUSE( + "While parsing `list-plugins` command's command-line arguments: %s", + argpar_parse_ret.error); goto error; } - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_HELP: - print_list_plugins_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; + if (help_option_is_specified(&argpar_parse_ret)) { + print_list_plugins_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; } - leftover = poptGetArg(pc); - if (leftover) { - printf_err("Unexpected argument: %s\n", leftover); - goto error; - } + if (argpar_parse_ret.items->n_items > 0) { + /* + * At this point we know there's at least one non-option + * argument because we don't reach here with `--help`, + * the only option. + */ + struct argpar_item_non_opt *non_opt = + (struct argpar_item_non_opt *) argpar_parse_ret.items->items[0]; - if (append_home_and_system_plugin_paths_cfg(cfg)) { + BT_CLI_LOGE_APPEND_CAUSE( + "Extraneous command-line argument specified to `list-plugins` command: `%s`.", + non_opt->arg); goto error; } @@ -2377,11 +1640,8 @@ error: BT_OBJECT_PUT_REF_AND_RESET(cfg); end: - if (pc) { - poptFreeContext(pc); - } + argpar_parse_ret_fini(&argpar_parse_ret); - free(arg); return cfg; } @@ -2399,27 +1659,18 @@ void print_run_usage(FILE *fp) fprintf(fp, " for all the following components until\n"); fprintf(fp, " --reset-base-params is encountered\n"); fprintf(fp, " (see the expected format of PARAMS below)\n"); - fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n"); + fprintf(fp, " -c, --component=NAME:TYPE.PLUGIN.CLS\n"); fprintf(fp, " Instantiate the component class CLS of type\n"); fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); fprintf(fp, " in the plugin PLUGIN, add it to the graph,\n"); - fprintf(fp, " and optionally name it NAME (you can also\n"); - fprintf(fp, " specify the name with --name)\n"); + fprintf(fp, " and name it NAME"); fprintf(fp, " -x, --connect=CONNECTION Connect two created components (see the\n"); fprintf(fp, " expected format of CONNECTION below)\n"); fprintf(fp, " -l, --log-level=LVL Set the log level of the current component to LVL\n"); - fprintf(fp, " (`N`, `V`, `D`, `I`, `W`, `E`, or `F`)\n"); - fprintf(fp, " -n, --name=NAME Set the name of the current component\n"); - fprintf(fp, " to NAME (must be unique amongst all the\n"); - fprintf(fp, " names of the created components)\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " (`N`, `T`, `D`, `I`, `W`, `E`, or `F`)\n"); fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); fprintf(fp, " current component (see the expected format\n"); fprintf(fp, " of PARAMS below)\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); fprintf(fp, " -r, --reset-base-params Reset the current base parameters to an\n"); fprintf(fp, " empty map\n"); fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n"); @@ -2436,8 +1687,8 @@ void print_run_usage(FILE *fp) fprintf(fp, "\n"); fprintf(fp, "UPSTREAM and DOWNSTREAM are names of the upstream and downstream\n"); fprintf(fp, "components to connect together. You must escape the following characters\n\n"); - fprintf(fp, "with `\\`: `\\`, `.`, and `:`. You can set the name of the current\n"); - fprintf(fp, "component with the --name option.\n"); + fprintf(fp, "with `\\`: `\\`, `.`, and `:`. You must set the name of the current\n"); + fprintf(fp, "component using the NAME prefix of the --component option.\n"); fprintf(fp, "\n"); fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are optional globbing patterns to\n"); fprintf(fp, "identify the upstream and downstream ports to use for the connection.\n"); @@ -2474,194 +1725,183 @@ void print_run_usage(FILE *fp) */ static struct bt_config *bt_config_run_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths, int default_log_level) + int *retcode, const bt_value *plugin_paths, + int default_log_level) { - poptContext pc = NULL; - char *arg = NULL; struct bt_config_component *cur_cfg_comp = NULL; - enum bt_config_component_dest cur_cfg_comp_dest = - BT_CONFIG_COMPONENT_DEST_UNKNOWN; bt_value *cur_base_params = NULL; - int opt, ret = 0; + int ret = 0; struct bt_config *cfg = NULL; bt_value *instance_names = NULL; bt_value *connection_args = NULL; char error_buf[256] = { 0 }; long retry_duration = -1; - bt_value_status status; - struct poptOption run_long_options[] = { - { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, - { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL }, - { "connect", 'x', POPT_ARG_STRING, NULL, OPT_CONNECT, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "log-level", 'l', POPT_ARG_STRING, NULL, OPT_LOG_LEVEL, NULL, NULL }, - { "name", 'n', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL }, - { "retry-duration", '\0', POPT_ARG_LONG, &retry_duration, OPT_RETRY_DURATION, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, + bt_value_map_extend_status extend_status; + GString *error_str = NULL; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + int i; + + static const struct argpar_opt_descr run_options[] = { + { OPT_BASE_PARAMS, 'b', "base-params", true }, + { OPT_COMPONENT, 'c', "component", true }, + { OPT_CONNECT, 'x', "connect", true }, + { OPT_HELP, 'h', "help", false }, + { OPT_LOG_LEVEL, 'l', "log-level", true }, + { OPT_PARAMS, 'p', "params", true }, + { OPT_RESET_BASE_PARAMS, 'r', "reset-base-params", false }, + { OPT_RETRY_DURATION, '\0', "retry-duration", true }, + ARGPAR_OPT_DESCR_SENTINEL }; *retcode = 0; - if (argc <= 1) { + error_str = g_string_new(NULL); + if (!error_str) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + if (argc < 1) { print_run_usage(stdout); *retcode = -1; goto end; } - cfg = bt_config_run_create(initial_plugin_paths); + cfg = bt_config_run_create(plugin_paths); if (!cfg) { goto error; } cfg->cmd_data.run.retry_duration_us = 100000; - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; cur_base_params = bt_value_map_create(); if (!cur_base_params) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } instance_names = bt_value_map_create(); if (!instance_names) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } connection_args = bt_value_array_create(); if (!connection_args) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { + /* Parse options */ + argpar_parse_ret = argpar_parse(argc, argv, run_options, true); + if (argpar_parse_ret.error) { + BT_CLI_LOGE_APPEND_CAUSE( + "While parsing `run` command's command-line arguments: %s", + argpar_parse_ret.error); goto error; } - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - run_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; + if (help_option_is_specified(&argpar_parse_ret)) { + print_run_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; } - poptReadDefaultConfig(pc, 0); + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *argpar_item = + argpar_parse_ret.items->items[i]; + struct argpar_item_opt *argpar_item_opt; + const char *arg; - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); + /* This command does not accept non-option arguments.*/ + if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) { + struct argpar_item_non_opt *argpar_nonopt_item = + (struct argpar_item_non_opt *) argpar_item; - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; + BT_CLI_LOGE_APPEND_CAUSE("Unexpected argument: `%s`", + argpar_nonopt_item->arg); + goto error; + } + + argpar_item_opt = (struct argpar_item_opt *) argpar_item; + arg = argpar_item_opt->arg; + + switch (argpar_item_opt->descr->id) { case OPT_COMPONENT: { - enum bt_config_component_dest new_dest; - - if (cur_cfg_comp) { - ret = add_run_cfg_comp_check_name(cfg, - cur_cfg_comp, cur_cfg_comp_dest, - instance_names); - BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); - if (ret) { - goto error; - } - } + enum bt_config_component_dest dest; + BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); cur_cfg_comp = bt_config_component_from_arg(arg, default_log_level); if (!cur_cfg_comp) { - printf_err("Invalid format for --component option's argument:\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --component option's argument:\n %s", arg); goto error; } switch (cur_cfg_comp->type) { case BT_COMPONENT_CLASS_TYPE_SOURCE: - new_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; + dest = BT_CONFIG_COMPONENT_DEST_SOURCE; break; case BT_COMPONENT_CLASS_TYPE_FILTER: - new_dest = BT_CONFIG_COMPONENT_DEST_FILTER; + dest = BT_CONFIG_COMPONENT_DEST_FILTER; break; case BT_COMPONENT_CLASS_TYPE_SINK: - new_dest = BT_CONFIG_COMPONENT_DEST_SINK; + dest = BT_CONFIG_COMPONENT_DEST_SINK; break; default: - abort(); + bt_common_abort(); } BT_ASSERT(cur_base_params); bt_value_put_ref(cur_cfg_comp->params); - status = bt_value_copy(cur_base_params, - &cur_cfg_comp->params); - if (status != BT_VALUE_STATUS_OK) { - print_err_oom(); + if (bt_value_copy(cur_base_params, + &cur_cfg_comp->params) < 0) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + ret = add_run_cfg_comp_check_name(cfg, + cur_cfg_comp, dest, + instance_names); + if (ret) { goto error; } - cur_cfg_comp_dest = new_dest; break; } case OPT_PARAMS: { bt_value *params; - bt_value *params_to_set; if (!cur_cfg_comp) { - printf_err("Cannot add parameters to unavailable component:\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Cannot add parameters to unavailable component:\n %s", arg); goto error; } - params = bt_value_from_arg(arg); + params = bt_param_parse(arg, error_str); if (!params) { - printf_err("Invalid format for --params option's argument:\n %s\n", - arg); + BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --params option's argument:\n %s", + error_str->str); goto error; } - status = bt_value_map_extend(cur_cfg_comp->params, - params, ¶ms_to_set); + extend_status = bt_value_map_extend(cur_cfg_comp->params, + params); BT_VALUE_PUT_REF_AND_RESET(params); - if (status != BT_VALUE_STATUS_OK) { - printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n", + if (extend_status != BT_VALUE_MAP_EXTEND_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE("Cannot extend current component parameters with --params option's argument:\n %s", arg); goto error; } - BT_OBJECT_MOVE_REF(cur_cfg_comp->params, params_to_set); break; } - case OPT_NAME: - if (!cur_cfg_comp) { - printf_err("Cannot set the name of unavailable component:\n %s\n", - arg); - goto error; - } - - g_string_assign(cur_cfg_comp->instance_name, arg); - break; case OPT_LOG_LEVEL: if (!cur_cfg_comp) { - printf_err("Cannot set the log level of unavailable component:\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Cannot set the log level of unavailable component:\n %s", arg); goto error; } @@ -2669,19 +1909,18 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], cur_cfg_comp->log_level = bt_log_get_level_from_string(arg); if (cur_cfg_comp->log_level < 0) { - printf_err("Invalid argument for --log-level option:\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Invalid argument for --log-level option:\n %s", arg); goto error; } break; case OPT_BASE_PARAMS: { - bt_value *params = - bt_value_from_arg(arg); + bt_value *params = bt_param_parse(arg, error_str); if (!params) { - printf_err("Invalid format for --base-params option's argument:\n %s\n", - arg); + BT_CLI_LOGE_APPEND_CAUSE("Invalid format for --base-params option's argument:\n %s", + error_str->str); goto error; } @@ -2692,20 +1931,32 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], BT_VALUE_PUT_REF_AND_RESET(cur_base_params); cur_base_params = bt_value_map_create(); if (!cur_base_params) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } break; case OPT_CONNECT: if (bt_value_array_append_string_element( connection_args, arg)) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } break; - case OPT_RETRY_DURATION: + case OPT_RETRY_DURATION: { + gchar *end; + size_t arg_len = strlen(argpar_item_opt->arg); + + retry_duration = g_ascii_strtoll(argpar_item_opt->arg, &end, 10); + + if (arg_len == 0 || end != (argpar_item_opt->arg + arg_len)) { + BT_CLI_LOGE_APPEND_CAUSE( + "Could not parse --retry-duration option's argument as an unsigned integer: `%s`", + argpar_item_opt->arg); + goto error; + } + if (retry_duration < 0) { - printf_err("--retry-duration option's argument must be positive or 0: %ld\n", + BT_CLI_LOGE_APPEND_CAUSE("--retry-duration option's argument must be positive or 0: %ld", retry_duration); goto error; } @@ -2713,55 +1964,23 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], cfg->cmd_data.run.retry_duration_us = (uint64_t) retry_duration; break; - case OPT_HELP: - print_run_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; + } default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); + BT_CLI_LOGE_APPEND_CAUSE("Unknown command-line option specified (option code %d).", + argpar_item_opt->descr->id); goto error; } - - free(arg); - arg = NULL; } - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - /* This command does not accept leftover arguments */ - if (poptPeekArg(pc)) { - printf_err("Unexpected argument: %s\n", poptPeekArg(pc)); - goto error; - } - - /* Add current component */ - if (cur_cfg_comp) { - ret = add_run_cfg_comp_check_name(cfg, cur_cfg_comp, - cur_cfg_comp_dest, instance_names); - BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); - if (ret) { - goto error; - } - } + BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); if (cfg->cmd_data.run.sources->len == 0) { - printf_err("Incomplete graph: no source component\n"); + BT_CLI_LOGE_APPEND_CAUSE("Incomplete graph: no source component."); goto error; } if (cfg->cmd_data.run.sinks->len == 0) { - printf_err("Incomplete graph: no sink component\n"); - goto error; - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { + BT_CLI_LOGE_APPEND_CAUSE("Incomplete graph: no sink component."); goto error; } @@ -2769,7 +1988,7 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], connection_args, error_buf, 256); if (ret) { - printf_err("Cannot creation connections:\n%s", error_buf); + BT_CLI_LOGE_APPEND_CAUSE("Cannot creation connections:\n%s", error_buf); goto error; } @@ -2780,11 +1999,11 @@ error: BT_OBJECT_PUT_REF_AND_RESET(cfg); end: - if (pc) { - poptFreeContext(pc); + if (error_str) { + g_string_free(error_str, TRUE); } - free(arg); + argpar_parse_ret_fini(&argpar_parse_ret); BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); BT_VALUE_PUT_REF_AND_RESET(cur_base_params); BT_VALUE_PUT_REF_AND_RESET(instance_names); @@ -2794,43 +2013,33 @@ end: static struct bt_config *bt_config_run_from_args_array(const bt_value *run_args, - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths, int default_log_level) + int *retcode, const bt_value *plugin_paths, + int default_log_level) { struct bt_config *cfg = NULL; const char **argv; - int64_t i, len; - const size_t argc = bt_value_array_get_size(run_args) + 1; + uint64_t i, len = bt_value_array_get_length(run_args); - argv = calloc(argc, sizeof(*argv)); + BT_ASSERT(len <= SIZE_MAX); + argv = calloc((size_t) len, sizeof(*argv)); if (!argv) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto end; } - argv[0] = "run"; - - len = bt_value_array_get_size(run_args); - if (len < 0) { - printf_err("Invalid executable arguments\n"); - goto end; - } for (i = 0; i < len; i++) { const bt_value *arg_value = bt_value_array_borrow_element_by_index_const(run_args, - i); + i); const char *arg; - BT_ASSERT(arg_value); arg = bt_value_string_get(arg_value); BT_ASSERT(arg); - argv[i + 1] = arg; + argv[i] = arg; } - cfg = bt_config_run_from_args(argc, argv, retcode, - force_omit_system_plugin_path, force_omit_home_plugin_path, - initial_plugin_paths, default_log_level); + cfg = bt_config_run_from_args((int) len, argv, retcode, + plugin_paths, default_log_level); end: free(argv); @@ -2852,22 +2061,12 @@ void print_convert_usage(FILE *fp) fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); fprintf(fp, " in the plugin PLUGIN, add it to the\n"); fprintf(fp, " conversion graph, and optionally name it\n"); - fprintf(fp, " NAME (you can also specify the name with\n"); - fprintf(fp, " --name)\n"); + fprintf(fp, " NAME\n"); fprintf(fp, " -l, --log-level=LVL Set the log level of the current component to LVL\n"); - fprintf(fp, " (`N`, `V`, `D`, `I`, `W`, `E`, or `F`)\n"); - fprintf(fp, " --name=NAME Set the name of the current component\n"); - fprintf(fp, " to NAME (must be unique amongst all the\n"); - fprintf(fp, " names of the created components)\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " (`N`, `T`, `D`, `I`, `W`, `E`, or `F`)\n"); fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); fprintf(fp, " current component (see the expected format\n"); fprintf(fp, " of PARAMS below)\n"); - fprintf(fp, " -P, --path=PATH Set the `path` string parameter of the\n"); - fprintf(fp, " current component to PATH\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n"); fprintf(fp, " the graph later, retry in DUR µs\n"); fprintf(fp, " (default: 100000)\n"); @@ -2880,12 +2079,12 @@ void print_convert_usage(FILE *fp) fprintf(fp, " formatted for `xargs -0`, and quit\n"); fprintf(fp, " --stream-intersection Only process events when all streams\n"); fprintf(fp, " are active\n"); - fprintf(fp, " -u, --url=URL Set the `url` string parameter of the\n"); - fprintf(fp, " current component to URL\n"); fprintf(fp, " -h, --help Show this help and quit\n"); fprintf(fp, "\n"); fprintf(fp, "Implicit `source.ctf.fs` component options:\n"); fprintf(fp, "\n"); + fprintf(fp, " --clock-force-correlate Force the origin of all clocks\n"); + fprintf(fp, " to the Unix epoch\n"); fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n"); fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS ns\n"); fprintf(fp, "\n"); @@ -2913,11 +2112,6 @@ void print_convert_usage(FILE *fp) fprintf(fp, " -w, --output=PATH Write output text to PATH instead of\n"); fprintf(fp, " the standard output\n"); fprintf(fp, "\n"); - fprintf(fp, "Implicit `filter.utils.muxer` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --clock-force-correlate Assume that clocks are inherently\n"); - fprintf(fp, " correlated across traces\n"); - fprintf(fp, "\n"); fprintf(fp, "Implicit `filter.utils.trimmer` component options:\n"); fprintf(fp, "\n"); fprintf(fp, " -b, --begin=BEGIN Set the beginning time of the conversion\n"); @@ -2976,46 +2170,43 @@ void print_convert_usage(FILE *fp) } static -struct poptOption convert_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "begin", 'b', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL }, - { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL }, - { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL }, - { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL }, - { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL }, - { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL }, - { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL }, - { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL }, - { "color", '\0', POPT_ARG_STRING, NULL, OPT_COLOR, NULL, NULL }, - { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL }, - { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL }, - { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL }, - { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL }, - { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL }, - { "end", 'e', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL }, - { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL }, - { "log-level", 'l', POPT_ARG_STRING, NULL, OPT_LOG_LEVEL, NULL, NULL }, - { "name", '\0', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, - { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL }, - { "debug-info", '\0', POPT_ARG_NONE, NULL, OPT_DEBUG_INFO, NULL, NULL }, - { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT, NULL, NULL }, - { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL }, - { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, - { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { "retry-duration", '\0', POPT_ARG_STRING, NULL, OPT_RETRY_DURATION, NULL, NULL }, - { "run-args", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS, NULL, NULL }, - { "run-args-0", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS_0, NULL, NULL }, - { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL }, - { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL }, - { "url", 'u', POPT_ARG_STRING, NULL, OPT_URL, NULL, NULL }, - { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, +const struct argpar_opt_descr convert_options[] = { + /* id, short_name, long_name, with_arg */ + { OPT_BEGIN, 'b', "begin", true }, + { OPT_CLOCK_CYCLES, '\0', "clock-cycles", false }, + { OPT_CLOCK_DATE, '\0', "clock-date", false }, + { OPT_CLOCK_FORCE_CORRELATE, '\0', "clock-force-correlate", false }, + { OPT_CLOCK_GMT, '\0', "clock-gmt", false }, + { OPT_CLOCK_OFFSET, '\0', "clock-offset", true }, + { OPT_CLOCK_OFFSET_NS, '\0', "clock-offset-ns", true }, + { OPT_CLOCK_SECONDS, '\0', "clock-seconds", false }, + { OPT_COLOR, '\0', "color", true }, + { OPT_COMPONENT, 'c', "component", true }, + { OPT_DEBUG, 'd', "debug", false }, + { OPT_DEBUG_INFO_DIR, '\0', "debug-info-dir", true }, + { OPT_DEBUG_INFO_FULL_PATH, '\0', "debug-info-full-path", false }, + { OPT_DEBUG_INFO_TARGET_PREFIX, '\0', "debug-info-target-prefix", true }, + { OPT_END, 'e', "end", true }, + { OPT_FIELDS, 'f', "fields", true }, + { OPT_HELP, 'h', "help", false }, + { OPT_INPUT_FORMAT, 'i', "input-format", true }, + { OPT_LOG_LEVEL, 'l', "log-level", true }, + { OPT_NAMES, 'n', "names", true }, + { OPT_DEBUG_INFO, '\0', "debug-info", false }, + { OPT_NO_DELTA, '\0', "no-delta", false }, + { OPT_OMIT_HOME_PLUGIN_PATH, '\0', "omit-home-plugin-path", false }, + { OPT_OMIT_SYSTEM_PLUGIN_PATH, '\0', "omit-system-plugin-path", false }, + { OPT_OUTPUT, 'w', "output", true }, + { OPT_OUTPUT_FORMAT, 'o', "output-format", true }, + { OPT_PARAMS, 'p', "params", true }, + { OPT_PLUGIN_PATH, '\0', "plugin-path", true }, + { OPT_RETRY_DURATION, '\0', "retry-duration", true }, + { OPT_RUN_ARGS, '\0', "run-args", false }, + { OPT_RUN_ARGS_0, '\0', "run-args-0", false }, + { OPT_STREAM_INTERSECTION, '\0', "stream-intersection", false }, + { OPT_TIMERANGE, '\0', "timerange", true }, + { OPT_VERBOSE, 'v', "verbose", false }, + ARGPAR_OPT_DESCR_SENTINEL }; static @@ -3026,7 +2217,7 @@ GString *get_component_auto_name(const char *prefix, GString *auto_name = g_string_new(NULL); if (!auto_name) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto end; } @@ -3046,8 +2237,13 @@ end: struct implicit_component_args { bool exists; + + /* The component class name (e.g. src.ctf.fs). */ GString *comp_arg; + + /* The component instance name. */ GString *name_arg; + GString *params_arg; bt_value *extra_params; }; @@ -3076,7 +2272,7 @@ int assign_name_to_implicit_component(struct implicit_component_args *args, if (bt_value_map_insert_entry(existing_names, name->str, bt_value_null)) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } @@ -3100,61 +2296,61 @@ int append_run_args_for_implicit_component( bt_value *run_args) { int ret = 0; - size_t i; + uint64_t i; + GString *component_arg_for_run = NULL; if (!impl_args->exists) { goto end; } - if (bt_value_array_append_string_element(run_args, "--component")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, impl_args->comp_arg->str)) { - print_err_oom(); + component_arg_for_run = g_string_new(NULL); + if (!component_arg_for_run) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - if (bt_value_array_append_string_element(run_args, "--name")) { - print_err_oom(); + /* Build the full `name:type.plugin.cls`. */ + BT_ASSERT(!strchr(impl_args->name_arg->str, '\\')); + BT_ASSERT(!strchr(impl_args->name_arg->str, ':')); + g_string_printf(component_arg_for_run, "%s:%s", + impl_args->name_arg->str, impl_args->comp_arg->str); + + if (bt_value_array_append_string_element(run_args, "--component")) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - if (bt_value_array_append_string_element(run_args, impl_args->name_arg->str)) { - print_err_oom(); + if (bt_value_array_append_string_element(run_args, + component_arg_for_run->str)) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } if (impl_args->params_arg->len > 0) { if (bt_value_array_append_string_element(run_args, "--params")) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } if (bt_value_array_append_string_element(run_args, impl_args->params_arg->str)) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } } - for (i = 0; i < bt_value_array_get_size(impl_args->extra_params); - i++) { + for (i = 0; i < bt_value_array_get_length(impl_args->extra_params); i++) { const bt_value *elem; const char *arg; - elem = bt_value_array_borrow_element_by_index(impl_args->extra_params, - i); - if (!elem) { - goto error; - } + elem = bt_value_array_borrow_element_by_index( + impl_args->extra_params, i); BT_ASSERT(bt_value_is_string(elem)); arg = bt_value_string_get(elem); ret = bt_value_array_append_string_element(run_args, arg); if (ret) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } } @@ -3165,9 +2361,15 @@ error: ret = -1; end: + if (component_arg_for_run) { + g_string_free(component_arg_for_run, TRUE); + } + return ret; } +/* Free the fields of a `struct implicit_component_args`. */ + static void finalize_implicit_component_args(struct implicit_component_args *args) { @@ -3188,6 +2390,17 @@ void finalize_implicit_component_args(struct implicit_component_args *args) bt_value_put_ref(args->extra_params); } +/* Destroy a dynamically-allocated `struct implicit_component_args`. */ + +static +void destroy_implicit_component_args(struct implicit_component_args *args) +{ + finalize_implicit_component_args(args); + g_free(args); +} + +/* Initialize the fields of an already allocated `struct implicit_component_args`. */ + static int init_implicit_component_args(struct implicit_component_args *args, const char *comp_arg, bool exists) @@ -3204,7 +2417,7 @@ int init_implicit_component_args(struct implicit_component_args *args, !args->params_arg || !args->extra_params) { ret = -1; finalize_implicit_component_args(args); - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto end; } @@ -3212,6 +2425,31 @@ end: return ret; } +/* Dynamically allocate and initialize a `struct implicit_component_args`. */ + +static +struct implicit_component_args *create_implicit_component_args( + const char *comp_arg) +{ + struct implicit_component_args *args; + int status; + + args = g_new(struct implicit_component_args, 1); + if (!args) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto end; + } + + status = init_implicit_component_args(args, comp_arg, true); + if (status != 0) { + g_free(args); + args = NULL; + } + +end: + return args; +} + static void append_implicit_component_param(struct implicit_component_args *args, const char *key, const char *value) @@ -3222,6 +2460,33 @@ void append_implicit_component_param(struct implicit_component_args *args, append_param_arg(args->params_arg, key, value); } +/* + * Append the given parameter (`key=value`) to all component specifications + * in `implicit_comp_args` (an array of `struct implicit_component_args *`) + * which match `comp_arg`. + * + * Return the number of matching components. + */ + +static +int append_multiple_implicit_components_param(GPtrArray *implicit_comp_args, + const char *comp_arg, const char *key, const char *value) +{ + int i; + int n = 0; + + for (i = 0; i < implicit_comp_args->len; i++) { + struct implicit_component_args *args = implicit_comp_args->pdata[i]; + + if (strcmp(args->comp_arg->str, comp_arg) == 0) { + append_implicit_component_param(args, key, value); + n++; + } + } + + return n; +} + /* Escape value to make it suitable to use as a string parameter value. */ static gchar *escape_string_value(const char *value) @@ -3231,7 +2496,7 @@ gchar *escape_string_value(const char *value) ret = g_string_new(NULL); if (!ret) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto end; } @@ -3278,10 +2543,9 @@ int bt_value_to_cli_param_value_append(const bt_value *value, GString *buf) } case BT_VALUE_TYPE_ARRAY: { g_string_append_c(buf, '['); - uint64_t sz = bt_value_array_get_size(value); + uint64_t sz = bt_value_array_get_length(value); for (uint64_t i = 0; i < sz; i++) { const bt_value *item; - int ret; if (i > 0) { g_string_append(buf, ", "); @@ -3299,7 +2563,7 @@ int bt_value_to_cli_param_value_append(const bt_value *value, GString *buf) break; } default: - abort(); + bt_common_abort(); } ret = 0; @@ -3321,7 +2585,7 @@ gchar *bt_value_to_cli_param_value(bt_value *value) buf = g_string_new(NULL); if (!buf) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -3356,7 +2620,7 @@ int append_parameter_to_args(bt_value *args, const char *key, bt_value *value) GString *parameter = NULL; if (bt_value_array_append_string_element(args, "--params")) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } @@ -3369,7 +2633,7 @@ int append_parameter_to_args(bt_value *args, const char *key, bt_value *value) parameter = g_string_new(NULL); if (!parameter) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } @@ -3377,7 +2641,7 @@ int append_parameter_to_args(bt_value *args, const char *key, bt_value *value) g_string_printf(parameter, "%s=%s", key, str_value); if (bt_value_array_append_string_element(args, parameter->str)) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } @@ -3405,7 +2669,7 @@ int append_string_parameter_to_args(bt_value *args, const char *key, const char str_value = bt_value_string_create_init(value); if (!str_value) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } @@ -3424,103 +2688,6 @@ int append_implicit_component_extra_param(struct implicit_component_args *args, return append_string_parameter_to_args(args->extra_params, key, value); } -static -int convert_append_name_param(enum bt_config_component_dest dest, - GString *cur_name, GString *cur_name_prefix, - bt_value *run_args, - bt_value *all_names, - GList **source_names, GList **filter_names, - GList **sink_names) -{ - int ret = 0; - - if (cur_name_prefix->len > 0) { - /* We're after a --component option */ - GString *name = NULL; - bool append_name_opt = false; - - if (cur_name->len == 0) { - /* - * No explicit name was provided for the user - * component. - */ - name = get_component_auto_name(cur_name_prefix->str, - all_names); - append_name_opt = true; - } else { - /* - * An explicit name was provided for the user - * component. - */ - if (bt_value_map_has_entry(all_names, - cur_name->str)) { - printf_err("Duplicate component instance name:\n %s\n", - cur_name->str); - goto error; - } - - name = g_string_new(cur_name->str); - } - - if (!name) { - print_err_oom(); - goto error; - } - - /* - * Remember this name globally, for the uniqueness of - * all component names. - */ - if (bt_value_map_insert_entry(all_names, name->str, bt_value_null)) { - print_err_oom(); - goto error; - } - - /* - * Append the --name option if necessary. - */ - if (append_name_opt) { - if (bt_value_array_append_string_element(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, name->str)) { - print_err_oom(); - goto error; - } - } - - /* - * Remember this name specifically for the type of the - * component. This is to create connection arguments. - */ - switch (dest) { - case BT_CONFIG_COMPONENT_DEST_SOURCE: - *source_names = g_list_append(*source_names, name); - break; - case BT_CONFIG_COMPONENT_DEST_FILTER: - *filter_names = g_list_append(*filter_names, name); - break; - case BT_CONFIG_COMPONENT_DEST_SINK: - *sink_names = g_list_append(*sink_names, name); - break; - default: - abort(); - } - - g_string_assign(cur_name_prefix, ""); - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - /* * Escapes `.`, `:`, and `\` of `input` with `\`. */ @@ -3531,7 +2698,7 @@ GString *escape_dot_colon(const char *input) const char *ch; if (!output) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto end; } @@ -3562,14 +2729,14 @@ int append_connect_arg(bt_value *run_args, GString *arg = g_string_new(NULL); if (!e_upstream_name || !e_downstream_name || !arg) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } ret = bt_value_array_append_string_element(run_args, "--connect"); if (ret) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } @@ -3579,7 +2746,7 @@ int append_connect_arg(bt_value *run_args, g_string_append(arg, e_downstream_name->str); ret = bt_value_array_append_string_element(run_args, arg->str); if (ret) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); ret = -1; goto end; } @@ -3619,7 +2786,7 @@ int convert_auto_connect(bt_value *run_args, BT_ASSERT(sink_names); /* Connect all sources to the first filter */ - for (source_at = source_names; source_at != NULL; source_at = g_list_next(source_at)) { + for (source_at = source_names; source_at; source_at = g_list_next(source_at)) { GString *source_name = source_at->data; GString *filter_name = filter_at->data; @@ -3634,7 +2801,7 @@ int convert_auto_connect(bt_value *run_args, filter_at = g_list_next(filter_at); /* Connect remaining filters */ - for (; filter_at != NULL; filter_prev = filter_at, filter_at = g_list_next(filter_at)) { + for (; filter_at; filter_prev = filter_at, filter_at = g_list_next(filter_at)) { GString *filter_name = filter_at->data; GString *filter_prev_name = filter_prev->data; @@ -3646,7 +2813,7 @@ int convert_auto_connect(bt_value *run_args, } /* Connect last filter to all sinks */ - for (sink_at = sink_names; sink_at != NULL; sink_at = g_list_next(sink_at)) { + for (sink_at = sink_names; sink_at; sink_at = g_list_next(sink_at)) { GString *filter_name = filter_prev->data; GString *sink_name = sink_at->data; @@ -3727,7 +2894,7 @@ int g_list_prepend_gstring(GList **list, const char *string) BT_ASSERT(list); if (!gs) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto end; } @@ -3737,6 +2904,196 @@ end: return ret; } +/* + * Create `struct implicit_component_args` structures for each of the + * source components we identified. Add them to `component_args`. + * + * `non_opts` is an array of the non-option arguments passed on the command + * line. + * + * `non_opt_params` is an array where each element is an array of + * strings containing all the arguments to `--params` that apply to the + * non-option argument at the same index. For example, if, for a + * non-option argument, the following `--params` options applied: + * + * --params=a=2 --params=b=3,c=4 + * + * its entry in `non_opt_params` would contain + * + * ["a=2", "b=3,c=4"] + */ + +static +int create_implicit_component_args_from_auto_discovered_sources( + const struct auto_source_discovery *auto_disc, + const bt_value *non_opts, + const bt_value *non_opt_params, + const bt_value *non_opt_loglevels, + GPtrArray *component_args) +{ + gchar *cc_name = NULL; + struct implicit_component_args *comp = NULL; + int status; + guint i, len; + + len = auto_disc->results->len; + + for (i = 0; i < len; i++) { + struct auto_source_discovery_result *res = + g_ptr_array_index(auto_disc->results, i); + uint64_t orig_indices_i, orig_indices_count; + + g_free(cc_name); + cc_name = g_strdup_printf("source.%s.%s", res->plugin_name, res->source_cc_name); + if (!cc_name) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + comp = create_implicit_component_args(cc_name); + if (!comp) { + goto error; + } + + /* + * Append parameters and log levels of all the + * non-option arguments that contributed to this + * component instance coming into existence. + */ + orig_indices_count = bt_value_array_get_length(res->original_input_indices); + for (orig_indices_i = 0; orig_indices_i < orig_indices_count; orig_indices_i++) { + const bt_value *orig_idx_value = + bt_value_array_borrow_element_by_index( + res->original_input_indices, orig_indices_i); + uint64_t orig_idx = bt_value_integer_unsigned_get(orig_idx_value); + const bt_value *params_array = + bt_value_array_borrow_element_by_index_const( + non_opt_params, orig_idx); + uint64_t params_i, params_count; + const bt_value *loglevel_value; + + params_count = bt_value_array_get_length(params_array); + for (params_i = 0; params_i < params_count; params_i++) { + const bt_value *params_value = + bt_value_array_borrow_element_by_index_const( + params_array, params_i); + const char *params = bt_value_string_get(params_value); + bt_value_array_append_element_status append_status; + + append_status = bt_value_array_append_string_element( + comp->extra_params, "--params"); + if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element."); + goto error; + } + + append_status = bt_value_array_append_string_element( + comp->extra_params, params); + if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element."); + goto error; + } + } + + loglevel_value = bt_value_array_borrow_element_by_index_const( + non_opt_loglevels, orig_idx); + if (bt_value_get_type(loglevel_value) == BT_VALUE_TYPE_STRING) { + const char *loglevel = bt_value_string_get(loglevel_value); + bt_value_array_append_element_status append_status; + + append_status = bt_value_array_append_string_element( + comp->extra_params, "--log-level"); + if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element."); + goto error; + } + + append_status = bt_value_array_append_string_element( + comp->extra_params, loglevel); + if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE("Failed to append array element."); + goto error; + } + } + } + + /* + * If single input and a src.ctf.fs component, provide the + * relative path from the path passed on the command line to the + * found trace. + */ + if (bt_value_array_get_length(res->inputs) == 1 && + strcmp(res->plugin_name, "ctf") == 0 && + strcmp(res->source_cc_name, "fs") == 0) { + const bt_value *orig_idx_value = + bt_value_array_borrow_element_by_index( + res->original_input_indices, 0); + uint64_t orig_idx = bt_value_integer_unsigned_get(orig_idx_value); + const bt_value *non_opt_value = + bt_value_array_borrow_element_by_index_const( + non_opts, orig_idx); + const char *non_opt = bt_value_string_get(non_opt_value); + const bt_value *input_value = + bt_value_array_borrow_element_by_index_const( + res->inputs, 0); + const char *input = bt_value_string_get(input_value); + + BT_ASSERT(orig_indices_count == 1); + BT_ASSERT(g_str_has_prefix(input, non_opt)); + + input += strlen(non_opt); + + while (G_IS_DIR_SEPARATOR(*input)) { + input++; + } + + if (strlen(input) > 0) { + append_string_parameter_to_args(comp->extra_params, + "trace-name", input); + } + } + + status = append_parameter_to_args(comp->extra_params, "inputs", res->inputs); + if (status != 0) { + goto error; + } + + g_ptr_array_add(component_args, comp); + comp = NULL; + } + + status = 0; + goto end; + +error: + status = -1; + +end: + g_free(cc_name); + + if (comp) { + destroy_implicit_component_args(comp); + } + + return status; +} + +/* + * As we iterate the arguments to the convert command, this tracks what is the + * type of the current item, to which some contextual options (e.g. --params) + * apply to. + */ +enum convert_current_item_type { + /* There is no current item. */ + CONVERT_CURRENT_ITEM_TYPE_NONE, + + /* Current item is a component. */ + CONVERT_CURRENT_ITEM_TYPE_COMPONENT, + + /* Current item is a non-option argument. */ + CONVERT_CURRENT_ITEM_TYPE_NON_OPT, +}; + /* * Creates a Babeltrace config object from the arguments of a convert * command. @@ -3745,24 +3102,18 @@ end: */ static struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths, int *default_log_level) + int *retcode, const bt_value *plugin_paths, + int *default_log_level, const bt_interrupter *interrupter) { - poptContext pc = NULL; - char *arg = NULL; - enum bt_config_component_dest cur_comp_dest = - BT_CONFIG_COMPONENT_DEST_UNKNOWN; - int opt, ret = 0; + enum convert_current_item_type current_item_type = + CONVERT_CURRENT_ITEM_TYPE_NONE; + int ret = 0; struct bt_config *cfg = NULL; bool got_input_format_opt = false; bool got_output_format_opt = false; bool trimmer_has_begin = false; bool trimmer_has_end = false; bool stream_intersection_mode = false; - GString *cur_name = NULL; - GString *cur_name_prefix = NULL; - const char *leftover = NULL; bool print_run_args = false; bool print_run_args_0 = false; bool print_ctf_metadata = false; @@ -3771,8 +3122,9 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], GList *source_names = NULL; GList *filter_names = NULL; GList *sink_names = NULL; - bt_value *leftovers = NULL; - struct implicit_component_args implicit_ctf_input_args = { 0 }; + bt_value *non_opts = NULL; + bt_value *non_opt_params = NULL; + bt_value *non_opt_loglevels = NULL; struct implicit_component_args implicit_ctf_output_args = { 0 }; struct implicit_component_args implicit_lttng_live_args = { 0 }; struct implicit_component_args implicit_dummy_args = { 0 }; @@ -3780,27 +3132,41 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], struct implicit_component_args implicit_debug_info_args = { 0 }; struct implicit_component_args implicit_muxer_args = { 0 }; struct implicit_component_args implicit_trimmer_args = { 0 }; - bt_value *plugin_paths; char error_buf[256] = { 0 }; size_t i; struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 }; char *output = NULL; + struct auto_source_discovery auto_disc = { NULL }; + GString *auto_disc_comp_name = NULL; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + GString *name_gstr = NULL; + GString *component_arg_for_run = NULL; + bt_value *live_inputs_array_val = NULL; - (void) bt_value_copy(initial_plugin_paths, &plugin_paths); + /* + * Array of `struct implicit_component_args *` created for the sources + * we have auto-discovered. + */ + GPtrArray *discovered_source_args = NULL; + + /* + * If set, restrict automatic source discovery to this component class + * of this plugin. + */ + const char *auto_source_discovery_restrict_plugin_name = NULL; + const char *auto_source_discovery_restrict_component_class_name = NULL; + bool ctf_fs_source_force_clock_class_unix_epoch_origin = false; + gchar *ctf_fs_source_clock_class_offset_arg = NULL; + gchar *ctf_fs_source_clock_class_offset_ns_arg = NULL; *retcode = 0; - if (argc <= 1) { + if (argc < 1) { print_convert_usage(stdout); *retcode = -1; goto end; } - if (init_implicit_component_args(&implicit_ctf_input_args, - "source.ctf.fs", false)) { - goto error; - } - if (init_implicit_component_args(&implicit_ctf_output_args, "sink.ctf.fs", false)) { goto error; @@ -3838,36 +3204,54 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], all_names = bt_value_map_create(); if (!all_names) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } run_args = bt_value_array_create(); if (!run_args) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - cur_name = g_string_new(NULL); - if (!cur_name) { - print_err_oom(); + component_arg_for_run = g_string_new(NULL); + if (!component_arg_for_run) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - cur_name_prefix = g_string_new(NULL); - if (!cur_name_prefix) { - print_err_oom(); + non_opts = bt_value_array_create(); + if (!non_opts) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - ret = append_env_var_plugin_paths(plugin_paths); - if (ret) { + non_opt_params = bt_value_array_create(); + if (!non_opt_params) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + non_opt_loglevels = bt_value_array_create(); + if (!non_opt_loglevels) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + if (auto_source_discovery_init(&auto_disc) != 0) { goto error; } - leftovers = bt_value_array_create(); - if (!leftovers) { - print_err_oom(); + discovered_source_args = + g_ptr_array_new_with_free_func((GDestroyNotify) destroy_implicit_component_args); + if (!discovered_source_args) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + auto_disc_comp_name = g_string_new(NULL); + if (!auto_disc_comp_name) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -3875,303 +3259,308 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], * First pass: collect all arguments which need to be passed * as is to the run command. This pass can also add --name * arguments if needed to automatically name unnamed component - * instances. Also it does the following transformations: - * - * --path=PATH -> --params=path="PATH" - * --url=URL -> --params=url="URL" - * - * Also it appends the plugin paths of --plugin-path to - * `plugin_paths`. + * instances. */ - pc = poptGetContext(NULL, argc, (const char **) argv, - convert_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); + argpar_parse_ret = argpar_parse(argc, argv, convert_options, true); + if (argpar_parse_ret.error) { + BT_CLI_LOGE_APPEND_CAUSE( + "While parsing `convert` command's command-line arguments: %s", + argpar_parse_ret.error); goto error; } - poptReadDefaultConfig(pc, 0); + if (help_option_is_specified(&argpar_parse_ret)) { + print_convert_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } - while ((opt = poptGetNextOpt(pc)) > 0) { + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *argpar_item = + argpar_parse_ret.items->items[i]; + struct argpar_item_opt *argpar_item_opt; char *name = NULL; char *plugin_name = NULL; char *comp_cls_name = NULL; + const char *arg; - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_COMPONENT: - { - bt_component_class_type type; - const char *type_prefix; - - /* Append current component's name if needed */ - ret = convert_append_name_param(cur_comp_dest, cur_name, - cur_name_prefix, run_args, all_names, - &source_names, &filter_names, &sink_names); - if (ret) { - goto error; - } - - /* Parse the argument */ - plugin_comp_cls_names(arg, &name, &plugin_name, - &comp_cls_name, &type); - if (!plugin_name || !comp_cls_name) { - printf_err("Invalid format for --component option's argument:\n %s\n", - arg); - goto error; - } - - if (name) { - g_string_assign(cur_name, name); - } else { - g_string_assign(cur_name, ""); - } - - switch (type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; - type_prefix = "source"; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cur_comp_dest = BT_CONFIG_COMPONENT_DEST_FILTER; - type_prefix = "filter"; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK; - type_prefix = "sink"; - break; - default: - abort(); - } - - if (bt_value_array_append_string_element(run_args, - "--component")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - - g_string_assign(cur_name_prefix, ""); - g_string_append_printf(cur_name_prefix, "%s.%s.%s", - type_prefix, plugin_name, comp_cls_name); - free(name); - free(plugin_name); - free(comp_cls_name); - name = NULL; - plugin_name = NULL; - comp_cls_name = NULL; - break; - } - case OPT_PARAMS: - if (cur_name_prefix->len == 0) { - printf_err("No current component of which to set parameters:\n %s\n", - arg); - goto error; - } - - if (bt_value_array_append_string_element(run_args, - "--params")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_PATH: - if (cur_name_prefix->len == 0) { - printf_err("No current component of which to set `path` parameter:\n %s\n", - arg); - goto error; - } + if (argpar_item->type == ARGPAR_ITEM_TYPE_OPT) { + argpar_item_opt = (struct argpar_item_opt *) argpar_item; + arg = argpar_item_opt->arg; - if (append_string_parameter_to_args(run_args, "path", arg)) { - goto error; - } - break; - case OPT_URL: - if (cur_name_prefix->len == 0) { - printf_err("No current component of which to set `url` parameter:\n %s\n", - arg); - goto error; - } + switch (argpar_item_opt->descr->id) { + case OPT_COMPONENT: + { + bt_component_class_type type; + current_item_type = CONVERT_CURRENT_ITEM_TYPE_COMPONENT; - if (append_string_parameter_to_args(run_args, "url", arg)) { - goto error; - } - break; - case OPT_NAME: - if (cur_name_prefix->len == 0) { - printf_err("No current component to name:\n %s\n", - arg); - goto error; - } + /* Parse the argument */ + plugin_comp_cls_names(arg, &name, &plugin_name, + &comp_cls_name, &type); + if (!plugin_name || !comp_cls_name) { + BT_CLI_LOGE_APPEND_CAUSE( + "Invalid format for --component option's argument:\n %s", + arg); + goto error; + } - if (bt_value_array_append_string_element(run_args, "--name")) { - print_err_oom(); - goto error; - } + if (name) { + /* + * Name was given by the user, verify it isn't + * taken. + */ + if (bt_value_map_has_entry(all_names, name)) { + BT_CLI_LOGE_APPEND_CAUSE( + "Duplicate component instance name:\n %s", + name); + goto error; + } + + name_gstr = g_string_new(name); + if (!name_gstr) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + g_string_assign(component_arg_for_run, arg); + } else { + /* Name not given by user, generate one. */ + name_gstr = get_component_auto_name(arg, all_names); + if (!name_gstr) { + goto error; + } + + g_string_printf(component_arg_for_run, "%s:%s", + name_gstr->str, arg); + } - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } + if (bt_value_array_append_string_element(run_args, + "--component")) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } - g_string_assign(cur_name, arg); - break; - case OPT_LOG_LEVEL: - if (cur_name_prefix->len == 0) { - printf_err("No current component to assign a log level to:\n %s\n", - arg); - goto error; - } + if (bt_value_array_append_string_element(run_args, + component_arg_for_run->str)) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } - if (bt_value_array_append_string_element(run_args, "--log-level")) { - print_err_oom(); - goto error; - } + /* + * Remember this name globally, for the uniqueness of + * all component names. + */ + if (bt_value_map_insert_entry(all_names, + name_gstr->str, bt_value_null)) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; + /* + * Remember this name specifically for the type of the + * component. This is to create connection arguments. + * + * The list takes ownership of `name_gstr`. + */ + switch (type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + source_names = g_list_append(source_names, name_gstr); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + filter_names = g_list_append(filter_names, name_gstr); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + sink_names = g_list_append(sink_names, name_gstr); + break; + default: + bt_common_abort(); + } + name_gstr = NULL; + + free(name); + free(plugin_name); + free(comp_cls_name); + name = NULL; + plugin_name = NULL; + comp_cls_name = NULL; + break; } + case OPT_PARAMS: + if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_COMPONENT) { + /* + * The current item is a component (--component option), + * pass it directly to the run args. + */ + if (bt_value_array_append_string_element(run_args, + "--params")) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + } else if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_NON_OPT) { + /* + * The current item is a + * non-option argument, record + * it in `non_opt_params`. + */ + bt_value *array; + bt_value_array_append_element_status append_element_status; + uint64_t idx = bt_value_array_get_length(non_opt_params) - 1; + + array = bt_value_array_borrow_element_by_index(non_opt_params, idx); + + append_element_status = bt_value_array_append_string_element(array, arg); + if (append_element_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + } else { + BT_CLI_LOGE_APPEND_CAUSE( + "No current component (--component option) or non-option argument of which to set parameters:\n %s", + arg); + goto error; + } + break; + case OPT_LOG_LEVEL: + if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_COMPONENT) { + if (bt_value_array_append_string_element(run_args, "--log-level")) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + } else if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_NON_OPT) { + uint64_t idx = bt_value_array_get_length(non_opt_loglevels) - 1; + enum bt_value_array_set_element_by_index_status set_element_status; + bt_value *log_level_str_value; + + log_level_str_value = bt_value_string_create_init(arg); + if (!log_level_str_value) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + set_element_status = + bt_value_array_set_element_by_index(non_opt_loglevels, + idx, log_level_str_value); + bt_value_put_ref(log_level_str_value); + if (set_element_status != BT_VALUE_ARRAY_SET_ELEMENT_BY_INDEX_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + } else { + BT_CLI_LOGE_APPEND_CAUSE( + "No current component (--component option) or non-option argument to assign a log level to:\n %s", + arg); + goto error; + } - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - force_omit_home_plugin_path = true; + break; + case OPT_RETRY_DURATION: + if (bt_value_array_append_string_element(run_args, + "--retry-duration")) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } - if (bt_value_array_append_string_element(run_args, - "--omit-home-plugin-path")) { - print_err_oom(); - goto error; - } - break; - case OPT_RETRY_DURATION: - if (bt_value_array_append_string_element(run_args, - "--retry-duration")) { - print_err_oom(); + if (bt_value_array_append_string_element(run_args, arg)) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + break; + case OPT_BEGIN: + case OPT_CLOCK_CYCLES: + case OPT_CLOCK_DATE: + case OPT_CLOCK_FORCE_CORRELATE: + case OPT_CLOCK_GMT: + case OPT_CLOCK_OFFSET: + case OPT_CLOCK_OFFSET_NS: + case OPT_CLOCK_SECONDS: + case OPT_COLOR: + case OPT_DEBUG: + case OPT_DEBUG_INFO: + case OPT_DEBUG_INFO_DIR: + case OPT_DEBUG_INFO_FULL_PATH: + case OPT_DEBUG_INFO_TARGET_PREFIX: + case OPT_END: + case OPT_FIELDS: + case OPT_INPUT_FORMAT: + case OPT_NAMES: + case OPT_NO_DELTA: + case OPT_OUTPUT_FORMAT: + case OPT_OUTPUT: + case OPT_RUN_ARGS: + case OPT_RUN_ARGS_0: + case OPT_STREAM_INTERSECTION: + case OPT_TIMERANGE: + case OPT_VERBOSE: + /* Ignore in this pass */ + break; + default: + BT_CLI_LOGE_APPEND_CAUSE("Unknown command-line option specified (option code %d).", + argpar_item_opt->descr->id); goto error; } + } else if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) { + struct argpar_item_non_opt *argpar_item_non_opt; + bt_value_array_append_element_status append_status; - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - force_omit_system_plugin_path = true; + current_item_type = CONVERT_CURRENT_ITEM_TYPE_NON_OPT; - if (bt_value_array_append_string_element(run_args, - "--omit-system-plugin-path")) { - print_err_oom(); - goto error; - } - break; - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - plugin_paths, arg)) { + argpar_item_non_opt = (struct argpar_item_non_opt *) argpar_item; + + append_status = bt_value_array_append_string_element(non_opts, + argpar_item_non_opt->arg); + if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - if (bt_value_array_append_string_element(run_args, - "--plugin-path")) { - print_err_oom(); + append_status = bt_value_array_append_empty_array_element( + non_opt_params, NULL); + if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); + append_status = bt_value_array_append_element(non_opt_loglevels, bt_value_null); + if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - break; - case OPT_HELP: - print_convert_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - case OPT_BEGIN: - case OPT_CLOCK_CYCLES: - case OPT_CLOCK_DATE: - case OPT_CLOCK_FORCE_CORRELATE: - case OPT_CLOCK_GMT: - case OPT_CLOCK_OFFSET: - case OPT_CLOCK_OFFSET_NS: - case OPT_CLOCK_SECONDS: - case OPT_COLOR: - case OPT_DEBUG: - case OPT_DEBUG_INFO: - case OPT_DEBUG_INFO_DIR: - case OPT_DEBUG_INFO_FULL_PATH: - case OPT_DEBUG_INFO_TARGET_PREFIX: - case OPT_END: - case OPT_FIELDS: - case OPT_INPUT_FORMAT: - case OPT_NAMES: - case OPT_NO_DELTA: - case OPT_OUTPUT_FORMAT: - case OPT_OUTPUT: - case OPT_RUN_ARGS: - case OPT_RUN_ARGS_0: - case OPT_STREAM_INTERSECTION: - case OPT_TIMERANGE: - case OPT_VERBOSE: - /* Ignore in this pass */ - break; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; + } else { + bt_common_abort(); } - - free(arg); - arg = NULL; } - /* Append current component's name if needed */ - ret = convert_append_name_param(cur_comp_dest, cur_name, - cur_name_prefix, run_args, all_names, &source_names, - &filter_names, &sink_names); - if (ret) { - goto error; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - poptFreeContext(pc); - free(arg); - arg = NULL; - /* * Second pass: transform the convert-specific options and * arguments into implicit component instances for the run * command. */ - pc = poptGetContext(NULL, argc, (const char **) argv, - convert_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *argpar_item = + argpar_parse_ret.items->items[i]; + struct argpar_item_opt *argpar_item_opt; + const char *arg; - poptReadDefaultConfig(pc, 0); + if (argpar_item->type != ARGPAR_ITEM_TYPE_OPT) { + continue; + } - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); + argpar_item_opt = (struct argpar_item_opt *) argpar_item; + arg = argpar_item_opt->arg; - switch (opt) { + switch (argpar_item_opt->descr->id) { case OPT_BEGIN: if (trimmer_has_begin) { printf("At --begin option: --begin or --timerange option already specified\n %s\n", @@ -4215,7 +3604,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], ret = split_timerange(arg, &begin, &end); if (ret) { - printf_err("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s", arg); goto error; } @@ -4243,9 +3632,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], implicit_text_args.exists = true; break; case OPT_CLOCK_FORCE_CORRELATE: - append_implicit_component_param( - &implicit_muxer_args, - "assume-absolute-clock-classes", "yes"); + ctf_fs_source_force_clock_class_unix_epoch_origin = true; break; case OPT_CLOCK_GMT: append_implicit_component_param( @@ -4255,16 +3642,28 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], implicit_text_args.exists = true; break; case OPT_CLOCK_OFFSET: - implicit_ctf_input_args.exists = true; - append_implicit_component_param( - &implicit_ctf_input_args, - "clock-class-offset-s", arg); + if (ctf_fs_source_clock_class_offset_arg) { + BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset option\n"); + goto error; + } + + ctf_fs_source_clock_class_offset_arg = g_strdup(arg); + if (!ctf_fs_source_clock_class_offset_arg) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } break; case OPT_CLOCK_OFFSET_NS: - implicit_ctf_input_args.exists = true; - append_implicit_component_param( - &implicit_ctf_input_args, - "clock-class-offset-ns", arg); + if (ctf_fs_source_clock_class_offset_ns_arg) { + BT_CLI_LOGE_APPEND_CAUSE("Duplicate --clock-offset-ns option\n"); + goto error; + } + + ctf_fs_source_clock_class_offset_ns_arg = g_strdup(arg); + if (!ctf_fs_source_clock_class_offset_ns_arg) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } break; case OPT_CLOCK_SECONDS: append_implicit_component_param( @@ -4347,25 +3746,28 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], break; case OPT_INPUT_FORMAT: if (got_input_format_opt) { - printf_err("Duplicate --input-format option\n"); + BT_CLI_LOGE_APPEND_CAUSE("Duplicate --input-format option."); goto error; } got_input_format_opt = true; if (strcmp(arg, "ctf") == 0) { - implicit_ctf_input_args.exists = true; + auto_source_discovery_restrict_plugin_name = "ctf"; + auto_source_discovery_restrict_component_class_name = "fs"; } else if (strcmp(arg, "lttng-live") == 0) { + auto_source_discovery_restrict_plugin_name = "ctf"; + auto_source_discovery_restrict_component_class_name = "lttng-live"; implicit_lttng_live_args.exists = true; } else { - printf_err("Unknown legacy input format:\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy input format:\n %s", arg); goto error; } break; case OPT_OUTPUT_FORMAT: if (got_output_format_opt) { - printf_err("Duplicate --output-format option\n"); + BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output-format option."); goto error; } @@ -4380,26 +3782,26 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], } else if (strcmp(arg, "ctf-metadata") == 0) { print_ctf_metadata = true; } else { - printf_err("Unknown legacy output format:\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Unknown legacy output format:\n %s", arg); goto error; } break; case OPT_OUTPUT: if (output) { - printf_err("Duplicate --output option\n"); + BT_CLI_LOGE_APPEND_CAUSE("Duplicate --output option"); goto error; } output = strdup(arg); if (!output) { - print_err_oom(); + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } break; case OPT_RUN_ARGS: if (print_run_args_0) { - printf_err("Cannot specify --run-args and --run-args-0\n"); + BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0."); goto error; } @@ -4407,7 +3809,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], break; case OPT_RUN_ARGS_0: if (print_run_args) { - printf_err("Cannot specify --run-args and --run-args-0\n"); + BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --run-args and --run-args-0."); goto error; } @@ -4415,34 +3817,25 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], break; case OPT_STREAM_INTERSECTION: /* - * Applies to all traces implementing the trace-info - * query. + * Applies to all traces implementing the + * babeltrace.trace-infos query. */ stream_intersection_mode = true; break; case OPT_VERBOSE: - if (*default_log_level != BT_LOG_TRACE && - *default_log_level != BT_LOG_DEBUG) { - *default_log_level = BT_LOG_INFO; - } + *default_log_level = + logging_level_min(*default_log_level, BT_LOG_INFO); break; case OPT_DEBUG: - *default_log_level = BT_LOG_TRACE; + *default_log_level = + logging_level_min(*default_log_level, BT_LOG_TRACE); break; default: break; } - - free(arg); - arg = NULL; } - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } + set_auto_log_levels(default_log_level); /* * Legacy behaviour: --verbose used to make the `text` output @@ -4455,36 +3848,17 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], "verbose", "yes"); } - /* - * Append home and system plugin paths now that we possibly got - * --plugin-path. - */ - if (append_home_and_system_plugin_paths(plugin_paths, - force_omit_system_plugin_path, - force_omit_home_plugin_path)) { - goto error; - } - - /* Consume and keep leftover arguments */ - while ((leftover = poptGetArg(pc))) { - bt_value_status status = bt_value_array_append_string_element(leftovers, leftover); - if (status != BT_VALUE_STATUS_OK) { - print_err_oom(); - goto error; - } - } - /* Print CTF metadata or print LTTng live sessions */ if (print_ctf_metadata) { - const bt_value *bt_val_leftover; + const bt_value *bt_val_non_opt; - if (bt_value_array_is_empty(leftovers)) { - printf_err("--output-format=ctf-metadata specified without a path\n"); + if (bt_value_array_is_empty(non_opts)) { + BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf-metadata specified without a path."); goto error; } - if (bt_value_array_get_size(leftovers) > 1) { - printf_err("Too many paths specified for --output-format=ctf-metadata\n"); + if (bt_value_array_get_length(non_opts) > 1) { + BT_CLI_LOGE_APPEND_CAUSE("Too many paths specified for --output-format=ctf-metadata."); goto error; } @@ -4493,9 +3867,9 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], goto error; } - bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); + bt_val_non_opt = bt_value_array_borrow_element_by_index_const(non_opts, 0); g_string_assign(cfg->cmd_data.print_ctf_metadata.path, - bt_value_string_get(bt_val_leftover)); + bt_value_string_get(bt_val_non_opt)); if (output) { g_string_assign( @@ -4514,7 +3888,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], */ if (implicit_ctf_output_args.exists) { if (!output) { - printf_err("--output-format=ctf specified without --output (trace output path)\n"); + BT_CLI_LOGE_APPEND_CAUSE("--output-format=ctf specified without --output (trace output path)."); goto error; } @@ -4531,7 +3905,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], * sink.ctf.fs implicit components. */ if (implicit_text_args.exists) { - printf_err("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text\n"); + BT_CLI_LOGE_APPEND_CAUSE("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text."); goto error; } } @@ -4560,22 +3934,22 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], } } - /* Decide where the leftover argument(s) go */ - if (bt_value_array_get_size(leftovers) > 0) { + /* Decide where the non-option argument(s) go */ + if (bt_value_array_get_length(non_opts) > 0) { if (implicit_lttng_live_args.exists) { - const bt_value *bt_val_leftover; + const bt_value *bt_val_non_opt; - if (bt_value_array_get_size(leftovers) > 1) { - printf_err("Too many URLs specified for --output-format=lttng-live\n"); + if (bt_value_array_get_length(non_opts) > 1) { + BT_CLI_LOGE_APPEND_CAUSE("Too many URLs specified for --input-format=lttng-live."); goto error; } - bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); + bt_val_non_opt = bt_value_array_borrow_element_by_index_const(non_opts, 0); lttng_live_url_parts = - bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_leftover), + bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_non_opt), error_buf, sizeof(error_buf)); if (!lttng_live_url_parts.proto) { - printf_err("Invalid LTTng live URL format: %s\n", + BT_CLI_LOGE_APPEND_CAUSE("Invalid LTTng live URL format: %s.", error_buf); goto error; } @@ -4589,7 +3963,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], } g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url, - bt_value_string_get(bt_val_leftover)); + bt_value_string_get(bt_val_non_opt)); if (output) { g_string_assign( @@ -4600,9 +3974,22 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], goto end; } - ret = append_implicit_component_extra_param( - &implicit_lttng_live_args, "url", - bt_value_string_get(bt_val_leftover)); + live_inputs_array_val = bt_value_array_create(); + if (!live_inputs_array_val) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + if (bt_value_array_append_string_element( + live_inputs_array_val, + bt_value_string_get(bt_val_non_opt))) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + ret = append_parameter_to_args( + implicit_lttng_live_args.extra_params, + "inputs", live_inputs_array_val); if (ret) { goto error; } @@ -4614,53 +4001,123 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], goto error; } } else { - /* - * Create one source.ctf.fs component, pass it an array - * with the leftovers. - * Note that it still has to be named later. - */ - implicit_ctf_input_args.exists = true; - ret = append_parameter_to_args(implicit_ctf_input_args.extra_params, - "paths", leftovers); - if (ret) { + int status; + size_t plugin_count; + const bt_plugin **plugins; + const bt_plugin *plugin; + + status = require_loaded_plugins(plugin_paths); + if (status != 0) { + goto error; + } + + if (auto_source_discovery_restrict_plugin_name) { + plugin_count = 1; + plugin = borrow_loaded_plugin_by_name(auto_source_discovery_restrict_plugin_name); + plugins = &plugin; + } else { + plugin_count = get_loaded_plugins_count(); + plugins = borrow_loaded_plugins(); + } + + status = auto_discover_source_components(non_opts, plugins, plugin_count, + auto_source_discovery_restrict_component_class_name, + *default_log_level, &auto_disc, interrupter); + + if (status != 0) { + if (status == AUTO_SOURCE_DISCOVERY_STATUS_INTERRUPTED) { + BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( + "Babeltrace CLI", "Automatic source discovery interrupted by the user"); + } + goto error; + } + + status = create_implicit_component_args_from_auto_discovered_sources( + &auto_disc, non_opts, non_opt_params, non_opt_loglevels, + discovered_source_args); + if (status != 0) { goto error; } } } + /* - * Ensure mutual exclusion between implicit `source.ctf.fs` and - * `source.ctf.lttng-live` components. + * If --clock-force-correlated was given, apply it to any src.ctf.fs + * component. */ - if (implicit_ctf_input_args.exists && implicit_lttng_live_args.exists) { - printf_err("Cannot create both implicit `%s` and `%s` components\n", - implicit_ctf_input_args.comp_arg->str, - implicit_lttng_live_args.comp_arg->str); - goto error; + if (ctf_fs_source_force_clock_class_unix_epoch_origin) { + int n; + + n = append_multiple_implicit_components_param( + discovered_source_args, "source.ctf.fs", "force-clock-class-origin-unix-epoch", + "yes"); + if (n == 0) { + BT_CLI_LOGE_APPEND_CAUSE("--clock-force-correlate specified, but no source.ctf.fs component instantiated."); + goto error; + } } - /* - * If the implicit `source.ctf.fs` or `source.ctf.lttng-live` - * components exists, make sure there's at least one leftover - * (which is the path or URL). - */ - if (implicit_ctf_input_args.exists && bt_value_array_is_empty(leftovers)) { - printf_err("Missing path for implicit `%s` component\n", - implicit_ctf_input_args.comp_arg->str); - goto error; + /* If --clock-offset was given, apply it to any src.ctf.fs component. */ + if (ctf_fs_source_clock_class_offset_arg) { + int n; + + n = append_multiple_implicit_components_param( + discovered_source_args, "source.ctf.fs", "clock-class-offset-s", + ctf_fs_source_clock_class_offset_arg); + + if (n == 0) { + BT_CLI_LOGE_APPEND_CAUSE("--clock-offset specified, but no source.ctf.fs component instantiated."); + goto error; + } + } + + /* If --clock-offset-ns was given, apply it to any src.ctf.fs component. */ + if (ctf_fs_source_clock_class_offset_ns_arg) { + int n; + + n = append_multiple_implicit_components_param( + discovered_source_args, "source.ctf.fs", "clock-class-offset-ns", + ctf_fs_source_clock_class_offset_ns_arg); + + if (n == 0) { + BT_CLI_LOGE_APPEND_CAUSE("--clock-offset-ns specified, but no source.ctf.fs component instantiated."); + goto error; + } } - if (implicit_lttng_live_args.exists && bt_value_array_is_empty(leftovers)) { - printf_err("Missing URL for implicit `%s` component\n", + /* + * If the implicit `source.ctf.lttng-live` component exists, + * make sure there's at least one non-option argument (which is + * the URL). + */ + if (implicit_lttng_live_args.exists && bt_value_array_is_empty(non_opts)) { + BT_CLI_LOGE_APPEND_CAUSE("Missing URL for implicit `%s` component.", implicit_lttng_live_args.comp_arg->str); goto error; } /* Assign names to implicit components */ - ret = assign_name_to_implicit_component(&implicit_ctf_input_args, - "source-ctf-fs", all_names, &source_names, true); - if (ret) { - goto error; + for (i = 0; i < discovered_source_args->len; i++) { + struct implicit_component_args *args; + int j; + + args = discovered_source_args->pdata[i]; + + g_string_printf(auto_disc_comp_name, "auto-disc-%s", args->comp_arg->str); + + /* Give it a name like `auto-disc-src-ctf-fs`. */ + for (j = 0; j < auto_disc_comp_name->len; j++) { + if (auto_disc_comp_name->str[j] == '.') { + auto_disc_comp_name->str[j] = '-'; + } + } + + ret = assign_name_to_implicit_component(args, + auto_disc_comp_name->str, all_names, &source_names, true); + if (ret) { + goto error; + } } ret = assign_name_to_implicit_component(&implicit_lttng_live_args, @@ -4707,12 +4164,19 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], /* Make sure there's at least one source and one sink */ if (!source_names) { - printf_err("No source component\n"); + BT_CLI_LOGE_APPEND_CAUSE("No source component."); goto error; } if (!sink_names) { - printf_err("No sink component\n"); + BT_CLI_LOGE_APPEND_CAUSE("No sink component."); + goto error; + } + + /* Make sure there's a single sink component */ + if (g_list_length(sink_names) != 1) { + BT_CLI_LOGE_APPEND_CAUSE( + "More than one sink component specified."); goto error; } @@ -4746,9 +4210,14 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], * Append the equivalent run arguments for the implicit * components. */ - ret = append_run_args_for_implicit_component(&implicit_ctf_input_args, run_args); - if (ret) { - goto error; + for (i = 0; i < discovered_source_args->len; i++) { + struct implicit_component_args *args = + discovered_source_args->pdata[i]; + + ret = append_run_args_for_implicit_component(args, run_args); + if (ret) { + goto error; + } } ret = append_run_args_for_implicit_component(&implicit_lttng_live_args, @@ -4797,7 +4266,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], ret = convert_auto_connect(run_args, source_names, filter_names, sink_names); if (ret) { - printf_err("Cannot auto-connect components\n"); + BT_CLI_LOGE_APPEND_CAUSE("Cannot auto-connect components."); goto error; } @@ -4807,20 +4276,21 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], * here. */ if (print_run_args || print_run_args_0) { + uint64_t args_idx, args_len; if (stream_intersection_mode) { - printf_err("Cannot specify --stream-intersection with --run-args or --run-args-0\n"); + BT_CLI_LOGE_APPEND_CAUSE("Cannot specify --stream-intersection with --run-args or --run-args-0."); goto error; } - for (i = 0; i < bt_value_array_get_size(run_args); i++) { + args_len = bt_value_array_get_length(run_args); + for (args_idx = 0; args_idx < args_len; args_idx++) { const bt_value *arg_value = bt_value_array_borrow_element_by_index(run_args, - i); + args_idx); const char *arg; GString *quoted = NULL; const char *arg_to_print; - BT_ASSERT(arg_value); arg = bt_value_string_get(arg_value); if (print_run_args) { @@ -4840,7 +4310,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], g_string_free(quoted, TRUE); } - if (i < bt_value_array_get_size(run_args) - 1) { + if (args_idx < args_len - 1) { if (print_run_args) { putchar(' '); } else { @@ -4854,18 +4324,8 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], goto end; } - /* - * If the log level is still unset at this point, set it to - * the program's default. - */ - if (*default_log_level < 0) { - *default_log_level = cli_default_log_level; - } - cfg = bt_config_run_from_args_array(run_args, retcode, - force_omit_system_plugin_path, - force_omit_home_plugin_path, - initial_plugin_paths, *default_log_level); + plugin_paths, *default_log_level); if (!cfg) { goto error; } @@ -4878,36 +4338,27 @@ error: BT_OBJECT_PUT_REF_AND_RESET(cfg); end: - /* - * If the log level is still unset at this point, set it to - * the program's default. - */ - if (*default_log_level < 0) { - *default_log_level = cli_default_log_level; - } - - if (pc) { - poptFreeContext(pc); - } + argpar_parse_ret_fini(&argpar_parse_ret); - free(arg); free(output); - if (cur_name) { - g_string_free(cur_name, TRUE); + if (component_arg_for_run) { + g_string_free(component_arg_for_run, TRUE); } - if (cur_name_prefix) { - g_string_free(cur_name_prefix, TRUE); + if (name_gstr) { + g_string_free(name_gstr, TRUE); } + bt_value_put_ref(live_inputs_array_val); bt_value_put_ref(run_args); bt_value_put_ref(all_names); destroy_glist_of_gstring(source_names); destroy_glist_of_gstring(filter_names); destroy_glist_of_gstring(sink_names); - bt_value_put_ref(leftovers); - finalize_implicit_component_args(&implicit_ctf_input_args); + bt_value_put_ref(non_opt_params); + bt_value_put_ref(non_opt_loglevels); + bt_value_put_ref(non_opts); finalize_implicit_component_args(&implicit_ctf_output_args); finalize_implicit_component_args(&implicit_lttng_live_args); finalize_implicit_component_args(&implicit_dummy_args); @@ -4915,8 +4366,20 @@ end: finalize_implicit_component_args(&implicit_debug_info_args); finalize_implicit_component_args(&implicit_muxer_args); finalize_implicit_component_args(&implicit_trimmer_args); - bt_value_put_ref(plugin_paths); bt_common_destroy_lttng_live_url_parts(<tng_live_url_parts); + auto_source_discovery_fini(&auto_disc); + + if (discovered_source_args) { + g_ptr_array_free(discovered_source_args, TRUE); + } + + g_free(ctf_fs_source_clock_class_offset_arg); + g_free(ctf_fs_source_clock_class_offset_ns_arg); + + if (auto_disc_comp_name) { + g_string_free(auto_disc_comp_name, TRUE); + } + return cfg; } @@ -4930,12 +4393,17 @@ void print_gen_usage(FILE *fp) fprintf(fp, "\n"); fprintf(fp, "General options:\n"); fprintf(fp, "\n"); - fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=V)\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, " -l, --log-level=LVL Set the default log level to LVL (`N`, `V`, `D`,\n"); - fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n"); - fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n"); - fprintf(fp, " -V, --version Show version and quit\n"); + fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=T)\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, " -l, --log-level=LVL Set the default log level to LVL (`N`, `T`, `D`,\n"); + fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n"); + fprintf(fp, " -V, --version Show version and quit\n"); fprintf(fp, "\n"); fprintf(fp, "Available commands:\n"); fprintf(fp, "\n"); @@ -4949,16 +4417,34 @@ void print_gen_usage(FILE *fp) } struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) + int *retcode, bool omit_system_plugin_path, + bool omit_home_plugin_path, + const bt_value *initial_plugin_paths, + const bt_interrupter *interrupter) { struct bt_config *config = NULL; int i; - const char **command_argv = NULL; + int top_level_argc; + const char **top_level_argv; int command_argc = -1; + const char **command_argv = NULL; const char *command_name = NULL; int default_log_level = -1; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + bt_value *plugin_paths = NULL; + + /* Top-level option descriptions. */ + static const struct argpar_opt_descr descrs[] = { + { OPT_DEBUG, 'd', "debug", false }, + { OPT_HELP, 'h', "help", false }, + { OPT_LOG_LEVEL, 'l', "log-level", true }, + { OPT_VERBOSE, 'v', "verbose", false }, + { OPT_VERSION, 'V', "version", false}, + { OPT_OMIT_HOME_PLUGIN_PATH, '\0', "omit-home-plugin-path", false }, + { OPT_OMIT_SYSTEM_PLUGIN_PATH, '\0', "omit-system-plugin-path", false }, + { OPT_PLUGIN_PATH, '\0', "plugin-path", true }, + ARGPAR_OPT_DESCR_SENTINEL + }; enum command_type { COMMAND_TYPE_NONE = -1, @@ -4972,13 +4458,27 @@ struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], *retcode = -1; if (!initial_plugin_paths) { - initial_plugin_paths = bt_value_array_create(); - if (!initial_plugin_paths) { - *retcode = 1; - goto end; + plugin_paths = bt_value_array_create(); + if (!plugin_paths) { + goto error; } } else { - bt_value_get_ref(initial_plugin_paths); + bt_value_copy_status copy_status = bt_value_copy( + initial_plugin_paths, &plugin_paths); + if (copy_status) { + goto error; + } + } + + BT_ASSERT(plugin_paths); + + /* + * The `BABELTRACE_PLUGIN_PATH` paths take precedence over the + * `--plugin-path` option's paths, so append it now before + * parsing the general options. + */ + if (append_env_var_plugin_paths(plugin_paths)) { + goto error; } if (argc <= 1) { @@ -4988,161 +4488,191 @@ struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], goto end; } - for (i = 1; i < argc; i++) { - const char *cur_arg = argv[i]; - const char *next_arg = i == (argc - 1) ? NULL : argv[i + 1]; - - if (strcmp(cur_arg, "-d") == 0 || - strcmp(cur_arg, "--debug") == 0) { - default_log_level = BT_LOG_TRACE; - } else if (strcmp(cur_arg, "-v") == 0 || - strcmp(cur_arg, "--verbose") == 0) { - if (default_log_level != BT_LOG_TRACE && - default_log_level != BT_LOG_DEBUG) { - /* - * Legacy: do not override a previous - * --debug because --verbose and --debug - * can be specified together (in this - * case we want the lowest log level to - * apply, TRACE). - */ - default_log_level = BT_LOG_INFO; - } - } else if (strcmp(cur_arg, "--log-level") == 0 || - strcmp(cur_arg, "-l") == 0) { - if (!next_arg) { - printf_err("Missing log level value for --log-level option\n"); - *retcode = 1; - goto end; - } - - default_log_level = - bt_log_get_level_from_string(next_arg); - if (default_log_level < 0) { - printf_err("Invalid argument for --log-level option:\n %s\n", - next_arg); - *retcode = 1; - goto end; - } - - i++; - } else if (strncmp(cur_arg, "--log-level=", 12) == 0) { - const char *arg = &cur_arg[12]; + /* Skip first argument, the name of the program. */ + top_level_argc = argc - 1; + top_level_argv = argv + 1; + argpar_parse_ret = argpar_parse(top_level_argc, top_level_argv, + descrs, false); - default_log_level = bt_log_get_level_from_string(arg); - if (default_log_level < 0) { - printf_err("Invalid argument for --log-level option:\n %s\n", - arg); - *retcode = 1; - goto end; - } - } else if (strncmp(cur_arg, "-l", 2) == 0) { - const char *arg = &cur_arg[2]; + if (argpar_parse_ret.error) { + BT_CLI_LOGE_APPEND_CAUSE( + "While parsing command-line arguments: %s", + argpar_parse_ret.error); + goto error; + } - default_log_level = bt_log_get_level_from_string(arg); - if (default_log_level < 0) { - printf_err("Invalid argument for --log-level option:\n %s\n", - arg); - *retcode = 1; - goto end; + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item; + + item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_DEBUG: + default_log_level = + logging_level_min(default_log_level, BT_LOG_TRACE); + break; + case OPT_VERBOSE: + default_log_level = + logging_level_min(default_log_level, BT_LOG_INFO); + break; + case OPT_LOG_LEVEL: + { + int level = bt_log_get_level_from_string(item_opt->arg); + + if (level < 0) { + BT_CLI_LOGE_APPEND_CAUSE( + "Invalid argument for --log-level option:\n %s", + item_opt->arg); + goto error; + } + + default_log_level = + logging_level_min(default_log_level, level); + break; + } + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + plugin_paths, item_opt->arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + omit_home_plugin_path = true; + break; + case OPT_VERSION: + print_version(); + goto end; + case OPT_HELP: + print_gen_usage(stdout); + goto end; } - } else if (strcmp(cur_arg, "-V") == 0 || - strcmp(cur_arg, "--version") == 0) { - print_version(); - goto end; - } else if (strcmp(cur_arg, "-h") == 0 || - strcmp(cur_arg, "--help") == 0) { - print_gen_usage(stdout); - goto end; - } else { + } else if (item->type == ARGPAR_ITEM_TYPE_NON_OPT) { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; /* * First unknown argument: is it a known command * name? */ - command_argv = &argv[i]; - command_argc = argc - i; + command_argc = + top_level_argc - item_non_opt->orig_index - 1; + command_argv = + &top_level_argv[item_non_opt->orig_index + 1]; - if (strcmp(cur_arg, "convert") == 0) { + if (strcmp(item_non_opt->arg, "convert") == 0) { command_type = COMMAND_TYPE_CONVERT; - } else if (strcmp(cur_arg, "list-plugins") == 0) { + command_name = "convert"; + } else if (strcmp(item_non_opt->arg, "list-plugins") == 0) { command_type = COMMAND_TYPE_LIST_PLUGINS; - } else if (strcmp(cur_arg, "help") == 0) { + command_name = "list-plugins"; + } else if (strcmp(item_non_opt->arg, "help") == 0) { command_type = COMMAND_TYPE_HELP; - } else if (strcmp(cur_arg, "query") == 0) { + command_name = "help"; + } else if (strcmp(item_non_opt->arg, "query") == 0) { command_type = COMMAND_TYPE_QUERY; - } else if (strcmp(cur_arg, "run") == 0) { + command_name = "query"; + } else if (strcmp(item_non_opt->arg, "run") == 0) { command_type = COMMAND_TYPE_RUN; + command_name = "run"; } else { /* - * Unknown argument, but not a known + * Non-option argument, but not a known * command name: assume the default * `convert` command. */ command_type = COMMAND_TYPE_CONVERT; command_name = "convert"; - command_argv = &argv[i - 1]; - command_argc = argc - i + 1; + command_argc++; + command_argv--; } break; } } if (command_type == COMMAND_TYPE_NONE) { + if (argpar_parse_ret.ingested_orig_args == top_level_argc) { + /* + * We only got non-help, non-version general options + * like --verbose and --debug, without any other + * arguments, so we can't do anything useful: print the + * usage and quit. + */ + print_gen_usage(stdout); + goto end; + } + /* - * We only got non-help, non-version general options - * like --verbose and --debug, without any other - * arguments, so we can't do anything useful: print the - * usage and quit. + * We stopped on an unknown option argument (and therefore + * didn't see a command name). Assume `convert` command. */ - print_gen_usage(stdout); - goto end; + command_type = COMMAND_TYPE_CONVERT; + command_name = "convert"; + command_argc = + top_level_argc - argpar_parse_ret.ingested_orig_args; + command_argv = + &top_level_argv[argpar_parse_ret.ingested_orig_args]; } BT_ASSERT(command_argv); BT_ASSERT(command_argc >= 0); /* - * The convert command can set its own default log level for - * backward compatibility reasons. It only does so if there's no - * log level yet, so do not force one for this command. + * For all commands other than `convert`, we now know the log level to + * use, so we can apply it with `set_auto_log_levels`. + * + * The convert command has `--debug` and `--verbose` arguments that are + * equivalent to the top-level arguments of the same name. So after it + * has parsed its arguments, `bt_config_convert_from_args` calls + * `set_auto_log_levels` itself. + */ + if (command_type != COMMAND_TYPE_CONVERT) { + set_auto_log_levels(&default_log_level); + } + + /* + * At this point, `plugin_paths` contains the initial plugin + * paths, the paths from the `BABELTRACE_PLUGIN_PATH` paths, and + * the paths from the `--plugin-path` option. + * + * Now append the user and system plugin paths. */ - if (command_type != COMMAND_TYPE_CONVERT && default_log_level < 0) { - /* Default log level */ - default_log_level = cli_default_log_level; + if (append_home_and_system_plugin_paths(plugin_paths, + omit_system_plugin_path, omit_home_plugin_path)) { + goto error; } switch (command_type) { case COMMAND_TYPE_RUN: config = bt_config_run_from_args(command_argc, command_argv, - retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths, + retcode, plugin_paths, default_log_level); break; case COMMAND_TYPE_CONVERT: config = bt_config_convert_from_args(command_argc, command_argv, - retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, - initial_plugin_paths, &default_log_level); + retcode, plugin_paths, &default_log_level, interrupter); break; case COMMAND_TYPE_LIST_PLUGINS: config = bt_config_list_plugins_from_args(command_argc, - command_argv, retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths); + command_argv, retcode, plugin_paths); break; case COMMAND_TYPE_HELP: config = bt_config_help_from_args(command_argc, - command_argv, retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths, + command_argv, retcode, plugin_paths, default_log_level); break; case COMMAND_TYPE_QUERY: config = bt_config_query_from_args(command_argc, - command_argv, retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths, + command_argv, retcode, plugin_paths, default_log_level); break; default: - abort(); + bt_common_abort(); } if (config) { @@ -5151,7 +4681,13 @@ struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], config->command_name = command_name; } + goto end; + +error: + *retcode = 1; + end: - bt_value_put_ref(initial_plugin_paths); + argpar_parse_ret_fini(&argpar_parse_ret); + bt_value_put_ref(plugin_paths); return config; }