libvirt/src/conf/nwfilter_params.c
Eric Blake b43efdaa13 maint: avoid 'const fooPtr' in hashes
'const fooPtr' is the same as 'foo * const' (the pointer won't
change, but it's contents can).  But in general, if an interface
is trying to be const-correct, it should be using 'const foo *'
(the pointer is to data that can't be changed).

Fix up virhash to provide a const-correct interface: all actions
that don't modify the table take a const table.  Note that in
one case (virHashSearch), we actually strip const away - we aren't
modifying the contents of the table, so much as associated data
for ensuring that the code uses the table correctly (if this were
C++, it would be a case for the 'mutable' keyword).

* src/util/virhash.h (virHashKeyComparator, virHashEqual): Use
intended type.
(virHashSize, virHashTableSize, virHashLookup, virHashSearch):
Make const-correct.
* src/util/virhash.c (virHashEqualData, virHashEqual)
(virHashLookup, virHashSize, virHashTableSize, virHashSearch)
(virHashComputeKey): Fix fallout.
* src/conf/nwfilter_params.c
(virNWFilterFormatParameterNameSorter): Likewise.
* src/nwfilter/nwfilter_ebiptables_driver.c
(ebiptablesFilterOrderSort): Likewise.
* tests/virhashtest.c (testHashGetItemsCompKey)
(testHashGetItemsCompValue): Likewise.

Signed-off-by: Eric Blake <eblake@redhat.com>
2013-10-14 11:40:24 -06:00

1145 lines
30 KiB
C

/*
* nwfilter_params.c: parsing and data maintenance of filter parameters
*
* Copyright (C) 2011-2013 Red Hat, Inc.
* Copyright (C) 2010 IBM Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Stefan Berger <stefanb@us.ibm.com>
*/
#include <config.h>
#include "internal.h"
#include "viralloc.h"
#include "virerror.h"
#include "datatypes.h"
#include "nwfilter_params.h"
#include "domain_conf.h"
#include "virlog.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_NWFILTER
static bool isValidVarValue(const char *value);
static void virNWFilterVarAccessSetIntIterId(virNWFilterVarAccessPtr,
unsigned int);
static unsigned int virNWFilterVarAccessGetIntIterId(
const virNWFilterVarAccessPtr);
void
virNWFilterVarValueFree(virNWFilterVarValuePtr val)
{
size_t i;
if (!val)
return;
switch (val->valType) {
case NWFILTER_VALUE_TYPE_SIMPLE:
VIR_FREE(val->u.simple.value);
break;
case NWFILTER_VALUE_TYPE_ARRAY:
for (i = 0; i < val->u.array.nValues; i++)
VIR_FREE(val->u.array.values[i]);
VIR_FREE(val->u.array.values);
break;
case NWFILTER_VALUE_TYPE_LAST:
break;
}
VIR_FREE(val);
}
virNWFilterVarValuePtr
virNWFilterVarValueCopy(const virNWFilterVarValuePtr val)
{
virNWFilterVarValuePtr res;
size_t i;
char *str;
if (VIR_ALLOC(res) < 0)
return NULL;
res->valType = val->valType;
switch (res->valType) {
case NWFILTER_VALUE_TYPE_SIMPLE:
if (VIR_STRDUP(res->u.simple.value, val->u.simple.value) < 0)
goto err_exit;
break;
case NWFILTER_VALUE_TYPE_ARRAY:
if (VIR_ALLOC_N(res->u.array.values, val->u.array.nValues) < 0)
goto err_exit;
res->u.array.nValues = val->u.array.nValues;
for (i = 0; i < val->u.array.nValues; i++) {
if (VIR_STRDUP(str, val->u.array.values[i]) < 0)
goto err_exit;
res->u.array.values[i] = str;
}
break;
case NWFILTER_VALUE_TYPE_LAST:
break;
}
return res;
err_exit:
virNWFilterVarValueFree(res);
return NULL;
}
virNWFilterVarValuePtr
virNWFilterVarValueCreateSimple(char *value)
{
virNWFilterVarValuePtr val;
if (!isValidVarValue(value)) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Variable value contains invalid character"));
return NULL;
}
if (VIR_ALLOC(val) < 0)
return NULL;
val->valType = NWFILTER_VALUE_TYPE_SIMPLE;
val->u.simple.value = value;
return val;
}
virNWFilterVarValuePtr
virNWFilterVarValueCreateSimpleCopyValue(const char *value)
{
char *val;
if (VIR_STRDUP(val, value) < 0)
return NULL;
return virNWFilterVarValueCreateSimple(val);
}
const char *
virNWFilterVarValueGetSimple(const virNWFilterVarValuePtr val)
{
if (val->valType == NWFILTER_VALUE_TYPE_SIMPLE)
return val->u.simple.value;
return NULL;
}
const char *
virNWFilterVarValueGetNthValue(virNWFilterVarValuePtr val, unsigned int idx)
{
const char *res = NULL;
if (!val)
return NULL;
switch (val->valType) {
case NWFILTER_VALUE_TYPE_SIMPLE:
if (idx == 0)
res = val->u.simple.value;
break;
case NWFILTER_VALUE_TYPE_ARRAY:
if (idx < val->u.array.nValues)
res = val->u.array.values[idx];
break;
case NWFILTER_VALUE_TYPE_LAST:
break;
}
return res;
}
unsigned int
virNWFilterVarValueGetCardinality(const virNWFilterVarValuePtr val)
{
switch (val->valType) {
case NWFILTER_VALUE_TYPE_SIMPLE:
return 1;
break;
case NWFILTER_VALUE_TYPE_ARRAY:
return val->u.array.nValues;
break;
case NWFILTER_VALUE_TYPE_LAST:
return 0;
}
return 0;
}
bool
virNWFilterVarValueEqual(const virNWFilterVarValuePtr a,
const virNWFilterVarValuePtr b)
{
unsigned int card;
size_t i, j;
const char *s;
if (!a || !b)
return false;
card = virNWFilterVarValueGetCardinality(a);
if (card != virNWFilterVarValueGetCardinality(b))
return false;
/* brute force O(n^2) comparison */
for (i = 0; i < card; i++) {
bool eq = false;
s = virNWFilterVarValueGetNthValue(a, i);
for (j = 0; j < card; j++) {
if (STREQ_NULLABLE(s, virNWFilterVarValueGetNthValue(b, j))) {
eq = true;
break;
}
}
if (!eq)
return false;
}
return true;
}
int
virNWFilterVarValueAddValue(virNWFilterVarValuePtr val, char *value)
{
char *tmp;
int rc = -1;
switch (val->valType) {
case NWFILTER_VALUE_TYPE_SIMPLE:
/* switch to array */
tmp = val->u.simple.value;
if (VIR_ALLOC_N(val->u.array.values, 2) < 0) {
val->u.simple.value = tmp;
return -1;
}
val->valType = NWFILTER_VALUE_TYPE_ARRAY;
val->u.array.nValues = 2;
val->u.array.values[0] = tmp;
val->u.array.values[1] = value;
rc = 0;
break;
case NWFILTER_VALUE_TYPE_ARRAY:
if (VIR_EXPAND_N(val->u.array.values,
val->u.array.nValues, 1) < 0)
return -1;
val->u.array.values[val->u.array.nValues - 1] = value;
rc = 0;
break;
case NWFILTER_VALUE_TYPE_LAST:
break;
}
return rc;
}
static int
virNWFilterVarValueDelNthValue(virNWFilterVarValuePtr val, unsigned int pos)
{
switch (val->valType) {
case NWFILTER_VALUE_TYPE_SIMPLE:
return -1;
case NWFILTER_VALUE_TYPE_ARRAY:
if (pos < val->u.array.nValues) {
VIR_FREE(val->u.array.values[pos]);
val->u.array.nValues--;
if (pos < val->u.array.nValues)
memmove(&val->u.array.values[pos],
&val->u.array.values[pos + 1],
sizeof(val->u.array.values[0]) *
(val->u.array.nValues - pos));
return 0;
}
break;
case NWFILTER_VALUE_TYPE_LAST:
break;
}
return -1;
}
int
virNWFilterVarValueDelValue(virNWFilterVarValuePtr val, const char *value)
{
size_t i;
switch (val->valType) {
case NWFILTER_VALUE_TYPE_SIMPLE:
return -1;
case NWFILTER_VALUE_TYPE_ARRAY:
for (i = 0; i < val->u.array.nValues; i++)
if (STREQ(value, val->u.array.values[i]))
return virNWFilterVarValueDelNthValue(val, i);
break;
case NWFILTER_VALUE_TYPE_LAST:
break;
}
return -1;
}
void
virNWFilterVarCombIterFree(virNWFilterVarCombIterPtr ci)
{
size_t i;
if (!ci)
return;
for (i = 0; i < ci->nIter; i++)
VIR_FREE(ci->iter[i].varNames);
VIR_FREE(ci);
}
static int
virNWFilterVarCombIterGetIndexByIterId(virNWFilterVarCombIterPtr ci,
unsigned int iterId)
{
size_t i;
for (i = 0; i < ci->nIter; i++)
if (ci->iter[i].iterId == iterId)
return i;
return -1;
}
static void
virNWFilterVarCombIterEntryInit(virNWFilterVarCombIterEntryPtr cie,
unsigned int iterId)
{
memset(cie, 0, sizeof(*cie));
cie->iterId = iterId;
}
static int
virNWFilterVarCombIterAddVariable(virNWFilterVarCombIterEntryPtr cie,
virNWFilterHashTablePtr hash,
const virNWFilterVarAccessPtr varAccess)
{
virNWFilterVarValuePtr varValue;
unsigned int maxValue = 0, minValue = 0;
const char *varName = virNWFilterVarAccessGetVarName(varAccess);
varValue = virHashLookup(hash->hashTable, varName);
if (varValue == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not find value for variable '%s'"),
varName);
return -1;
}
switch (virNWFilterVarAccessGetType(varAccess)) {
case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
maxValue = virNWFilterVarAccessGetIndex(varAccess);
minValue = maxValue;
break;
case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
maxValue = virNWFilterVarValueGetCardinality(varValue) - 1;
minValue = 0;
break;
case VIR_NWFILTER_VAR_ACCESS_LAST:
return -1;
}
if (cie->nVarNames == 0) {
cie->maxValue = maxValue;
cie->minValue = minValue;
cie->curValue = minValue;
} else {
if (cie->maxValue != maxValue) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Cardinality of list items must be "
"the same for processing them in "
"parallel"));
return -1;
}
}
if (VIR_EXPAND_N(cie->varNames, cie->nVarNames, 1) < 0)
return -1;
cie->varNames[cie->nVarNames - 1] = varName;
return 0;
}
/*
* Test whether the iterator entry points to a distinguished set of entries
* that have not been seen before at one of the previous iterations.
*
* The point of this function is to eliminate duplicates.
* Example with two lists:
*
* list1 = [1,2,1]
* list2 = [1,3,1]
*
* The 1st iteration would take the 1st items of each list -> 1,1
* The 2nd iteration would take the 2nd items of each list -> 2,3
* The 3rd iteration would take the 3rd items of each list -> 1,1 but
* skip them since this pair has already been encountered in the 1st iteration
*/
static bool
virNWFilterVarCombIterEntryAreUniqueEntries(virNWFilterVarCombIterEntryPtr cie,
virNWFilterHashTablePtr hash)
{
size_t i, j;
virNWFilterVarValuePtr varValue, tmp;
const char *value;
varValue = virHashLookup(hash->hashTable, cie->varNames[0]);
if (!varValue) {
/* caller's error */
VIR_ERROR(_("hash lookup resulted in NULL pointer"));
return true;
}
value = virNWFilterVarValueGetNthValue(varValue, cie->curValue);
if (!value) {
VIR_ERROR(_("Lookup of value at index %u resulted in a NULL "
"pointer"), cie->curValue);
return true;
}
for (i = 0; i < cie->curValue; i++) {
if (STREQ(value, virNWFilterVarValueGetNthValue(varValue, i))) {
bool isSame = true;
for (j = 1; j < cie->nVarNames; j++) {
tmp = virHashLookup(hash->hashTable, cie->varNames[j]);
if (!tmp) {
/* should never occur to step on a NULL here */
return true;
}
if (!STREQ(virNWFilterVarValueGetNthValue(tmp, cie->curValue),
virNWFilterVarValueGetNthValue(tmp, i))) {
isSame = false;
break;
}
}
if (isSame)
return false;
}
}
return true;
}
/*
* Create an iterator over the contents of the given variables. All variables
* must have entries in the hash table.
* The iterator that is created processes all given variables in parallel,
* meaning it will access $ITEM1[0] and $ITEM2[0] then $ITEM1[1] and $ITEM2[1]
* up to $ITEM1[n] and $ITEM2[n]. For this to work, the cardinality of all
* processed lists must be the same.
* The notation $ITEM1 and $ITEM2 (in one rule) therefore will always have to
* process the items in parallel. This will be an implicit notation for
* $ITEM1[@0] and $ITEM2[@0] to 'lock' the two together. Future notations of
* $ITEM1[@1] and $ITEM2[@2] will make them be processed independently,
* which then would cause all combinations of the items of the two lists to
* be created.
*/
virNWFilterVarCombIterPtr
virNWFilterVarCombIterCreate(virNWFilterHashTablePtr hash,
virNWFilterVarAccessPtr *varAccess,
size_t nVarAccess)
{
virNWFilterVarCombIterPtr res;
size_t i;
unsigned int iterId;
int iterIndex = -1;
unsigned int nextIntIterId = VIR_NWFILTER_MAX_ITERID + 1;
if (VIR_ALLOC_VAR(res, virNWFilterVarCombIterEntry, 1 + nVarAccess) < 0)
return NULL;
res->hashTable = hash;
/* create the default iterator to support @0 */
iterId = 0;
res->nIter = 1;
virNWFilterVarCombIterEntryInit(&res->iter[0], iterId);
for (i = 0; i < nVarAccess; i++) {
switch (virNWFilterVarAccessGetType(varAccess[i])) {
case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
iterId = virNWFilterVarAccessGetIterId(varAccess[i]);
iterIndex = virNWFilterVarCombIterGetIndexByIterId(res, iterId);
if (iterIndex < 0) {
iterIndex = res->nIter;
virNWFilterVarCombIterEntryInit(&res->iter[iterIndex], iterId);
res->nIter++;
}
break;
case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
iterIndex = res->nIter;
virNWFilterVarAccessSetIntIterId(varAccess[i], nextIntIterId);
virNWFilterVarCombIterEntryInit(&res->iter[iterIndex],
nextIntIterId);
nextIntIterId++;
res->nIter++;
break;
case VIR_NWFILTER_VAR_ACCESS_LAST:
goto err_exit;
}
if (virNWFilterVarCombIterAddVariable(&res->iter[iterIndex],
hash, varAccess[i]) < 0)
goto err_exit;
}
return res;
err_exit:
virNWFilterVarCombIterFree(res);
return NULL;
}
virNWFilterVarCombIterPtr
virNWFilterVarCombIterNext(virNWFilterVarCombIterPtr ci)
{
size_t i;
for (i = 0; i < ci->nIter; i++) {
next:
ci->iter[i].curValue++;
if (ci->iter[i].curValue <= ci->iter[i].maxValue) {
if (!virNWFilterVarCombIterEntryAreUniqueEntries(
&ci->iter[i], ci->hashTable))
goto next;
break;
} else {
ci->iter[i].curValue = ci->iter[i].minValue;
}
}
if (ci->nIter == i) {
virNWFilterVarCombIterFree(ci);
return NULL;
}
return ci;
}
const char *
virNWFilterVarCombIterGetVarValue(virNWFilterVarCombIterPtr ci,
const virNWFilterVarAccessPtr vap)
{
size_t i;
unsigned int iterId;
bool found = false;
const char *res = NULL;
virNWFilterVarValuePtr value;
int iterIndex = -1;
const char *varName = virNWFilterVarAccessGetVarName(vap);
switch (virNWFilterVarAccessGetType(vap)) {
case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
iterId = virNWFilterVarAccessGetIterId(vap);
iterIndex = virNWFilterVarCombIterGetIndexByIterId(ci, iterId);
if (iterIndex < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get iterator index for "
"iterator ID %u"), iterId);
return NULL;
}
break;
case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
iterId = virNWFilterVarAccessGetIntIterId(vap);
iterIndex = virNWFilterVarCombIterGetIndexByIterId(ci, iterId);
if (iterIndex < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get iterator index for "
"(internal) iterator ID %u"), iterId);
return NULL;
}
break;
case VIR_NWFILTER_VAR_ACCESS_LAST:
return NULL;
}
for (i = 0; i < ci->iter[iterIndex].nVarNames; i++) {
if (STREQ(ci->iter[iterIndex].varNames[i], varName)) {
found = true;
break;
}
}
if (!found) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not find variable '%s' in iterator"),
varName);
return NULL;
}
value = virHashLookup(ci->hashTable->hashTable, varName);
if (!value) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not find value for variable '%s'"),
varName);
return NULL;
}
res = virNWFilterVarValueGetNthValue(value, ci->iter[iterIndex].curValue);
if (!res) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get nth (%u) value of "
"variable '%s'"),
ci->iter[iterIndex].curValue, varName);
return NULL;
}
return res;
}
static void
hashDataFree(void *payload, const void *name ATTRIBUTE_UNUSED)
{
virNWFilterVarValueFree(payload);
}
/**
* virNWFilterHashTablePut:
* @table: Pointer to a virNWFilterHashTable
* @name: name of the key to enter
* @val: The value associated with the key
* @freeName: Whether the name must be freed on table destruction
*
* Returns 0 on success, -1 on failure.
*
* Put an entry into the hashmap replacing and freeing an existing entry
* if one existed.
*/
int
virNWFilterHashTablePut(virNWFilterHashTablePtr table,
const char *name,
virNWFilterVarValuePtr val,
int copyName)
{
if (!virHashLookup(table->hashTable, name)) {
char *newName;
if (copyName) {
if (VIR_STRDUP(newName, name) < 0)
return -1;
if (VIR_REALLOC_N(table->names, table->nNames + 1) < 0) {
VIR_FREE(newName);
return -1;
}
table->names[table->nNames++] = newName;
}
if (virHashAddEntry(table->hashTable, name, val) < 0) {
if (copyName) {
VIR_FREE(newName);
table->nNames--;
}
return -1;
}
} else {
if (virHashUpdateEntry(table->hashTable, name, val) < 0) {
return -1;
}
}
return 0;
}
/**
* virNWFilterHashTableFree:
* @table: Pointer to virNWFilterHashTable
*
* Free a hashtable de-allocating memory for all its entries.
*
* All hash tables within the NWFilter driver must use this
* function to deallocate and free their content.
*/
void
virNWFilterHashTableFree(virNWFilterHashTablePtr table)
{
size_t i;
if (!table)
return;
virHashFree(table->hashTable);
for (i = 0; i < table->nNames; i++)
VIR_FREE(table->names[i]);
VIR_FREE(table->names);
VIR_FREE(table);
}
virNWFilterHashTablePtr
virNWFilterHashTableCreate(int n) {
virNWFilterHashTablePtr ret;
if (VIR_ALLOC(ret) < 0)
return NULL;
ret->hashTable = virHashCreate(n, hashDataFree);
if (!ret->hashTable) {
VIR_FREE(ret);
return NULL;
}
return ret;
}
void *
virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr ht,
const char *entry)
{
size_t i;
void *value = virHashSteal(ht->hashTable, entry);
if (value) {
for (i = 0; i < ht->nNames; i++) {
if (STREQ(ht->names[i], entry)) {
VIR_FREE(ht->names[i]);
ht->names[i] = ht->names[--ht->nNames];
ht->names[ht->nNames] = NULL;
break;
}
}
}
return value;
}
struct addToTableStruct {
virNWFilterHashTablePtr target;
int errOccurred;
};
static void
addToTable(void *payload, const void *name, void *data)
{
struct addToTableStruct *atts = (struct addToTableStruct *)data;
virNWFilterVarValuePtr val;
if (atts->errOccurred)
return;
val = virNWFilterVarValueCopy((virNWFilterVarValuePtr)payload);
if (!val) {
atts->errOccurred = 1;
return;
}
if (virNWFilterHashTablePut(atts->target, (const char *)name, val, 1) < 0){
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not put variable '%s' into hashmap"),
(const char *)name);
atts->errOccurred = 1;
virNWFilterVarValueFree(val);
}
}
int
virNWFilterHashTablePutAll(virNWFilterHashTablePtr src,
virNWFilterHashTablePtr dest)
{
struct addToTableStruct atts = {
.target = dest,
.errOccurred = 0,
};
virHashForEach(src->hashTable, addToTable, &atts);
if (atts.errOccurred)
goto err_exit;
return 0;
err_exit:
return -1;
}
/* The general purpose function virNWFilterVarValueEqual returns a
* bool, but the comparison callback for virHashEqual (called below)
* needs to return an int of 0 for == and non-0 for !=
*/
static int
virNWFilterVarValueCompare(const void *a, const void *b)
{
return virNWFilterVarValueEqual((const virNWFilterVarValuePtr) a,
(const virNWFilterVarValuePtr) b) ? 0 : 1;
}
bool
virNWFilterHashTableEqual(virNWFilterHashTablePtr a,
virNWFilterHashTablePtr b)
{
if (!(a || b))
return true;
if (!(a && b))
return false;
return virHashEqual(a->hashTable, b->hashTable, virNWFilterVarValueCompare);
}
static bool
isValidVarName(const char *var)
{
return var[strspn(var, VALID_VARNAME)] == 0;
}
static bool
isValidVarValue(const char *value)
{
return (value[strspn(value, VALID_VARVALUE)] == 0) && (strlen(value) != 0);
}
static virNWFilterVarValuePtr
virNWFilterParseVarValue(const char *val)
{
return virNWFilterVarValueCreateSimpleCopyValue(val);
}
virNWFilterHashTablePtr
virNWFilterParseParamAttributes(xmlNodePtr cur)
{
char *nam, *val;
virNWFilterVarValuePtr value;
virNWFilterHashTablePtr table = virNWFilterHashTableCreate(0);
if (!table)
return NULL;
cur = cur->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur->name, BAD_CAST "parameter")) {
nam = virXMLPropString(cur, "name");
val = virXMLPropString(cur, "value");
value = NULL;
if (nam != NULL && val != NULL) {
if (!isValidVarName(nam))
goto skip_entry;
if (!isValidVarValue(val))
goto skip_entry;
value = virHashLookup(table->hashTable, nam);
if (value) {
/* add value to existing value -> list */
if (virNWFilterVarValueAddValue(value, val) < 0) {
value = NULL;
goto err_exit;
}
val = NULL;
} else {
value = virNWFilterParseVarValue(val);
if (!value)
goto skip_entry;
if (virNWFilterHashTablePut(table, nam, value, 1) < 0)
goto err_exit;
}
value = NULL;
}
skip_entry:
virNWFilterVarValueFree(value);
VIR_FREE(nam);
VIR_FREE(val);
}
}
cur = cur->next;
}
return table;
err_exit:
VIR_FREE(nam);
VIR_FREE(val);
virNWFilterVarValueFree(value);
virNWFilterHashTableFree(table);
return NULL;
}
static int
virNWFilterFormatParameterNameSorter(const virHashKeyValuePair *a,
const virHashKeyValuePair *b)
{
return strcmp(a->key, b->key);
}
int
virNWFilterFormatParamAttributes(virBufferPtr buf,
virNWFilterHashTablePtr table,
const char *filterref)
{
virHashKeyValuePairPtr items;
size_t i, j;
int card, numKeys;
numKeys = virHashSize(table->hashTable);
if (numKeys < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing filter parameter table"));
return -1;
}
items = virHashGetItems(table->hashTable,
virNWFilterFormatParameterNameSorter);
if (!items)
return -1;
virBufferAsprintf(buf, "<filterref filter='%s'", filterref);
if (numKeys) {
virBufferAddLit(buf, ">\n");
for (i = 0; i < numKeys; i++) {
const virNWFilterVarValuePtr value =
(const virNWFilterVarValuePtr)items[i].value;
card = virNWFilterVarValueGetCardinality(value);
for (j = 0; j < card; j++)
virBufferAsprintf(buf,
" <parameter name='%s' value='%s'/>\n",
(const char *)items[i].key,
virNWFilterVarValueGetNthValue(value, j));
}
virBufferAddLit(buf, "</filterref>\n");
} else {
virBufferAddLit(buf, "/>\n");
}
VIR_FREE(items);
return 0;
}
void
virNWFilterVarAccessFree(virNWFilterVarAccessPtr varAccess)
{
if (!varAccess)
return;
VIR_FREE(varAccess->varName);
VIR_FREE(varAccess);
}
bool
virNWFilterVarAccessEqual(const virNWFilterVarAccessPtr a,
const virNWFilterVarAccessPtr b)
{
if (a->accessType != b->accessType)
return false;
if (STRNEQ(a->varName, b->varName))
return false;
switch (a->accessType) {
case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
return (a->u.index.index == b->u.index.index &&
a->u.index.intIterId == b->u.index.intIterId);
break;
case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
return a->u.iterId == b->u.iterId;
break;
case VIR_NWFILTER_VAR_ACCESS_LAST:
break;
}
return false;
}
/*
* Parse a variable access like
* IP, IP[@2], IP[3]
*/
virNWFilterVarAccessPtr
virNWFilterVarAccessParse(const char *varAccess)
{
size_t idx, varNameLen;
virNWFilterVarAccessPtr dest;
const char *input = varAccess;
if (VIR_ALLOC(dest) < 0)
return NULL;
idx = strspn(input, VALID_VARNAME);
if (input[idx] == '\0') {
/* in the form 'IP', which is equivalent to IP[@0] */
if (VIR_STRNDUP(dest->varName, input, idx) < 0)
goto err_exit;
dest->accessType = VIR_NWFILTER_VAR_ACCESS_ITERATOR;
dest->u.iterId = 0;
return dest;
}
if (input[idx] == '[') {
char *end_ptr;
unsigned int result;
bool parseError = false;
varNameLen = idx;
if (VIR_STRNDUP(dest->varName, input, varNameLen) < 0)
goto err_exit;
input += idx + 1;
virSkipSpaces(&input);
if (*input == '@') {
/* in the form 'IP[@<number>] -> iterator */
dest->accessType = VIR_NWFILTER_VAR_ACCESS_ITERATOR;
input++;
} else {
/* in the form 'IP[<number>] -> element */
dest->accessType = VIR_NWFILTER_VAR_ACCESS_ELEMENT;
}
if (virStrToLong_ui(input, &end_ptr, 10, &result) < 0)
parseError = true;
if (!parseError) {
input = end_ptr;
virSkipSpaces(&input);
if (*input != ']')
parseError = true;
}
if (parseError) {
if (dest->accessType == VIR_NWFILTER_VAR_ACCESS_ELEMENT)
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Malformatted array index"));
else
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Malformatted iterator id"));
goto err_exit;
}
switch (dest->accessType) {
case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
dest->u.index.index = result;
dest->u.index.intIterId = ~0;
break;
case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
if (result > VIR_NWFILTER_MAX_ITERID) {
virReportError(VIR_ERR_INVALID_ARG,
_("Iterator ID exceeds maximum ID "
"of %u"), VIR_NWFILTER_MAX_ITERID);
goto err_exit;
}
dest->u.iterId = result;
break;
case VIR_NWFILTER_VAR_ACCESS_LAST:
goto err_exit;
}
return dest;
} else {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Malformatted variable"));
}
err_exit:
virNWFilterVarAccessFree(dest);
return NULL;
}
void
virNWFilterVarAccessPrint(virNWFilterVarAccessPtr vap, virBufferPtr buf)
{
virBufferAdd(buf, vap->varName, -1);
switch (vap->accessType) {
case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
virBufferAsprintf(buf, "[%u]", vap->u.index.index);
break;
case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
if (vap->u.iterId != 0)
virBufferAsprintf(buf, "[@%u]", vap->u.iterId);
break;
case VIR_NWFILTER_VAR_ACCESS_LAST:
break;
}
}
const char *
virNWFilterVarAccessGetVarName(const virNWFilterVarAccessPtr vap)
{
return vap->varName;
}
enum virNWFilterVarAccessType
virNWFilterVarAccessGetType(const virNWFilterVarAccessPtr vap)
{
return vap->accessType;
}
unsigned int
virNWFilterVarAccessGetIterId(const virNWFilterVarAccessPtr vap)
{
return vap->u.iterId;
}
unsigned int
virNWFilterVarAccessGetIndex(const virNWFilterVarAccessPtr vap)
{
return vap->u.index.index;
}
static void
virNWFilterVarAccessSetIntIterId(virNWFilterVarAccessPtr vap,
unsigned int intIterId)
{
vap->u.index.intIterId = intIterId;
}
static unsigned int
virNWFilterVarAccessGetIntIterId(const virNWFilterVarAccessPtr vap)
{
return vap->u.index.intIterId;
}
bool
virNWFilterVarAccessIsAvailable(const virNWFilterVarAccessPtr varAccess,
const virNWFilterHashTablePtr hash)
{
const char *varName = virNWFilterVarAccessGetVarName(varAccess);
const char *res;
unsigned int idx;
virNWFilterVarValuePtr varValue;
varValue = virHashLookup(hash->hashTable, varName);
if (!varValue)
return false;
switch (virNWFilterVarAccessGetType(varAccess)) {
case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
idx = virNWFilterVarAccessGetIndex(varAccess);
res = virNWFilterVarValueGetNthValue(varValue, idx);
if (res == NULL)
return false;
break;
case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
break;
case VIR_NWFILTER_VAR_ACCESS_LAST:
return false;
}
return true;
}