2006-08-29 22:27:07 +00:00
|
|
|
/**
|
|
|
|
* conf.c: parser for a subset of the Python encoded Xen configuration files
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* See COPYING.LIB for the License of this software
|
|
|
|
*
|
|
|
|
* Daniel Veillard <veillard@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define _GNU_SOURCE /* want strndup ! */
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "xml.h"
|
|
|
|
#include "conf.h"
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* *
|
|
|
|
* Structures and macros used by the mini parser *
|
|
|
|
* *
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
typedef struct _virConfParserCtxt virConfParserCtxt;
|
|
|
|
typedef virConfParserCtxt *virConfParserCtxtPtr;
|
|
|
|
|
|
|
|
struct _virConfParserCtxt {
|
|
|
|
const char* filename;
|
|
|
|
const char* base;
|
|
|
|
const char* cur;
|
|
|
|
const char *end;
|
|
|
|
int line;
|
|
|
|
|
|
|
|
virConfPtr conf;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define CUR (*ctxt->cur)
|
|
|
|
#define NEXT if (ctxt->cur < ctxt->end) ctxt->cur++;
|
|
|
|
#define IS_EOL(c) (((c) == '\n') || ((c) == '\r'))
|
|
|
|
#define IS_BLANK(c) (((c) == ' ') || ((c) == '\n') || ((c) == '\r') || \
|
|
|
|
((c) == '\t'))
|
|
|
|
#define SKIP_BLANKS {while ((ctxt->cur < ctxt->end) && (IS_BLANK(CUR))){\
|
|
|
|
if (CUR == '\n') ctxt->line++; \
|
|
|
|
ctxt->cur++;}}
|
|
|
|
#define IS_SPACE(c) (((c) == ' ') || ((c) == '\t'))
|
|
|
|
#define SKIP_SPACES {while ((ctxt->cur < ctxt->end) && (IS_SPACE(CUR))) \
|
|
|
|
ctxt->cur++;}
|
|
|
|
#define IS_CHAR(c) ((((c) >= 'a') && ((c) <= 'z')) || \
|
|
|
|
(((c) >= 'A') && ((c) <= 'Z')))
|
|
|
|
#define IS_DIGIT(c) (((c) >= '0') && ((c) <= '9'))
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* *
|
|
|
|
* Structures used by configuration data *
|
|
|
|
* *
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
typedef struct _virConfEntry virConfEntry;
|
|
|
|
typedef virConfEntry *virConfEntryPtr;
|
|
|
|
|
|
|
|
struct _virConfEntry {
|
|
|
|
virConfEntryPtr next;
|
|
|
|
char* name;
|
|
|
|
char* comment;
|
|
|
|
virConfValuePtr value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _virConf {
|
|
|
|
const char* filename;
|
|
|
|
virConfEntryPtr entries;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfError:
|
|
|
|
* @conf: the configuration if available
|
|
|
|
* @error: the error number
|
|
|
|
* @info: extra information string
|
|
|
|
* @line: line for the error
|
|
|
|
*
|
|
|
|
* Handle an error at the xend daemon interface
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
virConfError(virConfPtr conf ATTRIBUTE_UNUSED,
|
|
|
|
virErrorNumber error, const char *info, int line)
|
|
|
|
{
|
|
|
|
const char *errmsg;
|
|
|
|
|
|
|
|
if (error == VIR_ERR_OK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
errmsg = __virErrorMsg(error, info);
|
Tue Feb 14 15:37:17 EST 2007 Mark McLoughlin <markmc@redhat.com>
Note: potential ABI break here, but people should
only really be using virError structs returned from
libvirt itself.
* include/libvirt/virterror.h: add virNetwork
to virError
* src/internal.h, src/virterror.c: add network param
to __virRaiseError()
* src/conf.c, src/hash.c, src/libvirt.c, src/proxy_internal.c,
src/qemu_internal.c, src/sexpr.c, src/test.c, src/xen_internal.c,
src/xend_internal.c, src/xm_internal.c, src/xml.c, src/xmlrpc.c,
src/xs_internal.c: update.
2007-02-14 15:40:53 +00:00
|
|
|
__virRaiseError(NULL, NULL, NULL, VIR_FROM_CONF, error, VIR_ERR_ERROR,
|
2006-08-29 22:27:07 +00:00
|
|
|
errmsg, info, NULL, line, 0, errmsg, info, line);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* *
|
|
|
|
* Structures allocations and deallocations *
|
|
|
|
* *
|
|
|
|
************************************************************************/
|
|
|
|
static void virConfFreeValue(virConfValuePtr val);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfFreeList:
|
|
|
|
* @list: the list to free
|
|
|
|
*
|
|
|
|
* Free a list
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
virConfFreeList(virConfValuePtr list)
|
|
|
|
{
|
|
|
|
virConfValuePtr next;
|
|
|
|
|
|
|
|
while (list != NULL) {
|
|
|
|
next = list->next;
|
2007-01-19 20:10:04 +00:00
|
|
|
list->next = NULL;
|
|
|
|
virConfFreeValue(list);
|
|
|
|
list = next;
|
2006-08-29 22:27:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfFreeValue:
|
|
|
|
* @val: the value to free
|
|
|
|
*
|
|
|
|
* Free a value
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
virConfFreeValue(virConfValuePtr val)
|
|
|
|
{
|
|
|
|
if (val == NULL)
|
|
|
|
return;
|
2007-01-19 20:10:04 +00:00
|
|
|
if (val->type == VIR_CONF_STRING &&
|
|
|
|
val->str != NULL)
|
2006-08-29 22:27:07 +00:00
|
|
|
free(val->str);
|
2007-01-19 20:10:04 +00:00
|
|
|
if (val->type == VIR_CONF_LIST &&
|
|
|
|
val->list != NULL)
|
2006-08-29 22:27:07 +00:00
|
|
|
virConfFreeList(val->list);
|
|
|
|
free(val);
|
|
|
|
}
|
|
|
|
|
2006-11-15 19:46:23 +00:00
|
|
|
virConfPtr virConfNew(void)
|
2006-08-29 22:27:07 +00:00
|
|
|
{
|
|
|
|
virConfPtr ret;
|
|
|
|
|
|
|
|
ret = (virConfPtr) malloc(sizeof(virConf));
|
|
|
|
if (ret == NULL) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_NO_MEMORY, _("allocating configuration"), 0);
|
2006-08-29 22:27:07 +00:00
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
memset(ret, 0, sizeof(virConf));
|
|
|
|
|
2006-11-15 19:46:23 +00:00
|
|
|
ret->filename = NULL;
|
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
2006-08-29 22:27:07 +00:00
|
|
|
|
2006-11-15 19:46:23 +00:00
|
|
|
/**
|
|
|
|
* virConfCreate:
|
|
|
|
* @filename: the name to report errors
|
|
|
|
*
|
|
|
|
* Create a configuration internal structure
|
|
|
|
*
|
|
|
|
* Returns a pointer or NULL in case of error.
|
|
|
|
*/
|
|
|
|
static virConfPtr
|
|
|
|
virConfCreate(const char *filename)
|
|
|
|
{
|
|
|
|
virConfPtr ret = virConfNew();
|
|
|
|
if (ret)
|
|
|
|
ret->filename = filename;
|
2006-08-29 22:27:07 +00:00
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfAddEntry:
|
|
|
|
* @conf: the conf structure
|
|
|
|
* @name: name of the entry or NULL for comment
|
|
|
|
* @value: the value if any
|
|
|
|
* @comm: extra comment for that entry if any
|
|
|
|
*
|
|
|
|
* add one entry to the conf, the parameters are included in the conf
|
|
|
|
* if successful and freed on virConfFree()
|
|
|
|
*
|
|
|
|
* Returns a pointer to the entry or NULL in case of failure
|
|
|
|
*/
|
|
|
|
static virConfEntryPtr
|
|
|
|
virConfAddEntry(virConfPtr conf, char *name, virConfValuePtr value, char *comm)
|
|
|
|
{
|
|
|
|
virConfEntryPtr ret, prev;
|
|
|
|
|
|
|
|
if (conf == NULL)
|
|
|
|
return(NULL);
|
|
|
|
if ((comm == NULL) && (name == NULL))
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
ret = (virConfEntryPtr) malloc(sizeof(virConfEntry));
|
|
|
|
if (ret == NULL) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_NO_MEMORY, _("allocating configuration"), 0);
|
2006-08-29 22:27:07 +00:00
|
|
|
return(NULL);
|
|
|
|
}
|
2007-01-19 20:10:04 +00:00
|
|
|
|
2006-08-29 22:27:07 +00:00
|
|
|
memset(ret, 0, sizeof(virConfEntry));
|
|
|
|
ret->name = name;
|
|
|
|
ret->value = value;
|
|
|
|
ret->comment = comm;
|
|
|
|
|
|
|
|
if (conf->entries == NULL) {
|
|
|
|
conf->entries = ret;
|
|
|
|
} else {
|
|
|
|
prev = conf->entries;
|
|
|
|
while (prev->next != NULL)
|
|
|
|
prev = prev->next;
|
|
|
|
prev->next = ret;
|
|
|
|
}
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* *
|
|
|
|
* Serialization *
|
|
|
|
* *
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfSaveValue:
|
|
|
|
* @buf: output buffer
|
|
|
|
* @val: a value
|
|
|
|
*
|
|
|
|
* Serialize the value to the buffer
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success, -1 in case of error.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virConfSaveValue(virBufferPtr buf, virConfValuePtr val)
|
|
|
|
{
|
|
|
|
if (val == NULL)
|
|
|
|
return(-1);
|
|
|
|
switch (val->type) {
|
|
|
|
case VIR_CONF_NONE:
|
|
|
|
return(-1);
|
|
|
|
case VIR_CONF_LONG:
|
|
|
|
virBufferVSprintf(buf, "%ld", val->l);
|
|
|
|
break;
|
|
|
|
case VIR_CONF_STRING:
|
|
|
|
if (strchr(val->str, '\n') != NULL) {
|
|
|
|
virBufferVSprintf(buf, "\"\"\"%s\"\"\"", val->str);
|
|
|
|
} else if (strchr(val->str, '"') == NULL) {
|
|
|
|
virBufferVSprintf(buf, "\"%s\"", val->str);
|
|
|
|
} else if (strchr(val->str, '\'') == NULL) {
|
|
|
|
virBufferVSprintf(buf, "'%s'", val->str);
|
|
|
|
} else {
|
|
|
|
virBufferVSprintf(buf, "\"\"\"%s\"\"\"", val->str);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_CONF_LIST: {
|
|
|
|
virConfValuePtr cur;
|
|
|
|
|
|
|
|
cur = val->list;
|
|
|
|
virBufferAdd(buf, "[ ", 2);
|
|
|
|
if (cur != NULL) {
|
|
|
|
virConfSaveValue(buf, cur);
|
|
|
|
cur = cur->next;
|
|
|
|
while (cur != NULL) {
|
|
|
|
virBufferAdd(buf, ", ", 2);
|
|
|
|
virConfSaveValue(buf, cur);
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virBufferAdd(buf, " ]", 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfSaveEntry:
|
|
|
|
* @buf: output buffer
|
|
|
|
* @cur: a conf entry
|
|
|
|
*
|
|
|
|
* Serialize the entry to the buffer
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success, -1 in case of error.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virConfSaveEntry(virBufferPtr buf, virConfEntryPtr cur)
|
|
|
|
{
|
|
|
|
if (cur->name != NULL) {
|
|
|
|
virBufferAdd(buf, cur->name, -1);
|
|
|
|
virBufferAdd(buf, " = ", 3);
|
|
|
|
virConfSaveValue(buf, cur->value);
|
|
|
|
if (cur->comment != NULL) {
|
|
|
|
virBufferAdd(buf, " #", 2);
|
|
|
|
virBufferAdd(buf, cur->comment, -1);
|
|
|
|
}
|
|
|
|
} else if (cur->comment != NULL) {
|
|
|
|
virBufferAdd(buf, "#", 1);
|
|
|
|
virBufferAdd(buf, cur->comment, -1);
|
|
|
|
}
|
|
|
|
virBufferAdd(buf, "\n", 1);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* *
|
|
|
|
* The parser core *
|
|
|
|
* *
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfParseLong:
|
|
|
|
* @ctxt: the parsing context
|
|
|
|
* @val: the result
|
|
|
|
*
|
|
|
|
* Parse one long int value
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success and -1 in case of error
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virConfParseLong(virConfParserCtxtPtr ctxt, long *val)
|
|
|
|
{
|
|
|
|
long l = 0;
|
|
|
|
int neg = 0;
|
|
|
|
|
|
|
|
if (CUR == '-') {
|
|
|
|
neg = 1;
|
|
|
|
NEXT;
|
|
|
|
} else if (CUR == '+') {
|
|
|
|
NEXT;
|
|
|
|
}
|
|
|
|
if ((ctxt->cur >= ctxt->end) || (!IS_DIGIT(CUR))) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX, _("unterminated number"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
while ((ctxt->cur < ctxt->end) && (IS_DIGIT(CUR))) {
|
|
|
|
l = l * 10 + (CUR - '0');
|
|
|
|
NEXT;
|
|
|
|
}
|
|
|
|
*val = l;
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfParseString:
|
|
|
|
* @ctxt: the parsing context
|
|
|
|
*
|
|
|
|
* Parse one string
|
|
|
|
*
|
|
|
|
* Returns a pointer to the string or NULL in case of error
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
virConfParseString(virConfParserCtxtPtr ctxt)
|
|
|
|
{
|
|
|
|
const char *base;
|
|
|
|
char *ret = NULL;
|
|
|
|
|
|
|
|
if (CUR == '\'') {
|
|
|
|
NEXT;
|
|
|
|
base = ctxt->cur;
|
|
|
|
while ((ctxt->cur < ctxt->end) && (CUR != '\'') && (!IS_EOL(CUR)))
|
|
|
|
NEXT;
|
|
|
|
if (CUR != '\'') {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX, _("unterminated string"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
ret = strndup(base, ctxt->cur - base);
|
|
|
|
NEXT;
|
|
|
|
} else if ((ctxt->cur + 6 < ctxt->end) && (ctxt->cur[0] == '"') &&
|
|
|
|
(ctxt->cur[1] == '"') && (ctxt->cur[2] == '"')) {
|
|
|
|
ctxt->cur += 3;
|
|
|
|
base = ctxt->cur;
|
|
|
|
while ((ctxt->cur + 2 < ctxt->end) && (ctxt->cur[0] == '"') &&
|
|
|
|
(ctxt->cur[1] == '"') && (ctxt->cur[2] == '"')) {
|
|
|
|
if (CUR == '\n') ctxt->line++;
|
|
|
|
NEXT;
|
|
|
|
}
|
|
|
|
if ((ctxt->cur[0] != '"') || (ctxt->cur[1] != '"') ||
|
|
|
|
(ctxt->cur[2] != '"')) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX, _("unterminated string"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
ret = strndup(base, ctxt->cur - base);
|
|
|
|
ctxt->cur += 3;
|
|
|
|
} else if (CUR == '"') {
|
|
|
|
NEXT;
|
|
|
|
base = ctxt->cur;
|
|
|
|
while ((ctxt->cur < ctxt->end) && (CUR != '"') && (!IS_EOL(CUR)))
|
|
|
|
NEXT;
|
|
|
|
if (CUR != '"') {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX, _("unterminated string"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
ret = strndup(base, ctxt->cur - base);
|
|
|
|
NEXT;
|
|
|
|
}
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfParseValue:
|
|
|
|
* @ctxt: the parsing context
|
|
|
|
*
|
|
|
|
* Parse one value
|
|
|
|
*
|
|
|
|
* Returns a pointer to the value or NULL in case of error
|
|
|
|
*/
|
|
|
|
static virConfValuePtr
|
|
|
|
virConfParseValue(virConfParserCtxtPtr ctxt)
|
|
|
|
{
|
|
|
|
virConfValuePtr ret, lst = NULL, tmp, prev;
|
|
|
|
virConfType type = VIR_CONF_NONE;
|
|
|
|
char *str = NULL;
|
|
|
|
long l = 0;
|
|
|
|
|
|
|
|
SKIP_SPACES;
|
|
|
|
if (ctxt->cur >= ctxt->end) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX, _("expecting a value"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
if ((CUR == '"') || (CUR == '\'')) {
|
|
|
|
type = VIR_CONF_STRING;
|
|
|
|
str = virConfParseString(ctxt);
|
|
|
|
if (str == NULL)
|
|
|
|
return(NULL);
|
|
|
|
} else if (CUR == '[') {
|
|
|
|
type = VIR_CONF_LIST;
|
|
|
|
NEXT;
|
|
|
|
SKIP_BLANKS;
|
|
|
|
if ((ctxt->cur < ctxt->end) && (CUR != ']')) {
|
|
|
|
lst = virConfParseValue(ctxt);
|
|
|
|
SKIP_BLANKS;
|
|
|
|
}
|
|
|
|
while ((ctxt->cur < ctxt->end) && (CUR != ']')) {
|
|
|
|
if (CUR != ',') {
|
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX,
|
2006-09-21 15:24:37 +00:00
|
|
|
_("expecting a separator in list"), ctxt->line);
|
2006-08-29 22:27:07 +00:00
|
|
|
virConfFreeList(lst);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
NEXT;
|
|
|
|
SKIP_BLANKS;
|
2006-09-03 17:46:32 +00:00
|
|
|
if (CUR == ']') {
|
|
|
|
break;
|
|
|
|
}
|
2006-08-29 22:27:07 +00:00
|
|
|
tmp = virConfParseValue(ctxt);
|
|
|
|
if (tmp == NULL) {
|
|
|
|
virConfFreeList(lst);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
prev = lst;
|
|
|
|
while (prev->next != NULL) prev = prev->next;
|
|
|
|
prev->next = tmp;
|
|
|
|
SKIP_BLANKS;
|
|
|
|
}
|
|
|
|
if (CUR == ']') {
|
|
|
|
NEXT;
|
|
|
|
} else {
|
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX,
|
2006-09-21 15:24:37 +00:00
|
|
|
_("list is not closed with ] "), ctxt->line);
|
2006-08-29 22:27:07 +00:00
|
|
|
virConfFreeList(lst);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
} else if (IS_DIGIT(CUR) || (CUR == '-') || (CUR == '+')) {
|
|
|
|
if (virConfParseLong(ctxt, &l) < 0) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
type = VIR_CONF_LONG;
|
|
|
|
} else {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX, _("expecting a value"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
ret = (virConfValuePtr) malloc(sizeof(virConfValue));
|
|
|
|
if (ret == NULL) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_NO_MEMORY, _("allocating configuration"), 0);
|
2006-08-29 22:27:07 +00:00
|
|
|
if (str != NULL)
|
|
|
|
free(str);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
memset(ret, 0, sizeof(virConfValue));
|
|
|
|
ret->type = type;
|
|
|
|
ret->l = l;
|
|
|
|
ret->str = str;
|
|
|
|
ret->list = lst;
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfParseName:
|
|
|
|
* @ctxt: the parsing context
|
|
|
|
*
|
|
|
|
* Parse one name
|
|
|
|
*
|
|
|
|
* Returns a copy of the new string, NULL in case of error
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
virConfParseName(virConfParserCtxtPtr ctxt)
|
|
|
|
{
|
|
|
|
const char *base;
|
|
|
|
char *ret;
|
|
|
|
|
|
|
|
SKIP_SPACES;
|
|
|
|
base = ctxt->cur;
|
|
|
|
/* TODO: probably need encoding support and UTF-8 parsing ! */
|
|
|
|
if (!IS_CHAR(CUR)) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX, _("expecting a name"), ctxt->line);
|
2006-08-29 22:27:07 +00:00
|
|
|
return(NULL);
|
|
|
|
}
|
2006-09-03 17:46:32 +00:00
|
|
|
while ((ctxt->cur < ctxt->end) && ((IS_CHAR(CUR)) || (IS_DIGIT(CUR)) || (CUR == '_')))
|
2006-08-29 22:27:07 +00:00
|
|
|
NEXT;
|
|
|
|
ret = strndup(base, ctxt->cur - base);
|
|
|
|
if (ret == NULL) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_NO_MEMORY, _("allocating configuration"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfParseComment:
|
|
|
|
* @ctxt: the parsing context
|
|
|
|
*
|
|
|
|
* Parse one standalone comment in the configuration file
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success and -1 in case of error
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virConfParseComment(virConfParserCtxtPtr ctxt)
|
|
|
|
{
|
|
|
|
const char *base;
|
|
|
|
char *comm;
|
|
|
|
|
|
|
|
if (CUR != '#')
|
|
|
|
return(-1);
|
|
|
|
NEXT;
|
|
|
|
base = ctxt->cur;
|
|
|
|
while ((ctxt->cur < ctxt->end) && (!IS_EOL(CUR))) NEXT;
|
|
|
|
comm = strndup(base, ctxt->cur - base);
|
|
|
|
if (comm == NULL) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_NO_MEMORY, _("allocating configuration"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
virConfAddEntry(ctxt->conf, NULL, NULL, comm);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfParseSeparator:
|
|
|
|
* @ctxt: the parsing context
|
|
|
|
*
|
|
|
|
* Parse one separator between statement if not at the end.
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success and -1 in case of error
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virConfParseSeparator(virConfParserCtxtPtr ctxt)
|
|
|
|
{
|
|
|
|
SKIP_SPACES;
|
|
|
|
if (ctxt->cur >= ctxt->end)
|
|
|
|
return(0);
|
|
|
|
if (IS_EOL(CUR)) {
|
|
|
|
SKIP_BLANKS
|
|
|
|
} else if (CUR == ';') {
|
|
|
|
NEXT;
|
|
|
|
SKIP_BLANKS;
|
|
|
|
} else {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX, _("expecting a separator"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfParseStatement:
|
|
|
|
* @ctxt: the parsing context
|
|
|
|
*
|
|
|
|
* Parse one statement in the conf file
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success and -1 in case of error
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virConfParseStatement(virConfParserCtxtPtr ctxt)
|
|
|
|
{
|
|
|
|
const char *base;
|
|
|
|
char *name;
|
|
|
|
virConfValuePtr value;
|
|
|
|
char *comm = NULL;
|
|
|
|
|
|
|
|
SKIP_BLANKS;
|
|
|
|
if (CUR == '#') {
|
|
|
|
return(virConfParseComment(ctxt));
|
|
|
|
}
|
|
|
|
name = virConfParseName(ctxt);
|
|
|
|
if (name == NULL)
|
|
|
|
return(-1);
|
|
|
|
SKIP_SPACES;
|
|
|
|
if (CUR != '=') {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_CONF_SYNTAX, _("expecting an assignment"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
NEXT;
|
|
|
|
SKIP_SPACES;
|
|
|
|
value = virConfParseValue(ctxt);
|
|
|
|
if (value == NULL) {
|
|
|
|
free(name);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
SKIP_SPACES;
|
|
|
|
if (CUR == '#') {
|
|
|
|
NEXT;
|
|
|
|
base = ctxt->cur;
|
|
|
|
while ((ctxt->cur < ctxt->end) && (!IS_EOL(CUR))) NEXT;
|
|
|
|
comm = strndup(base, ctxt->cur - base);
|
|
|
|
if (comm == NULL) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_NO_MEMORY, _("allocating configuration"),
|
2006-08-29 22:27:07 +00:00
|
|
|
ctxt->line);
|
|
|
|
free(name);
|
|
|
|
virConfFreeValue(value);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (virConfAddEntry(ctxt->conf, name, value, comm) == NULL) {
|
|
|
|
free(name);
|
|
|
|
virConfFreeValue(value);
|
|
|
|
if (comm != NULL)
|
|
|
|
free(comm);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfParse:
|
|
|
|
* @filename: the name to report errors
|
|
|
|
* @content: the configuration content in memory
|
|
|
|
* @len: the length in bytes
|
|
|
|
*
|
|
|
|
* Parse the subset of the Python language needed to handle simple
|
|
|
|
* Xen configuration files.
|
|
|
|
*
|
|
|
|
* Returns an handle to lookup settings or NULL if it failed to
|
|
|
|
* read or parse the file, use virConfFree() to free the data.
|
|
|
|
*/
|
|
|
|
static virConfPtr
|
|
|
|
virConfParse(const char *filename, const char *content, int len) {
|
|
|
|
virConfParserCtxt ctxt;
|
|
|
|
|
|
|
|
ctxt.filename = filename;
|
|
|
|
ctxt.base = ctxt.cur = content;
|
|
|
|
ctxt.end = content + len - 1;
|
|
|
|
ctxt.line = 1;
|
|
|
|
|
|
|
|
ctxt.conf = virConfCreate(filename);
|
|
|
|
if (ctxt.conf == NULL)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
while (ctxt.cur < ctxt.end) {
|
|
|
|
if (virConfParseStatement(&ctxt) < 0)
|
|
|
|
goto error;
|
|
|
|
if (virConfParseSeparator(&ctxt) < 0)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(ctxt.conf);
|
|
|
|
|
|
|
|
error:
|
|
|
|
virConfFree(ctxt.conf);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* *
|
|
|
|
* The module entry points *
|
|
|
|
* *
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfReadFile:
|
|
|
|
* @filename: the path to the configuration file.
|
|
|
|
*
|
|
|
|
* Reads a configuration file.
|
|
|
|
*
|
|
|
|
* Returns an handle to lookup settings or NULL if it failed to
|
|
|
|
* read or parse the file, use virConfFree() to free the data.
|
|
|
|
*/
|
|
|
|
virConfPtr
|
|
|
|
virConfReadFile(const char *filename)
|
|
|
|
{
|
|
|
|
char content[4096];
|
|
|
|
int fd;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (filename == NULL) {
|
|
|
|
virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__, 0);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
fd = open(filename, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
virConfError(NULL, VIR_ERR_OPEN_FAILED, filename, 0);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
len = read(fd, content, sizeof(content));
|
|
|
|
close(fd);
|
|
|
|
if (len <= 0) {
|
|
|
|
virConfError(NULL, VIR_ERR_READ_FAILED, filename, 0);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
return(virConfParse(filename, content, len));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfReadMem:
|
|
|
|
* @memory: pointer to the content of the configuration file
|
|
|
|
* @len: lenght in byte
|
|
|
|
*
|
|
|
|
* Reads a configuration file loaded in memory. The string can be
|
|
|
|
* zero terminated in which case @len can be 0
|
|
|
|
*
|
|
|
|
* Returns an handle to lookup settings or NULL if it failed to
|
|
|
|
* parse the content, use virConfFree() to free the data.
|
|
|
|
*/
|
|
|
|
virConfPtr
|
|
|
|
virConfReadMem(const char *memory, int len)
|
|
|
|
{
|
|
|
|
if ((memory == NULL) || (len < 0)) {
|
|
|
|
virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__, 0);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
if (len == 0)
|
|
|
|
len = strlen(memory);
|
|
|
|
|
|
|
|
return(virConfParse("memory conf", memory, len));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfFree:
|
|
|
|
* @conf: a configuration file handle
|
|
|
|
*
|
|
|
|
* Frees all data associated to the handle
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success, -1 in case of error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virConfFree(virConfPtr conf)
|
|
|
|
{
|
2007-01-19 20:10:04 +00:00
|
|
|
virConfEntryPtr tmp;
|
2006-08-29 22:27:07 +00:00
|
|
|
if (conf == NULL) {
|
|
|
|
virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__, 0);
|
2007-01-19 20:10:04 +00:00
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = conf->entries;
|
|
|
|
while (tmp) {
|
|
|
|
virConfEntryPtr next;
|
|
|
|
free(tmp->name);
|
|
|
|
virConfFreeValue(tmp->value);
|
|
|
|
if (tmp->comment)
|
|
|
|
free(tmp->comment);
|
|
|
|
next = tmp->next;
|
|
|
|
free(tmp);
|
|
|
|
tmp = next;
|
2006-08-29 22:27:07 +00:00
|
|
|
}
|
|
|
|
free(conf);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfGetValue:
|
|
|
|
* @conf: a configuration file handle
|
|
|
|
* @entry: the name of the entry
|
|
|
|
*
|
|
|
|
* Lookup the value associated to this entry in the configuration file
|
|
|
|
*
|
|
|
|
* Returns a pointer to the value or NULL if the lookup failed, the data
|
|
|
|
* associated will be freed when virConfFree() is called
|
|
|
|
*/
|
|
|
|
virConfValuePtr
|
|
|
|
virConfGetValue(virConfPtr conf, const char *setting)
|
|
|
|
{
|
2006-08-29 22:45:44 +00:00
|
|
|
virConfEntryPtr cur;
|
|
|
|
|
|
|
|
cur = conf->entries;
|
|
|
|
while (cur != NULL) {
|
|
|
|
if ((cur->name != NULL) && (!strcmp(cur->name, setting)))
|
|
|
|
return(cur->value);
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
return(NULL);
|
2006-08-29 22:27:07 +00:00
|
|
|
}
|
|
|
|
|
2006-11-15 19:46:23 +00:00
|
|
|
/**
|
|
|
|
* virConfGetValue:
|
|
|
|
* @conf: a configuration file handle
|
|
|
|
* @entry: the name of the entry
|
|
|
|
* @value: the new configuration value
|
|
|
|
*
|
|
|
|
* Set (or replace) the value associated to this entry in the configuration
|
|
|
|
* file. The passed in 'value' will be owned by the conf object upon return
|
|
|
|
* of this method, even in case of error. It should not be referenced again
|
|
|
|
* by the caller.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, or -1 on failure.
|
|
|
|
*/
|
|
|
|
int virConfSetValue (virConfPtr conf,
|
|
|
|
const char *setting,
|
|
|
|
virConfValuePtr value) {
|
|
|
|
virConfEntryPtr cur, prev = NULL;
|
|
|
|
|
|
|
|
cur = conf->entries;
|
|
|
|
while (cur != NULL) {
|
|
|
|
if ((cur->name != NULL) && (!strcmp(cur->name, setting))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prev = cur;
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
2007-01-19 20:10:04 +00:00
|
|
|
|
2006-11-15 19:46:23 +00:00
|
|
|
if (!cur) {
|
|
|
|
if (!(cur = malloc(sizeof(virConfEntry)))) {
|
|
|
|
virConfFreeValue(value);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
cur->comment = NULL;
|
|
|
|
if (!(cur->name = strdup(setting))) {
|
|
|
|
virConfFreeValue(value);
|
|
|
|
free(cur);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
cur->value = value;
|
|
|
|
if (prev) {
|
2007-01-19 20:10:04 +00:00
|
|
|
cur->next = prev->next;
|
2006-11-15 19:46:23 +00:00
|
|
|
prev->next = cur;
|
|
|
|
} else {
|
2007-01-19 20:10:04 +00:00
|
|
|
cur->next = conf->entries;
|
2006-11-15 19:46:23 +00:00
|
|
|
conf->entries = cur;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (cur->value) {
|
|
|
|
virConfFreeValue(cur->value);
|
|
|
|
}
|
|
|
|
cur->value = value;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-29 22:27:07 +00:00
|
|
|
/**
|
|
|
|
* virConfWriteFile:
|
|
|
|
* @filename: the path to the configuration file.
|
|
|
|
* @conf: the conf
|
|
|
|
*
|
|
|
|
* Writes a configuration file back to a file.
|
|
|
|
*
|
|
|
|
* Returns the number of bytes written or -1 in case of error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virConfWriteFile(const char *filename, virConfPtr conf)
|
|
|
|
{
|
|
|
|
virBufferPtr buf;
|
|
|
|
virConfEntryPtr cur;
|
|
|
|
int ret;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (conf == NULL)
|
|
|
|
return(-1);
|
|
|
|
|
|
|
|
buf = virBufferNew(500);
|
|
|
|
if (buf == NULL)
|
|
|
|
return(-1);
|
|
|
|
|
|
|
|
cur = conf->entries;
|
|
|
|
while (cur != NULL) {
|
|
|
|
virConfSaveEntry(buf, cur);
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR );
|
|
|
|
if (fd < 0) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_WRITE_FAILED, _("failed to open file"), 0);
|
2006-08-29 22:27:07 +00:00
|
|
|
ret = -1;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = write(fd, buf->content, buf->use);
|
|
|
|
close(fd);
|
|
|
|
if (ret != (int) buf->use) {
|
2006-09-21 15:24:37 +00:00
|
|
|
virConfError(NULL, VIR_ERR_WRITE_FAILED, _("failed to save content"), 0);
|
2006-08-29 22:27:07 +00:00
|
|
|
ret = -1;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
error:
|
|
|
|
virBufferFree(buf);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virConfWriteMem:
|
|
|
|
* @memory: pointer to the memory to store the config file
|
|
|
|
* @len: pointer to the lenght in byte of the store, on output the size
|
|
|
|
* @conf: the conf
|
|
|
|
*
|
|
|
|
* Writes a configuration file back to a memory area. @len is an IN/OUT
|
|
|
|
* parameter, it indicates the size available in bytes, and on output the
|
|
|
|
* size required for the configuration file (even if the call fails due to
|
|
|
|
* insufficient space).
|
|
|
|
*
|
|
|
|
* Returns the number of bytes written or -1 in case of error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virConfWriteMem(char *memory, int *len, virConfPtr conf)
|
|
|
|
{
|
|
|
|
virBufferPtr buf;
|
|
|
|
virConfEntryPtr cur;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((memory == NULL) || (len == NULL) || (*len <= 0) || (conf == NULL))
|
|
|
|
return(-1);
|
|
|
|
|
|
|
|
buf = virBufferNew(500);
|
|
|
|
if (buf == NULL)
|
|
|
|
return(-1);
|
|
|
|
|
|
|
|
cur = conf->entries;
|
|
|
|
while (cur != NULL) {
|
|
|
|
virConfSaveEntry(buf, cur);
|
2007-01-19 20:10:04 +00:00
|
|
|
cur = cur->next;
|
2006-08-29 22:27:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((int) buf->use >= *len) {
|
|
|
|
*len = buf->use;
|
2007-01-19 20:10:04 +00:00
|
|
|
ret = -1;
|
|
|
|
goto error;
|
2006-08-29 22:27:07 +00:00
|
|
|
}
|
|
|
|
memcpy(memory, buf->content, buf->use);
|
|
|
|
ret = buf->use;
|
2007-01-19 20:10:04 +00:00
|
|
|
*len = buf->use;
|
2006-08-29 22:27:07 +00:00
|
|
|
error:
|
|
|
|
virBufferFree(buf);
|
|
|
|
return(ret);
|
|
|
|
}
|
2006-11-15 19:46:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
* c-indent-level: 4
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 4
|
|
|
|
* End:
|
|
|
|
*/
|