diff --git a/ChangeLog b/ChangeLog index d58e41f026..20c2c0a3a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Tue Aug 29 23:28:31 CEST 2006 Daniel Veillard + + * TODO libvirt.spec.in: update + * configure.in include/libvirt/virterror.h src/Makefile.am + src/conf.c src/conf.h src/virterror.c src/xen_internal.c: + adding a subset of Xen config file parser, and serializer + * tests/Makefile.am tests/conftest.c tests/test_conf.sh + tests/confdata/Makefile.am tests/confdata/fc4.conf + tests/confdata/fc4.out: adding test program for config in and out + Tue Aug 29 13:14:20 EDT 2006 Daniel Berrange * src/xend_internal.c: Add handling of HTTP 500 error code diff --git a/TODO b/TODO index 726d79ccb9..a302f42958 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,5 @@ TODO: +- libvirt-proxy dropping root priviledge after initialization. - check impact of HVM device rename http://lists.xensource.com/archives/html/xen-devel/2006-08/msg00369.html - Finish integration of vCPU and affinity APIs diff --git a/configure.in b/configure.in index 0891cc87c3..87c9069b5f 100644 --- a/configure.in +++ b/configure.in @@ -257,4 +257,4 @@ AC_OUTPUT(Makefile src/Makefile include/Makefile docs/Makefile \ include/libvirt/Makefile include/libvirt/libvirt.h \ python/Makefile python/tests/Makefile \ tests/Makefile proxy/Makefile \ - tests/virshdata/Makefile) + tests/virshdata/Makefile tests/confdata/Makefile) diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index f5cc2b13cf..5c07cbef49 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -45,7 +45,8 @@ typedef enum { VIR_FROM_XML, /* Error in the XML code */ VIR_FROM_DOM, /* Error when operating on a domain */ VIR_FROM_RPC, /* Error in the XML-RPC code */ - VIR_FROM_PROXY /* Error in the proxy code */ + VIR_FROM_PROXY, /* Error in the proxy code */ + VIR_FROM_CONF /* Error in the configuration file handling */ } virErrorDomain; @@ -106,7 +107,12 @@ typedef enum { VIR_ERR_CALL_FAILED, /* not supported by the drivers */ VIR_ERR_XML_ERROR, /* an XML description is not well formed or broken */ VIR_ERR_DOM_EXIST,/* the domain already exist */ - VIR_ERR_OPERATION_DENIED /* operation forbidden on read-only connections */ + VIR_ERR_OPERATION_DENIED, /* operation forbidden on read-only connections */ + VIR_ERR_OPEN_FAILED, /* failed to open a conf file */ + VIR_ERR_READ_FAILED, /* failed to read a conf file */ + VIR_ERR_PARSE_FAILED, /* failed to parse a conf file */ + VIR_ERR_CONF_SYNTAX, /* failed to parse the syntax of a conf file */ + VIR_ERR_WRITE_FAILED /* failed to write a conf file */ } virErrorNumber; /** diff --git a/libvirt.spec.in b/libvirt.spec.in index a78fc37187..6ffa3edd8d 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -7,7 +7,7 @@ Group: Development/Libraries Source: libvirt-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-root URL: http://libvir.org/ -BuildRequires: xen python python-devel +BuildRequires: python python-devel Requires: xen Requires: libxml2 Requires: readline @@ -107,6 +107,25 @@ rm -fr %{buildroot} %doc docs/examples/python %changelog +* Wed Aug 16 2006 Daniel Veillard 0.1.4-1 +- vCPUs and affinity support +- more complete XML, console and boot options +- specific features support +- enforced read-only connections +- various improvements, bug fixes + +* Wed Aug 2 2006 Jeremy Katz - 0.1.3-6 +- add patch from pvetere to allow getting uuid from libvirt + +* Wed Aug 2 2006 Jeremy Katz - 0.1.3-5 +- build on ia64 now + +* Thu Jul 27 2006 Jeremy Katz - 0.1.3-4 +- don't BR xen, we just need xen-devel + +* Thu Jul 27 2006 Daniel Veillard 0.1.3-3 +- need rebuild since libxenstore is now versionned + * Mon Jul 24 2006 Mark McLoughlin - 0.1.3-2 - Add BuildRequires: xen-devel diff --git a/src/Makefile.am b/src/Makefile.am index e85793ab34..7adfed32ba 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,7 +25,8 @@ libvirt_la_SOURCES = \ sexpr.c sexpr.h \ virterror.c \ driver.h \ - proxy_internal.c proxy_internal.h + proxy_internal.c proxy_internal.h \ + conf.c conf.h bin_PROGRAMS = virsh diff --git a/src/conf.c b/src/conf.c new file mode 100644 index 0000000000..143936cddb --- /dev/null +++ b/src/conf.c @@ -0,0 +1,868 @@ +/** + * 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 + */ + +#define _GNU_SOURCE /* want strndup ! */ +#include + +#include +#include +#include +#include +#include + +#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); + __virRaiseError(NULL, NULL, VIR_FROM_CONF, error, VIR_ERR_ERROR, + 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; + list->next = NULL; + virConfFreeValue(list); + list = next; + } +} + +/** + * virConfFreeValue: + * @val: the value to free + * + * Free a value + */ +static void +virConfFreeValue(virConfValuePtr val) +{ + if (val == NULL) + return; + if (val->str != NULL) + free(val->str); + if (val->list != NULL) + virConfFreeList(val->list); + free(val); +} + +/** + * 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; + + ret = (virConfPtr) malloc(sizeof(virConf)); + if (ret == NULL) { + virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration", 0); + return(NULL); + } + memset(ret, 0, sizeof(virConf)); + + ret->filename = filename; + + 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) { + virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration", 0); + return(NULL); + } + 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))) { + virConfError(NULL, VIR_ERR_CONF_SYNTAX, "unterminated number", + 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 != '\'') { + virConfError(NULL, VIR_ERR_CONF_SYNTAX, "unterminated string", + 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] != '"')) { + virConfError(NULL, VIR_ERR_CONF_SYNTAX, "unterminated string", + 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 != '"') { + virConfError(NULL, VIR_ERR_CONF_SYNTAX, "unterminated string", + 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) { + virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting a value", + 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, + "expecting a separator in list", ctxt->line); + virConfFreeList(lst); + return(NULL); + } + NEXT; + SKIP_BLANKS; + 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, + "list is not closed with ] ", ctxt->line); + virConfFreeList(lst); + return(NULL); + } + } else if (IS_DIGIT(CUR) || (CUR == '-') || (CUR == '+')) { + if (virConfParseLong(ctxt, &l) < 0) { + return(NULL); + } + type = VIR_CONF_LONG; + } else { + virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting a value", + ctxt->line); + return(NULL); + } + ret = (virConfValuePtr) malloc(sizeof(virConfValue)); + if (ret == NULL) { + virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration", 0); + 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)) { + virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting a name", ctxt->line); + return(NULL); + } + while ((ctxt->cur < ctxt->end) && ((IS_CHAR(CUR)) || (IS_DIGIT(CUR)))) + NEXT; + ret = strndup(base, ctxt->cur - base); + if (ret == NULL) { + virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration", + 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) { + virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration", + 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 { + virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting a separator", + 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 != '=') { + virConfError(NULL, VIR_ERR_CONF_SYNTAX, "expecting an assignment", + 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) { + virConfError(NULL, VIR_ERR_NO_MEMORY, "Allocating configuration", + 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) +{ + if (conf == NULL) { + virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__, 0); + return(-1); + } + 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) +{ +} + +/** + * 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) { + virConfError(NULL, VIR_ERR_WRITE_FAILED, "failed to open file", 0); + ret = -1; + goto error; + } + + ret = write(fd, buf->content, buf->use); + close(fd); + if (ret != (int) buf->use) { + virConfError(NULL, VIR_ERR_WRITE_FAILED, "failed to save content", 0); + 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); + cur = cur->next; + } + + if ((int) buf->use >= *len) { + *len = buf->use; + ret = -1; + goto error; + } + memcpy(memory, buf->content, buf->use); + ret = buf->use; + +error: + virBufferFree(buf); + return(ret); +} diff --git a/src/conf.h b/src/conf.h new file mode 100644 index 0000000000..fb2ebab38c --- /dev/null +++ b/src/conf.h @@ -0,0 +1,69 @@ +/** + * conf.h: 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 + */ + +#ifndef __VIR_CONF_H__ +#define __VIR_CONF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * virConfType: + * one of the possible type for a value from the configuration file + * + * TODO: we probably need a float too. + */ +typedef enum { + VIR_CONF_NONE = 0, /* undefined */ + VIR_CONF_LONG = 1, /* a long int */ + VIR_CONF_STRING = 2, /* a string */ + VIR_CONF_LIST = 3 /* a list */ +} virConfType; + +/** + * virConfValue: + * a value from the configuration file + */ +typedef struct _virConfValue virConfValue; +typedef virConfValue *virConfValuePtr; + +struct _virConfValue { + virConfType type; /* the virConfType */ + virConfValuePtr next; /* next element if in a list */ + long l; /* long integer */ + char *str; /* pointer to 0 terminated string */ + virConfValuePtr list; /* list of a list */ +}; + +/** + * virConfPtr: + * a pointer to a parsed configuration file + */ +typedef struct _virConf virConf; +typedef virConf *virConfPtr; + +virConfPtr virConfReadFile (const char *filename); +virConfPtr virConfReadMem (const char *memory, + int len); +int virConfFree (virConfPtr conf); + +virConfValuePtr virConfGetValue (virConfPtr conf, + const char *setting); +int virConfWriteFile (const char *filename, + virConfPtr conf); +int virConfWriteMem (char *memory, + int *len, + virConfPtr conf); + +#ifdef __cplusplus +} +#endif +#endif /* __VIR_CONF_H__ */ diff --git a/src/virterror.c b/src/virterror.c index c6452b31f2..406a0da8de 100644 --- a/src/virterror.c +++ b/src/virterror.c @@ -539,6 +539,36 @@ __virErrorMsg(virErrorNumber error, const char *info) else errmsg = "operation %s forbidden for read only access"; break; + case VIR_ERR_OPEN_FAILED: + if (info == NULL) + errmsg = "failed to open configuration file for reading"; + else + errmsg = "failed to open %s for reading"; + break; + case VIR_ERR_READ_FAILED: + if (info == NULL) + errmsg = "failed to read configuration file"; + else + errmsg = "failed to read configuration file %s"; + break; + case VIR_ERR_PARSE_FAILED: + if (info == NULL) + errmsg = "failed to parse configuration file"; + else + errmsg = "failed to parse configuration file %s"; + break; + case VIR_ERR_CONF_SYNTAX: + if (info == NULL) + errmsg = "configuration file syntax error"; + else + errmsg = "configuration file syntax error: %s"; + break; + case VIR_ERR_WRITE_FAILED: + if (info == NULL) + errmsg = "failed to write configuration file"; + else + errmsg = "failed to write configuration file: %s"; + break; } return (errmsg); } diff --git a/src/xen_internal.c b/src/xen_internal.c index 88cc1658b7..afde550423 100644 --- a/src/xen_internal.c +++ b/src/xen_internal.c @@ -1,7 +1,7 @@ /* * xen_internal.c: direct access to Xen hypervisor level * - * Copyright (C) 2005 Red Hat, Inc. + * Copyright (C) 2005, 2006 Red Hat, Inc. * * See COPYING.LIB for the License of this software * diff --git a/tests/Makefile.am b/tests/Makefile.am index ef5af65470..a95076cf42 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -17,11 +17,11 @@ LDADDS = \ @LIBXML_LIBS@ \ $(LIBVIRT) -EXTRA_DIST = xmlrpcserver.py +EXTRA_DIST = xmlrpcserver.py test_conf.sh -noinst_PROGRAMS = xmlrpctest xml2sexprtest sexpr2xmltest virshtest +noinst_PROGRAMS = xmlrpctest xml2sexprtest sexpr2xmltest virshtest conftest -TESTS = xml2sexprtest sexpr2xmltest virshtest +TESTS = xml2sexprtest sexpr2xmltest virshtest test_conf.sh valgrind: $(MAKE) check TESTS_ENVIRONMENT="valgrind --quiet" @@ -54,5 +54,10 @@ virshtest_SOURCES = \ virshtest_LDFLAGS = virshtest_LDADD = $(LDADDS) +conftest_SOURCES = \ + conftest.c +conftest_LDFLAGS = +conftest_LDADD = $(LDADDS) + $(LIBVIRT): -@(cd $(top_builddir)/src && $(MAKE) MAKEFLAGS+=--silent) diff --git a/tests/confdata/Makefile.am b/tests/confdata/Makefile.am new file mode 100644 index 0000000000..8393a6f2b9 --- /dev/null +++ b/tests/confdata/Makefile.am @@ -0,0 +1,2 @@ + +EXTRA_DIST = $(wildcard *.in) $(wildcard *.out) diff --git a/tests/confdata/fc4.conf b/tests/confdata/fc4.conf new file mode 100644 index 0000000000..745a4c409c --- /dev/null +++ b/tests/confdata/fc4.conf @@ -0,0 +1,10 @@ +kernel="/boot/vmlinuz-2.6.15-1.2054_FC5xenU" +ramdisk="/boot/initrd-2.6.15-1.2054_FC5xenU.img" +memory=128 # should be enough +name="fc4" +vif = [ 'mac=aa:00:00:00:00:11, bridge=xenbr0' ] +disk = ['file:/xen/fc4.img,sda1,w'] +root = "/dev/sda1" +extra = "ro selinux=0 3" +# just for testing ... +tst = [ 1, 2, [ 3, 4 ], 5] diff --git a/tests/confdata/fc4.out b/tests/confdata/fc4.out new file mode 100644 index 0000000000..1fad85ceb2 --- /dev/null +++ b/tests/confdata/fc4.out @@ -0,0 +1,10 @@ +kernel = "/boot/vmlinuz-2.6.15-1.2054_FC5xenU" +ramdisk = "/boot/initrd-2.6.15-1.2054_FC5xenU.img" +memory = 128 # should be enough +name = "fc4" +vif = [ "mac=aa:00:00:00:00:11, bridge=xenbr0" ] +disk = [ "file:/xen/fc4.img,sda1,w" ] +root = "/dev/sda1" +extra = "ro selinux=0 3" +# just for testing ... +tst = [ 1, 2, [ 3, 4 ], 5 ] diff --git a/tests/conftest.c b/tests/conftest.c new file mode 100644 index 0000000000..16346a49b2 --- /dev/null +++ b/tests/conftest.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include "conf.h" + +int main(int argc, char **argv) { + int ret; + virConfPtr conf; + int len = 10000; + char buffer[10000]; + + if (argc != 2) { + fprintf(stderr, "Usage: %s conf_file\n", argv[0]); + exit(1); + } + + conf = virConfReadFile(argv[1]); + if (conf == NULL) { + fprintf(stderr, "Failed to process %s\n", argv[1]); + exit(2); + } + ret = virConfWriteMem(&buffer[0], &len, conf); + if (ret < 0) { + fprintf(stderr, "Failed to serialize %s back\n", argv[1]); + exit(3); + } + printf("%s", buffer); + virConfFree(conf); + exit(0); +} diff --git a/tests/test_conf.sh b/tests/test_conf.sh new file mode 100755 index 0000000000..b5a636633e --- /dev/null +++ b/tests/test_conf.sh @@ -0,0 +1,17 @@ +#!/bin/bash +NOK=0 +for f in confdata/*.conf +do + ./conftest $f > conftest.$$ + outfile=`echo $f | sed s+\.conf+\.out+` + diff $outfile conftest.$$ > /dev/null + if [ $? != 0 ] + then + echo "$f FAILED" + NOK=1 + else + echo "$f OK" + fi +done +rm -f conftest.$$ +exit $NOK