mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 05:35:25 +00:00
nss: convert findMACs to use json-c
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>
This commit is contained in:
parent
a8d828c88b
commit
84198ad88a
@ -25,179 +25,89 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include <yajl/yajl_gen.h>
|
#include <json.h>
|
||||||
#include <yajl/yajl_parse.h>
|
|
||||||
|
|
||||||
#include "libvirt_nss_macs.h"
|
#include "libvirt_nss_macs.h"
|
||||||
#include "libvirt_nss.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
|
static int
|
||||||
findMACsParserString(void *ctx,
|
findMACsFromJSON(json_object *jobj,
|
||||||
const unsigned char *stringVal,
|
const char *name,
|
||||||
size_t stringLen)
|
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;
|
|
||||||
|
|
||||||
parser->entry.macs = macs;
|
|
||||||
if (!(macs[parser->entry.nmacs++] = strndup((char *)stringVal, stringLen)))
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
|
|
||||||
free(parser->key);
|
|
||||||
if (!(parser->key = strndup((char *)stringVal, stringLen)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
findMACsParserStartMap(void *ctx)
|
|
||||||
{
|
|
||||||
findMACsParser *parser = ctx;
|
|
||||||
|
|
||||||
DEBUG("Parse start map state=%d", parser->state);
|
|
||||||
|
|
||||||
if (parser->state != FIND_MACS_STATE_LIST)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
free(parser->key);
|
|
||||||
parser->key = NULL;
|
|
||||||
parser->state = FIND_MACS_STATE_ENTRY;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
findMACsParserEndMap(void *ctx)
|
|
||||||
{
|
|
||||||
findMACsParser *parser = ctx;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
int len;
|
||||||
|
|
||||||
DEBUG("Parse end map state=%d", parser->state);
|
if (!json_object_is_type(jobj, json_type_array)) {
|
||||||
|
ERROR("parsed JSON does not contain the leases array");
|
||||||
if (parser->entry.name == NULL)
|
return -1;
|
||||||
return 0;
|
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
free(parser->entry.macs);
|
|
||||||
parser->entry.macs = NULL;
|
|
||||||
parser->entry.nmacs = 0;
|
|
||||||
|
|
||||||
parser->state = FIND_MACS_STATE_LIST;
|
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;
|
||||||
|
|
||||||
return 1;
|
entry = json_object_array_get_idx(jobj, i);
|
||||||
}
|
if (!entry)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DEBUG("Processing item %zu", i);
|
||||||
|
|
||||||
static int
|
domain = json_object_object_get(entry, "domain");
|
||||||
findMACsParserStartArray(void *ctx)
|
if (!domain)
|
||||||
{
|
continue;
|
||||||
findMACsParser *parser = ctx;
|
|
||||||
|
|
||||||
DEBUG("Parse start array state=%d", parser->state);
|
domainName = json_object_get_string(domain);
|
||||||
|
if (!domainName)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (parser->state == FIND_MACS_STATE_START)
|
DEBUG("Processing domain %s", domainName);
|
||||||
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;
|
if (strcasecmp(domainName, name))
|
||||||
}
|
continue;
|
||||||
|
|
||||||
|
macsArray = json_object_object_get(entry, "macs");
|
||||||
|
if (!macsArray)
|
||||||
|
continue;
|
||||||
|
|
||||||
static int
|
newmacs = json_object_array_length(macsArray);
|
||||||
findMACsParserEndArray(void *ctx)
|
DEBUG("Found %zu MAC addresses", newmacs);
|
||||||
{
|
|
||||||
findMACsParser *parser = ctx;
|
|
||||||
|
|
||||||
DEBUG("Parse end array state=%d", parser->state);
|
tmpMacs = realloc(*macs, sizeof(char *) * (*nmacs + newmacs + 1));
|
||||||
|
if (!tmpMacs)
|
||||||
|
return -1;
|
||||||
|
|
||||||
if (parser->state == FIND_MACS_STATE_LIST)
|
*macs = tmpMacs;
|
||||||
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;
|
for (j = 0; j < newmacs; j++) {
|
||||||
|
json_object *macobj = NULL;
|
||||||
|
char *macstr;
|
||||||
|
|
||||||
|
macobj = json_object_array_get_idx(macsArray, j);
|
||||||
|
macstr = strdup(json_object_get_string(macobj));
|
||||||
|
if (!macstr)
|
||||||
|
return -1;
|
||||||
|
(*macs)[(*nmacs)++] = macstr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -209,66 +119,50 @@ findMACs(const char *file,
|
|||||||
{
|
{
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
int ret = -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];
|
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;
|
int rv;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
if ((fd = open(file, O_RDONLY)) < 0) {
|
if ((fd = open(file, O_RDONLY)) < 0) {
|
||||||
ERROR("Cannot open %s", file);
|
ERROR("Cannot open %s", file);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser = yajl_alloc(&parserCallbacks, NULL, &parserState);
|
tok = json_tokener_new();
|
||||||
if (!parser) {
|
json_tokener_set_flags(tok, jsonflags);
|
||||||
ERROR("Unable to create JSON parser");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
do {
|
||||||
rv = read(fd, line, sizeof(line));
|
rv = read(fd, line, sizeof(line));
|
||||||
if (rv < 0)
|
if (rv < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
if (rv == 0)
|
if (rv == 0)
|
||||||
break;
|
break;
|
||||||
|
nreadTotal += rv;
|
||||||
|
|
||||||
if (yajl_parse(parser, (const unsigned char *)line, rv) !=
|
jobj = json_tokener_parse_ex(tok, line, rv);
|
||||||
yajl_status_ok) {
|
jerr = json_tokener_get_error(tok);
|
||||||
unsigned char *err = yajl_get_error(parser, 1,
|
} while (jerr == json_tokener_continue);
|
||||||
(const unsigned char*)line, rv);
|
|
||||||
ERROR("Parse failed %s", (const char *) err);
|
|
||||||
yajl_free_error(parser, err);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (yajl_complete_parse(parser) != yajl_status_ok) {
|
if (jerr == json_tokener_continue) {
|
||||||
ERROR("Parse failed %s",
|
ERROR("Cannot parse %s: incomplete json found", file);
|
||||||
yajl_get_error(parser, 1, NULL, 0));
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
if (nreadTotal > 0 && jerr != json_tokener_success) {
|
||||||
|
ERROR("Cannot parse %s: %s", file, json_tokener_error_desc(jerr));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = findMACsFromJSON(jobj, name, macs, nmacs);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
json_object_put(jobj);
|
||||||
|
json_tokener_free(tok);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
for (i = 0; i < *nmacs; i++) {
|
for (i = 0; i < *nmacs; i++) {
|
||||||
char *mac = (*macs)[i];
|
char *mac = (*macs)[i];
|
||||||
@ -278,13 +172,6 @@ findMACs(const char *file,
|
|||||||
*macs = NULL;
|
*macs = NULL;
|
||||||
*nmacs = 0;
|
*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)
|
if (fd != -1)
|
||||||
close(fd);
|
close(fd);
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
Reference in New Issue
Block a user