[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH] libxl: new xlu_disk_parse function



From: Ian Jackson <ijackson@xxxxxxxxxxxxxxxxxxxxxx>

Introduce new flex/regexp-based parser for disk configuration strings.

Callers will be updated in following patches.

Signed-off-by: Ian Jackson <ijackson@xxxxxxxxxxxxxxxxxxxxxx>
---
 config/StdGNU.mk              |    1 +
 tools/libxl/Makefile          |    9 +-
 tools/libxl/libxlu_cfg.c      |   42 +++++++---
 tools/libxl/libxlu_disk.c     |  164 +++++++++++++++++++++++++++++++++++++++++
 tools/libxl/libxlu_disk_i.h   |   19 +++++
 tools/libxl/libxlu_disk_l.l   |   54 ++++++++++++++
 tools/libxl/libxlu_internal.h |   20 +++++
 tools/libxl/libxlutil.h       |   12 +++
 8 files changed, 304 insertions(+), 17 deletions(-)
 create mode 100644 tools/libxl/libxlu_disk.c
 create mode 100644 tools/libxl/libxlu_disk_i.h
 create mode 100644 tools/libxl/libxlu_disk_l.l

diff --git a/config/StdGNU.mk b/config/StdGNU.mk
index 25aeb4d..9b331f0 100644
--- a/config/StdGNU.mk
+++ b/config/StdGNU.mk
@@ -67,6 +67,7 @@ CURSES_LIBS = -lncurses
 PTHREAD_LIBS = -lpthread
 UTIL_LIBS = -lutil
 DLOPEN_LIBS = -ldl
+PCRE_LIBS = -lpcre
 
 SONAME_LDFLAG = -soname
 SHLIB_LDFLAGS = -shared
diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index a7b1d51..f0c473f 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -22,7 +22,7 @@ endif
 LIBXL_LIBS =
 LIBXL_LIBS = $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenstore) 
$(LDLIBS_libblktapctl) $(UTIL_LIBS) $(LIBUUID_LIBS)
 
-LIBXLU_LIBS =
+LIBXLU_LIBS = $(PCRE_LIBS)
 
 LIBXL_OBJS-y = osdeps.o libxl_paths.o libxl_bootloader.o flexarray.o
 ifeq ($(LIBXL_BLKTAP),y)
@@ -38,9 +38,10 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o 
libxl_pci.o \
                        libxl_internal.o libxl_utils.o $(LIBXL_OBJS-y)
 LIBXL_OBJS += _libxl_types.o
 
-AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h
-AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c
-LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o
+AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h libxlu_disk_l.h
+AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c libxlu_disk_l.c
+LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o \
+       libxlu_disk_l.o libxlu_disk.o
 
 CLIENTS = xl
 
diff --git a/tools/libxl/libxlu_cfg.c b/tools/libxl/libxlu_cfg.c
index f947c21..fd3e102 100644
--- a/tools/libxl/libxlu_cfg.c
+++ b/tools/libxl/libxlu_cfg.c
@@ -15,6 +15,7 @@ XLU_Config *xlu_cfg_init(FILE *report, const char 
*report_filename) {
     if (!cfg->filename) { free(cfg); return 0; }
 
     cfg->settings= 0;
+    cfg->disk_re= 0;
     return cfg;
 }
 
@@ -38,6 +39,29 @@ static int ctx_prep(CfgParseContext *ctx, XLU_Config *cfg) {
 
 static void ctx_dispose(CfgParseContext *ctx) {
     if (ctx->scanner) xlu__cfg_yylex_destroy(ctx->scanner);
+    if (ctx->disk_re) pcre_free(ctx->disk_re);
+}
+
+int xlu__scanner_string_prep(CfgParseContext *ctx, YY_BUFFER_STATE *buf,
+                            XLU_Config *cfg, const char *data, int length) {
+    int e;
+
+    e= ctx_prep(ctx, cfg);
+    if (e) return e;
+
+    *buf = xlu__cfg_yy_scan_bytes(data, length, ctx->scanner);
+    if (!*buf) {
+        fprintf(cfg->report,"%s: unable to allocate scanner buffer\n",
+                cfg->filename);
+        return ENOMEM;
+    }
+
+    return 0;
+}
+
+void xlu__scanner_string_dispose(CfgParseContext *ctx, YY_BUFFER_STATE *buf) {
+    if (*buf) xlu__cfg_yy_delete_buffer(*buf, ctx->scanner);
+    ctx_dispose(ctx);
 }
 
 static void parse(CfgParseContext *ctx) {
@@ -87,25 +111,17 @@ int xlu_cfg_readfile(XLU_Config *cfg, const char 
*real_filename) {
 
 int xlu_cfg_readdata(XLU_Config *cfg, const char *data, int length) {
     int e;
-    YY_BUFFER_STATE buf= 0;
-
+    YY_BUFFER_STATE buf=0;
     CfgParseContext ctx;
-    e= ctx_prep(&ctx, cfg);
-    if (e) { ctx.err= e; goto xe; }
+    ctx.scanner=0;
 
-    buf = xlu__cfg_yy_scan_bytes(data, length, ctx.scanner);
-    if (!buf) {
-        fprintf(cfg->report,"%s: unable to allocate scanner buffer\n",
-                cfg->filename);
-        ctx.err= ENOMEM;
-        goto xe;
-    }
+    e = xlu__scanner_string_prep(&ctx,&buf, cfg,data,length);
+    if (e) { ctx.err = e; goto xe; }
 
     parse(&ctx);
 
  xe:
-    if (buf) xlu__cfg_yy_delete_buffer(buf, ctx.scanner);
-    ctx_dispose(&ctx);
+    xlu__scanner_string_dispose(&ctx,&buf);
 
     return ctx.err;
 }
diff --git a/tools/libxl/libxlu_disk.c b/tools/libxl/libxlu_disk.c
new file mode 100644
index 0000000..be523f7
--- /dev/null
+++ b/tools/libxl/libxlu_disk.c
@@ -0,0 +1,164 @@
+
+/* Parsing the disk spec a tricky problem, because the target
+ * string might contain "," which is the delimiter we use for
+ * stripping off things on the RHS, and ":", which is the delimiter
+ * we use for stripping off things on the LHS.
+ *
+ * For the LHS we use a flex scanner, see libxlu_disk_l.l.
+ * That chops prefixes like "phy:" from the front of the string,
+ * accumulating information into the disk structure.
+ *
+ * For the RHS, we use a regexp using pcre.
+ */
+
+#include "libxlu_internal.h"
+#include "libxlu_disk_l.h"
+#include "libxlu_disk_i.h"
+#include "libxlu_cfg_i.h"
+
+static void err_core(DiskParseContext *dpc, const char *message,
+                    const char *in, int inlen) {
+    fprintf(dpc->ctx.cfg->report,
+           "%s: config parsing error in disk specification:"
+           " %s: near `%s' in `%.*s'\n",
+           dpc->ctx.cfg->filename, message, dpc->spec, inlen, in);
+    if (!dpc->ctx.err) dpc->ctx.err= EINVAL;
+}
+
+void xlu__disk_err(DiskParseContext *dpc, const char *message) {
+    err_core(dpc, message,
+            xlu__disk_yyget_text(dpc->ctx.scanner),
+            xlu__disk_yyget_leng(dpc->ctx.scanner));
+}
+
+void xlu__disk_rhs(DiskParseContext *dpc) {
+
+    /* variables used by pcre */
+#define WORKSPACE_SIZE 20 /* wtf no manifest constant from pcre */
+#define OVECTOR_SIZE 10
+    int ovector[OVECTOR_SIZE];
+    int workspace[WORKSPACE_SIZE];
+
+    int rc, len;
+    const char *text;
+    libxl_device_disk *disk = dpc->disk;
+
+    static const char *re_text=
+       "(.*),(?:/dev/)?([0-9a-zA-Z]*)(:[0-9a-z]+)?(,[0-9a-z]*)?"
+       /* 1:target     2:vdev         3:type        4:attrib (r/w) */;
+
+    /* compile the regexp if we haven't got one already */
+    if (!dpc->ctx.disk_re) {
+       const char *re_errstr;
+       int re_errcode, re_erroffset;
+
+       dpc->ctx.disk_re = pcre_compile2(re_text, PCRE_DOTALL,
+                                        &re_errcode, &re_errstr, &re_erroffset,
+                                        0);
+       if (!dpc->ctx.disk_re) {
+           if (re_errcode == 21 /* wtf, no manifest constant */) {
+               dpc->ctx.err = ENOMEM;
+               return;
+           }
+           fprintf(dpc->ctx.cfg->report, "config parsing:"
+                   " unable to compile regexp: %s [%d] (at `%s')", 
+                   re_errstr, re_errcode, re_text+re_erroffset);
+           dpc->ctx.err = EIO;
+           return;
+       }
+    }
+
+    /* actually run the regexp match */
+
+    text = xlu__disk_yyget_text(dpc->ctx.scanner);
+    len = xlu__disk_yyget_leng(dpc->ctx.scanner);
+
+    rc = pcre_dfa_exec(dpc->ctx.disk_re, 0,
+                      text, len, 0,
+                      PCRE_ANCHORED,
+                      ovector, OVECTOR_SIZE,
+                      workspace, WORKSPACE_SIZE);
+    switch (rc) {
+    case PCRE_ERROR_NOMATCH:
+       xlu__disk_err(dpc, "bad syntax for target, or missing vdev");
+       return;
+    case 2:
+    case 3:
+    case 4:
+       break;
+    default:
+       abort();
+    }
+
+    /* macros for processing info from capturing parens; see pcreapi(3) */
+
+#define CAPTURED(cap) (START((cap)) >= 0)
+#define START(cap) (ovector[(cap)*2])
+#define END(cap)   (ovector[(cap)*2+1])
+#define LEN(cap)   (END((cap)) - START((cap)))
+#define TEXT(cap)  (text + START((cap)))
+
+#define STORE(cap, member) do{                                 \
+    assert(CAPTURED((cap)));                                   \
+    free(disk->member);                                                \
+    disk->member = malloc(LEN((cap)) + 1);                     \
+    if (!disk->member) { dpc->ctx.err = ENOMEM; return; }      \
+    memcpy(disk->member, TEXT((cap)), LEN((cap)));             \
+    disk->member[LEN((cap))] = 0;                              \
+}while(0)
+
+#define EXACTLY(cap, string)                           \
+    (CAPTURED((cap)) &&                                        \
+     LEN((cap))==strlen((string)) &&                   \
+     !memcmp(TEXT((cap)), (string), LEN((cap))))
+
+#define ERR(cap, msg) \
+    err_core(dpc, (msg), TEXT((cap)), LEN((cap)))
+
+    /* actually process the four capturing parens in our regexp */
+
+    STORE(1, pdev_path);
+
+    STORE(2, vdev);
+
+    if (!CAPTURED(3) || EXACTLY(3, ":")) {
+       disk->is_cdrom = 0;
+    } else if (EXACTLY(3, ":cdrom")) {
+       disk->is_cdrom = 1;
+    } else {
+       ERR(3, "unknown type (only `:' and `:cdrom' supported)");
+    }
+
+    if (!CAPTURED(4) || EXACTLY(4, ",w") || EXACTLY(4, ",")) {
+       disk->readwrite = 1;
+    } else if (EXACTLY(4, ",r")) {
+       disk->readwrite = 0;
+    } else {
+       ERR(4, "unknown read/write attribute");
+    }
+}
+
+int xlu_disk_parse(XLU_Config *cfg, const char *spec,
+                  libxl_device_disk *disk) {
+    YY_BUFFER_STATE buf = 0;
+    DiskParseContext dpc;
+    int e, r;
+
+    dpc.disk = disk;
+    dpc.spec = spec;
+    dpc.ctx.scanner = 0;
+
+    e = xlu__scanner_string_prep(&dpc.ctx,&buf, cfg,spec,strlen(spec));
+    if (e) { dpc.ctx.err = e; goto xe; }
+
+    r = xlu__disk_yylex(dpc.ctx.scanner);
+    if (r) assert(dpc.ctx.err);
+    if (dpc.ctx.err) goto xe;
+
+    if (disk->format == DISK_FORMAT_UNKNOWN) disk->format = DISK_FORMAT_RAW;
+
+ xe:
+    xlu__scanner_string_dispose(&dpc.ctx,&buf);
+    return dpc.ctx.err;
+}
+
diff --git a/tools/libxl/libxlu_disk_i.h b/tools/libxl/libxlu_disk_i.h
new file mode 100644
index 0000000..deb429f
--- /dev/null
+++ b/tools/libxl/libxlu_disk_i.h
@@ -0,0 +1,19 @@
+#ifndef LIBXLU_DISK_I_H
+#define LIBXLU_DISK_I_H
+
+#include "libxlu_internal.h"
+#include "libxl_internal.h"
+
+
+typedef struct {
+    CfgParseContext ctx;
+    const char *spec;
+    libxl_device_disk *disk;
+} DiskParseContext;
+
+void xlu__disk_err(DiskParseContext *dpc, const char *message);
+
+void xlu__disk_rhs(DiskParseContext *dpc);
+
+
+#endif /*LIBXLU_DISK_I_H*/
diff --git a/tools/libxl/libxlu_disk_l.l b/tools/libxl/libxlu_disk_l.l
new file mode 100644
index 0000000..82e2248
--- /dev/null
+++ b/tools/libxl/libxlu_disk_l.l
@@ -0,0 +1,54 @@
+/* -*- fundamental -*- */
+
+%{
+#include "libxlu_disk_i.h"
+
+#define dpc ((DiskParseContext*)yyextra)
+#define YY_NO_INPUT
+
+#define DSET(member,enumname,valname) do{                              \
+       if (dpc->disk->member != DISK_##enumname##_UNKNOWN &&           \
+           dpc->disk->member != DISK_##enumname##_##valname) {         \
+           xlu__disk_err(dpc, TOSTRING(member) " respecified");        \
+           return 0;                                                   \
+       } else {                                                        \
+           dpc->disk->member = DISK_##enumname##_##valname;            \
+       }                                                               \
+    }while(0)
+
+/* Some versions of flex have a bug (Fedora bugzilla 612465) which causes
+ * it to fail to declare these functions, which it defines.  So declare
+ * them ourselves.  Hopefully we won't have to simultaneously support
+ * a flex version which declares these differently somehow. */
+int xlu__disk_yyget_column(yyscan_t yyscanner);
+void xlu__disk_yyset_column(int  column_no, yyscan_t yyscanner);
+
+%}
+
+%option warn
+%option nodefault
+%option batch
+%option 8bit
+%option noyywrap
+%option reentrant
+%option prefix="xlu__disk_yy"
+%option nounput
+
+%%
+
+raw:           { DSET(format,FORMAT,RAW); }
+qcow:          { DSET(format,FORMAT,QCOW); }
+qcow2:         { DSET(format,FORMAT,QCOW2); }
+vhd:           { DSET(format,FORMAT,QCOW2); }
+
+phy:           { DSET(format,FORMAT,RAW); DSET(backend,BACKEND,PHY); }
+file:          { DSET(format,FORMAT,RAW); DSET(backend,BACKEND,TAP); }
+tapdisk:|tap2?:        { DSET(backend,BACKEND,TAP); }
+aio:           { }
+ioemu:         { }
+
+[a-z][a-z0-9-]*: { xlu__disk_err(dpc,"unknown disk format/method"); return 0; }
+
+[^:]*(\/.*)?   { xlu__disk_rhs(dpc); return 0; }
+
+.*             { xlu__disk_err(dpc,"bad disk syntax"); return 0; }
diff --git a/tools/libxl/libxlu_internal.h b/tools/libxl/libxlu_internal.h
index e251a63..d681fba 100644
--- a/tools/libxl/libxlu_internal.h
+++ b/tools/libxl/libxlu_internal.h
@@ -21,10 +21,15 @@
 #include <string.h>
 #include <assert.h>
 
+#include <pcre.h>
+
 #define XLU_ConfigList XLU_ConfigSetting
 
 #include "libxlutil.h"
 
+
+
+
 struct XLU_ConfigSetting { /* transparent */
     struct XLU_ConfigSetting *next;
     char *name;
@@ -37,12 +42,27 @@ struct XLU_Config {
     XLU_ConfigSetting *settings;
     FILE *report;
     char *filename;
+    pcre *disk_re;
 };
 
 typedef struct {
     XLU_Config *cfg;
     int err, lexerrlineno, likely_python;
     void *scanner;
+    pcre *disk_re;
 } CfgParseContext;
 
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+int xlu__scanner_string_prep(CfgParseContext *ctx, YY_BUFFER_STATE *buf,
+                            XLU_Config *cfg, const char *data, int length);
+  /* Caller must initialise ctx->scanner=0 and buf=0 beforehand. */
+
+void xlu__scanner_string_dispose(CfgParseContext *ctx, YY_BUFFER_STATE *buf);
+
+
 #endif /*LIBXLU_INTERNAL_H*/
diff --git a/tools/libxl/libxlutil.h b/tools/libxl/libxlutil.h
index 8a6fcbd..acf96fd 100644
--- a/tools/libxl/libxlutil.h
+++ b/tools/libxl/libxlutil.h
@@ -58,4 +58,16 @@ const char *xlu_cfg_get_listitem(const XLU_ConfigList*, int 
entry);
   /* xlu_cfg_get_listitem cannot fail, except that if entry is
    * out of range it returns 0 (not setting errno) */
 
+
+/*
+ * Disk specification parsing.
+ */
+
+int xlu_disk_parse(XLU_Config *cfg, const char *spec,
+                  libxl_device_disk *disk);
+  /* disk must have been initialised; on error, returns errno value;
+   * bad strings, returns EINVAL and prints a message to cfg's report.
+   * That's all cfg is used for. */
+
+
 #endif /* LIBXLUTIL_H */
-- 
1.5.6.5


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.