On 18 Jan 2024, at 16:26, [email protected] wrote:
> From: Jakob Meng <[email protected]>
>
> For monitoring systems such as Prometheus it would be beneficial if
> OVS would expose statistics in a machine-readable format.
>
> This patch introduces support for different output formats to ovs-xxx
> tools. They gain a global option '-f,--format' which allows users to
> request JSON instead of plain-text for humans. An example call
> implemented in a later patch is 'ovs-appctl --format json dpif/show'.
>
> For that a new 'set-options' command has been added to lib/unixctl.c
> which allows to change the output format of the following commands.
> It is supposed to be run by ovs-xxx tools transparently for the user
> when a specific output format has been requested. For example, when a
> user calls 'ovs-appctl --format json dpif/show', then ovs-appctl will
> call 'set-options' to set the output format as requested by the user
> and then the actual command 'dpif/show'.
> This ovs-appctl behaviour has been implemented in a backward compatible
> way. One can use an updated client (ovs-appctl) with an old server
> (ovs-vswitchd) and vice versa. Of course, JSON output only works when
> both sides have been updated.
>
> To demonstrate the necessary API changes for supporting output formats,
> unixctl_command_register() in lib/unixctl.* has been cloned to
> unixctl_command_register_fmt(). The latter will be replaced with the
> former in a later patch. The new function gained an int argument called
> 'output_fmts' with which commands have to announce their support for
> output formats.
>
> Function type unixctl_cb_func in lib/unixctl.h has gained a new arg
> 'enum ovs_output_fmt fmt'. For now, it has been added as a comment
> only for the huge changes reason mentioned earlier. The output format
> which has been passed via 'set-options' to ovs-vswitchd will be
> converted into a value of type 'enum ovs_output_fmt' in lib/unixctl.c
> and then passed to said 'fmt' arg of the choosen command handler (in a
> future patch). When a requested output format is not supported by a
> command, then process_command() in lib/unixctl.c will return an error.
>
> This patch does not yet pass the choosen output format to commands.
> Doing so requires changes to all unixctl_command_register() calls and
> all command callbacks. To improve readibility those changes have been
> split out into a follow up patch. Respectively, whenever an output
> format other than 'text' is choosen for ovs-xxx tools, they will fail.
> By default, the output format is plain-text as before.
>
> In popular tools like kubectl the option for output control is usually
> called '-o|--output' instead of '-f,--format'. But ovs-appctl already
> has an short option '-o' which prints the available ovs-appctl options
> ('--option'). The now choosen name also better alines with ovsdb-client
> where '-f,--format' controls output formatting.
>
> Reported-at: https://bugzilla.redhat.com/1824861
> Signed-off-by: Jakob Meng <[email protected]>
Hi Jakob,
Thank you for submitting this series; I believe it's a valuable addition to
OVS! Apologies for the delayed response. I've reviewed the entire series, and
most of the comments are minor change requests. I'll hold off on sending a new
revision until Ilya has had a chance to review it, as he provided some comments
on the previous revision.
Cheers,
Eelco
> ---
> NEWS | 2 +
> lib/command-line.c | 36 +++++++++++
> lib/command-line.h | 10 +++
> lib/dpctl.h | 4 ++
> lib/unixctl.c | 142 ++++++++++++++++++++++++++++++++++-------
> lib/unixctl.h | 16 ++++-
> utilities/ovs-appctl.c | 97 ++++++++++++++++++++++++----
> utilities/ovs-dpctl.c | 1 +
> 8 files changed, 271 insertions(+), 37 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 2153b4805..8631ea45e 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -33,6 +33,8 @@ v3.3.0 - xx xxx xxxx
> "ovs-appctl dpctl/ct-del-limits default".
> * 'dpctl/flush-conntrack' is now capable of flushing connections based
> on mark and labels.
> + * Added new option [-f|--format] to choose the output format, e.g.
> 'json'
> + or 'text' (by default).
> - ovs-vsctl:
> * New commands 'set-zone-limit', 'del-zone-limit' and 'list-zone-limits'
> to manage the maximum number of connections in conntrack zones via
> diff --git a/lib/command-line.c b/lib/command-line.c
> index 967f4f5d5..775e0430a 100644
> --- a/lib/command-line.c
> +++ b/lib/command-line.c
> @@ -24,6 +24,7 @@
> #include "ovs-thread.h"
> #include "util.h"
> #include "openvswitch/vlog.h"
> +#include "openvswitch/json.h"
>
> VLOG_DEFINE_THIS_MODULE(command_line);
>
> @@ -53,6 +54,41 @@ ovs_cmdl_long_options_to_short_options(const struct option
> options[])
> return xstrdup(short_options);
> }
>
> +const char *
> +ovs_output_fmt_to_string(enum ovs_output_fmt fmt)
> +{
> + switch (fmt) {
> + case OVS_OUTPUT_FMT_TEXT:
> + return "text";
> +
> + case OVS_OUTPUT_FMT_JSON:
> + return "json";
> +
> + default:
> + return NULL;
> + }
> +}
> +
> +struct json *
> +ovs_output_fmt_to_json(enum ovs_output_fmt fmt)
> +{
> + const char *string = ovs_output_fmt_to_string(fmt);
> + return string ? json_string_create(string) : NULL;
> +}
> +
> +bool
> +ovs_output_fmt_from_string(const char *string, enum ovs_output_fmt *fmt)
> +{
> + if (!strcmp(string, "text")) {
> + *fmt = OVS_OUTPUT_FMT_TEXT;
> + } else if (!strcmp(string, "json")) {
> + *fmt = OVS_OUTPUT_FMT_JSON;
> + } else {
> + return false;
> + }
> + return true;
> +}
> +
> static char * OVS_WARN_UNUSED_RESULT
> build_short_options(const struct option *long_options)
> {
> diff --git a/lib/command-line.h b/lib/command-line.h
> index fc2a2690f..494274cec 100644
> --- a/lib/command-line.h
> +++ b/lib/command-line.h
> @@ -20,6 +20,7 @@
> /* Utilities for command-line parsing. */
>
> #include "compiler.h"
> +#include <openvswitch/list.h>
>
> struct option;
>
> @@ -46,6 +47,15 @@ struct ovs_cmdl_command {
>
> char *ovs_cmdl_long_options_to_short_options(const struct option *options);
>
> +enum ovs_output_fmt {
> + OVS_OUTPUT_FMT_TEXT = 1 << 0,
> + OVS_OUTPUT_FMT_JSON = 1 << 1
> +};
> +
> +const char *ovs_output_fmt_to_string(enum ovs_output_fmt);
> +struct json *ovs_output_fmt_to_json(enum ovs_output_fmt);
> +bool ovs_output_fmt_from_string(const char *, enum ovs_output_fmt *);
> +
> struct ovs_cmdl_parsed_option {
> const struct option *o;
> char *arg;
> diff --git a/lib/dpctl.h b/lib/dpctl.h
> index 9d0052152..1c9b2409f 100644
> --- a/lib/dpctl.h
> +++ b/lib/dpctl.h
> @@ -19,6 +19,7 @@
> #include <stdbool.h>
>
> #include "compiler.h"
> +#include "command-line.h"
>
> struct dpctl_params {
> /* True if it is called by ovs-appctl command. */
> @@ -42,6 +43,9 @@ struct dpctl_params {
> /* --names: Use port names in output? */
> bool names;
>
> + /* Output format. */
> + enum ovs_output_fmt format;
> +
> /* Callback for printing. This function is called from
> dpctl_run_command()
> * to output data. The 'aux' parameter is set to the 'aux'
> * member. The 'error' parameter is true if 'string' is an error
> diff --git a/lib/unixctl.c b/lib/unixctl.c
> index 103357ee9..dd1450f22 100644
> --- a/lib/unixctl.c
> +++ b/lib/unixctl.c
> @@ -21,7 +21,6 @@
> #include "coverage.h"
> #include "dirs.h"
> #include "openvswitch/dynamic-string.h"
> -#include "openvswitch/json.h"
> #include "jsonrpc.h"
> #include "openvswitch/list.h"
> #include "openvswitch/poll-loop.h"
> @@ -39,6 +38,7 @@ COVERAGE_DEFINE(unixctl_replied);
> struct unixctl_command {
> const char *usage;
> int min_args, max_args;
> + int output_fmts;
Considering that this involves combining bit-mask enums, I suggest converting
this to an unsigned int.
> unixctl_cb_func *cb;
> void *aux;
> };
> @@ -50,6 +50,8 @@ struct unixctl_conn {
> /* Only one request can be in progress at a time. While the request is
> * being processed, 'request_id' is populated, otherwise it is null. */
> struct json *request_id; /* ID of the currently active request. */
> +
> + enum ovs_output_fmt fmt;
> };
>
> /* Server for control connection. */
> @@ -94,6 +96,39 @@ unixctl_version(struct unixctl_conn *conn, int argc
> OVS_UNUSED,
> unixctl_command_reply(conn, ovs_get_program_version());
> }
>
> +static void
> +unixctl_set_options(struct unixctl_conn *conn, int argc, const char *argv[],
> + void *aux OVS_UNUSED)
> +{
> + char * error = NULL;
> +
> + for (size_t i = 1; i < argc; i++) {
> + if ((i + 1) == argc) {
> + error = xasprintf("option requires an argument -- %s", argv[i]);
> + goto error;
> + } else if (!strcmp(argv[i], "--format")) {
> + /* Move index to option argument. */
> + i++;
> +
> + /* Parse output format argument. */
> + if (!ovs_output_fmt_from_string(argv[i], &conn->fmt)) {
> + error = xasprintf("option %s has invalid value %s",
> + argv[i - 1], argv[i]);
> + goto error;
> + }
> + } else {
> + error = xasprintf("invalid option -- %s", argv[i]);
> + goto error;
> +
> + }
> + }
> + unixctl_command_reply(conn, NULL);
> + return;
> +error:
> + unixctl_command_reply_error(conn, error);
> + free(error);
> +}
> +
> /* Registers a unixctl command with the given 'name'. 'usage' describes the
> * arguments to the command; it is used only for presentation to the user in
> * "list-commands" output. (If 'usage' is NULL, then the command is hidden.)
> @@ -109,6 +144,28 @@ void
> unixctl_command_register(const char *name, const char *usage,
> int min_args, int max_args,
> unixctl_cb_func *cb, void *aux)
> +{
> + unixctl_command_register_fmt(name, usage, min_args, max_args,
> + OVS_OUTPUT_FMT_TEXT, cb, aux);
> +}
> +
> +/* Registers a unixctl command with the given 'name'. 'usage' describes the
> + * arguments to the command; it is used only for presentation to the user in
> + * "list-commands" output. (If 'usage' is NULL, then the command is hidden.)
> + * 'output_fmts' is a bitmap that defines what output formats a command
> + * supports, e.g. OVS_OUTPUT_FMT_TEXT | OVS_OUTPUT_FMT_JSON.
> + *
> + * 'cb' is called when the command is received. It is passed an array
> + * containing the command name and arguments, plus a copy of 'aux'. Normally
> + * 'cb' should reply by calling unixctl_command_reply() or
> + * unixctl_command_reply_error() before it returns, but if the command cannot
> + * be handled immediately then it can defer the reply until later. A given
> + * connection can only process a single request at a time, so a reply must be
> + * made eventually to avoid blocking that connection. */
> +void
> +unixctl_command_register_fmt(const char *name, const char *usage,
> + int min_args, int max_args, int output_fmts,
Unsigned int for output_fmts?
> + unixctl_cb_func *cb, void *aux)
> {
> struct unixctl_command *command;
> struct unixctl_command *lookup = shash_find_data(&commands, name);
> @@ -123,41 +180,48 @@ unixctl_command_register(const char *name, const char
> *usage,
> command->usage = usage;
> command->min_args = min_args;
> command->max_args = max_args;
> + command->output_fmts = output_fmts;
> command->cb = cb;
> command->aux = aux;
> shash_add(&commands, name, command);
> }
>
> -static void
> -unixctl_command_reply__(struct unixctl_conn *conn,
> - bool success, const char *body)
> +static struct json *
> +json_string_create__(const char *body)
> {
> - struct json *body_json;
> - struct jsonrpc_msg *reply;
> -
> - COVERAGE_INC(unixctl_replied);
> - ovs_assert(conn->request_id);
> -
> if (!body) {
> body = "";
> }
>
> if (body[0] && body[strlen(body) - 1] != '\n') {
> - body_json = json_string_create_nocopy(xasprintf("%s\n", body));
> + return json_string_create_nocopy(xasprintf("%s\n", body));
> } else {
> - body_json = json_string_create(body);
> + return json_string_create(body);
We can remove the else clause here, so it will become:
static struct json *
json_string_create__(const char *body)
{
if (!body) {
body = "";
}
if (body[0] && body[strlen(body) - 1] != '\n') {
return json_string_create_nocopy(xasprintf("%s\n", body));
}
return json_string_create(body);
}
> }
> +}
> +
> +/* Takes ownership of 'body'. */
> +static void
> +unixctl_command_reply__(struct unixctl_conn *conn,
> + bool success, struct json *body)
> +{
> + struct jsonrpc_msg *reply;
> +
> + COVERAGE_INC(unixctl_replied);
> + ovs_assert(conn->request_id);
>
> if (success) {
> - reply = jsonrpc_create_reply(body_json, conn->request_id);
> + reply = jsonrpc_create_reply(body, conn->request_id);
> } else {
> - reply = jsonrpc_create_error(body_json, conn->request_id);
> + reply = jsonrpc_create_error(body, conn->request_id);
> }
>
> if (VLOG_IS_DBG_ENABLED()) {
> char *id = json_to_string(conn->request_id, 0);
> + char *msg = json_to_string(body, 0);
> VLOG_DBG("replying with %s, id=%s: \"%s\"",
> - success ? "success" : "error", id, body);
> + success ? "success" : "error", id, msg);
> + free(msg);
> free(id);
> }
>
> @@ -170,22 +234,34 @@ unixctl_command_reply__(struct unixctl_conn *conn,
>
> /* Replies to the active unixctl connection 'conn'. 'result' is sent to the
> * client indicating the command was processed successfully. Only one call
> to
> - * unixctl_command_reply() or unixctl_command_reply_error() may be made per
> - * request. */
> + * unixctl_command_reply(), unixctl_command_reply_error() or
> + * unixctl_command_reply_json() may be made per request. */
> void
> unixctl_command_reply(struct unixctl_conn *conn, const char *result)
> {
> - unixctl_command_reply__(conn, true, result);
> + unixctl_command_reply__(conn, true, json_string_create__(result));
> }
>
> /* Replies to the active unixctl connection 'conn'. 'error' is sent to the
> - * client indicating an error occurred processing the command. Only one
> call to
> - * unixctl_command_reply() or unixctl_command_reply_error() may be made per
> - * request. */
> + * client indicating an error occurred processing the command. Only one call
> + * to unixctl_command_reply(), unixctl_command_reply_error() or
> + * unixctl_command_reply_json() may be made per request. */
> void
> unixctl_command_reply_error(struct unixctl_conn *conn, const char *error)
> {
> - unixctl_command_reply__(conn, false, error);
> + unixctl_command_reply__(conn, false, json_string_create__(error));
> +}
> +
> +/* Replies to the active unixctl connection 'conn'. 'result' is sent to the
> + * client indicating the command was processed successfully. Only one call
> to
> + * unixctl_command_reply(), unixctl_command_reply_error() or
> + * unixctl_command_reply_json() may be made per request.
> + *
> + * Takes ownership of 'body'. */
> +void
> +unixctl_command_reply_json(struct unixctl_conn *conn, struct json *body)
> +{
> + unixctl_command_reply__(conn, true, body);
> }
>
> /* Creates a unixctl server listening on 'path', which for POSIX may be:
> @@ -250,6 +326,8 @@ unixctl_server_create(const char *path, struct
> unixctl_server **serverp)
> unixctl_command_register("list-commands", "", 0, 0,
> unixctl_list_commands,
> NULL);
> unixctl_command_register("version", "", 0, 0, unixctl_version, NULL);
> + unixctl_command_register("set-options", "[--format text|json]", 2, 2,
> + unixctl_set_options, NULL);
>
> struct unixctl_server *server = xmalloc(sizeof *server);
> server->listener = listener;
> @@ -291,6 +369,18 @@ process_command(struct unixctl_conn *conn, struct
> jsonrpc_msg *request)
> } else if (params->n > command->max_args) {
> error = xasprintf("\"%s\" command takes at most %d arguments",
> request->method, command->max_args);
> + } else if ((!command->output_fmts && conn->fmt != OVS_OUTPUT_FMT_TEXT) ||
> + (command->output_fmts && !(conn->fmt & command->output_fmts)))
> + {
> + error = xasprintf("\"%s\" command does not support output format"\
> + " \"%s\" (supported: %d, requested: %d)",
> + request->method,
> ovs_output_fmt_to_string(conn->fmt),
> + command->output_fmts, conn->fmt);
> + } else if (conn->fmt != OVS_OUTPUT_FMT_TEXT) {
> + /* FIXME: Remove this check once output format will be passed to the
> + * command handler below. */
> + error = xasprintf("output format \"%s\" has not been implemented
> yet",
> + ovs_output_fmt_to_string(conn->fmt));
> } else {
> struct svec argv = SVEC_EMPTY_INITIALIZER;
> int i;
> @@ -307,8 +397,10 @@ process_command(struct unixctl_conn *conn, struct
> jsonrpc_msg *request)
> svec_terminate(&argv);
>
> if (!error) {
> + /* FIXME: Output format will be passed as 'fmt' to the command in
> + * later patch. */
> command->cb(conn, argv.n, (const char **) argv.names,
> - command->aux);
> + /* conn->fmt, */ command->aux);
> }
>
> svec_destroy(&argv);
> @@ -381,6 +473,7 @@ unixctl_server_run(struct unixctl_server *server)
> struct unixctl_conn *conn = xzalloc(sizeof *conn);
> ovs_list_push_back(&server->conns, &conn->node);
> conn->rpc = jsonrpc_open(stream);
> + conn->fmt = OVS_OUTPUT_FMT_TEXT;
> } else if (error == EAGAIN) {
> break;
> } else {
> @@ -518,6 +611,9 @@ unixctl_client_transact(struct jsonrpc *client, const
> char *command, int argc,
> } else if (reply->result) {
> if (reply->result->type == JSON_STRING) {
> *result = xstrdup(json_string(reply->result));
> + } else if (reply->result->type == JSON_OBJECT ||
> + reply->result->type == JSON_ARRAY) {
> + *result = json_to_string(reply->result, 0);
> } else {
> VLOG_WARN("%s: unexpected result type in JSON rpc reply: %s",
> jsonrpc_get_name(client),
> diff --git a/lib/unixctl.h b/lib/unixctl.h
> index 4562dbc49..8200b9e11 100644
> --- a/lib/unixctl.h
> +++ b/lib/unixctl.h
> @@ -17,6 +17,9 @@
> #ifndef UNIXCTL_H
> #define UNIXCTL_H 1
>
> +#include "openvswitch/json.h"
> +#include "command-line.h"
> +
> #ifdef __cplusplus
> extern "C" {
> #endif
> @@ -40,13 +43,24 @@ int unixctl_client_transact(struct jsonrpc *client,
>
> /* Command registration. */
> struct unixctl_conn;
> +/* FIXME: Output format will be passed as 'fmt' to the command in later
> patch.
> + */
> typedef void unixctl_cb_func(struct unixctl_conn *,
> - int argc, const char *argv[], void *aux);
> + int argc, const char *argv[],
> + /* enum ovs_output_fmt fmt, */ void *aux);
> +/* FIXME: unixctl_command_register() will be replaced with
> + * unixctl_command_register_fmt() in a later patch of this series. It
> + * is kept temporarily to reduce the amount of changes in this patch.
> */
> void unixctl_command_register(const char *name, const char *usage,
> int min_args, int max_args,
> unixctl_cb_func *cb, void *aux);
> +void unixctl_command_register_fmt(const char *name, const char *usage,
> + int min_args, int max_args, int
> output_fmts,
> + unixctl_cb_func *cb, void *aux);
> void unixctl_command_reply_error(struct unixctl_conn *, const char *error);
> void unixctl_command_reply(struct unixctl_conn *, const char *body);
> +void unixctl_command_reply_json(struct unixctl_conn *,
> + struct json *body);
>
> #ifdef __cplusplus
> }
> diff --git a/utilities/ovs-appctl.c b/utilities/ovs-appctl.c
> index ba0c172e6..02df8ba97 100644
> --- a/utilities/ovs-appctl.c
> +++ b/utilities/ovs-appctl.c
> @@ -29,12 +29,22 @@
> #include "jsonrpc.h"
> #include "process.h"
> #include "timeval.h"
> +#include "svec.h"
> #include "unixctl.h"
> #include "util.h"
> #include "openvswitch/vlog.h"
>
> static void usage(void);
> -static const char *parse_command_line(int argc, char *argv[]);
> +
> +/* Parsed command line args. */
> +struct cmdl_args {
> + enum ovs_output_fmt format;
> + char *target;
> +};
> +
> +static struct cmdl_args *cmdl_args_create(void);
> +static void cmdl_args_destroy(struct cmdl_args *);
> +static struct cmdl_args *parse_command_line(int argc, char *argv[]);
> static struct jsonrpc *connect_to_target(const char *target);
>
> int
> @@ -43,30 +53,59 @@ main(int argc, char *argv[])
> char *cmd_result, *cmd_error;
> struct jsonrpc *client;
> char *cmd, **cmd_argv;
> - const char *target;
> + struct cmdl_args *args;
> int cmd_argc;
> int error;
> + struct svec opt_argv = SVEC_EMPTY_INITIALIZER;
Can we keep the variables in reversed Christmas tree order?
>
> set_program_name(argv[0]);
>
> /* Parse command line and connect to target. */
> - target = parse_command_line(argc, argv);
> - client = connect_to_target(target);
> + args = parse_command_line(argc, argv);
> + client = connect_to_target(args->target);
> +
> + /* Transact options request (if required) and process reply */
> + if (args->format != OVS_OUTPUT_FMT_TEXT) {
> + svec_add(&opt_argv, "--format");
> + svec_add(&opt_argv, ovs_output_fmt_to_string(args->format));
> + }
> + svec_terminate(&opt_argv);
> +
> + if (opt_argv.n > 0) {
You can use svec_is_empty() here.
> + error = unixctl_client_transact(client, "set-options",
> + opt_argv.n, opt_argv.names,
> + &cmd_result, &cmd_error);
> +
> + if (error) {
> + ovs_fatal(error, "%s: transaction error", args->target);
> + }
>
> - /* Transact request and process reply. */
> + if (cmd_error) {
> + jsonrpc_close(client);
> + fputs(cmd_error, stderr);
> + ovs_error(0, "%s: server returned an error", args->target);
> + exit(2);
> + }
> +
> + free(cmd_result);
> + free(cmd_error);
cmd_error will never end up here, as you call exit(2) above, so the free should
also move up.
Should we not exit on a transaction error also? I think error handling might
need some more cleanup.
> + }
> + svec_destroy(&opt_argv);
> +
> + /* Transact command request and process reply. */
> cmd = argv[optind++];
> cmd_argc = argc - optind;
> cmd_argv = cmd_argc ? argv + optind : NULL;
> error = unixctl_client_transact(client, cmd, cmd_argc, cmd_argv,
> &cmd_result, &cmd_error);
> if (error) {
> - ovs_fatal(error, "%s: transaction error", target);
> + ovs_fatal(error, "%s: transaction error", args->target);
> }
>
> if (cmd_error) {
> jsonrpc_close(client);
> fputs(cmd_error, stderr);
> - ovs_error(0, "%s: server returned an error", target);
> + ovs_error(0, "%s: server returned an error", args->target);
> exit(2);
> } else if (cmd_result) {
> fputs(cmd_result, stdout);
> @@ -74,6 +113,7 @@ main(int argc, char *argv[])
> OVS_NOT_REACHED();
> }
>
> + cmdl_args_destroy(args);
> jsonrpc_close(client);
> free(cmd_result);
> free(cmd_error);
> @@ -101,13 +141,34 @@ Common commands:\n\
> vlog/reopen Make the program reopen its log file\n\
> Other options:\n\
> --timeout=SECS wait at most SECS seconds for a response\n\
> + -f, --format=FMT Output format. One of: 'json', or 'text'\n\
> + ('text', by default)\n\
> -h, --help Print this helpful information\n\
> -V, --version Display ovs-appctl version information\n",
> program_name, program_name);
> exit(EXIT_SUCCESS);
> }
>
> -static const char *
> +static struct cmdl_args *
> +cmdl_args_create(void) {
> + struct cmdl_args *args = xmalloc(sizeof *args);
> +
> + args->format = OVS_OUTPUT_FMT_TEXT;
> + args->target = NULL;
> +
> + return args;
> +}
> +
> +static void
> +cmdl_args_destroy(struct cmdl_args *args) {
> + if (args->target) {
No need to check for NULL here, free() will do that for you.
> + free(args->target);
> + }
> +
> + free(args);
> +}
> +
> +static struct cmdl_args *
> parse_command_line(int argc, char *argv[])
> {
> enum {
> @@ -117,6 +178,7 @@ parse_command_line(int argc, char *argv[])
> static const struct option long_options[] = {
> {"target", required_argument, NULL, 't'},
> {"execute", no_argument, NULL, 'e'},
> + {"format", required_argument, NULL, 'f'},
> {"help", no_argument, NULL, 'h'},
> {"option", no_argument, NULL, 'o'},
> {"version", no_argument, NULL, 'V'},
> @@ -126,11 +188,11 @@ parse_command_line(int argc, char *argv[])
> };
> char *short_options_ =
> ovs_cmdl_long_options_to_short_options(long_options);
> char *short_options = xasprintf("+%s", short_options_);
> - const char *target;
> +
No need for extra newline.
> + struct cmdl_args *args = cmdl_args_create();
> int e_options;
> unsigned int timeout = 0;
While your add it maybe swap the above two lines.
>
> - target = NULL;
> e_options = 0;
> for (;;) {
> int option;
> @@ -141,10 +203,10 @@ parse_command_line(int argc, char *argv[])
> }
> switch (option) {
> case 't':
> - if (target) {
> + if (args->target) {
> ovs_fatal(0, "-t or --target may be specified only once");
> }
> - target = optarg;
> + args->target = xstrdup(optarg);
> break;
>
> case 'e':
> @@ -157,6 +219,12 @@ parse_command_line(int argc, char *argv[])
> }
> break;
>
> + case 'f':
> + if (!ovs_output_fmt_from_string(optarg, &args->format)) {
> + ovs_fatal(0, "value %s on -f or --format is invalid",
> optarg);
> + }
> + break;
> +
> case 'h':
> usage();
> break;
> @@ -194,7 +262,10 @@ parse_command_line(int argc, char *argv[])
> "(use --help for help)");
> }
>
> - return target ? target : "ovs-vswitchd";
> + if (!args->target) {
> + args->target = xstrdup("ovs-vswitchd");
> + }
> + return args;
> }
>
> static struct jsonrpc *
> diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
> index 56d7a942b..30104ddb2 100644
> --- a/utilities/ovs-dpctl.c
> +++ b/utilities/ovs-dpctl.c
> @@ -63,6 +63,7 @@ main(int argc, char *argv[])
> fatal_ignore_sigpipe();
>
> dpctl_p.is_appctl = false;
> + dpctl_p.format = OVS_OUTPUT_FMT_TEXT;
> dpctl_p.output = dpctl_print;
> dpctl_p.usage = usage;
>
> --
> 2.39.2
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev