]> git.ipfire.org Git - thirdparty/ipset.git/commitdiff
Add json output to list command
authorThomas Oberhammer <toberhammer@open-systems.com>
Mon, 18 Sep 2023 21:24:59 +0000 (23:24 +0200)
committerJozsef Kadlecsik <kadlec@netfilter.org>
Mon, 18 Sep 2023 21:40:45 +0000 (23:40 +0200)
Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org>
include/libipset/session.h
lib/ipset.c
lib/session.c
src/ipset.8
src/ui.c
utils/ipset_bash_completion/ipset

index 5f18a6e2da1a969bcad33a8beff37fd752076b27..f0da10f51a2ca7660fc00a0eb7846d0efa368b2f 100644 (file)
@@ -98,6 +98,7 @@ enum ipset_output_mode {
        IPSET_LIST_PLAIN,
        IPSET_LIST_SAVE,
        IPSET_LIST_XML,
+       IPSET_LIST_JSON,
 };
 
 extern int ipset_session_output(struct ipset_session *session,
index 8e63af56d2a5149569f4a33ad8db09deed29e368..c910d88805c288af314164f6f53432d1c04bd25a 100644 (file)
@@ -235,7 +235,7 @@ const struct ipset_envopts ipset_envopts[] = {
        { .name = { "-o", "-output" },
          .has_arg = IPSET_MANDATORY_ARG,       .flag = IPSET_OPT_MAX,
          .parse = ipset_parse_output,
-         .help = "plain|save|xml\n"
+         .help = "plain|save|xml|json\n"
                  "       Specify output mode for listing sets.\n"
                  "       Default value for \"list\" command is mode \"plain\"\n"
                  "       and for \"save\" command is mode \"save\".",
@@ -429,6 +429,8 @@ ipset_parse_output(struct ipset *ipset,
                return ipset_session_output(session, IPSET_LIST_PLAIN);
        else if (STREQ(str, "xml"))
                return ipset_session_output(session, IPSET_LIST_XML);
+       else if (STREQ(str, "json"))
+               return ipset_session_output(session, IPSET_LIST_JSON);
        else if (STREQ(str, "save"))
                return ipset_session_output(session, IPSET_LIST_SAVE);
 
index cdc59e0f25c6cf6da6c766052b4154eb76616a99..a10238d87ec1abf8353e79d92b67d0e3509a555e 100644 (file)
@@ -860,6 +860,7 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
        const struct ipset_arg *arg;
        size_t offset = 0;
        int i, found = 0;
+       static char last_setname[IPSET_MAXNAMELEN] = "";
 
        D("enter");
        /* Check and load type, family */
@@ -894,6 +895,13 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
        case IPSET_LIST_XML:
                safe_snprintf(session, "<member><elem>");
                break;
+       case IPSET_LIST_JSON:
+               /* print separator if a member for this set was printed before */
+               if (STREQ(ipset_data_setname(data), last_setname))
+                       safe_snprintf(session, ",");
+               strcpy(last_setname, ipset_data_setname(data));
+               safe_snprintf(session, "\n      {\n        \"elem\" : \"");
+               break;
        case IPSET_LIST_PLAIN:
        default:
                break;
@@ -902,6 +910,8 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
        safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM);
        if (session->mode == IPSET_LIST_XML)
                safe_snprintf(session, "</elem>");
+       if (session->mode == IPSET_LIST_JSON)
+               safe_snprintf(session, "\"");
 
        for (i = 0; type->cmd[IPSET_ADD].args[i] != IPSET_ARG_NONE; i++) {
                arg = ipset_keyword(type->cmd[IPSET_ADD].args[i]);
@@ -929,6 +939,15 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
                        safe_dprintf(session, arg->print, arg->opt);
                        safe_snprintf(session, "</%s>", arg->name[0]);
                        break;
+               case IPSET_LIST_JSON:
+                       if (arg->has_arg == IPSET_NO_ARG) {
+                               safe_snprintf(session,
+                                             ",\n        \"%s\" : true", arg->name[0]);
+                               break;
+                       }
+                       safe_snprintf(session, ",\n        \"%s\" : ", arg->name[0]);
+                       safe_dprintf(session, arg->print, arg->opt);
+                       break;
                default:
                        break;
                }
@@ -936,6 +955,8 @@ list_adt(struct ipset_session *session, struct nlattr *nla[])
 
        if (session->mode == IPSET_LIST_XML)
                safe_snprintf(session, "</member>\n");
+       else if (session->mode == IPSET_LIST_JSON)
+               safe_snprintf(session, "\n      }");
        else
                safe_snprintf(session, "\n");
 
@@ -972,6 +993,7 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
        const struct ipset_arg *arg;
        uint8_t family;
        int i;
+       static bool firstipset = true;
 
        for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CREATE_MAX; i++)
                if (nla[i]) {
@@ -1007,6 +1029,19 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
                              ipset_data_setname(data),
                              type->name, type->revision);
                break;
+       case IPSET_LIST_JSON:
+               if (!firstipset)
+                       safe_snprintf(session, ",\n");
+               firstipset = false;
+               safe_snprintf(session,
+                             "  \{\n"
+                             "    \"name\" : \"%s\",\n"
+                             "    \"type\" : \"%s\",\n"
+                             "    \"revision\" : %u,\n"
+                             "    \"header\" : \{\n",
+                             ipset_data_setname(data),
+                             type->name, type->revision);
+               break;
        default:
                break;
        }
@@ -1042,6 +1077,22 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
                        safe_dprintf(session, arg->print, arg->opt);
                        safe_snprintf(session, "</%s>", arg->name[0]);
                        break;
+               case IPSET_LIST_JSON:
+                       if (arg->has_arg == IPSET_NO_ARG) {
+                               safe_snprintf(session,
+                                             "      \"%s\" : true,\n", arg->name[0]);
+                               break;
+                       }
+                       if (arg->opt == IPSET_OPT_FAMILY) {
+                               safe_snprintf(session, "      \"%s\" : \"", arg->name[0]);
+                               safe_dprintf(session, arg->print, arg->opt);
+                               safe_snprintf(session, "\",\n", arg->name[0]);
+                               break;
+                       }
+                       safe_snprintf(session, "      \"%s\" : ", arg->name[0]);
+                       safe_dprintf(session, arg->print, arg->opt);
+                       safe_snprintf(session, ",\n", arg->name[0]);
+                       break;
                default:
                        break;
                }
@@ -1079,6 +1130,21 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
                        "</header>\n" :
                        "</header>\n<members>\n");
                break;
+       case IPSET_LIST_JSON:
+               safe_snprintf(session, "      \"memsize\" : ");
+               safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
+               safe_snprintf(session, ",\n      \"references\" : ");
+               safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
+               if (ipset_data_test(data, IPSET_OPT_ELEMENTS)) {
+                       safe_snprintf(session, ",\n      \"numentries\" : ");
+                       safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
+               }
+               safe_snprintf(session, "\n");
+               safe_snprintf(session,
+                       session->envopts & IPSET_ENV_LIST_HEADER ?
+                       "    },\n" :
+                       "    },\n    \"members\" : [");
+               break;
        default:
                break;
        }
@@ -1214,11 +1280,24 @@ print_set_done(struct ipset_session *session, bool callback_done)
                if (session->saved_setname[0] != '\0')
                        safe_snprintf(session, "</members>\n</ipset>\n");
                break;
+       case IPSET_LIST_JSON:
+               if (session->envopts & IPSET_ENV_LIST_SETNAME)
+                       break;
+               if (session->envopts & IPSET_ENV_LIST_HEADER) {
+                       if (session->saved_setname[0] != '\0')
+                               safe_snprintf(session, "    }");
+                       break;
+               }
+               if (session->saved_setname[0] != '\0')
+                       safe_snprintf(session, "\n    ]\n  }");
+               break;
        default:
                break;
        }
        if (callback_done && session->mode == IPSET_LIST_XML)
                safe_snprintf(session, "</ipsets>\n");
+       if (callback_done && session->mode == IPSET_LIST_JSON)
+               safe_snprintf(session, "\n]\n");
        return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP;
 }
 
@@ -1245,6 +1324,9 @@ callback_list(struct ipset_session *session, struct nlattr *nla[],
                if (session->mode == IPSET_LIST_XML)
                        safe_snprintf(session, "<ipset name=\"%s\"/>\n",
                                      ipset_data_setname(data));
+               if (session->mode == IPSET_LIST_JSON)
+                       safe_snprintf(session, "\"name\" : \"%s\"\n",
+                                     ipset_data_setname(data));
                else
                        safe_snprintf(session, "%s\n",
                                      ipset_data_setname(data));
@@ -2208,6 +2290,11 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
            session->mode == IPSET_LIST_XML)
                safe_snprintf(session, "<ipsets>\n");
 
+       /* Start the root element in json mode */
+       if ((cmd == IPSET_CMD_LIST || cmd == IPSET_CMD_SAVE) &&
+           session->mode == IPSET_LIST_JSON)
+               safe_snprintf(session, "[\n");
+
        D("next: build_msg");
        /* Build new message or append buffered commands */
        ret = build_msg(session, aggregate);
index f9a880b9d7fc769922396a16107b91136d90d9de..04febdadcde04770ae3195fe09b886a0cac74ad4 100644 (file)
@@ -21,7 +21,7 @@ ipset \(em administration tool for IP sets
 .PP
 COMMANDS := { \fBcreate\fR | \fBadd\fR | \fBdel\fR | \fBtest\fR | \fBdestroy\fR | \fBlist\fR | \fBsave\fR | \fBrestore\fR | \fBflush\fR | \fBrename\fR | \fBswap\fR | \fBhelp\fR | \fBversion\fR | \fB\-\fR }
 .PP
-\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR | \fB\-name\fR | \fB\-terse\fR | \fB\-file\fR \fIfilename\fR }
+\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fBjson\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR | \fB\-name\fR | \fB\-terse\fR | \fB\-file\fR \fIfilename\fR }
 .PP
 \fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ]
 .PP
@@ -118,7 +118,7 @@ option is given, the entries are listed/saved sorted (which may be slow).
 The option
 \fB\-output\fR
 can be used to control the format of the listing:
-\fBplain\fR, \fBsave\fR or \fBxml\fR.
+\fBplain\fR, \fBsave\fR, \fBxml\fR or \fBjson\fR.
 (The default is
 \fBplain\fR.)
 If the option
@@ -187,7 +187,7 @@ cannot be abbreviated.
 Ignore errors when exactly the same set is to be created or already
 added entry is added or missing entry is deleted.
 .TP 
-\fB\-o\fP, \fB\-output\fP { \fBplain\fR | \fBsave\fR | \fBxml\fR }
+\fB\-o\fP, \fB\-output\fP { \fBplain\fR | \fBsave\fR | \fBxml\fR | \fBjson\fR }
 Select the output format to the
 \fBlist\fR
 command.
index 55433b88555c4648a290dea8740206cf4c56bb60..5b4a1d7fce8f3dd6efbd35bc5bb57ee191c17389 100644 (file)
--- a/src/ui.c
+++ b/src/ui.c
@@ -180,7 +180,7 @@ const struct ipset_envopts ipset_envopts[] = {
        { .name = { "-o", "-output" },
          .has_arg = IPSET_MANDATORY_ARG,       .flag = IPSET_OPT_MAX,
          .parse = ipset_parse_output,
-         .help = "plain|save|xml\n"
+         .help = "plain|save|xml|json\n"
                  "       Specify output mode for listing sets.\n"
                  "       Default value for \"list\" command is mode \"plain\"\n"
                  "       and for \"save\" command is mode \"save\".",
index d258be234806396a5da14963b0c7a0caaae0e823..17694e06f49cae4d7da6efb3a3ddb50b3561e665 100644 (file)
@@ -1130,9 +1130,9 @@ if [[ $prev = @(-o|-output) ]]; then
     # make sure it's not a filename named -o or -output
     if [[ $str_filename != $prev ]]; then
         if ((names_only || headers_only)); then
-            COMPREPLY=( $( compgen -W 'plain xml' -- "$cur" ) )
+            COMPREPLY=( $( compgen -W 'plain xml json' -- "$cur" ) )
         else
-            COMPREPLY=( $( compgen -W 'plain save xml' -- "$cur" ) )
+            COMPREPLY=( $( compgen -W 'plain save xml json' -- "$cur" ) )
         fi
         return 0
     fi