#include <stdbool.h>
#include <fcntl.h>
-#include <yajl/yajl_gen.h>
-#include <yajl/yajl_parse.h>
+#include <json.h>
#include "libvirt_nss_leases.h"
#include "libvirt_nss.h"
-enum {
- FIND_LEASES_STATE_START,
- FIND_LEASES_STATE_LIST,
- FIND_LEASES_STATE_ENTRY,
-};
-
-
-typedef struct {
- const char *name;
- char **macs;
- size_t nmacs;
- int state;
- unsigned long long now;
- int af;
- bool *found;
- leaseAddress **addrs;
- size_t *naddrs;
-
- char *key;
- struct {
- unsigned long long expiry;
- char *ipaddr;
- char *macaddr;
- char *hostname;
- } entry;
-} findLeasesParser;
-
static int
appendAddr(const char *name __attribute__((unused)),
}
+/**
+ * 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
-findLeasesParserInteger(void *ctx,
- long long val)
+findLeaseInJSON(json_object *jobj,
+ const char *name,
+ char **macs,
+ size_t nmacs,
+ int af,
+ time_t now,
+ leaseAddress **addrs,
+ size_t *naddrs,
+ bool *found)
{
- findLeasesParser *parser = ctx;
-
- DEBUG("Parse int state=%d '%lld' (map key '%s')",
- parser->state, val, NULLSTR(parser->key));
- if (!parser->key)
- return 0;
-
- if (parser->state == FIND_LEASES_STATE_ENTRY) {
- if (strcmp(parser->key, "expiry-time"))
- return 0;
+ size_t i;
+ int len;
- parser->entry.expiry = val;
- } 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
-findLeasesParserString(void *ctx,
- const unsigned char *stringVal,
- size_t stringLen)
-{
- findLeasesParser *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_LEASES_STATE_ENTRY) {
- if (!strcmp(parser->key, "ip-address")) {
- if (!(parser->entry.ipaddr = strndup((char *)stringVal, stringLen)))
- return 0;
- } else if (!strcmp(parser->key, "mac-address")) {
- if (!(parser->entry.macaddr = strndup((char *)stringVal, stringLen)))
- return 0;
- } else if (!strcmp(parser->key, "hostname")) {
- if (!(parser->entry.hostname = strndup((char *)stringVal, stringLen)))
- return 0;
+ 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) == 0) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ continue;
} else {
- return 1;
- }
- } else {
- return 0;
- }
- return 1;
-}
+ const char *leaseName;
+ json_object *val;
+ val = json_object_object_get(lease, "hostname");
+ if (!val)
+ continue;
-static int
-findLeasesParserMapKey(void *ctx,
- const unsigned char *stringVal,
- size_t stringLen)
-{
- findLeasesParser *parser = ctx;
-
- DEBUG("Parse map key state=%d '%.*s'",
- parser->state, (int)stringLen, (const char *)stringVal);
+ leaseName = json_object_get_string(val);
+ if (!leaseName)
+ continue;
- free(parser->key);
- if (!(parser->key = strndup((char *)stringVal, stringLen)))
- return 0;
-
- return 1;
-}
-
-
-static int
-findLeasesParserStartMap(void *ctx)
-{
- findLeasesParser *parser = ctx;
-
- DEBUG("Parse start map state=%d", parser->state);
-
- if (parser->state != FIND_LEASES_STATE_LIST)
- return 0;
-
- free(parser->key);
- parser->key = NULL;
- parser->state = FIND_LEASES_STATE_ENTRY;
-
- return 1;
-}
-
-
-static int
-findLeasesParserEndMap(void *ctx)
-{
- findLeasesParser *parser = ctx;
- size_t i;
- bool found = false;
-
- DEBUG("Parse end map state=%d", parser->state);
-
- if (parser->entry.macaddr == NULL)
- return 0;
-
- if (parser->state != FIND_LEASES_STATE_ENTRY)
- return 0;
-
- if (parser->nmacs) {
- DEBUG("Check %zu macs", parser->nmacs);
- for (i = 0; i < parser->nmacs && !found; i++) {
- DEBUG("Check mac '%s' vs '%s'", parser->macs[i], NULLSTR(parser->entry.macaddr));
- if (parser->entry.macaddr && !strcmp(parser->macs[i], parser->entry.macaddr))
- found = true;
+ if (strcasecmp(leaseName, name) != 0)
+ continue;
}
- } else {
- DEBUG("Check name '%s' vs '%s'", parser->name, NULLSTR(parser->entry.hostname));
- if (parser->entry.hostname && !strcasecmp(parser->name, parser->entry.hostname))
- found = true;
- }
- DEBUG("Found %d", found);
- if (parser->entry.expiry != 0 &&
- parser->entry.expiry < parser->now) {
- DEBUG("Entry expired at %llu vs now %llu",
- parser->entry.expiry, parser->now);
- found = false;
- }
- if (!parser->entry.ipaddr)
- found = false;
-
- if (found) {
- *parser->found = true;
-
- if (appendAddr(parser->name,
- parser->addrs, parser->naddrs,
- parser->entry.ipaddr,
- parser->entry.expiry,
- parser->af) < 0)
- return 0;
- }
-
- free(parser->entry.macaddr);
- free(parser->entry.ipaddr);
- free(parser->entry.hostname);
- parser->entry.expiry = 0;
- parser->entry.macaddr = NULL;
- parser->entry.ipaddr = NULL;
- parser->entry.hostname = NULL;
- parser->state = FIND_LEASES_STATE_LIST;
-
- return 1;
-}
+ 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;
+ }
-static int
-findLeasesParserStartArray(void *ctx)
-{
- findLeasesParser *parser = ctx;
+ 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("Parse start array state=%d", parser->state);
+ DEBUG("Found record for %s", name);
+ *found = true;
- if (parser->state == FIND_LEASES_STATE_START) {
- parser->state = FIND_LEASES_STATE_LIST;
- } else {
- return 0;
+ if (appendAddr(name,
+ addrs, naddrs,
+ ipaddr,
+ expiryTime,
+ af) < 0)
+ return -1;
}
- return 1;
-}
-
-
-static int
-findLeasesParserEndArray(void *ctx)
-{
- findLeasesParser *parser = ctx;
-
- DEBUG("Parse end array state=%d", parser->state);
-
- if (parser->state == FIND_LEASES_STATE_LIST)
- parser->state = FIND_LEASES_STATE_START;
- else
- return 0;
-
- return 1;
+ return 0;
}
{
int fd = -1;
int ret = -1;
- const yajl_callbacks parserCallbacks = {
- NULL, /* null */
- NULL, /* bool */
- findLeasesParserInteger,
- NULL, /* double */
- NULL, /* number */
- findLeasesParserString,
- findLeasesParserStartMap,
- findLeasesParserMapKey,
- findLeasesParserEndMap,
- findLeasesParserStartArray,
- findLeasesParserEndArray,
- };
- findLeasesParser parserState = {
- .name = name,
- .macs = macs,
- .nmacs = nmacs,
- .af = af,
- .now = now,
- .found = found,
- .addrs = addrs,
- .naddrs = naddrs,
- };
- yajl_handle parser = NULL;
+ 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;
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;
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 (nreadTotal > 0 &&
- 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 = 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 (parser)
- yajl_free(parser);
- free(parserState.entry.ipaddr);
- free(parserState.entry.macaddr);
- free(parserState.entry.hostname);
- free(parserState.key);
if (fd != -1)
close(fd);
return ret;