{"id":1887843,"url":"http://patchwork.ozlabs.org/api/patches/1887843/","web_url":"http://patchwork.ozlabs.org/project/openvswitch/patch/20240118095742.731853-2-jmeng@redhat.com/","project":{"id":47,"url":"http://patchwork.ozlabs.org/api/projects/47/","name":"Open vSwitch","link_name":"openvswitch","list_id":"ovs-dev.openvswitch.org","list_email":"ovs-dev@openvswitch.org","web_url":"http://openvswitch.org/","scm_url":"git@github.com:openvswitch/ovs.git","webscm_url":"https://github.com/openvswitch/ovs","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20240118095742.731853-2-jmeng@redhat.com>","list_archive_url":null,"date":"2024-01-18T09:57:37","name":"[ovs-dev,v6,1/6] Add global option for JSON output to ovs-appctl.","commit_ref":null,"pull_url":null,"state":"superseded","archived":true,"hash":"f0a1fc83841c7d73e4bf09f4ce1250b2a132edfd","submitter":{"id":87271,"url":"http://patchwork.ozlabs.org/api/people/87271/","name":"Jakob Meng","email":"jmeng@redhat.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/openvswitch/patch/20240118095742.731853-2-jmeng@redhat.com/mbox/","series":[{"id":390996,"url":"http://patchwork.ozlabs.org/api/series/390996/","web_url":"http://patchwork.ozlabs.org/project/openvswitch/list/?series=390996","date":"2024-01-18T09:57:36","name":"Add global option to output JSON from ovs-appctl cmds.","version":6,"mbox":"http://patchwork.ozlabs.org/series/390996/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/1887843/comments/","check":"fail","checks":"http://patchwork.ozlabs.org/api/patches/1887843/checks/","tags":{},"related":[],"headers":{"Return-Path":"<ovs-dev-bounces@openvswitch.org>","X-Original-To":["incoming@patchwork.ozlabs.org","dev@openvswitch.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","ovs-dev@lists.linuxfoundation.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=Go39YP3J;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)","smtp1.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=Go39YP3J","smtp4.osuosl.org;\n dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com\n header.a=rsa-sha256 header.s=mimecast20190719 header.b=Go39YP3J"],"Received":["from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4TFyqw3zMfz1yPv\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 18 Jan 2024 20:58:36 +1100 (AEDT)","from localhost (localhost [127.0.0.1])\n\tby smtp1.osuosl.org (Postfix) with ESMTP id 36BDC80D41;\n\tThu, 18 Jan 2024 09:58:33 +0000 (UTC)","from smtp1.osuosl.org ([127.0.0.1])\n\tby localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id UIE2oM7dUsPo; Thu, 18 Jan 2024 09:58:30 +0000 (UTC)","from lists.linuxfoundation.org (lf-lists.osuosl.org\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby smtp1.osuosl.org (Postfix) with ESMTPS id 34A7680C83;\n\tThu, 18 Jan 2024 09:58:29 +0000 (UTC)","from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 0D5A0C0DD5;\n\tThu, 18 Jan 2024 09:58:27 +0000 (UTC)","from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137])\n by lists.linuxfoundation.org (Postfix) with ESMTP id A88C1C0037\n for <dev@openvswitch.org>; Thu, 18 Jan 2024 09:58:25 +0000 (UTC)","from localhost (localhost [127.0.0.1])\n by smtp4.osuosl.org (Postfix) with ESMTP id 8381941610\n for <dev@openvswitch.org>; Thu, 18 Jan 2024 09:58:25 +0000 (UTC)","from smtp4.osuosl.org ([127.0.0.1])\n by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n with ESMTP id qiALN5Dr2nDk for <dev@openvswitch.org>;\n Thu, 18 Jan 2024 09:58:23 +0000 (UTC)","from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.133.124])\n by smtp4.osuosl.org (Postfix) with ESMTPS id 8C7C84094B\n for <dev@openvswitch.org>; Thu, 18 Jan 2024 09:58:23 +0000 (UTC)","from mail-wr1-f71.google.com (mail-wr1-f71.google.com\n [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS\n (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n us-mta-176-BFEXqUlpO52p6xq7xOCXxA-1; Thu, 18 Jan 2024 04:58:21 -0500","by mail-wr1-f71.google.com with SMTP id\n ffacd0b85a97d-337d05db5bbso193315f8f.3\n for <dev@openvswitch.org>; Thu, 18 Jan 2024 01:58:20 -0800 (PST)","from positronik4lide.redhat.com ([87.122.58.78])\n by smtp.gmail.com with ESMTPSA id\n iv11-20020a05600c548b00b0040d8ff79fd8sm25226688wmb.7.2024.01.18.01.58.18\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 18 Jan 2024 01:58:18 -0800 (PST)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 smtp1.osuosl.org 36BDC80D41","OpenDKIM Filter v2.11.0 smtp1.osuosl.org 34A7680C83","OpenDKIM Filter v2.11.0 smtp4.osuosl.org 8381941610","OpenDKIM Filter v2.11.0 smtp4.osuosl.org 8C7C84094B"],"X-Virus-Scanned":["amavisd-new at osuosl.org","amavisd-new at osuosl.org"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1705571902;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=aSknX6Jjcl6sDkKTGji/zcLwrc7A7kfKuN4TwTydOoQ=;\n b=Go39YP3JwSTQcKMHcXhsWJMpPWq9y8CrKra45vCotFibP7jxODMsoDXEZ3fVNxdpQ6FLXP\n VbCUB12KMErCnKX7AIXwKgld9y9VyEehpRm3witK6fxPHh42Ou0BjeLmsx7FlqEwqFj1Ci\n wwzji7ZROxAV3fGqnE1nrLcrpPXFTrk=","X-MC-Unique":"BFEXqUlpO52p6xq7xOCXxA-1","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1705571900; x=1706176700;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n :subject:date:message-id:reply-to;\n bh=aSknX6Jjcl6sDkKTGji/zcLwrc7A7kfKuN4TwTydOoQ=;\n b=hVW1jzZEWE7es85ZL/5MnNcE21L0Sx+QxWvuU9pN+AuIa23RVNo/vKWQKZ3x0iwjQj\n glsw03Flao2BQcFdeVZgNlrn/jWd1qwvdOour+R+OjE++qZcbX7bXqV0Q+6WW/JmYVQx\n XNQGvrYirSm4RKtcdEw4T23CCkhDoMHtgpcSvqW6zw85bjOACULbPprfIX//rtFT1tu6\n XQnYMTc2EzC6tuXlc0F4GYEE2Nt7lf9DOTMx+6hRjUNQAwxHt541zWdBTFLN3wXqJpO5\n lgbU2iMusvTCSOgQoyN6PdyCu2hvchPI/VGoLEHR50hCmq/EnUahFLBRybNRtRSi5Jis\n lzYg==","X-Gm-Message-State":"AOJu0Yx08dvLPSZ8XAiqCLKCrYuQ6H86b8abhbPs4/lNLWliEpvvjROo\n 9AlknLfZ2tREqJXXvbep4SHAhDB+O2O6+SDgD6z4Ab4YEFzsxXLESLv4Dwk2hfCZm1dCkxy9W9H\n lDPsxuGMFqFBBLkAp7IJpLkn9zog1nDKjFuPCO5N84Sp+yRpNvO7QgpjHvcwU3eFOg2/EvrbrrT\n JnNsoQhEtc5IcP8Tj8PSC2brwTWsgL","X-Received":["by 2002:a05:600c:4fd1:b0:40e:5320:16ae with SMTP id\n o17-20020a05600c4fd100b0040e532016aemr186625wmq.149.1705571899813;\n Thu, 18 Jan 2024 01:58:19 -0800 (PST)","by 2002:a05:600c:4fd1:b0:40e:5320:16ae with SMTP id\n o17-20020a05600c4fd100b0040e532016aemr186612wmq.149.1705571899251;\n Thu, 18 Jan 2024 01:58:19 -0800 (PST)"],"X-Google-Smtp-Source":"\n AGHT+IEliDq0kNphUGkE59LOuFCx4IAYswdKNVg/fDfCfzz24Qsp2Il8zSPyt8rBXR8vUv4QJbSOtA==","From":"jmeng@redhat.com","To":"dev@openvswitch.org, i.maximets@ovn.org, echaudro@redhat.com,\n ktraynor@redhat.com, aconole@redhat.com, rjarry@redhat.com","Date":"Thu, 18 Jan 2024 10:57:37 +0100","Message-Id":"<20240118095742.731853-2-jmeng@redhat.com>","X-Mailer":"git-send-email 2.39.2","In-Reply-To":"<20240118095742.731853-1-jmeng@redhat.com>","References":"<20240118095742.731853-1-jmeng@redhat.com>","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-Originator":"redhat.com","Subject":"[ovs-dev] [PATCH v6 1/6] Add global option for JSON output to\n\tovs-appctl.","X-BeenThere":"ovs-dev@openvswitch.org","X-Mailman-Version":"2.1.15","Precedence":"list","List-Id":"<ovs-dev.openvswitch.org>","List-Unsubscribe":"<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>","List-Archive":"<http://mail.openvswitch.org/pipermail/ovs-dev/>","List-Post":"<mailto:ovs-dev@openvswitch.org>","List-Help":"<mailto:ovs-dev-request@openvswitch.org?subject=help>","List-Subscribe":"<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"ovs-dev-bounces@openvswitch.org","Sender":"\"dev\" <ovs-dev-bounces@openvswitch.org>"},"content":"From: Jakob Meng <code@jakobmeng.de>\n\nFor monitoring systems such as Prometheus it would be beneficial if\nOVS would expose statistics in a machine-readable format.\n\nThis patch introduces support for different output formats to ovs-xxx\ntools. They gain a global option '-f,--format' which allows users to\nrequest JSON instead of plain-text for humans. An example call\nimplemented in a later patch is 'ovs-appctl --format json dpif/show'.\n\nFor that a new 'set-options' command has been added to lib/unixctl.c\nwhich allows to change the output format of the following commands.\nIt is supposed to be run by ovs-xxx tools transparently for the user\nwhen a specific output format has been requested. For example, when a\nuser calls 'ovs-appctl --format json dpif/show', then ovs-appctl will\ncall 'set-options' to set the output format as requested by the user\nand then the actual command 'dpif/show'.\nThis ovs-appctl behaviour has been implemented in a backward compatible\nway. One can use an updated client (ovs-appctl) with an old server\n(ovs-vswitchd) and vice versa. Of course, JSON output only works when\nboth sides have been updated.\n\nTo demonstrate the necessary API changes for supporting output formats,\nunixctl_command_register() in lib/unixctl.* has been cloned to\nunixctl_command_register_fmt(). The latter will be replaced with the\nformer in a later patch. The new function gained an int argument called\n'output_fmts' with which commands have to announce their support for\noutput formats.\n\nFunction type unixctl_cb_func in lib/unixctl.h has gained a new arg\n'enum ovs_output_fmt fmt'. For now, it has been added as a comment\nonly for the huge changes reason mentioned earlier. The output format\nwhich has been passed via 'set-options' to ovs-vswitchd will be\nconverted into a value of type 'enum ovs_output_fmt' in lib/unixctl.c\nand then passed to said 'fmt' arg of the choosen command handler (in a\nfuture patch). When a requested output format is not supported by a\ncommand, then process_command() in lib/unixctl.c will return an error.\n\nThis patch does not yet pass the choosen output format to commands.\nDoing so requires changes to all unixctl_command_register() calls and\nall command callbacks. To improve readibility those changes have been\nsplit out into a follow up patch. Respectively, whenever an output\nformat other than 'text' is choosen for ovs-xxx tools, they will fail.\nBy default, the output format is plain-text as before.\n\nIn popular tools like kubectl the option for output control is usually\ncalled '-o|--output' instead of '-f,--format'. But ovs-appctl already\nhas an short option '-o' which prints the available ovs-appctl options\n('--option'). The now choosen name also better alines with ovsdb-client\nwhere '-f,--format' controls output formatting.\n\nReported-at: https://bugzilla.redhat.com/1824861\nSigned-off-by: Jakob Meng <code@jakobmeng.de>\n---\n NEWS                   |   2 +\n lib/command-line.c     |  36 +++++++++++\n lib/command-line.h     |  10 +++\n lib/dpctl.h            |   4 ++\n lib/unixctl.c          | 142 ++++++++++++++++++++++++++++++++++-------\n lib/unixctl.h          |  16 ++++-\n utilities/ovs-appctl.c |  97 ++++++++++++++++++++++++----\n utilities/ovs-dpctl.c  |   1 +\n 8 files changed, 271 insertions(+), 37 deletions(-)","diff":"diff --git a/NEWS b/NEWS\nindex 410dd68e5..94dabeb5b 100644\n--- a/NEWS\n+++ b/NEWS\n@@ -27,6 +27,8 @@ Post-v3.2.0\n        \"ovs-appctl dpctl/ct-del-limits default\".\n      * 'dpctl/flush-conntrack' is now capable of flushing connections based\n        on mark and labels.\n+     * Added new option [-f|--format] to choose the output format, e.g. 'json'\n+       or 'text' (by default).\n    - ovs-vsctl:\n      * New commands 'set-zone-limit', 'del-zone-limit' and 'list-zone-limits'\n        to manage the maximum number of connections in conntrack zones via\ndiff --git a/lib/command-line.c b/lib/command-line.c\nindex 967f4f5d5..775e0430a 100644\n--- a/lib/command-line.c\n+++ b/lib/command-line.c\n@@ -24,6 +24,7 @@\n #include \"ovs-thread.h\"\n #include \"util.h\"\n #include \"openvswitch/vlog.h\"\n+#include \"openvswitch/json.h\"\n \n VLOG_DEFINE_THIS_MODULE(command_line);\n \n@@ -53,6 +54,41 @@ ovs_cmdl_long_options_to_short_options(const struct option options[])\n     return xstrdup(short_options);\n }\n \n+const char *\n+ovs_output_fmt_to_string(enum ovs_output_fmt fmt)\n+{\n+    switch (fmt) {\n+    case OVS_OUTPUT_FMT_TEXT:\n+        return \"text\";\n+\n+    case OVS_OUTPUT_FMT_JSON:\n+        return \"json\";\n+\n+    default:\n+        return NULL;\n+    }\n+}\n+\n+struct json *\n+ovs_output_fmt_to_json(enum ovs_output_fmt fmt)\n+{\n+    const char *string = ovs_output_fmt_to_string(fmt);\n+    return string ? json_string_create(string) : NULL;\n+}\n+\n+bool\n+ovs_output_fmt_from_string(const char *string, enum ovs_output_fmt *fmt)\n+{\n+    if (!strcmp(string, \"text\")) {\n+        *fmt = OVS_OUTPUT_FMT_TEXT;\n+    } else if (!strcmp(string, \"json\")) {\n+        *fmt = OVS_OUTPUT_FMT_JSON;\n+    } else {\n+        return false;\n+    }\n+    return true;\n+}\n+\n static char * OVS_WARN_UNUSED_RESULT\n build_short_options(const struct option *long_options)\n {\ndiff --git a/lib/command-line.h b/lib/command-line.h\nindex fc2a2690f..494274cec 100644\n--- a/lib/command-line.h\n+++ b/lib/command-line.h\n@@ -20,6 +20,7 @@\n /* Utilities for command-line parsing. */\n \n #include \"compiler.h\"\n+#include <openvswitch/list.h>\n \n struct option;\n \n@@ -46,6 +47,15 @@ struct ovs_cmdl_command {\n \n char *ovs_cmdl_long_options_to_short_options(const struct option *options);\n \n+enum ovs_output_fmt {\n+    OVS_OUTPUT_FMT_TEXT = 1 << 0,\n+    OVS_OUTPUT_FMT_JSON = 1 << 1\n+};\n+\n+const char *ovs_output_fmt_to_string(enum ovs_output_fmt);\n+struct json *ovs_output_fmt_to_json(enum ovs_output_fmt);\n+bool ovs_output_fmt_from_string(const char *, enum ovs_output_fmt *);\n+\n struct ovs_cmdl_parsed_option {\n     const struct option *o;\n     char *arg;\ndiff --git a/lib/dpctl.h b/lib/dpctl.h\nindex 9d0052152..1c9b2409f 100644\n--- a/lib/dpctl.h\n+++ b/lib/dpctl.h\n@@ -19,6 +19,7 @@\n #include <stdbool.h>\n \n #include \"compiler.h\"\n+#include \"command-line.h\"\n \n struct dpctl_params {\n     /* True if it is called by ovs-appctl command. */\n@@ -42,6 +43,9 @@ struct dpctl_params {\n     /* --names: Use port names in output? */\n     bool names;\n \n+    /* Output format. */\n+    enum ovs_output_fmt format;\n+\n     /* Callback for printing.  This function is called from dpctl_run_command()\n      * to output data.  The 'aux' parameter is set to the 'aux'\n      * member.  The 'error' parameter is true if 'string' is an error\ndiff --git a/lib/unixctl.c b/lib/unixctl.c\nindex 103357ee9..dd1450f22 100644\n--- a/lib/unixctl.c\n+++ b/lib/unixctl.c\n@@ -21,7 +21,6 @@\n #include \"coverage.h\"\n #include \"dirs.h\"\n #include \"openvswitch/dynamic-string.h\"\n-#include \"openvswitch/json.h\"\n #include \"jsonrpc.h\"\n #include \"openvswitch/list.h\"\n #include \"openvswitch/poll-loop.h\"\n@@ -39,6 +38,7 @@ COVERAGE_DEFINE(unixctl_replied);\n struct unixctl_command {\n     const char *usage;\n     int min_args, max_args;\n+    int output_fmts;\n     unixctl_cb_func *cb;\n     void *aux;\n };\n@@ -50,6 +50,8 @@ struct unixctl_conn {\n     /* Only one request can be in progress at a time.  While the request is\n      * being processed, 'request_id' is populated, otherwise it is null. */\n     struct json *request_id;   /* ID of the currently active request. */\n+\n+    enum ovs_output_fmt fmt;\n };\n \n /* Server for control connection. */\n@@ -94,6 +96,39 @@ unixctl_version(struct unixctl_conn *conn, int argc OVS_UNUSED,\n     unixctl_command_reply(conn, ovs_get_program_version());\n }\n \n+static void\n+unixctl_set_options(struct unixctl_conn *conn, int argc, const char *argv[],\n+                    void *aux OVS_UNUSED)\n+{\n+    char * error = NULL;\n+\n+    for (size_t i = 1; i < argc; i++) {\n+        if ((i + 1) == argc) {\n+            error = xasprintf(\"option requires an argument -- %s\", argv[i]);\n+            goto error;\n+        } else if (!strcmp(argv[i], \"--format\")) {\n+            /* Move index to option argument. */\n+            i++;\n+\n+            /* Parse output format argument. */\n+            if (!ovs_output_fmt_from_string(argv[i], &conn->fmt)) {\n+                error = xasprintf(\"option %s has invalid value %s\",\n+                                  argv[i - 1], argv[i]);\n+                goto error;\n+            }\n+        } else {\n+            error = xasprintf(\"invalid option -- %s\", argv[i]);\n+            goto error;\n+\n+        }\n+    }\n+    unixctl_command_reply(conn, NULL);\n+    return;\n+error:\n+    unixctl_command_reply_error(conn, error);\n+    free(error);\n+}\n+\n /* Registers a unixctl command with the given 'name'.  'usage' describes the\n  * arguments to the command; it is used only for presentation to the user in\n  * \"list-commands\" output.  (If 'usage' is NULL, then the command is hidden.)\n@@ -109,6 +144,28 @@ void\n unixctl_command_register(const char *name, const char *usage,\n                          int min_args, int max_args,\n                          unixctl_cb_func *cb, void *aux)\n+{\n+    unixctl_command_register_fmt(name, usage, min_args, max_args,\n+                                 OVS_OUTPUT_FMT_TEXT, cb, aux);\n+}\n+\n+/* Registers a unixctl command with the given 'name'.  'usage' describes the\n+ * arguments to the command; it is used only for presentation to the user in\n+ * \"list-commands\" output.  (If 'usage' is NULL, then the command is hidden.)\n+ * 'output_fmts' is a bitmap that defines what output formats a command\n+ * supports, e.g. OVS_OUTPUT_FMT_TEXT | OVS_OUTPUT_FMT_JSON.\n+ *\n+ * 'cb' is called when the command is received.  It is passed an array\n+ * containing the command name and arguments, plus a copy of 'aux'.  Normally\n+ * 'cb' should reply by calling unixctl_command_reply() or\n+ * unixctl_command_reply_error() before it returns, but if the command cannot\n+ * be handled immediately then it can defer the reply until later.  A given\n+ * connection can only process a single request at a time, so a reply must be\n+ * made eventually to avoid blocking that connection. */\n+void\n+unixctl_command_register_fmt(const char *name, const char *usage,\n+                             int min_args, int max_args, int output_fmts,\n+                             unixctl_cb_func *cb, void *aux)\n {\n     struct unixctl_command *command;\n     struct unixctl_command *lookup = shash_find_data(&commands, name);\n@@ -123,41 +180,48 @@ unixctl_command_register(const char *name, const char *usage,\n     command->usage = usage;\n     command->min_args = min_args;\n     command->max_args = max_args;\n+    command->output_fmts = output_fmts;\n     command->cb = cb;\n     command->aux = aux;\n     shash_add(&commands, name, command);\n }\n \n-static void\n-unixctl_command_reply__(struct unixctl_conn *conn,\n-                        bool success, const char *body)\n+static struct json *\n+json_string_create__(const char *body)\n {\n-    struct json *body_json;\n-    struct jsonrpc_msg *reply;\n-\n-    COVERAGE_INC(unixctl_replied);\n-    ovs_assert(conn->request_id);\n-\n     if (!body) {\n         body = \"\";\n     }\n \n     if (body[0] && body[strlen(body) - 1] != '\\n') {\n-        body_json = json_string_create_nocopy(xasprintf(\"%s\\n\", body));\n+        return json_string_create_nocopy(xasprintf(\"%s\\n\", body));\n     } else {\n-        body_json = json_string_create(body);\n+        return json_string_create(body);\n     }\n+}\n+\n+/* Takes ownership of 'body'. */\n+static void\n+unixctl_command_reply__(struct unixctl_conn *conn,\n+                        bool success, struct json *body)\n+{\n+    struct jsonrpc_msg *reply;\n+\n+    COVERAGE_INC(unixctl_replied);\n+    ovs_assert(conn->request_id);\n \n     if (success) {\n-        reply = jsonrpc_create_reply(body_json, conn->request_id);\n+        reply = jsonrpc_create_reply(body, conn->request_id);\n     } else {\n-        reply = jsonrpc_create_error(body_json, conn->request_id);\n+        reply = jsonrpc_create_error(body, conn->request_id);\n     }\n \n     if (VLOG_IS_DBG_ENABLED()) {\n         char *id = json_to_string(conn->request_id, 0);\n+        char *msg = json_to_string(body, 0);\n         VLOG_DBG(\"replying with %s, id=%s: \\\"%s\\\"\",\n-                 success ? \"success\" : \"error\", id, body);\n+                 success ? \"success\" : \"error\", id, msg);\n+        free(msg);\n         free(id);\n     }\n \n@@ -170,22 +234,34 @@ unixctl_command_reply__(struct unixctl_conn *conn,\n \n /* Replies to the active unixctl connection 'conn'.  'result' is sent to the\n  * client indicating the command was processed successfully.  Only one call to\n- * unixctl_command_reply() or unixctl_command_reply_error() may be made per\n- * request. */\n+ * unixctl_command_reply(), unixctl_command_reply_error() or\n+ * unixctl_command_reply_json() may be made per request. */\n void\n unixctl_command_reply(struct unixctl_conn *conn, const char *result)\n {\n-    unixctl_command_reply__(conn, true, result);\n+    unixctl_command_reply__(conn, true, json_string_create__(result));\n }\n \n /* Replies to the active unixctl connection 'conn'. 'error' is sent to the\n- * client indicating an error occurred processing the command.  Only one call to\n- * unixctl_command_reply() or unixctl_command_reply_error() may be made per\n- * request. */\n+ * client indicating an error occurred processing the command.  Only one call\n+ * to unixctl_command_reply(), unixctl_command_reply_error() or\n+ * unixctl_command_reply_json() may be made per request. */\n void\n unixctl_command_reply_error(struct unixctl_conn *conn, const char *error)\n {\n-    unixctl_command_reply__(conn, false, error);\n+    unixctl_command_reply__(conn, false, json_string_create__(error));\n+}\n+\n+/* Replies to the active unixctl connection 'conn'.  'result' is sent to the\n+ * client indicating the command was processed successfully.  Only one call to\n+ * unixctl_command_reply(), unixctl_command_reply_error() or\n+ * unixctl_command_reply_json() may be made per request.\n+ *\n+ * Takes ownership of 'body'. */\n+void\n+unixctl_command_reply_json(struct unixctl_conn *conn, struct json *body)\n+{\n+    unixctl_command_reply__(conn, true, body);\n }\n \n /* Creates a unixctl server listening on 'path', which for POSIX may be:\n@@ -250,6 +326,8 @@ unixctl_server_create(const char *path, struct unixctl_server **serverp)\n     unixctl_command_register(\"list-commands\", \"\", 0, 0, unixctl_list_commands,\n                              NULL);\n     unixctl_command_register(\"version\", \"\", 0, 0, unixctl_version, NULL);\n+    unixctl_command_register(\"set-options\", \"[--format text|json]\", 2, 2,\n+                             unixctl_set_options, NULL);\n \n     struct unixctl_server *server = xmalloc(sizeof *server);\n     server->listener = listener;\n@@ -291,6 +369,18 @@ process_command(struct unixctl_conn *conn, struct jsonrpc_msg *request)\n     } else if (params->n > command->max_args) {\n         error = xasprintf(\"\\\"%s\\\" command takes at most %d arguments\",\n                           request->method, command->max_args);\n+    } else if ((!command->output_fmts && conn->fmt != OVS_OUTPUT_FMT_TEXT) ||\n+               (command->output_fmts && !(conn->fmt & command->output_fmts)))\n+    {\n+        error = xasprintf(\"\\\"%s\\\" command does not support output format\"\\\n+                          \" \\\"%s\\\" (supported: %d, requested: %d)\",\n+                          request->method, ovs_output_fmt_to_string(conn->fmt),\n+                          command->output_fmts, conn->fmt);\n+    } else if (conn->fmt != OVS_OUTPUT_FMT_TEXT) {\n+        /* FIXME: Remove this check once output format will be passed to the\n+         *        command handler below. */\n+        error = xasprintf(\"output format \\\"%s\\\" has not been implemented yet\",\n+                          ovs_output_fmt_to_string(conn->fmt));\n     } else {\n         struct svec argv = SVEC_EMPTY_INITIALIZER;\n         int  i;\n@@ -307,8 +397,10 @@ process_command(struct unixctl_conn *conn, struct jsonrpc_msg *request)\n         svec_terminate(&argv);\n \n         if (!error) {\n+            /* FIXME: Output format will be passed as 'fmt' to the command in\n+             *        later patch. */\n             command->cb(conn, argv.n, (const char **) argv.names,\n-                        command->aux);\n+                        /* conn->fmt, */ command->aux);\n         }\n \n         svec_destroy(&argv);\n@@ -381,6 +473,7 @@ unixctl_server_run(struct unixctl_server *server)\n             struct unixctl_conn *conn = xzalloc(sizeof *conn);\n             ovs_list_push_back(&server->conns, &conn->node);\n             conn->rpc = jsonrpc_open(stream);\n+            conn->fmt = OVS_OUTPUT_FMT_TEXT;\n         } else if (error == EAGAIN) {\n             break;\n         } else {\n@@ -518,6 +611,9 @@ unixctl_client_transact(struct jsonrpc *client, const char *command, int argc,\n     } else if (reply->result) {\n         if (reply->result->type == JSON_STRING) {\n             *result = xstrdup(json_string(reply->result));\n+        } else if (reply->result->type == JSON_OBJECT ||\n+                   reply->result->type == JSON_ARRAY) {\n+            *result = json_to_string(reply->result, 0);\n         } else {\n             VLOG_WARN(\"%s: unexpected result type in JSON rpc reply: %s\",\n                       jsonrpc_get_name(client),\ndiff --git a/lib/unixctl.h b/lib/unixctl.h\nindex 4562dbc49..8200b9e11 100644\n--- a/lib/unixctl.h\n+++ b/lib/unixctl.h\n@@ -17,6 +17,9 @@\n #ifndef UNIXCTL_H\n #define UNIXCTL_H 1\n \n+#include \"openvswitch/json.h\"\n+#include \"command-line.h\"\n+\n #ifdef  __cplusplus\n extern \"C\" {\n #endif\n@@ -40,13 +43,24 @@ int unixctl_client_transact(struct jsonrpc *client,\n \n /* Command registration. */\n struct unixctl_conn;\n+/* FIXME: Output format will be passed as 'fmt' to the command in later patch.\n+ */\n typedef void unixctl_cb_func(struct unixctl_conn *,\n-                             int argc, const char *argv[], void *aux);\n+                             int argc, const char *argv[],\n+                             /* enum ovs_output_fmt fmt, */ void *aux);\n+/* FIXME: unixctl_command_register() will be replaced with\n+ *        unixctl_command_register_fmt() in a later patch of this series.  It\n+ *        is kept temporarily to reduce the amount of changes in this patch. */\n void unixctl_command_register(const char *name, const char *usage,\n                               int min_args, int max_args,\n                               unixctl_cb_func *cb, void *aux);\n+void unixctl_command_register_fmt(const char *name, const char *usage,\n+                                  int min_args, int max_args, int output_fmts,\n+                                  unixctl_cb_func *cb, void *aux);\n void unixctl_command_reply_error(struct unixctl_conn *, const char *error);\n void unixctl_command_reply(struct unixctl_conn *, const char *body);\n+void unixctl_command_reply_json(struct unixctl_conn *,\n+                                struct json *body);\n \n #ifdef  __cplusplus\n }\ndiff --git a/utilities/ovs-appctl.c b/utilities/ovs-appctl.c\nindex ba0c172e6..02df8ba97 100644\n--- a/utilities/ovs-appctl.c\n+++ b/utilities/ovs-appctl.c\n@@ -29,12 +29,22 @@\n #include \"jsonrpc.h\"\n #include \"process.h\"\n #include \"timeval.h\"\n+#include \"svec.h\"\n #include \"unixctl.h\"\n #include \"util.h\"\n #include \"openvswitch/vlog.h\"\n \n static void usage(void);\n-static const char *parse_command_line(int argc, char *argv[]);\n+\n+/* Parsed command line args. */\n+struct cmdl_args {\n+    enum ovs_output_fmt format;\n+    char *target;\n+};\n+\n+static struct cmdl_args *cmdl_args_create(void);\n+static void cmdl_args_destroy(struct cmdl_args *);\n+static struct cmdl_args *parse_command_line(int argc, char *argv[]);\n static struct jsonrpc *connect_to_target(const char *target);\n \n int\n@@ -43,30 +53,59 @@ main(int argc, char *argv[])\n     char *cmd_result, *cmd_error;\n     struct jsonrpc *client;\n     char *cmd, **cmd_argv;\n-    const char *target;\n+    struct cmdl_args *args;\n     int cmd_argc;\n     int error;\n+    struct svec opt_argv = SVEC_EMPTY_INITIALIZER;\n \n     set_program_name(argv[0]);\n \n     /* Parse command line and connect to target. */\n-    target = parse_command_line(argc, argv);\n-    client = connect_to_target(target);\n+    args = parse_command_line(argc, argv);\n+    client = connect_to_target(args->target);\n+\n+    /* Transact options request (if required) and process reply */\n+    if (args->format != OVS_OUTPUT_FMT_TEXT) {\n+        svec_add(&opt_argv, \"--format\");\n+        svec_add(&opt_argv, ovs_output_fmt_to_string(args->format));\n+    }\n+    svec_terminate(&opt_argv);\n+\n+    if (opt_argv.n > 0) {\n+        error = unixctl_client_transact(client, \"set-options\",\n+                                        opt_argv.n, opt_argv.names,\n+                                        &cmd_result, &cmd_error);\n+\n+        if (error) {\n+            ovs_fatal(error, \"%s: transaction error\", args->target);\n+        }\n \n-    /* Transact request and process reply. */\n+        if (cmd_error) {\n+            jsonrpc_close(client);\n+            fputs(cmd_error, stderr);\n+            ovs_error(0, \"%s: server returned an error\", args->target);\n+            exit(2);\n+        }\n+\n+        free(cmd_result);\n+        free(cmd_error);\n+    }\n+    svec_destroy(&opt_argv);\n+\n+    /* Transact command request and process reply. */\n     cmd = argv[optind++];\n     cmd_argc = argc - optind;\n     cmd_argv = cmd_argc ? argv + optind : NULL;\n     error = unixctl_client_transact(client, cmd, cmd_argc, cmd_argv,\n                                     &cmd_result, &cmd_error);\n     if (error) {\n-        ovs_fatal(error, \"%s: transaction error\", target);\n+        ovs_fatal(error, \"%s: transaction error\", args->target);\n     }\n \n     if (cmd_error) {\n         jsonrpc_close(client);\n         fputs(cmd_error, stderr);\n-        ovs_error(0, \"%s: server returned an error\", target);\n+        ovs_error(0, \"%s: server returned an error\", args->target);\n         exit(2);\n     } else if (cmd_result) {\n         fputs(cmd_result, stdout);\n@@ -74,6 +113,7 @@ main(int argc, char *argv[])\n         OVS_NOT_REACHED();\n     }\n \n+    cmdl_args_destroy(args);\n     jsonrpc_close(client);\n     free(cmd_result);\n     free(cmd_error);\n@@ -101,13 +141,34 @@ Common commands:\\n\\\n   vlog/reopen        Make the program reopen its log file\\n\\\n Other options:\\n\\\n   --timeout=SECS     wait at most SECS seconds for a response\\n\\\n+  -f, --format=FMT   Output format. One of: 'json', or 'text'\\n\\\n+                     ('text', by default)\\n\\\n   -h, --help         Print this helpful information\\n\\\n   -V, --version      Display ovs-appctl version information\\n\",\n            program_name, program_name);\n     exit(EXIT_SUCCESS);\n }\n \n-static const char *\n+static struct cmdl_args *\n+cmdl_args_create(void) {\n+    struct cmdl_args *args = xmalloc(sizeof *args);\n+\n+    args->format = OVS_OUTPUT_FMT_TEXT;\n+    args->target = NULL;\n+\n+    return args;\n+}\n+\n+static void\n+cmdl_args_destroy(struct cmdl_args *args) {\n+    if (args->target) {\n+        free(args->target);\n+    }\n+\n+    free(args);\n+}\n+\n+static struct cmdl_args *\n parse_command_line(int argc, char *argv[])\n {\n     enum {\n@@ -117,6 +178,7 @@ parse_command_line(int argc, char *argv[])\n     static const struct option long_options[] = {\n         {\"target\", required_argument, NULL, 't'},\n         {\"execute\", no_argument, NULL, 'e'},\n+        {\"format\", required_argument, NULL, 'f'},\n         {\"help\", no_argument, NULL, 'h'},\n         {\"option\", no_argument, NULL, 'o'},\n         {\"version\", no_argument, NULL, 'V'},\n@@ -126,11 +188,11 @@ parse_command_line(int argc, char *argv[])\n     };\n     char *short_options_ = ovs_cmdl_long_options_to_short_options(long_options);\n     char *short_options = xasprintf(\"+%s\", short_options_);\n-    const char *target;\n+\n+    struct cmdl_args *args = cmdl_args_create();\n     int e_options;\n     unsigned int timeout = 0;\n \n-    target = NULL;\n     e_options = 0;\n     for (;;) {\n         int option;\n@@ -141,10 +203,10 @@ parse_command_line(int argc, char *argv[])\n         }\n         switch (option) {\n         case 't':\n-            if (target) {\n+            if (args->target) {\n                 ovs_fatal(0, \"-t or --target may be specified only once\");\n             }\n-            target = optarg;\n+            args->target = xstrdup(optarg);\n             break;\n \n         case 'e':\n@@ -157,6 +219,12 @@ parse_command_line(int argc, char *argv[])\n             }\n             break;\n \n+        case 'f':\n+            if (!ovs_output_fmt_from_string(optarg, &args->format)) {\n+                ovs_fatal(0, \"value %s on -f or --format is invalid\", optarg);\n+            }\n+            break;\n+\n         case 'h':\n             usage();\n             break;\n@@ -194,7 +262,10 @@ parse_command_line(int argc, char *argv[])\n                   \"(use --help for help)\");\n     }\n \n-    return target ? target : \"ovs-vswitchd\";\n+    if (!args->target) {\n+        args->target = xstrdup(\"ovs-vswitchd\");\n+    }\n+    return args;\n }\n \n static struct jsonrpc *\ndiff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c\nindex 56d7a942b..30104ddb2 100644\n--- a/utilities/ovs-dpctl.c\n+++ b/utilities/ovs-dpctl.c\n@@ -63,6 +63,7 @@ main(int argc, char *argv[])\n     fatal_ignore_sigpipe();\n \n     dpctl_p.is_appctl = false;\n+    dpctl_p.format = OVS_OUTPUT_FMT_TEXT;\n     dpctl_p.output = dpctl_print;\n     dpctl_p.usage = usage;\n \n","prefixes":["ovs-dev","v6","1/6"]}