2012-01-02 22:03:19 +00:00
|
|
|
/*
|
|
|
|
* virtypedparam.c: utility functions for dealing with virTypedParameters
|
|
|
|
*
|
2013-12-28 03:26:03 +00:00
|
|
|
* Copyright (C) 2011-2014 Red Hat, Inc.
|
2012-01-02 22:03:19 +00:00
|
|
|
*
|
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2012-01-02 22:03:19 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include "virtypedparam.h"
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 17:44:57 +00:00
|
|
|
#include "virutil.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2012-01-02 22:03:19 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
2019-03-16 18:20:32 +00:00
|
|
|
VIR_ENUM_IMPL(virTypedParameter,
|
|
|
|
VIR_TYPED_PARAM_LAST,
|
2012-01-02 22:03:19 +00:00
|
|
|
"unknown",
|
|
|
|
"int",
|
|
|
|
"uint",
|
|
|
|
"llong",
|
|
|
|
"ullong",
|
|
|
|
"double",
|
|
|
|
"boolean",
|
2019-01-20 16:30:15 +00:00
|
|
|
"string",
|
|
|
|
);
|
2012-01-02 22:03:19 +00:00
|
|
|
|
2015-06-15 22:42:06 +00:00
|
|
|
static int
|
|
|
|
virTypedParamsSortName(const void *left, const void *right)
|
|
|
|
{
|
|
|
|
const virTypedParameter *param_left = left, *param_right = right;
|
|
|
|
return strcmp(param_left->field, param_right->field);
|
|
|
|
}
|
|
|
|
|
2012-01-02 22:03:19 +00:00
|
|
|
/* Validate that PARAMS contains only recognized parameter names with
|
2015-06-15 22:42:06 +00:00
|
|
|
* correct types, and with no duplicates except for parameters
|
|
|
|
* specified with VIR_TYPED_PARAM_MULTIPLE flag in type.
|
|
|
|
* Pass in as many name/type pairs as appropriate, and pass NULL to end
|
|
|
|
* the list of accepted parameters. Return 0 on success, -1 on failure
|
|
|
|
* with error message already issued. */
|
2012-01-02 22:03:19 +00:00
|
|
|
int
|
2013-05-03 13:34:10 +00:00
|
|
|
virTypedParamsValidate(virTypedParameterPtr params, int nparams, ...)
|
2012-01-02 22:03:19 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
int ret = -1;
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i, j;
|
2016-04-12 14:13:27 +00:00
|
|
|
const char *name, *last_name = NULL;
|
2012-01-02 22:03:19 +00:00
|
|
|
int type;
|
2015-06-15 22:42:06 +00:00
|
|
|
size_t nkeys = 0, nkeysalloc = 0;
|
|
|
|
virTypedParameterPtr sorted = NULL, keys = NULL;
|
2012-01-02 22:03:19 +00:00
|
|
|
|
|
|
|
va_start(ap, nparams);
|
|
|
|
|
2015-06-15 22:42:06 +00:00
|
|
|
if (VIR_ALLOC_N(sorted, nparams) < 0)
|
|
|
|
goto cleanup;
|
2012-01-02 22:03:19 +00:00
|
|
|
|
2015-06-15 22:42:06 +00:00
|
|
|
/* Here we intentionally don't copy values */
|
|
|
|
memcpy(sorted, params, sizeof(*params) * nparams);
|
|
|
|
qsort(sorted, nparams, sizeof(*sorted), virTypedParamsSortName);
|
|
|
|
|
|
|
|
name = va_arg(ap, const char *);
|
|
|
|
while (name) {
|
|
|
|
type = va_arg(ap, int);
|
|
|
|
if (VIR_RESIZE_N(keys, nkeysalloc, nkeys, 1) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2018-07-20 07:50:37 +00:00
|
|
|
if (virStrcpyStatic(keys[nkeys].field, name) < 0) {
|
2015-06-15 22:42:06 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Field name '%s' too long"), name);
|
2012-01-02 22:03:19 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2015-06-15 22:42:06 +00:00
|
|
|
|
|
|
|
keys[nkeys].type = type & ~VIR_TYPED_PARAM_MULTIPLE;
|
|
|
|
/* Value is not used anyway */
|
|
|
|
keys[nkeys].value.i = type & VIR_TYPED_PARAM_MULTIPLE;
|
|
|
|
|
|
|
|
nkeys++;
|
|
|
|
name = va_arg(ap, const char *);
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort(keys, nkeys, sizeof(*keys), virTypedParamsSortName);
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < nparams && j < nkeys;) {
|
|
|
|
if (STRNEQ(sorted[i].field, keys[j].field)) {
|
|
|
|
j++;
|
|
|
|
} else {
|
2016-04-12 14:13:27 +00:00
|
|
|
if (STREQ_NULLABLE(last_name, sorted[i].field) &&
|
|
|
|
!(keys[j].value.i & VIR_TYPED_PARAM_MULTIPLE)) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("parameter '%s' occurs multiple times"),
|
2015-06-15 22:42:06 +00:00
|
|
|
sorted[i].field);
|
2012-01-02 22:03:19 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2015-06-15 22:42:06 +00:00
|
|
|
if (sorted[i].type != keys[j].type) {
|
|
|
|
const char *badtype;
|
|
|
|
|
|
|
|
badtype = virTypedParameterTypeToString(sorted[i].type);
|
|
|
|
if (!badtype)
|
|
|
|
badtype = virTypedParameterTypeToString(0);
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("invalid type '%s' for parameter '%s', "
|
|
|
|
"expected '%s'"),
|
|
|
|
badtype, sorted[i].field,
|
|
|
|
virTypedParameterTypeToString(keys[j].type));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2016-04-12 14:13:27 +00:00
|
|
|
last_name = sorted[i].field;
|
2015-06-15 22:42:06 +00:00
|
|
|
i++;
|
2012-01-02 22:03:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-15 22:42:06 +00:00
|
|
|
if (j == nkeys && i != nparams) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
|
|
|
|
_("parameter '%s' not supported"),
|
|
|
|
sorted[i].field);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-01-02 22:03:19 +00:00
|
|
|
ret = 0;
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2012-01-02 22:03:19 +00:00
|
|
|
va_end(ap);
|
2015-06-15 22:42:06 +00:00
|
|
|
VIR_FREE(sorted);
|
|
|
|
VIR_FREE(keys);
|
2012-01-02 22:03:19 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-06-15 22:42:06 +00:00
|
|
|
|
2013-05-06 10:04:06 +00:00
|
|
|
/* Check if params contains only specified parameter names. Return true if
|
|
|
|
* only specified names are present in params, false if params contains any
|
|
|
|
* unspecified parameter name. */
|
|
|
|
bool
|
|
|
|
virTypedParamsCheck(virTypedParameterPtr params,
|
|
|
|
int nparams,
|
|
|
|
const char **names,
|
|
|
|
int nnames)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i, j;
|
2013-05-06 10:04:06 +00:00
|
|
|
|
|
|
|
for (i = 0; i < nparams; i++) {
|
|
|
|
bool found = false;
|
|
|
|
for (j = 0; j < nnames; j++) {
|
|
|
|
if (STREQ(params[i].field, names[j])) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-06-06 16:54:48 +00:00
|
|
|
char *
|
|
|
|
virTypedParameterToString(virTypedParameterPtr param)
|
|
|
|
{
|
2013-07-04 10:17:18 +00:00
|
|
|
char *value = NULL;
|
2013-06-06 16:54:48 +00:00
|
|
|
|
|
|
|
switch (param->type) {
|
|
|
|
case VIR_TYPED_PARAM_INT:
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(virAsprintf(&value, "%d", param->value.i));
|
2013-06-06 16:54:48 +00:00
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_UINT:
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(virAsprintf(&value, "%u", param->value.ui));
|
2013-06-06 16:54:48 +00:00
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_LLONG:
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(virAsprintf(&value, "%lld", param->value.l));
|
2013-06-06 16:54:48 +00:00
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_ULLONG:
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(virAsprintf(&value, "%llu", param->value.ul));
|
2013-06-06 16:54:48 +00:00
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_DOUBLE:
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(virAsprintf(&value, "%g", param->value.d));
|
2013-06-06 16:54:48 +00:00
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_BOOLEAN:
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(virAsprintf(&value, "%d", param->value.b));
|
2013-06-06 16:54:48 +00:00
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_STRING:
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(VIR_STRDUP(value, param->value.s));
|
2013-06-06 16:54:48 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected type %d for field %s"),
|
|
|
|
param->type, param->field);
|
|
|
|
}
|
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
return value;
|
2013-06-06 16:54:48 +00:00
|
|
|
}
|
|
|
|
|
2012-01-02 22:03:19 +00:00
|
|
|
|
2019-08-14 11:00:13 +00:00
|
|
|
static int
|
|
|
|
virTypedParameterAssignValueVArgs(virTypedParameterPtr param,
|
|
|
|
int type,
|
2019-08-14 12:52:46 +00:00
|
|
|
va_list ap,
|
|
|
|
bool copystr)
|
2019-08-14 11:00:13 +00:00
|
|
|
{
|
2012-01-02 22:03:19 +00:00
|
|
|
param->type = type;
|
2014-09-03 22:24:43 +00:00
|
|
|
switch (type) {
|
2012-01-02 22:03:19 +00:00
|
|
|
case VIR_TYPED_PARAM_INT:
|
|
|
|
param->value.i = va_arg(ap, int);
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_UINT:
|
|
|
|
param->value.ui = va_arg(ap, unsigned int);
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_LLONG:
|
|
|
|
param->value.l = va_arg(ap, long long int);
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_ULLONG:
|
|
|
|
param->value.ul = va_arg(ap, unsigned long long int);
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_DOUBLE:
|
|
|
|
param->value.d = va_arg(ap, double);
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_BOOLEAN:
|
|
|
|
param->value.b = !!va_arg(ap, int);
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_STRING:
|
2019-08-14 12:52:46 +00:00
|
|
|
if (copystr) {
|
|
|
|
if (VIR_STRDUP(param->value.s, va_arg(ap, char *)) < 0)
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
param->value.s = va_arg(ap, char *);
|
|
|
|
}
|
|
|
|
|
2013-05-24 07:19:51 +00:00
|
|
|
if (!param->value.s && VIR_STRDUP(param->value.s, "") < 0)
|
2019-08-14 11:00:13 +00:00
|
|
|
return -1;
|
2012-01-02 22:03:19 +00:00
|
|
|
break;
|
|
|
|
default:
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
2019-08-14 11:00:13 +00:00
|
|
|
_("unexpected type %d for field %s"), type,
|
|
|
|
NULLSTR(param->field));
|
|
|
|
return -1;
|
2012-01-02 22:03:19 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 11:00:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-19 07:20:49 +00:00
|
|
|
static int
|
|
|
|
virTypedParameterAssignValue(virTypedParameterPtr param,
|
|
|
|
bool copystr,
|
|
|
|
int type,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, type);
|
|
|
|
ret = virTypedParameterAssignValueVArgs(param, type, ap, copystr);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-14 11:00:13 +00:00
|
|
|
/* Assign name, type, and the appropriately typed arg to param; in the
|
|
|
|
* case of a string, the caller is assumed to have malloc'd a string,
|
|
|
|
* or can pass NULL to have this function malloc an empty string.
|
|
|
|
* Return 0 on success, -1 after an error message on failure. */
|
|
|
|
int
|
|
|
|
virTypedParameterAssign(virTypedParameterPtr param, const char *name,
|
|
|
|
int type, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virStrcpyStatic(param->field, name) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, _("Field name '%s' too long"),
|
|
|
|
name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
va_start(ap, type);
|
2019-08-14 12:52:46 +00:00
|
|
|
ret = virTypedParameterAssignValueVArgs(param, type, ap, false);
|
2012-01-02 22:03:19 +00:00
|
|
|
va_end(ap);
|
2019-08-14 11:00:13 +00:00
|
|
|
|
2012-01-02 22:03:19 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2012-09-06 12:47:40 +00:00
|
|
|
|
2013-01-15 13:51:45 +00:00
|
|
|
|
2013-05-21 13:11:56 +00:00
|
|
|
/**
|
|
|
|
* virTypedParamsReplaceString:
|
|
|
|
* @params: pointer to the array of typed parameters
|
|
|
|
* @nparams: number of parameters in the @params array
|
|
|
|
* @name: name of the parameter to set
|
|
|
|
* @value: the value to store into the parameter
|
|
|
|
*
|
|
|
|
* Sets new value @value to parameter called @name with char * type. If the
|
|
|
|
* parameter does not exist yet in @params, it is automatically created and
|
|
|
|
* @naprams is incremented by one. Otherwise current value of the parameter
|
|
|
|
* is freed on success. The function creates its own copy of @value string,
|
|
|
|
* which needs to be freed using virTypedParamsFree or virTypedParamsClear.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, -1 on error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virTypedParamsReplaceString(virTypedParameterPtr *params,
|
|
|
|
int *nparams,
|
|
|
|
const char *name,
|
|
|
|
const char *value)
|
|
|
|
{
|
|
|
|
char *str = NULL;
|
|
|
|
char *old = NULL;
|
|
|
|
size_t n = *nparams;
|
|
|
|
virTypedParameterPtr param;
|
|
|
|
|
|
|
|
param = virTypedParamsGet(*params, n, name);
|
|
|
|
if (param) {
|
|
|
|
if (param->type != VIR_TYPED_PARAM_STRING) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Parameter '%s' is not a string"),
|
|
|
|
param->field);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
old = param->value.s;
|
|
|
|
} else {
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_EXPAND_N(*params, n, 1) < 0)
|
2013-05-21 13:11:56 +00:00
|
|
|
goto error;
|
|
|
|
param = *params + n - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_STRDUP(str, value) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (virTypedParameterAssign(param, name,
|
|
|
|
VIR_TYPED_PARAM_STRING, str) < 0) {
|
|
|
|
param->value.s = old;
|
|
|
|
VIR_FREE(str);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
VIR_FREE(old);
|
|
|
|
|
|
|
|
*nparams = n;
|
|
|
|
return 0;
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
error:
|
2013-05-21 13:11:56 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-07 14:34:13 +00:00
|
|
|
int
|
|
|
|
virTypedParamsCopy(virTypedParameterPtr *dst,
|
|
|
|
virTypedParameterPtr src,
|
|
|
|
int nparams)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2013-06-07 14:34:13 +00:00
|
|
|
|
|
|
|
*dst = NULL;
|
|
|
|
if (!src || nparams <= 0)
|
|
|
|
return 0;
|
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_ALLOC_N(*dst, nparams) < 0)
|
2013-06-07 14:34:13 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = 0; i < nparams; i++) {
|
|
|
|
ignore_value(virStrcpyStatic((*dst)[i].field, src[i].field));
|
|
|
|
(*dst)[i].type = src[i].type;
|
|
|
|
if (src[i].type == VIR_TYPED_PARAM_STRING) {
|
|
|
|
if (VIR_STRDUP((*dst)[i].value.s, src[i].value.s) < 0) {
|
|
|
|
virTypedParamsFree(*dst, i - 1);
|
|
|
|
*dst = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(*dst)[i].value = src[i].value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-15 22:42:08 +00:00
|
|
|
/**
|
|
|
|
* virTypedParamsFilter:
|
|
|
|
* @params: array of typed parameters
|
|
|
|
* @nparams: number of parameters in the @params array
|
|
|
|
* @name: name of the parameter to find
|
|
|
|
* @ret: pointer to the returned array
|
|
|
|
*
|
|
|
|
* Filters @params retaining only the parameters named @name in the
|
|
|
|
* resulting array @ret. Caller should free the @ret array but not
|
|
|
|
* the items since they are pointing to the @params elements.
|
|
|
|
*
|
|
|
|
* Returns amount of elements in @ret on success, -1 on error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virTypedParamsFilter(virTypedParameterPtr params,
|
|
|
|
int nparams,
|
|
|
|
const char *name,
|
|
|
|
virTypedParameterPtr **ret)
|
|
|
|
{
|
2015-07-01 13:35:43 +00:00
|
|
|
size_t i, n = 0;
|
2015-06-15 22:42:08 +00:00
|
|
|
|
|
|
|
virCheckNonNullArgGoto(params, error);
|
|
|
|
virCheckNonNullArgGoto(name, error);
|
|
|
|
virCheckNonNullArgGoto(ret, error);
|
|
|
|
|
2015-07-01 13:35:43 +00:00
|
|
|
if (VIR_ALLOC_N(*ret, nparams) < 0)
|
|
|
|
goto error;
|
2015-06-15 22:42:08 +00:00
|
|
|
|
|
|
|
for (i = 0; i < nparams; i++) {
|
|
|
|
if (STREQ(params[i].field, name)) {
|
|
|
|
(*ret)[n] = ¶ms[i];
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virTypedParamsGetStringList:
|
|
|
|
* @params: array of typed parameters
|
|
|
|
* @nparams: number of parameters in the @params array
|
|
|
|
* @name: name of the parameter to find
|
|
|
|
* @values: array of returned values
|
|
|
|
*
|
|
|
|
* Finds all parameters with desired @name within @params and
|
|
|
|
* store their values into @values. The @values array is self
|
|
|
|
* allocated and its length is stored into @picked. When no
|
|
|
|
* longer needed, caller should free the returned array, but not
|
|
|
|
* the items since they are taken from @params array.
|
|
|
|
*
|
|
|
|
* Returns amount of strings in @values array on success,
|
|
|
|
* -1 otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virTypedParamsGetStringList(virTypedParameterPtr params,
|
|
|
|
int nparams,
|
|
|
|
const char *name,
|
|
|
|
const char ***values)
|
|
|
|
{
|
|
|
|
size_t i, n;
|
|
|
|
int nfiltered;
|
|
|
|
virTypedParameterPtr *filtered = NULL;
|
|
|
|
|
|
|
|
virCheckNonNullArgGoto(values, error);
|
|
|
|
*values = NULL;
|
|
|
|
|
|
|
|
nfiltered = virTypedParamsFilter(params, nparams, name, &filtered);
|
|
|
|
|
|
|
|
if (nfiltered < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (nfiltered &&
|
|
|
|
VIR_ALLOC_N(*values, nfiltered) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
for (n = 0, i = 0; i < nfiltered; i++) {
|
|
|
|
if (filtered[i]->type == VIR_TYPED_PARAM_STRING)
|
|
|
|
(*values)[n++] = filtered[i]->value.s;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(filtered);
|
|
|
|
return n;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (values)
|
|
|
|
VIR_FREE(*values);
|
|
|
|
VIR_FREE(filtered);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-02 12:19:35 +00:00
|
|
|
/**
|
|
|
|
* virTypedParamsRemoteFree:
|
|
|
|
* @remote_params_val: array of typed parameters as specified by
|
|
|
|
* (remote|admin)_protocol.h
|
|
|
|
* @remote_params_len: number of parameters in @remote_params_val
|
|
|
|
*
|
|
|
|
* Frees memory used by string representations of parameter identificators,
|
|
|
|
* memory used by string values of parameters and the memory occupied by
|
|
|
|
* @remote_params_val itself.
|
|
|
|
*
|
|
|
|
* Returns nothing.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virTypedParamsRemoteFree(virTypedParameterRemotePtr remote_params_val,
|
|
|
|
unsigned int remote_params_len)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!remote_params_val)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < remote_params_len; i++) {
|
|
|
|
VIR_FREE(remote_params_val[i].field);
|
|
|
|
if (remote_params_val[i].value.type == VIR_TYPED_PARAM_STRING)
|
|
|
|
VIR_FREE(remote_params_val[i].value.remote_typed_param_value.s);
|
|
|
|
}
|
|
|
|
VIR_FREE(remote_params_val);
|
|
|
|
}
|
|
|
|
|
2016-01-28 16:27:42 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virTypedParamsDeserialize:
|
|
|
|
* @remote_params: protocol data to be deserialized (obtained from remote side)
|
|
|
|
* @remote_params_len: number of parameters returned in @remote_params
|
|
|
|
* @limit: user specified maximum limit to @remote_params_len
|
|
|
|
* @params: pointer which will hold the deserialized @remote_params data
|
|
|
|
* @nparams: number of entries in @params
|
|
|
|
*
|
|
|
|
* This function will attempt to deserialize protocol-encoded data obtained
|
|
|
|
* from remote side. Two modes of operation are supported, depending on the
|
|
|
|
* caller's design:
|
|
|
|
* 1) Older APIs do not rely on deserializer allocating memory for @params,
|
|
|
|
* thus calling the deserializer twice, once to find out the actual number of
|
|
|
|
* parameters for @params to hold, followed by an allocation of @params and
|
|
|
|
* a second call to the deserializer to actually retrieve the data.
|
|
|
|
* 2) Newer APIs rely completely on the deserializer to allocate the right
|
2016-11-15 14:00:08 +00:00
|
|
|
* amount of memory for @params to hold all the data obtained in
|
2016-01-28 16:27:42 +00:00
|
|
|
* @remote_params.
|
|
|
|
*
|
|
|
|
* If used with model 1, two checks are performed, first one comparing the user
|
|
|
|
* specified limit to the actual size of remote data and the second one
|
|
|
|
* verifying the user allocated amount of memory is indeed capable of holding
|
|
|
|
* remote data @remote_params.
|
|
|
|
* With model 2, only the first check against @limit is performed.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of an error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virTypedParamsDeserialize(virTypedParameterRemotePtr remote_params,
|
|
|
|
unsigned int remote_params_len,
|
|
|
|
int limit,
|
|
|
|
virTypedParameterPtr *params,
|
|
|
|
int *nparams)
|
|
|
|
{
|
|
|
|
size_t i = 0;
|
|
|
|
int rv = -1;
|
|
|
|
bool userAllocated = *params != NULL;
|
|
|
|
|
|
|
|
if (limit && remote_params_len > limit) {
|
|
|
|
virReportError(VIR_ERR_RPC,
|
|
|
|
_("too many parameters '%u' for limit '%d'"),
|
|
|
|
remote_params_len, limit);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (userAllocated) {
|
|
|
|
/* Check the length of the returned list carefully. */
|
|
|
|
if (remote_params_len > *nparams) {
|
|
|
|
virReportError(VIR_ERR_RPC,
|
|
|
|
_("too many parameters '%u' for nparams '%d'"),
|
|
|
|
remote_params_len, *nparams);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (VIR_ALLOC_N(*params, remote_params_len) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*nparams = remote_params_len;
|
|
|
|
|
|
|
|
/* Deserialize the result. */
|
|
|
|
for (i = 0; i < remote_params_len; ++i) {
|
|
|
|
virTypedParameterPtr param = *params + i;
|
|
|
|
virTypedParameterRemotePtr remote_param = remote_params + i;
|
|
|
|
|
|
|
|
if (virStrcpyStatic(param->field,
|
2018-07-20 07:50:37 +00:00
|
|
|
remote_param->field) < 0) {
|
2016-01-28 16:27:42 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("parameter %s too big for destination"),
|
|
|
|
remote_param->field);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
param->type = remote_param->value.type;
|
|
|
|
switch (param->type) {
|
|
|
|
case VIR_TYPED_PARAM_INT:
|
|
|
|
param->value.i =
|
|
|
|
remote_param->value.remote_typed_param_value.i;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_UINT:
|
|
|
|
param->value.ui =
|
|
|
|
remote_param->value.remote_typed_param_value.ui;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_LLONG:
|
|
|
|
param->value.l =
|
|
|
|
remote_param->value.remote_typed_param_value.l;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_ULLONG:
|
|
|
|
param->value.ul =
|
|
|
|
remote_param->value.remote_typed_param_value.ul;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_DOUBLE:
|
|
|
|
param->value.d =
|
|
|
|
remote_param->value.remote_typed_param_value.d;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_BOOLEAN:
|
|
|
|
param->value.b =
|
|
|
|
remote_param->value.remote_typed_param_value.b;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_STRING:
|
|
|
|
if (VIR_STRDUP(param->value.s,
|
|
|
|
remote_param->value.remote_typed_param_value.s) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
virReportError(VIR_ERR_RPC, _("unknown parameter type: %d"),
|
|
|
|
param->type);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (rv < 0) {
|
|
|
|
if (userAllocated) {
|
|
|
|
virTypedParamsClear(*params, i);
|
|
|
|
} else {
|
|
|
|
virTypedParamsFree(*params, i);
|
|
|
|
*params = NULL;
|
2018-07-17 12:02:33 +00:00
|
|
|
*nparams = 0;
|
2016-01-28 16:27:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
2016-02-02 13:13:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virTypedParamsSerialize:
|
|
|
|
* @params: array of parameters to be serialized and later sent to remote side
|
|
|
|
* @nparams: number of elements in @params
|
2019-08-27 08:57:49 +00:00
|
|
|
* @limit: user specified maximum limit to @remote_params_len
|
2016-02-02 13:13:15 +00:00
|
|
|
* @remote_params_val: protocol independent remote representation of @params
|
|
|
|
* @remote_params_len: the final number of elements in @remote_params_val
|
|
|
|
* @flags: bitwise-OR of virTypedParameterFlags
|
|
|
|
*
|
|
|
|
* This method serializes typed parameters provided by @params into
|
|
|
|
* @remote_params_val which is the representation actually being sent.
|
2019-08-27 08:57:49 +00:00
|
|
|
* It also checks, if the @limit imposed by RPC on the maximum number of
|
|
|
|
* parameters is not exceeded.
|
2016-02-02 13:13:15 +00:00
|
|
|
*
|
|
|
|
* Server side using this method also filters out any string parameters that
|
|
|
|
* must not be returned to older clients and handles possibly sparse arrays
|
|
|
|
* returned by some APIs.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, -1 on error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virTypedParamsSerialize(virTypedParameterPtr params,
|
|
|
|
int nparams,
|
2019-08-27 08:57:49 +00:00
|
|
|
int limit,
|
2016-02-02 13:13:15 +00:00
|
|
|
virTypedParameterRemotePtr *remote_params_val,
|
|
|
|
unsigned int *remote_params_len,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
size_t j;
|
|
|
|
int rv = -1;
|
2019-08-27 08:57:49 +00:00
|
|
|
virTypedParameterRemotePtr params_val = NULL;
|
2018-07-17 12:02:32 +00:00
|
|
|
int params_len = nparams;
|
2016-02-02 13:13:15 +00:00
|
|
|
|
2019-08-27 08:57:49 +00:00
|
|
|
if (nparams > limit) {
|
|
|
|
virReportError(VIR_ERR_RPC,
|
|
|
|
_("too many parameters '%d' for limit '%d'"),
|
|
|
|
nparams, limit);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2016-02-02 13:13:15 +00:00
|
|
|
if (VIR_ALLOC_N(params_val, nparams) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < nparams; ++i) {
|
|
|
|
virTypedParameterPtr param = params + i;
|
|
|
|
virTypedParameterRemotePtr val = params_val + j;
|
|
|
|
/* NOTE: Following snippet is relevant to server only, because
|
|
|
|
* virDomainGetCPUStats can return a sparse array; also, we can't pass
|
|
|
|
* back strings to older clients. */
|
|
|
|
if (!param->type ||
|
|
|
|
(!(flags & VIR_TYPED_PARAM_STRING_OKAY) &&
|
|
|
|
param->type == VIR_TYPED_PARAM_STRING)) {
|
2018-07-17 12:02:32 +00:00
|
|
|
--params_len;
|
2016-02-02 13:13:15 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This will be either freed by virNetServerDispatchCall or call(),
|
|
|
|
* depending on the calling side, i.e. server or client */
|
|
|
|
if (VIR_STRDUP(val->field, param->field) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
val->value.type = param->type;
|
|
|
|
switch (param->type) {
|
|
|
|
case VIR_TYPED_PARAM_INT:
|
|
|
|
val->value.remote_typed_param_value.i = param->value.i;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_UINT:
|
|
|
|
val->value.remote_typed_param_value.ui = param->value.ui;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_LLONG:
|
|
|
|
val->value.remote_typed_param_value.l = param->value.l;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_ULLONG:
|
|
|
|
val->value.remote_typed_param_value.ul = param->value.ul;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_DOUBLE:
|
|
|
|
val->value.remote_typed_param_value.d = param->value.d;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_BOOLEAN:
|
|
|
|
val->value.remote_typed_param_value.b = param->value.b;
|
|
|
|
break;
|
|
|
|
case VIR_TYPED_PARAM_STRING:
|
|
|
|
if (VIR_STRDUP(val->value.remote_typed_param_value.s, param->value.s) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
virReportError(VIR_ERR_RPC, _("unknown parameter type: %d"),
|
|
|
|
param->type);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*remote_params_val = params_val;
|
2018-07-17 12:02:32 +00:00
|
|
|
*remote_params_len = params_len;
|
2016-02-02 13:13:15 +00:00
|
|
|
params_val = NULL;
|
|
|
|
rv = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virTypedParamsRemoteFree(params_val, nparams);
|
|
|
|
return rv;
|
|
|
|
}
|
2019-09-19 07:20:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
virTypedParamListFree(virTypedParamListPtr list)
|
|
|
|
{
|
|
|
|
if (!list)
|
|
|
|
return;
|
|
|
|
|
|
|
|
virTypedParamsFree(list->par, list->npar);
|
|
|
|
VIR_FREE(list);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
virTypedParamListStealParams(virTypedParamListPtr list,
|
|
|
|
virTypedParameterPtr *params)
|
|
|
|
{
|
|
|
|
size_t ret = list->npar;
|
|
|
|
|
|
|
|
VIR_STEAL_PTR(*params, list->par);
|
|
|
|
list->npar = 0;
|
|
|
|
list->par_alloc = 0;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-15 11:35:07 +00:00
|
|
|
static int G_GNUC_PRINTF(2, 0)
|
2019-09-19 07:20:49 +00:00
|
|
|
virTypedParamSetNameVPrintf(virTypedParameterPtr par,
|
|
|
|
const char *fmt,
|
|
|
|
va_list ap)
|
|
|
|
{
|
|
|
|
if (vsnprintf(par->field, VIR_TYPED_PARAM_FIELD_LENGTH, fmt, ap) > VIR_TYPED_PARAM_FIELD_LENGTH) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Field name too long"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static virTypedParameterPtr
|
|
|
|
virTypedParamListExtend(virTypedParamListPtr list)
|
|
|
|
{
|
|
|
|
if (VIR_RESIZE_N(list->par, list->par_alloc, list->npar, 1) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
list->npar++;
|
|
|
|
|
|
|
|
return list->par + (list->npar - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virTypedParamListAddInt(virTypedParamListPtr list,
|
|
|
|
int value,
|
|
|
|
const char *namefmt,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
virTypedParameterPtr par;
|
|
|
|
va_list ap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(par = virTypedParamListExtend(list)) ||
|
|
|
|
virTypedParameterAssignValue(par, true, VIR_TYPED_PARAM_INT, value) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
va_start(ap, namefmt);
|
|
|
|
ret = virTypedParamSetNameVPrintf(par, namefmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virTypedParamListAddUInt(virTypedParamListPtr list,
|
|
|
|
unsigned int value,
|
|
|
|
const char *namefmt,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
virTypedParameterPtr par;
|
|
|
|
va_list ap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(par = virTypedParamListExtend(list)) ||
|
|
|
|
virTypedParameterAssignValue(par, true, VIR_TYPED_PARAM_UINT, value) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
va_start(ap, namefmt);
|
|
|
|
ret = virTypedParamSetNameVPrintf(par, namefmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virTypedParamListAddLLong(virTypedParamListPtr list,
|
|
|
|
long long value,
|
|
|
|
const char *namefmt,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
virTypedParameterPtr par;
|
|
|
|
va_list ap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(par = virTypedParamListExtend(list)) ||
|
|
|
|
virTypedParameterAssignValue(par, true, VIR_TYPED_PARAM_LLONG, value) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
va_start(ap, namefmt);
|
|
|
|
ret = virTypedParamSetNameVPrintf(par, namefmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virTypedParamListAddULLong(virTypedParamListPtr list,
|
|
|
|
unsigned long long value,
|
|
|
|
const char *namefmt,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
virTypedParameterPtr par;
|
|
|
|
va_list ap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(par = virTypedParamListExtend(list)) ||
|
|
|
|
virTypedParameterAssignValue(par, true, VIR_TYPED_PARAM_ULLONG, value) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
va_start(ap, namefmt);
|
|
|
|
ret = virTypedParamSetNameVPrintf(par, namefmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virTypedParamListAddString(virTypedParamListPtr list,
|
|
|
|
const char *value,
|
|
|
|
const char *namefmt,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
virTypedParameterPtr par;
|
|
|
|
va_list ap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(par = virTypedParamListExtend(list)) ||
|
|
|
|
virTypedParameterAssignValue(par, true, VIR_TYPED_PARAM_STRING, value) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
va_start(ap, namefmt);
|
|
|
|
ret = virTypedParamSetNameVPrintf(par, namefmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virTypedParamListAddBoolean(virTypedParamListPtr list,
|
|
|
|
bool value,
|
|
|
|
const char *namefmt,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
virTypedParameterPtr par;
|
|
|
|
va_list ap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(par = virTypedParamListExtend(list)) ||
|
|
|
|
virTypedParameterAssignValue(par, true, VIR_TYPED_PARAM_BOOLEAN, value) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
va_start(ap, namefmt);
|
|
|
|
ret = virTypedParamSetNameVPrintf(par, namefmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virTypedParamListAddDouble(virTypedParamListPtr list,
|
|
|
|
double value,
|
|
|
|
const char *namefmt,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
virTypedParameterPtr par;
|
|
|
|
va_list ap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(par = virTypedParamListExtend(list)) ||
|
|
|
|
virTypedParameterAssignValue(par, true, VIR_TYPED_PARAM_DOUBLE, value) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
va_start(ap, namefmt);
|
|
|
|
ret = virTypedParamSetNameVPrintf(par, namefmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|