Re: [libvirt PATCHv2 10/15] nss: convert findLeases to use json-c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Sep 05, 2024 at 15:49:37 +0200, Ján Tomko wrote:
> 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@xxxxxxxxxx>
> ---
>  tools/nss/libvirt_nss_leases.c | 370 +++++++++++----------------------
>  1 file changed, 119 insertions(+), 251 deletions(-)

[...]

> @@ -156,190 +128,120 @@ appendAddr(const char *name __attribute__((unused)),
>  }

Subsequent hunks have the deletions trimmed to just show the new code.

>  
>  
> +/**
> + * findLeaseInJSON
> + *
> + * @jobj: the json object containing the leases
> + * @name: the requested hostname (optional if a MAC address is present)
> + * @macs: the array of MAC addresses we're matching (optional if we have a hostname)
> + * @nmacs: the size of the MAC array
> + * @af: the requested address family
> + * @now: current time (to eliminate expired leases)
> + * @addrs: the returned matching addresses
> + * @naddrs: size of the returned array
> + * @found: whether a match was found
> + *
> + * Returns 0 even if nothing was found
> + *        -1 on error
> + */
>  static int
> +findLeaseInJSON(json_object *jobj,
> +                const char *name,
> +                char **macs,
> +                size_t nmacs,
> +                int af,
> +                time_t now,
> +                leaseAddress **addrs,
> +                size_t *naddrs,
> +                bool *found)
>  {
>      size_t i;
> +    int len;
>  
> +    if (!json_object_is_type(jobj, json_type_array)) {
> +        ERROR("parsed JSON does not contain the leases array");
> +        return -1;
>      }
>  
> +    len = json_object_array_length(jobj);
> +    for (i = 0; i < len; i++) {
> +        json_object *lease = NULL;
> +        json_object *expiry = NULL;
> +        json_object *ipobj = NULL;
> +        unsigned long long expiryTime;
> +        const char *ipaddr;
> +
> +        lease = json_object_array_get_idx(jobj, i);
> +
> +        if (macs) {
> +            const char *macAddr;
> +            bool match = false;
> +            json_object *val;
> +            size_t j;
> +
> +            val = json_object_object_get(lease, "mac-address");
> +            if (!val)
> +                continue;
> +
> +            macAddr = json_object_get_string(val);
> +            if (!macAddr)
> +                continue;
> +
> +            for (j = 0; j < nmacs; j++) {
> +                if (!strcmp(macs[j], macAddr)) {

strcmp(..) != 0

> +                    match = true;
> +                    break;
> +                }
> +            }
> +            if (!match)
> +                continue;
> +        } else {
> +            const char *leaseName;
> +            json_object *val;
> +
> +            val = json_object_object_get(lease, "hostname");
> +            if (!val)
> +                continue;
> +
> +            leaseName = json_object_get_string(val);
> +            if (!leaseName)
> +                continue;
> +
> +            if (strcasecmp(leaseName, name) != 0)
> +                continue;
> +        }
>  
> +        expiry = json_object_object_get(lease, "expiry-time");
> +        if (!expiry) {
> +            ERROR("Missing expiry time for %s", name);
> +            return -1;
> +        }
>  
> +        expiryTime = json_object_get_uint64(expiry);
> +        if (expiryTime > 0 && expiryTime < now) {
> +            DEBUG("Skipping expired lease for %s", name);
> +            continue;
> +        }
>  
> +        ipobj = json_object_object_get(lease, "ip-address");
> +        if (!ipobj) {
> +            DEBUG("Missing IP address for %s", name);
> +            continue;
> +        }
> +        ipaddr = json_object_get_string(ipobj);
>  
> +        DEBUG("Found record for %s", name);
> +        *found = true;
>  
> +        if (appendAddr(name,
> +                       addrs, naddrs,
> +                       ipaddr,
> +                       expiryTime,
> +                       af) < 0)
> +            return -1;
>      }
>  
> +    return 0;
>  }
>  



>  
> @@ -356,30 +258,10 @@ findLeases(const char *file,
>  {
>      int fd = -1;
>      int ret = -1;
> +    json_object *jobj = NULL;
> +    json_tokener *tok = NULL;
> +    enum json_tokener_error jerr;
> +    int jsonflags = JSON_TOKENER_STRICT | JSON_TOKENER_VALIDATE_UTF8;
>      char line[1024];
>      ssize_t nreadTotal = 0;
>      int rv;
> @@ -389,51 +271,37 @@ findLeases(const char *file,
>          goto cleanup;
>      }
>  
> +    tok = json_tokener_new();
> +    json_tokener_set_flags(tok, jsonflags);
>  
> +    do {
> +        rv = read(fd, line, sizeof(line) - 1);

-1 should not be needed here

>          if (rv < 0)
>              goto cleanup;
>          if (rv == 0)
>              break;

So if you get an incomplete JSON document of exactly sizeof(len) bytes,
this will go through the first loop adn thend break out ...

>          nreadTotal += rv;
>  
> +        jobj = json_tokener_parse_ex(tok, line, rv);
> +        jerr = json_tokener_get_error(tok);
> +    } while (jerr == json_tokener_continue);
>  
> +    if (nreadTotal > 0 && jerr != json_tokener_success) {

... this condition will match as we read something and 'jerr' is still

'json_tokener_continue'

> +        ERROR("Cannot parse %s: %s", file, json_tokener_error_desc(jerr));

So this error will read:

  Cannot parse /path/to/file: continue

Thus you also need to specialcase also json_tokener_continue case to
state that the file is incomplete.

>          goto cleanup;
>      }
>  
> +    ret = findLeaseInJSON(jobj, name, macs, nmacs, af, now,
> +                          addrs, naddrs, found);
>  
>   cleanup:
> +    json_object_put(jobj);
> +    json_tokener_free(tok);
>      if (ret != 0) {
>          free(*addrs);
>          *addrs = NULL;
>          *naddrs = 0;
>      }
>      if (fd != -1)
>          close(fd);
>      return ret;

With the corner case fixed:

Reviewed-by: Peter Krempa <pkrempa@xxxxxxxxxx>




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux