]> xenbits.xensource.com Git - libvirt.git/commitdiff
nss: convert findMACs to use json-c
authorJán Tomko <jtomko@redhat.com>
Wed, 14 Aug 2024 15:50:38 +0000 (17:50 +0200)
committerJán Tomko <jtomko@redhat.com>
Tue, 24 Sep 2024 06:24:00 +0000 (08:24 +0200)
While the parsing is still done by 1K buffers, the results
are no longer filtered during the parsing, but the whole JSON
has to live in memory at once, which was also the case before
the NSS plugin dropped its dependency on libvirt_util.

Also, the new parser might be more forgiving of missing elements.

Signed-off-by: Ján Tomko <jtomko@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
tools/nss/libvirt_nss_macs.c

index f45d149793d6a2dd7930ccfd75d2320b50e0e7d7..430023abec07f07b3baf33456cdf4d523e98120c 100644 (file)
 #include <stdlib.h>
 #include <fcntl.h>
 
-#include <yajl/yajl_gen.h>
-#include <yajl/yajl_parse.h>
+#include <json.h>
 
 #include "libvirt_nss_macs.h"
 #include "libvirt_nss.h"
 
-enum {
-    FIND_MACS_STATE_START,
-    FIND_MACS_STATE_LIST,
-    FIND_MACS_STATE_ENTRY,
-    FIND_MACS_STATE_ENTRY_MACS,
-};
-
-typedef struct {
-    const char *name;
-    char ***macs;
-    size_t *nmacs;
-    int state;
-
-    char *key;
-    struct {
-        char *name;
-        char **macs;
-        size_t nmacs;
-    } entry;
-} findMACsParser;
-
 
+/**
+ * findMACsFromJSON
+ *
+ * @jobj: JSON object containing the leases
+ * @name: requested hostname
+ * @macs: returned array of MAC addresses leased to the hostname
+ * @nmacs: size of the returned array
+ */
 static int
-findMACsParserString(void *ctx,
-                     const unsigned char *stringVal,
-                     size_t stringLen)
+findMACsFromJSON(json_object *jobj,
+                 const char *name,
+                 char ***macs,
+                 size_t *nmacs)
 {
-    findMACsParser *parser = ctx;
-
-    DEBUG("Parse string state=%d '%.*s' (map key '%s')",
-          parser->state, (int)stringLen, (const char *)stringVal,
-          NULLSTR(parser->key));
-    if (!parser->key)
-        return 0;
-
-    if (parser->state == FIND_MACS_STATE_ENTRY) {
-        if (strcmp(parser->key, "domain"))
-            return 1;
-
-        free(parser->entry.name);
-        if (!(parser->entry.name = strndup((char *)stringVal, stringLen)))
-            return 0;
-    } else if (parser->state == FIND_MACS_STATE_ENTRY_MACS) {
-        char **macs;
-        if (strcmp(parser->key, "macs"))
-            return 1;
-
-        if (!(macs = realloc(parser->entry.macs,
-                             sizeof(char *) * (parser->entry.nmacs + 1))))
-            return 0;
+    size_t i;
+    int len;
 
-        parser->entry.macs = macs;
-        if (!(macs[parser->entry.nmacs++] = strndup((char *)stringVal, stringLen)))
-            return 0;
-    } else {
-        return 0;
+    if (!json_object_is_type(jobj, json_type_array)) {
+        ERROR("parsed JSON does not contain the leases array");
+        return -1;
     }
-    return 1;
-}
-
-
-static int
-findMACsParserMapKey(void *ctx,
-                     const unsigned char *stringVal,
-                     size_t stringLen)
-{
-    findMACsParser *parser = ctx;
 
-    DEBUG("Parse map key state=%d '%.*s'",
-          parser->state, (int)stringLen, (const char *)stringVal);
+    len = json_object_array_length(jobj);
+    DEBUG("Found an array of length: %zu", len);
+    for (i = 0; i < len; i++) {
+        json_object *entry = NULL;
+        json_object *domain = NULL;
+        const char *domainName;
+        char **tmpMacs = NULL;
+        size_t newmacs = 0;
+        json_object *macsArray = NULL;
+        size_t j;
 
-    free(parser->key);
-    if (!(parser->key = strndup((char *)stringVal, stringLen)))
-        return 0;
+        entry = json_object_array_get_idx(jobj, i);
+        if (!entry)
+            continue;
 
-    return 1;
-}
+        DEBUG("Processing item %zu", i);
 
+        domain = json_object_object_get(entry, "domain");
+        if (!domain)
+            continue;
 
-static int
-findMACsParserStartMap(void *ctx)
-{
-    findMACsParser *parser = ctx;
+        domainName = json_object_get_string(domain);
+        if (!domainName)
+            continue;
 
-    DEBUG("Parse start map state=%d", parser->state);
+        DEBUG("Processing domain %s", domainName);
 
-    if (parser->state != FIND_MACS_STATE_LIST)
-        return 0;
+        if (strcasecmp(domainName, name))
+            continue;
 
-    free(parser->key);
-    parser->key = NULL;
-    parser->state = FIND_MACS_STATE_ENTRY;
+        macsArray = json_object_object_get(entry, "macs");
+        if (!macsArray)
+            continue;
 
-    return 1;
-}
+        newmacs = json_object_array_length(macsArray);
+        DEBUG("Found %zu MAC addresses", newmacs);
 
+        tmpMacs = realloc(*macs, sizeof(char *) * (*nmacs + newmacs + 1));
+        if (!tmpMacs)
+            return -1;
 
-static int
-findMACsParserEndMap(void *ctx)
-{
-    findMACsParser *parser = ctx;
-    size_t i;
-
-    DEBUG("Parse end map state=%d", parser->state);
+        *macs = tmpMacs;
 
-    if (parser->entry.name == NULL)
-        return 0;
+        for (j = 0; j < newmacs; j++) {
+            json_object *macobj = NULL;
+            char *macstr;
 
-    if (parser->state != FIND_MACS_STATE_ENTRY)
-        return 0;
-
-    if (!strcasecmp(parser->entry.name, parser->name)) {
-        char **macs = realloc(*parser->macs,
-                              sizeof(char *) * ((*parser->nmacs) + parser->entry.nmacs));
-        if (!macs)
-            return 0;
-
-        *parser->macs = macs;
-        for (i = 0; i < parser->entry.nmacs; i++)
-            (*parser->macs)[(*parser->nmacs)++] = parser->entry.macs[i];
-    } else {
-        for (i = 0; i < parser->entry.nmacs; i++)
-            free(parser->entry.macs[i]);
+            macobj = json_object_array_get_idx(macsArray, j);
+            macstr = strdup(json_object_get_string(macobj));
+            if (!macstr)
+                return -1;
+            (*macs)[(*nmacs)++] = macstr;
+        }
     }
-    free(parser->entry.macs);
-    parser->entry.macs = NULL;
-    parser->entry.nmacs = 0;
-
-    parser->state = FIND_MACS_STATE_LIST;
-
-    return 1;
-}
-
-
-static int
-findMACsParserStartArray(void *ctx)
-{
-    findMACsParser *parser = ctx;
-
-    DEBUG("Parse start array state=%d", parser->state);
-
-    if (parser->state == FIND_MACS_STATE_START)
-        parser->state = FIND_MACS_STATE_LIST;
-    else if (parser->state == FIND_MACS_STATE_ENTRY)
-        parser->state = FIND_MACS_STATE_ENTRY_MACS;
-    else
-        return 0;
-
-    return 1;
-}
-
-
-static int
-findMACsParserEndArray(void *ctx)
-{
-    findMACsParser *parser = ctx;
-
-    DEBUG("Parse end array state=%d", parser->state);
-
-    if (parser->state == FIND_MACS_STATE_LIST)
-        parser->state = FIND_MACS_STATE_START;
-    else if (parser->state == FIND_MACS_STATE_ENTRY_MACS)
-        parser->state = FIND_MACS_STATE_ENTRY;
-    else
-        return 0;
-
-    return 1;
+    return 0;
 }
 
 
@@ -209,66 +119,50 @@ findMACs(const char *file,
 {
     int fd = -1;
     int ret = -1;
-    const yajl_callbacks parserCallbacks = {
-        NULL, /* null */
-        NULL, /* bool */
-        NULL, /* integer */
-        NULL, /* double */
-        NULL, /* number */
-        findMACsParserString,
-        findMACsParserStartMap,
-        findMACsParserMapKey,
-        findMACsParserEndMap,
-        findMACsParserStartArray,
-        findMACsParserEndArray,
-    };
-    findMACsParser parserState = {
-        .name = name,
-        .macs = macs,
-        .nmacs = nmacs,
-    };
-    yajl_handle parser = NULL;
     char line[1024];
-    size_t i;
+    json_object *jobj = NULL;
+    json_tokener *tok = NULL;
+    enum json_tokener_error jerr;
+    int jsonflags = JSON_TOKENER_STRICT | JSON_TOKENER_VALIDATE_UTF8;
+    ssize_t nreadTotal = 0;
     int rv;
+    size_t i;
 
     if ((fd = open(file, O_RDONLY)) < 0) {
         ERROR("Cannot open %s", file);
         goto cleanup;
     }
 
-    parser = yajl_alloc(&parserCallbacks, NULL, &parserState);
-    if (!parser) {
-        ERROR("Unable to create JSON parser");
-        goto cleanup;
-    }
+    tok = json_tokener_new();
+    json_tokener_set_flags(tok, jsonflags);
 
-    while (1) {
+    do {
         rv = read(fd, line, sizeof(line));
         if (rv < 0)
             goto cleanup;
         if (rv == 0)
             break;
+        nreadTotal += rv;
 
-        if (yajl_parse(parser, (const unsigned char *)line, rv)  !=
-            yajl_status_ok) {
-            unsigned char *err = yajl_get_error(parser, 1,
-                                                (const unsigned char*)line, rv);
-            ERROR("Parse failed %s", (const char *) err);
-            yajl_free_error(parser, err);
-            goto cleanup;
-        }
+        jobj = json_tokener_parse_ex(tok, line, rv);
+        jerr = json_tokener_get_error(tok);
+    } while (jerr == json_tokener_continue);
+
+    if (jerr == json_tokener_continue) {
+        ERROR("Cannot parse %s: incomplete json found", file);
+        goto cleanup;
     }
 
-    if (yajl_complete_parse(parser) != yajl_status_ok) {
-        ERROR("Parse failed %s",
-              yajl_get_error(parser, 1, NULL, 0));
+    if (nreadTotal > 0 && jerr != json_tokener_success) {
+        ERROR("Cannot parse %s: %s", file, json_tokener_error_desc(jerr));
         goto cleanup;
     }
 
-    ret = 0;
+    ret = findMACsFromJSON(jobj, name, macs, nmacs);
 
  cleanup:
+    json_object_put(jobj);
+    json_tokener_free(tok);
     if (ret != 0) {
         for (i = 0; i < *nmacs; i++) {
             char *mac = (*macs)[i];
@@ -278,13 +172,6 @@ findMACs(const char *file,
         *macs = NULL;
         *nmacs = 0;
     }
-    if (parser)
-        yajl_free(parser);
-    for (i = 0; i < parserState.entry.nmacs; i++)
-        free(parserState.entry.macs[i]);
-    free(parserState.entry.macs);
-    free(parserState.entry.name);
-    free(parserState.key);
     if (fd != -1)
         close(fd);
     return ret;