X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=converter%2Fbabeltrace-cfg.c;h=6e07a43749822a3d705360e6f3e5a3aceea5a5b8;hb=63ce0e1d658d528f48fd0f16d5a6748c1b369ef1;hp=6c50d824caf924c84c43413dddb657c2924efebf;hpb=0f9915c6dc054b56b0fd4f30145e937c39127dfd;p=babeltrace.git diff --git a/converter/babeltrace-cfg.c b/converter/babeltrace-cfg.c index 6c50d824..6e07a437 100644 --- a/converter/babeltrace-cfg.c +++ b/converter/babeltrace-cfg.c @@ -30,10 +30,17 @@ #include #include #include +#include #include #include #include +#include +#include #include "babeltrace-cfg.h" +#include "babeltrace-cfg-connect.h" + +#define DEFAULT_SOURCE_COMPONENT_NAME "ctf.fs" +#define DEFAULT_SINK_COMPONENT_NAME "text.text" /* * Error printf() macro which prepends "Error: " the first time it's @@ -685,137 +692,6 @@ void print_version(void) puts("Babeltrace " VERSION); } -/* - * Prints the legacy, Babeltrace 1.x command usage. Those options are - * still compatible in Babeltrace 2.x, but it is recommended to use - * the more generic plugin/component parameters instead of those - * hard-coded option names. - */ -static -void print_legacy_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [OPTIONS] INPUT...\n"); - fprintf(fp, "\n"); - fprintf(fp, "The following options are compatible with the Babeltrace 1.x options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --help-legacy Show this help\n"); - fprintf(fp, " -V, --version Show version\n"); - fprintf(fp, " --clock-force-correlate Assume that clocks are inherently correlated\n"); - fprintf(fp, " across traces\n"); - fprintf(fp, " -d, --debug Enable debug mode\n"); - fprintf(fp, " -i, --input-format=FORMAT Input trace format (default: ctf)\n"); - fprintf(fp, " -l, --list List available formats\n"); - fprintf(fp, " -o, --output-format=FORMAT Output trace format (default: text)\n"); - fprintf(fp, " -v, --verbose Enable verbose output\n"); - fprintf(fp, "\n"); - fprintf(fp, " Available input formats: ctf, lttng-live, ctf-metadata\n"); - fprintf(fp, " Available output formats: text, dummy\n"); - fprintf(fp, "\n"); - fprintf(fp, "Input formats specific options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " INPUT... Input trace file(s), directory(ies), or URLs\n"); - fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n"); - fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS nanoseconds\n"); - fprintf(fp, " --stream-intersection Only process events when all streams are active\n"); - fprintf(fp, "\n"); - fprintf(fp, "text output format specific options:\n"); - fprintf(fp, " \n"); - fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n"); - fprintf(fp, " --clock-date Print timestamp dates\n"); - fprintf(fp, " --clock-gmt Print and parse timestamps in GMT time zone\n"); - fprintf(fp, " (default: local time zone)\n"); - fprintf(fp, " --clock-seconds Print the timestamps as [SEC.NS]\n"); - fprintf(fp, " (default format: [HH:MM:SS.NS])\n"); - fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n"); - fprintf(fp, " (default: `/usr/lib/debug`)\n"); - fprintf(fp, " --debug-info-full-path Show full debug info source and binary paths\n"); - fprintf(fp, " --debug-info-target-prefix=DIR Use directory DIR as a prefix when looking\n"); - fprintf(fp, " up executables during debug info analysis\n"); - fprintf(fp, " (default: `/usr/lib/debug`)\n"); - fprintf(fp, " -f, --fields=NAME[,NAME]... Print additional fields:\n"); - fprintf(fp, " all, trace, trace:hostname, trace:domain,\n"); - fprintf(fp, " trace:procname, trace:vpid, loglevel, emf\n"); - fprintf(fp, " (default: trace:hostname, trace:procname,\n"); - fprintf(fp, " trace:vpid)\n"); - fprintf(fp, " -n, --names=NAME[,NAME]... Print field names:\n"); - fprintf(fp, " payload (or arg or args)\n"); - fprintf(fp, " none, all, scope, header, context (or ctx)\n"); - fprintf(fp, " (default: payload, context)\n"); - fprintf(fp, " --no-delta Do not print time delta between consecutive\n"); - fprintf(fp, " events\n"); - fprintf(fp, " -w, --output=PATH Write output to PATH (default: standard output)\n"); -} - -/* - * Prints the Babeltrace 2.x usage. - */ -static -void print_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [OPTIONS]\n"); - fprintf(fp, "\n"); - fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n"); - fprintf(fp, " of the following source and sink component\n"); - fprintf(fp, " instances (see the exact format of PARAMS\n"); - fprintf(fp, " below)\n"); - fprintf(fp, " -d, --debug Enable debug mode\n"); - fprintf(fp, " -l, --list List available plugins and their components\n"); - fprintf(fp, " -P, --path=PATH Set the `path` parameter of the latest source\n"); - fprintf(fp, " or sink component to PATH\n"); - fprintf(fp, " -p, --params=PARAMS Set the parameters of the latest source or\n"); - fprintf(fp, " sink component instance (in command-line \n"); - fprintf(fp, " order) to PARAMS (see the exact format of\n"); - fprintf(fp, " PARAMS below)\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Set paths from which dynamic plugins can be\n"); - fprintf(fp, " loaded to PATH\n"); - fprintf(fp, " -r, --reset-base-params Reset the current base parameters of the\n"); - fprintf(fp, " following source and sink component\n"); - fprintf(fp, " instances to an empty map\n"); - fprintf(fp, " -o, --sink=PLUGIN.COMPCLS Instantiate a sink component from plugin\n"); - fprintf(fp, " PLUGIN and component class COMPCLS (may be\n"); - fprintf(fp, " repeated)\n"); - fprintf(fp, " -i, --source=PLUGIN.COMPCLS Instantiate a source component from plugin\n"); - fprintf(fp, " PLUGIN and component class COMPCLS (may be\n"); - fprintf(fp, " repeated)\n"); - fprintf(fp, " --begin Start time: [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n"); - fprintf(fp, " --end End time: [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n"); - fprintf(fp, " --timerange Time range: begin,end or [begin,end] (where [] are actual brackets)\n"); - fprintf(fp, " -h --help Show this help\n"); - fprintf(fp, " --help-legacy Show Babeltrace 1.x legacy options\n"); - fprintf(fp, " -v, --verbose Enable verbose output\n"); - fprintf(fp, " -V, --version Show version\n"); - fprintf(fp, "\n\n"); - fprintf(fp, "Format of PARAMS\n"); - fprintf(fp, "----------------\n"); - fprintf(fp, "\n"); - fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n"); - fprintf(fp, "\n"); - fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n"); - fprintf(fp, "where PARAM is the parameter name (C identifier plus [:.-] characters), and\n"); - fprintf(fp, "VALUE can be one of:\n"); - fprintf(fp, "\n"); - fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n"); - fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n"); - fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n"); - fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n"); - fprintf(fp, " (`0x` prefix) signed 64-bit integer.\n"); - fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n"); - fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n"); - fprintf(fp, " the null and boolean value symbols above.\n"); - fprintf(fp, "* Double-quoted string (accepts escape characters).\n"); - fprintf(fp, "\n"); - fprintf(fp, "Whitespaces are allowed around individual `=` and `,` tokens.\n"); - fprintf(fp, "\n"); - fprintf(fp, "Example:\n"); - fprintf(fp, "\n"); - fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n"); - fprintf(fp, " observe=3.14, simple=beef, needs-quotes=\"some string\",\n"); - fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\"\n"); - fprintf(fp, "\n"); - fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run babeltrace\n"); - fprintf(fp, "from a shell.\n"); -} - /* * Destroys a component configuration. */ @@ -837,6 +713,10 @@ void bt_config_component_destroy(struct bt_object *obj) g_string_free(bt_config_component->component_name, TRUE); } + if (bt_config_component->instance_name) { + g_string_free(bt_config_component->instance_name, TRUE); + } + BT_PUT(bt_config_component->params); g_free(bt_config_component); @@ -852,8 +732,9 @@ end: * Return value is owned by the caller. */ static -struct bt_config_component *bt_config_component_create(const char *plugin_name, - const char *component_name) +struct bt_config_component *bt_config_component_create( + enum bt_component_class_type type, + const char *plugin_name, const char *component_name) { struct bt_config_component *cfg_component = NULL; @@ -864,6 +745,7 @@ struct bt_config_component *bt_config_component_create(const char *plugin_name, } bt_object_init(cfg_component, bt_config_component_destroy); + cfg_component->type = type; cfg_component->plugin_name = g_string_new(plugin_name); if (!cfg_component->plugin_name) { print_err_oom(); @@ -876,6 +758,12 @@ struct bt_config_component *bt_config_component_create(const char *plugin_name, goto error; } + cfg_component->instance_name = g_string_new(NULL); + if (!cfg_component->instance_name) { + print_err_oom(); + goto error; + } + /* Start with empty parameters */ cfg_component->params = bt_value_map_create(); if (!cfg_component->params) { @@ -896,8 +784,8 @@ end: * Creates a component configuration from a command-line source/sink * option's argument. */ -static -struct bt_config_component *bt_config_component_from_arg(const char *arg) +struct bt_config_component *bt_config_component_from_arg( + enum bt_component_class_type type, const char *arg) { struct bt_config_component *bt_config_component = NULL; char *plugin_name; @@ -909,7 +797,7 @@ struct bt_config_component *bt_config_component_from_arg(const char *arg) goto error; } - bt_config_component = bt_config_component_create(plugin_name, + bt_config_component = bt_config_component_create(type, plugin_name, component_name); if (!bt_config_component) { goto error; @@ -932,90 +820,96 @@ end: static void bt_config_destroy(struct bt_object *obj) { - struct bt_config *bt_config = + struct bt_config *cfg = container_of(obj, struct bt_config, base); if (!obj) { goto end; } - if (bt_config->sources) { - g_ptr_array_free(bt_config->sources, TRUE); - } + switch (cfg->command) { + case BT_CONFIG_COMMAND_CONVERT: + if (cfg->cmd_data.convert.sources) { + g_ptr_array_free(cfg->cmd_data.convert.sources, TRUE); + } + + if (cfg->cmd_data.convert.filters) { + g_ptr_array_free(cfg->cmd_data.convert.filters, TRUE); + } + + if (cfg->cmd_data.convert.sinks) { + g_ptr_array_free(cfg->cmd_data.convert.sinks, TRUE); + } - if (bt_config->sinks) { - g_ptr_array_free(bt_config->sinks, TRUE); + if (cfg->cmd_data.convert.connections) { + g_ptr_array_free(cfg->cmd_data.convert.connections, + TRUE); + } + + BT_PUT(cfg->cmd_data.convert.plugin_paths); + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + BT_PUT(cfg->cmd_data.list_plugins.plugin_paths); + break; + case BT_CONFIG_COMMAND_HELP: + BT_PUT(cfg->cmd_data.help.plugin_paths); + BT_PUT(cfg->cmd_data.help.cfg_component); + break; + case BT_CONFIG_COMMAND_QUERY_INFO: + BT_PUT(cfg->cmd_data.query_info.plugin_paths); + BT_PUT(cfg->cmd_data.query_info.cfg_component); + + if (cfg->cmd_data.query_info.action) { + g_string_free(cfg->cmd_data.query_info.action, TRUE); + } + break; + default: + assert(false); } - BT_PUT(bt_config->plugin_paths); - g_free(bt_config); + g_free(cfg); end: return; } +static void destroy_gstring(void *data) +{ + g_string_free(data, TRUE); +} + /* * Extracts the various paths from the string arg, delimited by ':', - * and converts them to an array value object. - * - * Returned array value object is empty if arg is empty. - * - * Return value is owned by the caller. + * and appends them to the array value object plugin_paths. */ -static -struct bt_value *plugin_paths_from_arg(const char *arg) +enum bt_value_status bt_config_append_plugin_paths( + struct bt_value *plugin_paths, const char *arg) { - struct bt_value *plugin_paths; - const char *at = arg; - const char *end = arg + strlen(arg); + enum bt_value_status status = BT_VALUE_STATUS_OK; + GPtrArray *dirs = g_ptr_array_new_with_free_func(destroy_gstring); + int ret; + size_t i; - plugin_paths = bt_value_array_create(); - if (!plugin_paths) { - print_err_oom(); - goto error; + if (!dirs) { + status = BT_VALUE_STATUS_ERROR; + goto end; } - while (at < end) { - int ret; - GString *path; - const char *next_colon; - - next_colon = strchr(at, ':'); - if (next_colon == at) { - /* - * Empty path: try next character (supported - * to conform to the typical parsing of $PATH). - */ - at++; - continue; - } else if (!next_colon) { - /* No more colon: use the remaining */ - next_colon = arg + strlen(arg); - } - - path = g_string_new(NULL); - if (!path) { - print_err_oom(); - goto error; - } - - g_string_append_len(path, at, next_colon - at); - at = next_colon + 1; - ret = bt_value_array_append_string(plugin_paths, path->str); - g_string_free(path, TRUE); - if (ret) { - print_err_oom(); - goto error; - } + ret = bt_common_append_plugin_path_dirs(arg, dirs); + if (ret) { + status = BT_VALUE_STATUS_ERROR; + goto end; } - goto end; + for (i = 0; i < dirs->len; i++) { + GString *dir = g_ptr_array_index(dirs, i); -error: - BT_PUT(plugin_paths); + bt_value_array_append_string(plugin_paths, dir->str); + } end: - return plugin_paths; + g_ptr_array_free(dirs, TRUE); + return status; } /* @@ -1472,7 +1366,7 @@ int append_sinks_from_legacy_opts(GPtrArray *sinks, component_name = "metadata-text"; break; case LEGACY_OUTPUT_FORMAT_DUMMY: - plugin_name = "dummy"; + plugin_name = "utils"; component_name = "dummy"; break; default: @@ -1499,8 +1393,8 @@ int append_sinks_from_legacy_opts(GPtrArray *sinks, } /* Create a component configuration */ - bt_config_component = bt_config_component_create(plugin_name, - component_name); + bt_config_component = bt_config_component_create( + BT_COMPONENT_CLASS_TYPE_SINK, plugin_name, component_name); if (!bt_config_component) { goto error; } @@ -1628,8 +1522,8 @@ int append_sources_from_legacy_opts(GPtrArray *sources, } /* Create a component configuration */ - bt_config_component = bt_config_component_create("ctf", - component_name); + bt_config_component = bt_config_component_create( + BT_COMPONENT_CLASS_TYPE_SOURCE, "ctf", component_name); if (!bt_config_component) { goto error; } @@ -1793,7 +1687,7 @@ void print_output_legacy_to_sinks( enum legacy_output_format legacy_output_format, struct text_legacy_opts *text_legacy_opts) { - const char *input_format; + const char *output_format; GString *str = NULL; str = g_string_new(" "); @@ -1804,22 +1698,22 @@ void print_output_legacy_to_sinks( switch (legacy_output_format) { case LEGACY_OUTPUT_FORMAT_TEXT: - input_format = "text"; + output_format = "text"; break; case LEGACY_OUTPUT_FORMAT_CTF_METADATA: - input_format = "ctf-metadata"; + output_format = "ctf-metadata"; break; case LEGACY_OUTPUT_FORMAT_DUMMY: - input_format = "dummy"; + output_format = "dummy"; break; default: assert(false); } printf_err("Both `%s` legacy output format and non-legacy sink component\ninstances(s) specified.\n\n", - input_format); + output_format); printf_err("Specify the following non-legacy sink component instance instead of the\nlegacy `%s` output format options:\n\n", - input_format); + output_format); g_string_append(str, "-o "); switch (legacy_output_format) { @@ -1830,7 +1724,7 @@ void print_output_legacy_to_sinks( g_string_append(str, "ctf.metadata-text"); break; case LEGACY_OUTPUT_FORMAT_DUMMY: - g_string_append(str, "dummy.dummy"); + g_string_append(str, "utils.dummy"); break; default: assert(false); @@ -2003,14 +1897,12 @@ bool validate_cfg(struct bt_config *cfg, /* Determine if the input and output should be legacy-style */ if (*legacy_input_format != LEGACY_INPUT_FORMAT_NONE || - cfg->sources->len == 0 || !bt_value_array_is_empty(legacy_input_paths) || ctf_legacy_opts_is_any_set(ctf_legacy_opts)) { legacy_input = true; } if (*legacy_output_format != LEGACY_OUTPUT_FORMAT_NONE || - cfg->sinks->len == 0 || text_legacy_opts_is_any_set(text_legacy_opts)) { legacy_output = true; } @@ -2037,7 +1929,7 @@ bool validate_cfg(struct bt_config *cfg, } /* Make sure no non-legacy sources are specified */ - if (cfg->sources->len != 0) { + if (cfg->cmd_data.convert.sources->len != 0) { print_input_legacy_to_sources(*legacy_input_format, legacy_input_paths, ctf_legacy_opts); goto error; @@ -2065,7 +1957,7 @@ bool validate_cfg(struct bt_config *cfg, } /* Make sure no non-legacy sinks are specified */ - if (cfg->sinks->len != 0) { + if (cfg->cmd_data.convert.sinks->len != 0) { print_output_legacy_to_sinks(*legacy_output_format, text_legacy_opts); goto error; @@ -2119,22 +2011,26 @@ enum { OPT_CLOCK_OFFSET, OPT_CLOCK_OFFSET_NS, OPT_CLOCK_SECONDS, + OPT_CONNECT, OPT_DEBUG, OPT_DEBUG_INFO_DIR, OPT_DEBUG_INFO_FULL_PATH, OPT_DEBUG_INFO_TARGET_PREFIX, OPT_END, OPT_FIELDS, + OPT_FILTER, OPT_HELP, - OPT_HELP_LEGACY, OPT_INPUT_FORMAT, OPT_LIST, + OPT_NAME, OPT_NAMES, OPT_NO_DELTA, + OPT_OMIT_HOME_PLUGIN_PATH, + OPT_OMIT_SYSTEM_PLUGIN_PATH, OPT_OUTPUT_FORMAT, OPT_OUTPUT_PATH, - OPT_PATH, OPT_PARAMS, + OPT_PATH, OPT_PLUGIN_PATH, OPT_RESET_BASE_PARAMS, OPT_SINK, @@ -2142,46 +2038,6 @@ enum { OPT_STREAM_INTERSECTION, OPT_TIMERANGE, OPT_VERBOSE, - OPT_VERSION, -}; - -/* popt long option descriptions */ -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, - { "begin", '\0', 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 }, - { "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", '\0', 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 }, - { "help-legacy", '\0', POPT_ARG_NONE, NULL, OPT_HELP_LEGACY, NULL, NULL }, - { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL }, - { "list", 'l', POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL }, - { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL }, - { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL }, - { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT_PATH, NULL, NULL }, - { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL }, - { "path", 'P', POPT_ARG_STRING, NULL, OPT_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 }, - { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, - { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL }, - { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL }, - { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL }, - { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, - { "version", 'V', POPT_ARG_NONE, NULL, OPT_VERSION, NULL, NULL }, - { NULL, 0, 0, NULL, 0, NULL, NULL }, }; /* @@ -2207,9 +2063,9 @@ static void add_cfg_comp(struct bt_config *cfg, enum bt_config_component_dest dest) { if (dest == BT_CONFIG_COMPONENT_DEST_SOURCE) { - g_ptr_array_add(cfg->sources, cfg_comp); + g_ptr_array_add(cfg->cmd_data.convert.sources, cfg_comp); } else { - g_ptr_array_add(cfg->sinks, cfg_comp); + g_ptr_array_add(cfg->cmd_data.convert.sinks, cfg_comp); } } @@ -2246,83 +2102,1187 @@ not_found: return -1; } -/* - * Returns a Babeltrace configuration, out of command-line arguments, - * containing everything that is needed to instanciate specific - * components with given parameters. - * - * *exit_code is set to the appropriate exit code to use as far as this - * function goes. - * - * Return value is NULL on error, otherwise it's owned by the caller. - */ -struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_code) +static int append_env_var_plugin_paths(struct bt_value *plugin_paths) { - struct bt_config *cfg = NULL; - poptContext pc = NULL; - char *arg = NULL; - struct ctf_legacy_opts ctf_legacy_opts; - struct text_legacy_opts text_legacy_opts; - enum legacy_input_format legacy_input_format = LEGACY_INPUT_FORMAT_NONE; - enum legacy_output_format legacy_output_format = - LEGACY_OUTPUT_FORMAT_NONE; - struct bt_value *legacy_input_paths = NULL; - struct bt_config_component *cur_cfg_comp = NULL; - enum bt_config_component_dest cur_cfg_comp_dest = - BT_CONFIG_COMPONENT_DEST_SOURCE; - struct bt_value *cur_base_params = NULL; - int opt; - bool cur_cfg_comp_params_set = false; - - memset(&ctf_legacy_opts, 0, sizeof(ctf_legacy_opts)); - memset(&text_legacy_opts, 0, sizeof(text_legacy_opts)); - *exit_code = 0; + int ret = 0; + const char *envvar; - if (argc <= 1) { - print_usage(stdout); + if (bt_common_is_setuid_setgid()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); goto end; } - text_legacy_opts.output = g_string_new(NULL); - if (!text_legacy_opts.output) { - print_err_oom(); - goto error; + envvar = getenv("BABELTRACE_PLUGIN_PATH"); + if (!envvar) { + goto end; } - text_legacy_opts.dbg_info_dir = g_string_new(NULL); - if (!text_legacy_opts.dbg_info_dir) { - print_err_oom(); - goto error; - } + ret = bt_config_append_plugin_paths(plugin_paths, envvar); - text_legacy_opts.dbg_info_target_prefix = g_string_new(NULL); - if (!text_legacy_opts.dbg_info_target_prefix) { - print_err_oom(); - goto error; - } +end: + return ret; +} - cur_base_params = bt_value_map_create(); - if (!cur_base_params) { - print_err_oom(); - goto error; - } +static int append_home_and_system_plugin_paths(struct bt_value *plugin_paths, + bool omit_system_plugin_path, bool omit_home_plugin_path) +{ + int ret; - /* Create config */ - cfg = g_new0(struct bt_config, 1); - if (!cfg) { - print_err_oom(); - goto error; - } + if (!omit_home_plugin_path) { + if (bt_common_is_setuid_setgid()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); + } else { + char *home_plugin_dir = + bt_common_get_home_plugin_path(); - bt_object_init(cfg, bt_config_destroy); - cfg->sources = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put); - if (!cfg->sources) { - print_err_oom(); - goto error; + if (home_plugin_dir) { + ret = bt_config_append_plugin_paths( + plugin_paths, + home_plugin_dir); + free(home_plugin_dir); + + if (ret) { + printf_err("Invalid home plugin path\n"); + goto error; + } + } + } + } + + 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"); + goto error; + } + } + return 0; +error: + return -1; +} + +static int append_sources_from_implicit_params(GPtrArray *sources, + struct bt_config_component *implicit_source_comp) +{ + size_t i; + size_t len = sources->len; + + for (i = 0; i < len; i++) { + struct bt_config_component *comp; + struct bt_value *params_to_set; + + comp = g_ptr_array_index(sources, i); + params_to_set = bt_value_map_extend(comp->params, + implicit_source_comp->params); + if (!params_to_set) { + printf_err("Cannot extend legacy component parameters with non-legacy parameters\n"); + goto error; + } + BT_MOVE(comp->params, params_to_set); + } + return 0; +error: + return -1; +} + +static struct bt_config *bt_config_base_create(enum bt_config_command command) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = g_new0(struct bt_config, 1); + if (!cfg) { + print_err_oom(); + goto error; + } + + bt_object_init(cfg, bt_config_destroy); + cfg->command = command; + goto end; + +error: + BT_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_convert_create( + struct bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_CONVERT); + if (!cfg) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.convert.sources = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_put); + if (!cfg->cmd_data.convert.sources) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.convert.filters = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_put); + if (!cfg->cmd_data.convert.filters) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.convert.sinks = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_put); + if (!cfg->cmd_data.convert.sinks) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.convert.connections = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_config_connection_destroy); + if (!cfg->cmd_data.convert.connections) { + print_err_oom(); + goto error; + } + + if (initial_plugin_paths) { + cfg->cmd_data.convert.plugin_paths = + bt_get(initial_plugin_paths); + } else { + cfg->cmd_data.convert.plugin_paths = bt_value_array_create(); + if (!cfg->cmd_data.convert.plugin_paths) { + print_err_oom(); + goto error; + } + } + + goto end; + +error: + BT_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_list_plugins_create( + struct bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS); + if (!cfg) { + print_err_oom(); + goto error; + } + + if (initial_plugin_paths) { + cfg->cmd_data.list_plugins.plugin_paths = + bt_get(initial_plugin_paths); + } else { + cfg->cmd_data.list_plugins.plugin_paths = + bt_value_array_create(); + if (!cfg->cmd_data.list_plugins.plugin_paths) { + print_err_oom(); + goto error; + } + } + + goto end; + +error: + BT_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_help_create( + struct bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_HELP); + if (!cfg) { + print_err_oom(); + goto error; + } + + if (initial_plugin_paths) { + cfg->cmd_data.help.plugin_paths = + bt_get(initial_plugin_paths); + } else { + cfg->cmd_data.help.plugin_paths = + bt_value_array_create(); + if (!cfg->cmd_data.help.plugin_paths) { + print_err_oom(); + goto error; + } + } + + cfg->cmd_data.help.cfg_component = + bt_config_component_create(BT_COMPONENT_CLASS_TYPE_UNKNOWN, + NULL, NULL); + if (!cfg->cmd_data.help.cfg_component) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_query_info_create( + struct bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_QUERY_INFO); + if (!cfg) { + print_err_oom(); + goto error; + } + + if (initial_plugin_paths) { + cfg->cmd_data.query_info.plugin_paths = + bt_get(initial_plugin_paths); + } else { + cfg->cmd_data.query_info.plugin_paths = + bt_value_array_create(); + if (!cfg->cmd_data.query_info.plugin_paths) { + print_err_oom(); + goto error; + } + } + + cfg->cmd_data.query_info.action = g_string_new(NULL); + if (!cfg->cmd_data.query_info.action) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_PUT(cfg); + +end: + return cfg; +} + +/* + * Prints the expected format for a --params option. + */ +static +void print_expected_params_format(FILE *fp) +{ + fprintf(fp, "Expected format of PARAMS\n"); + fprintf(fp, "-------------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n"); + fprintf(fp, "\n"); + fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n"); + fprintf(fp, "where PARAM is the parameter name (C identifier plus [:.-] characters), and\n"); + fprintf(fp, "VALUE can be one of:\n"); + fprintf(fp, "\n"); + fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n"); + fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n"); + fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n"); + fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n"); + fprintf(fp, " (`0x` prefix) signed 64-bit integer.\n"); + fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n"); + fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n"); + fprintf(fp, " the null and boolean value symbols above.\n"); + fprintf(fp, "* Double-quoted string (accepts escape characters).\n"); + fprintf(fp, "\n"); + fprintf(fp, "Whitespaces are allowed around individual `=` and `,` tokens.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Example:\n"); + fprintf(fp, "\n"); + fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n"); + fprintf(fp, " observe=3.14, simple=beef, needs-quotes=\"some string\",\n"); + fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\"\n"); + fprintf(fp, "\n"); + fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run babeltrace\n"); + fprintf(fp, "from a shell.\n"); +} + + +/* + * Prints the help command usage. + */ +static +void print_help_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] help [OPTIONS] PLUGIN\n"); + fprintf(fp, " babeltrace [GENERAL OPTIONS] help [OPTIONS] --source=PLUGIN.COMPCLS\n"); + fprintf(fp, " babeltrace [GENERAL OPTIONS] help [OPTIONS] --filter=PLUGIN.COMPCLS\n"); + fprintf(fp, " babeltrace [GENERAL OPTIONS] help [OPTIONS] --sink=PLUGIN.COMPCLS\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --filter=PLUGIN.COMPCLS Get help for the filter component class\n"); + fprintf(fp, " COMPCLS found in the plugin PLUGIN\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace/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, " --sink=PLUGIN.COMPCLS Get help for the sink component class\n"); + fprintf(fp, " COMPCLS found in the plugin PLUGIN\n"); + fprintf(fp, " --source=PLUGIN.COMPCLS Get help for the source component class\n"); + fprintf(fp, " COMPCLS found in the plugin PLUGIN\n"); + fprintf(fp, " -h --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace --help` for the list of general options.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Use `babeltrace list-plugins` to show the list of available plugins.\n"); +} + +static struct poptOption help_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "filter", '\0', POPT_ARG_STRING, NULL, OPT_FILTER, NULL, NULL }, + { "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 }, + { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, + { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL }, + { NULL, 0, 0, NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a help + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +struct bt_config *bt_config_help_from_args(int argc, const char *argv[], + int *retcode, bool omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + char *plugin_name = NULL, *component_name = NULL; + char *plugin_comp_cls_names = NULL; + + *retcode = 0; + cfg = bt_config_help_create(initial_plugin_paths); + if (!cfg) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.help.omit_system_plugin_path = omit_system_plugin_path; + cfg->cmd_data.help.omit_home_plugin_path = omit_home_plugin_path; + ret = append_env_var_plugin_paths(cfg->cmd_data.help.plugin_paths); + if (ret) { + printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + help_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_common_is_setuid_setgid()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); + } else { + if (bt_config_append_plugin_paths( + cfg->cmd_data.help.plugin_paths, + arg)) { + printf_err("Invalid --plugin-path option's argument:\n %s\n", + arg); + goto error; + } + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->cmd_data.help.omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->cmd_data.help.omit_home_plugin_path = true; + break; + case OPT_SOURCE: + case OPT_FILTER: + case OPT_SINK: + if (cfg->cmd_data.help.cfg_component->type != + BT_COMPONENT_CLASS_TYPE_UNKNOWN) { + printf_err("Cannot specify more than one plugin and component class:\n %s\n", + arg); + goto error; + } + + switch (opt) { + case OPT_SOURCE: + cfg->cmd_data.help.cfg_component->type = + BT_COMPONENT_CLASS_TYPE_SOURCE; + break; + case OPT_FILTER: + cfg->cmd_data.help.cfg_component->type = + BT_COMPONENT_CLASS_TYPE_FILTER; + break; + case OPT_SINK: + cfg->cmd_data.help.cfg_component->type = + BT_COMPONENT_CLASS_TYPE_SINK; + break; + default: + assert(false); + } + plugin_comp_cls_names = strdup(arg); + if (!plugin_comp_cls_names) { + print_err_oom(); + goto error; + } + break; + case OPT_HELP: + print_help_usage(stdout); + *retcode = -1; + BT_PUT(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; + } + + leftover = poptGetArg(pc); + if (leftover) { + if (cfg->cmd_data.help.cfg_component->type != + BT_COMPONENT_CLASS_TYPE_UNKNOWN) { + printf_err("Cannot specify plugin name and --source/--filter/--sink component class:\n %s\n", + leftover); + goto error; + } + + g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name, + leftover); + } else { + if (cfg->cmd_data.help.cfg_component->type == + BT_COMPONENT_CLASS_TYPE_UNKNOWN) { + print_help_usage(stdout); + *retcode = -1; + BT_PUT(cfg); + goto end; + } + + plugin_component_names_from_arg(plugin_comp_cls_names, + &plugin_name, &component_name); + if (plugin_name && component_name) { + g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name, + plugin_name); + g_string_assign(cfg->cmd_data.help.cfg_component->component_name, + component_name); + } else { + printf_err("Invalid --source/--filter/--sink option's argument:\n %s\n", + plugin_comp_cls_names); + goto error; + } + } + + if (append_home_and_system_plugin_paths( + cfg->cmd_data.help.plugin_paths, + cfg->cmd_data.help.omit_system_plugin_path, + cfg->cmd_data.help.omit_home_plugin_path)) { + printf_err("Cannot append home and system plugin paths\n"); + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_PUT(cfg); + +end: + free(plugin_comp_cls_names); + g_free(plugin_name); + g_free(component_name); + + if (pc) { + poptFreeContext(pc); + } + + free(arg); + return cfg; +} + +/* + * Prints the help command usage. + */ +static +void print_query_info_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace [GEN OPTS] query-info [OPTS] ACTION --source=PLUGIN.COMPCLS\n"); + fprintf(fp, " babeltrace [GEN OPTS] query-info [OPTS] ACTION --filter=PLUGIN.COMPCLS\n"); + fprintf(fp, " babeltrace [GEN OPTS] query-info [OPTS] ACTION --sink=PLUGIN.COMPCLS\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --filter=PLUGIN.COMPCLS Query info from the filter component class\n"); + fprintf(fp, " COMPCLS found in the plugin PLUGIN\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace/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, " --sink=PLUGIN.COMPCLS Query info from the sink component class\n"); + fprintf(fp, " COMPCLS found in the plugin PLUGIN\n"); + fprintf(fp, " --source=PLUGIN.COMPCLS Query info from the source component class\n"); + fprintf(fp, " COMPCLS found in the plugin PLUGIN\n"); + fprintf(fp, " -h --help Show this help and quit\n"); + fprintf(fp, "\n\n"); + print_expected_params_format(fp); +} + +static struct poptOption query_info_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "filter", '\0', POPT_ARG_STRING, NULL, OPT_FILTER, NULL, NULL }, + { "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 }, + { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, + { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL }, + { NULL, 0, 0, NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a query-info + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +struct bt_config *bt_config_query_info_from_args(int argc, const char *argv[], + int *retcode, bool omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + struct bt_value *params = bt_value_null; + + *retcode = 0; + cfg = bt_config_query_info_create(initial_plugin_paths); + if (!cfg) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.query_info.omit_system_plugin_path = + omit_system_plugin_path; + cfg->cmd_data.query_info.omit_home_plugin_path = omit_home_plugin_path; + ret = append_env_var_plugin_paths(cfg->cmd_data.query_info.plugin_paths); + if (ret) { + printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + query_info_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_common_is_setuid_setgid()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); + } else { + if (bt_config_append_plugin_paths( + cfg->cmd_data.query_info.plugin_paths, + arg)) { + printf_err("Invalid --plugin-path option's argument:\n %s\n", + arg); + goto error; + } + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->cmd_data.query_info.omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->cmd_data.query_info.omit_home_plugin_path = true; + break; + case OPT_SOURCE: + case OPT_FILTER: + case OPT_SINK: + { + enum bt_component_class_type type; + + if (cfg->cmd_data.query_info.cfg_component) { + printf_err("Cannot specify more than one plugin and component class:\n %s\n", + arg); + goto error; + } + + switch (opt) { + case OPT_SOURCE: + type = BT_COMPONENT_CLASS_TYPE_SOURCE; + break; + case OPT_FILTER: + type = BT_COMPONENT_CLASS_TYPE_FILTER; + break; + case OPT_SINK: + type = BT_COMPONENT_CLASS_TYPE_SINK; + break; + default: + assert(false); + } + + cfg->cmd_data.query_info.cfg_component = + bt_config_component_from_arg(type, arg); + if (!cfg->cmd_data.query_info.cfg_component) { + printf_err("Invalid format for --source/--filter/--sink option's argument:\n %s\n", + arg); + goto error; + } + + /* Default parameters: null */ + bt_put(cfg->cmd_data.query_info.cfg_component->params); + cfg->cmd_data.query_info.cfg_component->params = + bt_value_null; + break; + } + case OPT_PARAMS: + { + params = bt_value_from_arg(arg); + if (!params) { + printf_err("Invalid format for --params option's argument:\n %s\n", + arg); + goto error; + } + break; + } + case OPT_HELP: + print_query_info_usage(stdout); + *retcode = -1; + BT_PUT(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + if (!cfg->cmd_data.query_info.cfg_component) { + printf_err("No target component class specified with --source/--filter/--sink option\n"); + goto error; + } + + assert(params); + BT_MOVE(cfg->cmd_data.query_info.cfg_component->params, params); + + /* 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; + } + + /* + * We need exactly one leftover argument which is the + * mandatory action. + */ + leftover = poptGetArg(pc); + if (leftover) { + if (strlen(leftover) == 0) { + printf_err("Invalid empty action\n"); + goto error; + } + + g_string_assign(cfg->cmd_data.query_info.action, leftover); + } else { + print_query_info_usage(stdout); + *retcode = -1; + BT_PUT(cfg); + goto end; + } + + leftover = poptGetArg(pc); + if (leftover) { + printf_err("Invalid argument: %s\n", leftover); + goto error; + } + + if (append_home_and_system_plugin_paths( + cfg->cmd_data.query_info.plugin_paths, + cfg->cmd_data.query_info.omit_system_plugin_path, + cfg->cmd_data.query_info.omit_home_plugin_path)) { + printf_err("Cannot append home and system plugin paths\n"); + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_PUT(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + BT_PUT(params); + free(arg); + return cfg; +} + +/* + * Prints the list-plugins command usage. + */ +static +void print_list_plugins_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] list-plugins [OPTIONS]\n"); + 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/babeltrace/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 `babeltrace --help` for the list of general options.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Use `babeltrace help` to get help for a specific plugin or component class.\n"); +} + +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 }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a + * list-plugins command. + * + * *retcode is set to the appropriate exit code to use. + */ +struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[], + int *retcode, bool omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + + *retcode = 0; + cfg = bt_config_list_plugins_create(initial_plugin_paths); + if (!cfg) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.list_plugins.omit_system_plugin_path = omit_system_plugin_path; + cfg->cmd_data.list_plugins.omit_home_plugin_path = omit_home_plugin_path; + ret = append_env_var_plugin_paths( + cfg->cmd_data.list_plugins.plugin_paths); + if (ret) { + printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); + 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"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_common_is_setuid_setgid()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); + } else { + if (bt_config_append_plugin_paths( + cfg->cmd_data.list_plugins.plugin_paths, + arg)) { + printf_err("Invalid --plugin-path option's argument:\n %s\n", + arg); + goto error; + } + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->cmd_data.list_plugins.omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->cmd_data.list_plugins.omit_home_plugin_path = true; + break; + case OPT_HELP: + print_list_plugins_usage(stdout); + *retcode = -1; + BT_PUT(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; + } + + leftover = poptGetArg(pc); + if (leftover) { + printf_err("Invalid argument: %s\n", leftover); + goto error; + } + + if (append_home_and_system_plugin_paths( + cfg->cmd_data.list_plugins.plugin_paths, + cfg->cmd_data.list_plugins.omit_system_plugin_path, + cfg->cmd_data.list_plugins.omit_home_plugin_path)) { + printf_err("Cannot append home and system plugin paths\n"); + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_PUT(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + free(arg); + return cfg; +} + +/* + * Prints the legacy, Babeltrace 1.x command usage. Those options are + * still compatible in Babeltrace 2.x, but it is recommended to use + * the more generic plugin/component parameters instead of those + * hard-coded option names. + */ +static +void print_legacy_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace [OPTIONS] INPUT...\n"); + fprintf(fp, "\n"); + fprintf(fp, "The following options are compatible with the Babeltrace 1.x options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --clock-force-correlate Assume that clocks are inherently correlated\n"); + fprintf(fp, " across traces\n"); + fprintf(fp, " -d, --debug Enable debug mode\n"); + fprintf(fp, " -i, --input-format=FORMAT Input trace format (default: ctf)\n"); + fprintf(fp, " -l, --list List available formats\n"); + fprintf(fp, " -o, --output-format=FORMAT Output trace format (default: text)\n"); + fprintf(fp, " -v, --verbose Enable verbose output\n"); + fprintf(fp, " --help-legacy Show this help and quit\n"); + fprintf(fp, " -V, --version Show version and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, " Available input formats: ctf, lttng-live, ctf-metadata\n"); + fprintf(fp, " Available output formats: text, dummy\n"); + fprintf(fp, "\n"); + fprintf(fp, "Input formats specific options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " INPUT... Input trace file(s), directory(ies), or URLs\n"); + fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n"); + fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS nanoseconds\n"); + fprintf(fp, " --stream-intersection Only process events when all streams are active\n"); + fprintf(fp, "\n"); + fprintf(fp, "text output format specific options:\n"); + fprintf(fp, " \n"); + fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n"); + fprintf(fp, " --clock-date Print timestamp dates\n"); + fprintf(fp, " --clock-gmt Print and parse timestamps in GMT time zone\n"); + fprintf(fp, " (default: local time zone)\n"); + fprintf(fp, " --clock-seconds Print the timestamps as [SEC.NS]\n"); + fprintf(fp, " (default format: [HH:MM:SS.NS])\n"); + fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n"); + fprintf(fp, " (default: `/usr/lib/debug`)\n"); + fprintf(fp, " --debug-info-full-path Show full debug info source and binary paths\n"); + fprintf(fp, " --debug-info-target-prefix=DIR Use directory DIR as a prefix when looking\n"); + fprintf(fp, " up executables during debug info analysis\n"); + fprintf(fp, " (default: `/usr/lib/debug`)\n"); + fprintf(fp, " -f, --fields=NAME[,NAME]... Print additional fields:\n"); + fprintf(fp, " all, trace, trace:hostname, trace:domain,\n"); + fprintf(fp, " trace:procname, trace:vpid, loglevel, emf\n"); + fprintf(fp, " (default: trace:hostname, trace:procname,\n"); + fprintf(fp, " trace:vpid)\n"); + fprintf(fp, " -n, --names=NAME[,NAME]... Print field names:\n"); + fprintf(fp, " payload (or arg or args)\n"); + fprintf(fp, " none, all, scope, header, context (or ctx)\n"); + fprintf(fp, " (default: payload, context)\n"); + fprintf(fp, " --no-delta Do not print time delta between consecutive\n"); + fprintf(fp, " events\n"); + fprintf(fp, " -w, --output=PATH Write output to PATH (default: standard output)\n"); +} + +/* + * Prints the convert command usage. + */ +static +void print_convert_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] convert [OPTIONS]\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n"); + fprintf(fp, " for the following component instances\n"); + fprintf(fp, " (see the expected format of PARAMS below)\n"); + fprintf(fp, " --begin=BEGIN Set the `begin` parameter of the latest\n"); + fprintf(fp, " source component instance to BEGIN\n"); + fprintf(fp, " (see the suggested format of BEGIN below)\n"); + fprintf(fp, " -c, --connect=CONNECTION Connect two component instances (see the\n"); + fprintf(fp, " expected format of CONNECTION below)\n"); + fprintf(fp, " -d, --debug Enable debug mode\n"); + fprintf(fp, " --end=END Set the `end` parameter of the latest\n"); + fprintf(fp, " source component instance to END\n"); + fprintf(fp, " (see the suggested format of BEGIN below)\n"); + fprintf(fp, " --name=NAME Set the name of the latest component\n"); + fprintf(fp, " instance to NAME (must be unique amongst\n"); + fprintf(fp, " all the names of the component instances)\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " -p, --params=PARAMS Set the parameters of the latest component\n"); + fprintf(fp, " instance (in command-line order) to PARAMS\n"); + fprintf(fp, " (see the expected format of PARAMS below)\n"); + fprintf(fp, " -P, --path=PATH Set the `path` parameter of the latest\n"); + fprintf(fp, " component instance to 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, " -r, --reset-base-params Reset the current base parameters of the\n"); + fprintf(fp, " following source and sink component\n"); + fprintf(fp, " instances to an empty map\n"); + fprintf(fp, " -o, --sink=PLUGIN.COMPCLS Instantiate a sink component from plugin\n"); + fprintf(fp, " PLUGIN and component class COMPCLS (may be\n"); + fprintf(fp, " repeated)\n"); + fprintf(fp, " -i, --source=PLUGIN.COMPCLS Instantiate a source component from plugin\n"); + fprintf(fp, " PLUGIN and component class COMPCLS (may be\n"); + fprintf(fp, " repeated)\n"); + fprintf(fp, " --timerange=TIMERANGE Set time range to TIMERANGE: BEGIN,END or\n"); + fprintf(fp, " [BEGIN,END] (literally `[` and `]`)\n"); + fprintf(fp, " (suggested format of BEGIN/END below)\n"); + fprintf(fp, " -v, --verbose Enable verbose output\n"); + fprintf(fp, " -h --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace --help` for the list of general options.\n"); + fprintf(fp, "\n\n"); + fprintf(fp, "Suggested format of BEGIN and END\n"); + fprintf(fp, "---------------------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, " [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n"); + fprintf(fp, "\n\n"); + fprintf(fp, "Expected format of CONNECTION\n"); + fprintf(fp, "-----------------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, " SRC[.SRCPORT]:DST[.DSTPORT]\n"); + fprintf(fp, "\n"); + fprintf(fp, "SRC and DST are the names of the source and destination component\n"); + fprintf(fp, "instances to connect together. You can set the name of a component\n"); + fprintf(fp, "instance with the --name option.\n"); + fprintf(fp, "\n"); + fprintf(fp, "SRCPORT and DSTPORT are the optional source and destination ports to use\n"); + fprintf(fp, "for the connection. When the port is not specified, the default port is\n"); + fprintf(fp, "used.\n"); + fprintf(fp, "\n"); + fprintf(fp, "You can connect a source component to a filter or sink component. You\n"); + fprintf(fp, "can connect a filter component to a sink component.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Example:\n"); + fprintf(fp, "\n"); + fprintf(fp, " my-filter.top10:json-out\n"); + fprintf(fp, "\n\n"); + print_expected_params_format(fp); +} + +static struct poptOption convert_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, + { "begin", '\0', 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 }, + { "connect", 'c', POPT_ARG_STRING, NULL, OPT_CONNECT, 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", '\0', 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 }, + { "name", '\0', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, + { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, 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_PATH, 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 }, + { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL }, + { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, + { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL }, + { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL }, + { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL }, + { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, + { NULL, 0, 0, NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a convert + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], + int *retcode, bool omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + struct ctf_legacy_opts ctf_legacy_opts; + struct text_legacy_opts text_legacy_opts; + enum legacy_input_format legacy_input_format = LEGACY_INPUT_FORMAT_NONE; + enum legacy_output_format legacy_output_format = + LEGACY_OUTPUT_FORMAT_NONE; + struct bt_value *legacy_input_paths = NULL; + struct bt_config_component *implicit_source_comp = NULL; + struct bt_config_component *cur_cfg_comp = NULL; + bool cur_is_implicit_source = false; + bool use_implicit_source = false; + enum bt_config_component_dest cur_cfg_comp_dest = + BT_CONFIG_COMPONENT_DEST_SOURCE; + struct bt_value *cur_base_params = NULL; + int opt, ret = 0; + struct bt_config *cfg = NULL; + struct bt_value *instance_names = NULL; + struct bt_value *connection_args = NULL; + char error_buf[256] = { 0 }; + + *retcode = 0; + memset(&ctf_legacy_opts, 0, sizeof(ctf_legacy_opts)); + memset(&text_legacy_opts, 0, sizeof(text_legacy_opts)); + + if (argc <= 1) { + print_convert_usage(stdout); + *retcode = -1; + goto end; + } + + cfg = bt_config_convert_create(initial_plugin_paths); + if (!cfg) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.convert.omit_system_plugin_path = omit_system_plugin_path; + cfg->cmd_data.convert.omit_home_plugin_path = omit_home_plugin_path; + text_legacy_opts.output = g_string_new(NULL); + if (!text_legacy_opts.output) { + print_err_oom(); + goto error; + } + + text_legacy_opts.dbg_info_dir = g_string_new(NULL); + if (!text_legacy_opts.dbg_info_dir) { + print_err_oom(); + goto error; + } + + text_legacy_opts.dbg_info_target_prefix = g_string_new(NULL); + if (!text_legacy_opts.dbg_info_target_prefix) { + print_err_oom(); + goto error; } - cfg->sinks = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put); - if (!cfg->sinks) { + cur_base_params = bt_value_map_create(); + if (!cur_base_params) { print_err_oom(); goto error; } @@ -2333,8 +3293,39 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co goto error; } + instance_names = bt_value_map_create(); + if (!instance_names) { + print_err_oom(); + goto error; + } + + connection_args = bt_value_array_create(); + if (!connection_args) { + print_err_oom(); + goto error; + } + + ret = append_env_var_plugin_paths(cfg->cmd_data.convert.plugin_paths); + if (ret) { + printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); + goto error; + } + + /* Note: implicit source never gets positional base params. */ + implicit_source_comp = bt_config_component_from_arg( + BT_COMPONENT_CLASS_TYPE_SOURCE, DEFAULT_SOURCE_COMPONENT_NAME); + if (!implicit_source_comp) { + print_err_oom(); + goto error; + } + + cur_cfg_comp = implicit_source_comp; + cur_is_implicit_source = true; + use_implicit_source = true; + /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0); + pc = poptGetContext(NULL, argc, (const char **) argv, + convert_long_options, 0); if (!pc) { printf_err("Cannot get popt context\n"); goto error; @@ -2347,17 +3338,24 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co switch (opt) { case OPT_PLUGIN_PATH: - if (cfg->plugin_paths) { - printf_err("Duplicate --plugin-path option\n"); - goto error; - } - - cfg->plugin_paths = plugin_paths_from_arg(arg); - if (!cfg->plugin_paths) { - printf_err("Invalid --plugin-path option's argument\n"); - goto error; + if (bt_common_is_setuid_setgid()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); + } else { + if (bt_config_append_plugin_paths( + cfg->cmd_data.convert.plugin_paths, + arg)) { + printf_err("Invalid --plugin-path option's argument:\n %s\n", + arg); + goto error; + } } break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->cmd_data.convert.omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->cmd_data.convert.omit_home_plugin_path = true; + break; case OPT_OUTPUT_PATH: if (text_legacy_opts.output->len > 0) { printf_err("Duplicate --output option\n"); @@ -2409,29 +3407,32 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co } } + use_implicit_source = false; + /* Non-legacy: try to create a component config */ - if (cur_cfg_comp) { + if (cur_cfg_comp && !cur_is_implicit_source) { add_cfg_comp(cfg, cur_cfg_comp, cur_cfg_comp_dest); } - cur_cfg_comp = bt_config_component_from_arg(arg); + cur_cfg_comp = bt_config_component_from_arg( + BT_COMPONENT_CLASS_TYPE_SOURCE, arg); if (!cur_cfg_comp) { printf_err("Invalid format for --source option's argument:\n %s\n", arg); goto error; } + cur_is_implicit_source = false; assert(cur_base_params); bt_put(cur_cfg_comp->params); cur_cfg_comp->params = bt_value_copy(cur_base_params); - if (!cur_cfg_comp) { + if (!cur_cfg_comp->params) { print_err_oom(); - goto end; + goto error; } cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; - cur_cfg_comp_params_set = false; break; } case OPT_OUTPUT_FORMAT: @@ -2472,28 +3473,29 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co } /* Non-legacy: try to create a component config */ - if (cur_cfg_comp) { + if (cur_cfg_comp && !cur_is_implicit_source) { add_cfg_comp(cfg, cur_cfg_comp, cur_cfg_comp_dest); } - cur_cfg_comp = bt_config_component_from_arg(arg); + cur_cfg_comp = bt_config_component_from_arg( + BT_COMPONENT_CLASS_TYPE_SINK, arg); if (!cur_cfg_comp) { printf_err("Invalid format for --sink option's argument:\n %s\n", arg); goto error; } + cur_is_implicit_source = false; assert(cur_base_params); bt_put(cur_cfg_comp->params); cur_cfg_comp->params = bt_value_copy(cur_base_params); - if (!cur_cfg_comp) { + if (!cur_cfg_comp->params) { print_err_oom(); - goto end; + goto error; } cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK; - cur_cfg_comp_params_set = false; break; } case OPT_PARAMS: @@ -2502,14 +3504,8 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co struct bt_value *params_to_set; if (!cur_cfg_comp) { - printf_err("--params option must follow a --source or --sink option\n"); - goto error; - } - - if (cur_cfg_comp_params_set) { - printf_err("Duplicate --params option for the same current component\ninstance (class %s.%s)\n", - cur_cfg_comp->plugin_name->str, - cur_cfg_comp->component_name->str); + printf_err("Cannot add parameters to unavailable default source component `%s`:\n %s\n", + DEFAULT_SOURCE_COMPONENT_NAME, arg); goto error; } @@ -2520,22 +3516,22 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co goto error; } - params_to_set = bt_value_map_extend(cur_base_params, + params_to_set = bt_value_map_extend(cur_cfg_comp->params, params); BT_PUT(params); if (!params_to_set) { - printf_err("Cannot extend current base parameters with --params option's argument:\n %s\n", + printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n", arg); goto error; } BT_MOVE(cur_cfg_comp->params, params_to_set); - cur_cfg_comp_params_set = true; break; } case OPT_PATH: if (!cur_cfg_comp) { - printf_err("--path option must follow a --source or --sink option\n"); + printf_err("Cannot add `path` parameter to unavailable default source component `%s`:\n %s\n", + DEFAULT_SOURCE_COMPONENT_NAME, arg); goto error; } @@ -2547,6 +3543,27 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co goto error; } break; + case OPT_NAME: + if (!cur_cfg_comp) { + printf_err("Cannot set the name of unavailable default source component `%s`:\n %s\n", + DEFAULT_SOURCE_COMPONENT_NAME, arg); + goto error; + } + + if (bt_value_map_has_key(instance_names, arg)) { + printf_err("Duplicate component instance name:\n %s\n", + arg); + goto error; + } + + g_string_assign(cur_cfg_comp->instance_name, arg); + + if (bt_value_map_insert(instance_names, + arg, bt_value_null)) { + print_err_oom(); + goto error; + } + break; case OPT_BASE_PARAMS: { struct bt_value *params = bt_value_from_arg(arg); @@ -2576,7 +3593,8 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co text_legacy_opts.names = names_from_arg(arg); if (!text_legacy_opts.names) { - printf_err("Invalid --names option's argument\n"); + printf_err("Invalid --names option's argument:\n %s\n", + arg); goto error; } break; @@ -2588,7 +3606,8 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co text_legacy_opts.fields = fields_from_arg(arg); if (!text_legacy_opts.fields) { - printf_err("Invalid --fields option's argument\n"); + printf_err("Invalid --fields option's argument:\n %s\n", + arg); goto error; } break; @@ -2620,7 +3639,8 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co } if (parse_int64(arg, &val)) { - printf_err("Invalid --clock-offset option's argument\n"); + printf_err("Invalid --clock-offset option's argument:\n %s\n", + arg); goto error; } @@ -2637,7 +3657,8 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co } if (parse_int64(arg, &val)) { - printf_err("Invalid --clock-offset-ns option's argument\n"); + printf_err("Invalid --clock-offset-ns option's argument:\n %s\n", + arg); goto error; } @@ -2648,16 +3669,17 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co ctf_legacy_opts.stream_intersection = true; break; case OPT_CLOCK_FORCE_CORRELATE: - cfg->force_correlate = true; + cfg->cmd_data.convert.force_correlate = true; break; case OPT_BEGIN: if (!cur_cfg_comp) { - //TODO - printf_err("Unimplemented: apply to default source\n"); + printf_err("Cannot add `begin` parameter to unavailable default source component `%s`:\n %s\n", + DEFAULT_SOURCE_COMPONENT_NAME, arg); goto error; } if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) { - printf_err("--begin option must follow a --source option\n"); + printf_err("--begin option must follow a --source option:\n %s\n", + arg); goto error; } if (bt_value_map_insert_string(cur_cfg_comp->params, @@ -2668,12 +3690,13 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co break; case OPT_END: if (!cur_cfg_comp) { - //TODO - printf_err("Unimplemented: apply to default source\n"); + printf_err("Cannot add `end` parameter to unavailable default source component `%s`:\n %s\n", + DEFAULT_SOURCE_COMPONENT_NAME, arg); goto error; } if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) { - printf_err("--end option must follow a --source option\n"); + printf_err("--end option must follow a --source option:\n %s\n", + arg); goto error; } if (bt_value_map_insert_string(cur_cfg_comp->params, @@ -2687,16 +3710,18 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co const char *begin, *end; if (!cur_cfg_comp) { - //TODO - printf_err("Unimplemented: apply to default source\n"); + printf_err("Cannot add `begin` and `end` parameters to unavailable default source component `%s`:\n %s\n", + DEFAULT_SOURCE_COMPONENT_NAME, arg); goto error; } if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) { - printf_err("--timerange option must follow a --source option\n"); + printf_err("--timerange option must follow a --source option:\n %s\n", + arg); goto error; } if (split_timerange(arg, &begin, &end)) { - printf_err("Invalid --timerange format, expecting: begin,end or [begin,end] (where [] are actual brackets)\n"); + printf_err("Invalid --timerange format: expecting BEGIN,END or [BEGIN,END]:\n %s\n", + arg); goto error; } if (bt_value_map_insert_string(cur_cfg_comp->params, @@ -2711,20 +3736,17 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co } break; } + case OPT_CONNECT: + if (bt_value_array_append_string(connection_args, + arg)) { + print_err_oom(); + goto error; + } + break; case OPT_HELP: + print_convert_usage(stdout); + *retcode = -1; BT_PUT(cfg); - print_usage(stdout); - goto end; - case OPT_HELP_LEGACY: - BT_PUT(cfg); - print_legacy_usage(stdout); - goto end; - case OPT_VERSION: - BT_PUT(cfg); - print_version(); - goto end; - case OPT_LIST: - cfg->do_list = true; goto end; case OPT_VERBOSE: cfg->verbose = true; @@ -2742,12 +3764,6 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co arg = NULL; } - /* Append current component configuration, if any */ - if (cur_cfg_comp) { - add_cfg_comp(cfg, cur_cfg_comp, cur_cfg_comp_dest); - cur_cfg_comp = NULL; - } - /* Check for option parsing error */ if (opt < -1) { printf_err("While parsing command-line options, at option %s: %s\n", @@ -2770,6 +3786,20 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co } } + if (append_home_and_system_plugin_paths( + cfg->cmd_data.convert.plugin_paths, + cfg->cmd_data.convert.omit_system_plugin_path, + cfg->cmd_data.convert.omit_home_plugin_path)) { + printf_err("Cannot append home and system plugin paths\n"); + goto error; + } + + /* Append current component configuration, if any */ + if (cur_cfg_comp && !cur_is_implicit_source) { + add_cfg_comp(cfg, cur_cfg_comp, cur_cfg_comp_dest); + } + cur_cfg_comp = NULL; + /* Validate legacy/non-legacy options */ if (!validate_cfg(cfg, &legacy_input_format, &legacy_output_format, legacy_input_paths, &ctf_legacy_opts, @@ -2783,12 +3813,32 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co * component configurations. */ if (legacy_input_format) { - if (append_sources_from_legacy_opts(cfg->sources, + if (append_sources_from_legacy_opts( + cfg->cmd_data.convert.sources, legacy_input_format, &ctf_legacy_opts, legacy_input_paths)) { printf_err("Cannot convert legacy input format options to source component instance(s)\n"); goto error; } + if (append_sources_from_implicit_params( + cfg->cmd_data.convert.sources, + implicit_source_comp)) { + printf_err("Cannot initialize legacy component parameters\n"); + goto error; + } + use_implicit_source = false; + } else { + if (use_implicit_source) { + add_cfg_comp(cfg, implicit_source_comp, + BT_CONFIG_COMPONENT_DEST_SOURCE); + implicit_source_comp = NULL; + } else { + if (implicit_source_comp + && !bt_value_map_is_empty(implicit_source_comp->params)) { + printf_err("Arguments specified for implicit source, but an explicit source has been specified, overriding it\n"); + goto error; + } + } } /* @@ -2796,19 +3846,39 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_co * component configurations. */ if (legacy_output_format) { - if (append_sinks_from_legacy_opts(cfg->sinks, + if (append_sinks_from_legacy_opts(cfg->cmd_data.convert.sinks, legacy_output_format, &text_legacy_opts)) { printf_err("Cannot convert legacy output format options to sink component instance(s)\n"); goto error; } } + if (cfg->cmd_data.convert.sinks->len == 0) { + /* Use implicit sink as default. */ + cur_cfg_comp = bt_config_component_from_arg( + BT_COMPONENT_CLASS_TYPE_SINK, + DEFAULT_SINK_COMPONENT_NAME); + if (!cur_cfg_comp) { + printf_error("Cannot find implicit sink plugin `%s`\n", + DEFAULT_SINK_COMPONENT_NAME); + } + add_cfg_comp(cfg, cur_cfg_comp, + BT_CONFIG_COMPONENT_DEST_SINK); + cur_cfg_comp = NULL; + } + + ret = bt_config_create_connections(cfg, connection_args, + error_buf, 256); + if (ret) { + printf_err("Cannot creation connections:\n%s", error_buf); + goto error; + } + goto end; error: + *retcode = 1; BT_PUT(cfg); - cfg = NULL; - *exit_code = 1; end: if (pc) { @@ -2828,10 +3898,172 @@ end: } free(arg); + BT_PUT(implicit_source_comp); BT_PUT(cur_cfg_comp); BT_PUT(cur_base_params); BT_PUT(text_legacy_opts.names); BT_PUT(text_legacy_opts.fields); BT_PUT(legacy_input_paths); + BT_PUT(instance_names); + BT_PUT(connection_args); return cfg; } + +/* + * Prints the Babeltrace 2.x general usage. + */ +static +void print_gen_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] [COMMAND] [COMMAND OPTIONS]\n"); + fprintf(fp, "\n"); + fprintf(fp, "General options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -d, --debug Enable debug mode\n"); + fprintf(fp, " -h --help Show this help and quit\n"); + fprintf(fp, " --help-legacy Show Babeltrace 1.x legacy help and quit\n"); + fprintf(fp, " -v, --verbose Enable verbose output\n"); + fprintf(fp, " -V, --version Show version and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "Available commands:\n"); + fprintf(fp, "\n"); + fprintf(fp, " convert Build a trace conversion graph and run it (default)\n"); + fprintf(fp, " help Get help for a plugin or a component class\n"); + fprintf(fp, " list-plugins List available plugins and their content\n"); + fprintf(fp, " query-info Query information from a component class\n"); + fprintf(fp, "\n"); + fprintf(fp, "Use `babeltrace COMMAND --help` to show the help of COMMAND.\n"); +} + +struct bt_config *bt_config_from_args(int argc, const char *argv[], + int *retcode, bool omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths) +{ + struct bt_config *config = NULL; + bool verbose = false; + bool debug = false; + int i; + enum bt_config_command command = -1; + const char **command_argv = NULL; + int command_argc = -1; + const char *command_name = NULL; + + *retcode = -1; + + if (argc <= 1) { + print_gen_usage(stdout); + goto end; + } + + for (i = 1; i < argc; i++) { + const char *cur_arg = argv[i]; + + if (strcmp(cur_arg, "-d") == 0 || + strcmp(cur_arg, "--debug") == 0) { + debug = true; + } else if (strcmp(cur_arg, "-v") == 0 || + strcmp(cur_arg, "--verbose") == 0) { + verbose = true; + } 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 if (strcmp(cur_arg, "--help-legacy") == 0) { + print_legacy_usage(stdout); + goto end; + } else { + bool has_command = true; + + /* + * First unknown argument: is it a known command + * name? + */ + if (strcmp(cur_arg, "convert") == 0) { + command = BT_CONFIG_COMMAND_CONVERT; + } else if (strcmp(cur_arg, "list-plugins") == 0) { + command = BT_CONFIG_COMMAND_LIST_PLUGINS; + } else if (strcmp(cur_arg, "help") == 0) { + command = BT_CONFIG_COMMAND_HELP; + } else if (strcmp(cur_arg, "query-info") == 0) { + command = BT_CONFIG_COMMAND_QUERY_INFO; + } else { + /* + * Unknown argument, but not a known + * command name: assume the whole + * arguments are for the default convert + * command. + */ + command = BT_CONFIG_COMMAND_CONVERT; + command_argv = argv; + command_argc = argc; + has_command = false; + } + + if (has_command) { + command_argv = &argv[i]; + command_argc = argc - i; + command_name = cur_arg; + } + break; + } + } + + if ((int) command < 0) { + /* + * 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; + } + + assert(command_argv); + assert(command_argc >= 0); + + switch (command) { + case BT_CONFIG_COMMAND_CONVERT: + config = bt_config_convert_from_args(command_argc, command_argv, + retcode, omit_system_plugin_path, + omit_home_plugin_path, initial_plugin_paths); + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + config = bt_config_list_plugins_from_args(command_argc, + command_argv, retcode, omit_system_plugin_path, + omit_home_plugin_path, initial_plugin_paths); + break; + case BT_CONFIG_COMMAND_HELP: + config = bt_config_help_from_args(command_argc, + command_argv, retcode, omit_system_plugin_path, + omit_home_plugin_path, initial_plugin_paths); + break; + case BT_CONFIG_COMMAND_QUERY_INFO: + config = bt_config_query_info_from_args(command_argc, + command_argv, retcode, omit_system_plugin_path, + omit_home_plugin_path, initial_plugin_paths); + break; + default: + assert(false); + } + + if (config) { + if (verbose) { + config->verbose = true; + } + + if (debug) { + config->debug = true; + } + + config->command_name = command_name; + } + +end: + return config; +}