]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
* TODO libvirt.spec.in: update
authorDaniel Veillard <veillard@redhat.com>
Tue, 29 Aug 2006 22:27:07 +0000 (22:27 +0000)
committerDaniel Veillard <veillard@redhat.com>
Tue, 29 Aug 2006 22:27:07 +0000 (22:27 +0000)
* 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
Daniel

16 files changed:
ChangeLog
TODO
configure.in
include/libvirt/virterror.h
libvirt.spec.in
src/Makefile.am
src/conf.c [new file with mode: 0644]
src/conf.h [new file with mode: 0644]
src/virterror.c
src/xen_internal.c
tests/Makefile.am
tests/confdata/Makefile.am [new file with mode: 0644]
tests/confdata/fc4.conf [new file with mode: 0644]
tests/confdata/fc4.out [new file with mode: 0644]
tests/conftest.c [new file with mode: 0644]
tests/test_conf.sh [new file with mode: 0755]

index d58e41f026fa73346c91962f1757ade084e7b309..20c2c0a3a930744bb98036f1c9d2727f70ad0ffb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Tue Aug 29 23:28:31 CEST 2006 Daniel Veillard <veillard@redhat.com>
+
+       * 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 <berrange@redhat.com>
 
        * src/xend_internal.c: Add handling of HTTP 500 error code
diff --git a/TODO b/TODO
index 726d79ccb9ac7ae0d4d69e6d884155872ea7677f..a302f4295882bf3065001f986c45886698e86f1a 100644 (file)
--- 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
index 0891cc87c3b64deb7697316f73a127745e6fc8ef..87c9069b5f90f70d416ffda0c4da58d3d72487ad 100644 (file)
@@ -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)
index f5cc2b13cf11ad9c64aca3364d585dbeb7f64c6d..5c07cbef49ff7e248b6ce52504f60878d81e0894 100644 (file)
@@ -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;
 
 /**
index a78fc37187253048769e63ab0eab7b8e7746e4fa..6ffa3edd8da37462653707a7981c13ebec0fdf2c 100644 (file)
@@ -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 <veillard@redhat.com> 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 <katzj@redhat.com> - 0.1.3-6
+- add patch from pvetere to allow getting uuid from libvirt
+
+* Wed Aug  2 2006 Jeremy Katz <katzj@redhat.com> - 0.1.3-5
+- build on ia64 now
+
+* Thu Jul 27 2006 Jeremy Katz <katzj@redhat.com> - 0.1.3-4
+- don't BR xen, we just need xen-devel
+
+* Thu Jul 27 2006 Daniel Veillard <veillard@redhat.com> 0.1.3-3
+- need rebuild since libxenstore is now versionned
+
 * Mon Jul 24 2006 Mark McLoughlin <markmc@redhat.com> - 0.1.3-2
 - Add BuildRequires: xen-devel
 
index e85793ab34bcab94d279fe12c6258ca2721b1baa..7adfed32ba076fb53188b640d6d31e03757b81fc 100644 (file)
@@ -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 (file)
index 0000000..143936c
--- /dev/null
@@ -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 <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);
+    __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 (file)
index 0000000..fb2ebab
--- /dev/null
@@ -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 <veillard@redhat.com>
+ */
+
+#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__ */
index c6452b31f2754858ec1d636b128d9f547e92b78c..406a0da8de6e2671eddd58b79425bee9b4a07315 100644 (file)
@@ -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);
 }
index 88cc1658b7455fdb2d980c04ddffecd741912017..afde550423648832b1217ed1a60fa6d235274b0b 100644 (file)
@@ -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
  *
index ef5af65470259c6998306684987df7cf34d63aa6..a95076cf42478784babf92f2b2890efbeedbbe8e 100644 (file)
@@ -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 (file)
index 0000000..8393a6f
--- /dev/null
@@ -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 (file)
index 0000000..745a4c4
--- /dev/null
@@ -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 (file)
index 0000000..1fad85c
--- /dev/null
@@ -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 (file)
index 0000000..16346a4
--- /dev/null
@@ -0,0 +1,30 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#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 (executable)
index 0000000..b5a6366
--- /dev/null
@@ -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