Introduce new flex/regexp-based parser for disk configuration strings.
Callers will be updated in following patches.
The existing xm command line syntax for block-attach expects multiple
arguments containing different parameters for different parts of the
disk specification, so we supply a parser function which can take
multiple strings and scan them in sequence.
Signed-off-by: Ian Jackson <Ian.Jackson@xxxxxxxxxxxxx>
---
tools/libxl/Makefile | 3 +-
tools/libxl/libxlu_disk.c | 93 ++++++++++++++++++
tools/libxl/libxlu_disk_i.h | 21 ++++
tools/libxl/libxlu_disk_l.l | 217 +++++++++++++++++++++++++++++++++++++++++++
tools/libxl/libxlutil.h | 23 +++++
5 files changed, 356 insertions(+), 1 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/tools/libxl/Makefile b/tools/libxl/Makefile
index 84ab76f..ddb639e 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -41,7 +41,8 @@ $(LIBXL_OBJS): CFLAGS += $(CFLAGS_libxenctrl)
$(CFLAGS_libxenguest) $(CFLAGS_lib
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
+LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o \
+ libxlu_disk_l.o libxlu_disk.o
$(LIBXLU_OBJS): CFLAGS += $(CFLAGS_libxenctrl) # For xentoollog.h
CLIENTS = xl
diff --git a/tools/libxl/libxlu_disk.c b/tools/libxl/libxlu_disk.c
new file mode 100644
index 0000000..0984145
--- /dev/null
+++ b/tools/libxl/libxlu_disk.c
@@ -0,0 +1,93 @@
+#include "libxlu_internal.h"
+#include "libxlu_disk_l.h"
+#include "libxlu_disk_i.h"
+#include "libxlu_cfg_i.h"
+
+void xlu__disk_err(DiskParseContext *dpc, const char *erroneous,
+ const char *message) {
+ fprintf(dpc->cfg->report,
+ "%s: config parsing error in disk specification: %s"
+ "%s%s%s"
+ " in `%s'\n",
+ dpc->cfg->filename, message,
+ erroneous?": near `":"", erroneous?erroneous:"", erroneous?"'":"",
+ dpc->spec);
+ if (!dpc->err) dpc->err= EINVAL;
+}
+
+static int dpc_prep(DiskParseContext *dpc, const char *spec) {
+ int e;
+
+ dpc->spec = spec;
+
+ e = xlu__disk_yylex_init_extra(dpc, &dpc->scanner);
+ if (e) goto fail;
+
+ dpc->buf = xlu__disk_yy_scan_bytes(spec, strlen(spec), dpc->scanner);
+ if (!dpc->buf) { e = ENOMEM; goto fail; }
+
+ return 0;
+
+ fail:
+ fprintf(dpc->cfg->report, "cannot init disk scanner: %s\n",
+ strerror(errno));
+ return e;
+}
+
+static void dpc_dispose(DiskParseContext *dpc) {
+ if (dpc->buf) {
+ xlu__disk_yy_delete_buffer(dpc->buf, dpc->scanner);
+ dpc->buf = 0;
+ }
+ if (dpc->scanner) {
+ xlu__disk_yylex_destroy(dpc->scanner);
+ dpc->scanner = 0;
+ }
+}
+
+int xlu_disk_parse(XLU_Config *cfg,
+ int nspecs, const char *const *specs,
+ libxl_device_disk *disk) {
+ DiskParseContext dpc;
+ int i, e;
+
+ memset(&dpc,0,sizeof(dpc));
+ dpc.cfg = cfg;
+ dpc.scanner = 0;
+ dpc.disk = disk;
+
+ disk->readwrite = 1;
+
+ for (i=0; i<nspecs; i++) {
+ e = dpc_prep(&dpc, specs[i]);
+ if (e) { dpc.err = e; goto x_err; }
+
+ xlu__disk_yylex(dpc.scanner);
+ assert(!e);
+ if (dpc.err) goto x_err;
+
+ dpc_dispose(&dpc);
+ }
+
+ if (disk->format == LIBXL_DISK_FORMAT_UNKNOWN) {
+ disk->format = LIBXL_DISK_FORMAT_RAW;
+ }
+ if (disk->is_cdrom) {
+ disk->removable = 1;
+ disk->readwrite = 0;
+ }
+
+ if (!disk->vdev) {
+ xlu__disk_err(&dpc,0, "no vdev specified");
+ goto x_err;
+ }
+ if (!disk->pdev_path && !disk->removable) {
+ xlu__disk_err(&dpc,0,"no target specified (and device not removable)");
+ goto x_err;
+ }
+
+ x_err:
+ dpc_dispose(&dpc);
+ return dpc.err;
+}
+
diff --git a/tools/libxl/libxlu_disk_i.h b/tools/libxl/libxlu_disk_i.h
new file mode 100644
index 0000000..578920a
--- /dev/null
+++ b/tools/libxl/libxlu_disk_i.h
@@ -0,0 +1,21 @@
+#ifndef LIBXLU_DISK_I_H
+#define LIBXLU_DISK_I_H
+
+#include "libxlu_internal.h"
+
+
+typedef struct {
+ XLU_Config *cfg;
+ int err;
+ void *scanner;
+ YY_BUFFER_STATE buf;
+ libxl_device_disk *disk;
+ int access_set, had_depr_prefix;
+ const char *spec;
+} DiskParseContext;
+
+void xlu__disk_err(DiskParseContext *dpc, const char *erroneous,
+ const char *message);
+
+
+#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..48a1314
--- /dev/null
+++ b/tools/libxl/libxlu_disk_l.l
@@ -0,0 +1,217 @@
+/* -*- fundamental -*- */
+/*
+ * libxlu_disk_l.l - parser for disk specification strings
+ *
+ * Copyright (C) 2011 Citrix Ltd.
+ * Author Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+/*
+ * Parsing the old xm/xend/xl-4.1 disk specs is a tricky problem,
+ * because the target string might in theory 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.
+ *
+ * In this parser we do not support such target strings in the old
+ * syntax; if the target string has to contain "," or ":" the new
+ * syntax's "target=" should be used.
+ */
+
+%{
+#include "libxlu_disk_i.h"
+
+#define YY_NO_INPUT
+
+/* 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);
+
+
+/*----- useful macros and functions used in actions -----
+ * we use macros in the actual rules to keep the actions short
+ * and particularly to avoid repeating boilerplate values such as
+ * DPC->disk, yytext, etc. */
+
+#define DPC ((DiskParseContext*)yyextra)
+
+/* Sets an enum, checking it hasn't already been set to a different value */
+#define DSET(dpc,member,enumname,str,valname) do{ \
+ if (dpc->disk->member != LIBXL_DISK_##enumname##_UNKNOWN && \
+ dpc->disk->member != LIBXL_DISK_##enumname##_##valname) { \
+ xlu__disk_err(dpc, str, TOSTRING(member) " respecified"); \
+ } else { \
+ dpc->disk->member = LIBXL_DISK_##enumname##_##valname; \
+ } \
+ }while(0)
+
+/* For actions whose patterns contain '=', finds the start of the value */
+#define FROMEQUALS (strchr(yytext,'=')+1)
+
+/* Chops the delimiter off, modifying yytext and yyleng. */
+#define STRIP(delim) do{ \
+ if (yyleng>0 && yytext[yyleng-1]==(delim)) \
+ yytext[--yyleng] = 0; \
+ }while(0)
+
+/* Sets a string value, checking it hasn't been set already. */
+#define SAVESTRING(what,loc,val) do{ \
+ savestring(DPC, what " respecified", &DPC->disk->loc, (val)); \
+ }while(0)
+static void savestring(DiskParseContext *dpc, const char *what_respecified,
+ char **update, const char *value) {
+ if (*update) {
+ if (**update) { xlu__disk_err(dpc,value,what_respecified); return; }
+ free(*update); /* do not complain about overwriting empty strings */
+ }
+ *update = strdup(value);
+}
+
+/* Sets ->readwrite from the string. This ought to be an enum, perhaps. */
+static void setaccess(DiskParseContext *dpc, const char *str) {
+ if (!strcmp(str, "r") || !strcmp(str, "ro")) {
+ dpc->disk->readwrite = 0;
+ } else if (!strcmp(str, "rw") || !strcmp(str, "w") || !strcmp(str,"")) {
+ dpc->disk->readwrite = 1;
+ } else {
+ xlu__disk_err(dpc,str,"unknown value for access");
+ }
+}
+
+/* Sets ->format from the string. IDL should provide something for this. */
+static void setformat(DiskParseContext *dpc, const char *str) {
+ if (!strcmp(str,"") ||
+ !strcmp(str,"raw")) DSET(dpc,format,FORMAT,str,RAW);
+ else if (!strcmp(str,"qcow")) DSET(dpc,format,FORMAT,str,QCOW);
+ else if (!strcmp(str,"qcow2")) DSET(dpc,format,FORMAT,str,QCOW2);
+ else if (!strcmp(str,"vhd")) DSET(dpc,format,FORMAT,str,VHD);
+ else xlu__disk_err(dpc,str,"unknown value for format");
+}
+
+#define DEPRECATE(usewhatinstead) /* not currently reported */
+
+%}
+
+%option warn
+%option nodefault
+%option batch
+%option 8bit
+%option noyywrap
+%option reentrant
+%option prefix="xlu__disk_yy"
+%option nounput
+
+%x LEXERR
+
+%%
+
+ /*----- the scanner rules which do the parsing -----*/
+
+[ \t\n]+/([^ \t\n].*)? { /* ignore whitespace before parameters */ }
+
+ /* ordinary parameters setting enums or strings */
+
+format=[^,]*,? { STRIP(','); setformat(DPC, FROMEQUALS); }
+
+cdrom,? { DPC->disk->is_cdrom = 1; }
+devtype=cdrom,? { DPC->disk->is_cdrom = 1; }
+devtype=disk,? { DPC->disk->is_cdrom = 0; }
+devtype=[^,]*,? { xlu__disk_err(DPC,yytext,"unknown value for type"); }
+
+access=[^,]*,? { STRIP(','); setaccess(DPC, FROMEQUALS); }
+
+vdev=[^,]*,? { STRIP(','); SAVESTRING("vdev", vdev, FROMEQUALS); }
+script=[^,]*,? { STRIP(','); SAVESTRING("script", script, FROMEQUALS); }
+
+ /* the target magic parameter, eats the rest of the string */
+
+target=.* { STRIP(','); SAVESTRING("target", pdev_path, FROMEQUALS); }
+
+ /* unknown parameters */
+
+[a-z][-a-z0-9]*=[^,],? { xlu__disk_err(DPC,yytext,"unknown parameter"); }
+
+ /* deprecated prefixes */
+
+ /* the "/.*" in these patterns ensures that they count as if they
+ * matched the whole string, so these patterns take precedence */
+
+(raw|qcow2?|vhd):/.* {
+ STRIP(':');
+ DPC->had_depr_prefix=1; DEPRECATE("use `[format=]...,'");
+ setformat(DPC, yytext);
+ }
+
+iscsi:|e?nbd:drbd:/.* {
+ STRIP(':');
+ DPC->had_depr_prefix=1; DEPRECATE("use `script=...'");
+ SAVESTRING("script", script, yytext);
+ }
+
+tapdisk:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
+tap2?:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
+aio:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
+ioemu:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
+file:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
+phy:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
+
+[a-z][a-z0-9]*:/([^a-z0-9].*)? {
+ xlu__disk_err(DPC,yytext,"unknown deprecated disk prefix");
+ return 0;
+ }
+
+ /* positional parameters */
+
+[^=,]*,|[^=,]+,? {
+ char *colon;
+ STRIP(',');
+
+ if (DPC->err) {
+ /* previous errors may just lead to subsequent ones */
+ } else if (!DPC->disk->pdev_path) {
+ SAVESTRING("target", pdev_path, yytext);
+ } else if (!DPC->had_depr_prefix &&
+ DPC->disk->format == LIBXL_DISK_FORMAT_UNKNOWN) {
+ setformat(DPC,yytext);
+ } else if (!DPC->disk->vdev) {
+ colon = strrchr(yytext, ':');
+ if (colon) {
+ DEPRECATE("use `devtype=...'");
+ *colon++ = 0;
+ if (!strcmp(colon,"cdrom")) {
+ DPC->disk->is_cdrom = 1;
+ } else if (!strcmp(colon,"disk")) {
+ DPC->disk->is_cdrom = 0;
+ } else {
+ xlu__disk_err(DPC,colon,"unknown deprecated type");
+ }
+ }
+ SAVESTRING("vdev", vdev, yytext);
+ } else if (!DPC->access_set) {
+ DPC->access_set = 1;
+ setaccess(DPC,yytext);
+ } else {
+ xlu__disk_err(DPC,yytext,"too many positional parameters");
+ return 0; /* don't print any more errors */
+ }
+}
+
+. {
+ BEGIN(LEXERR);
+ yymore();
+}
+<LEXERR>.* {
+ xlu__disk_err(DPC,yytext,"bad disk syntax"); return 0;
+}
diff --git a/tools/libxl/libxlutil.h b/tools/libxl/libxlutil.h
index 8a6fcbd..80c8753 100644
--- a/tools/libxl/libxlutil.h
+++ b/tools/libxl/libxlutil.h
@@ -58,4 +58,27 @@ 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, int nspecs, const char *const *specs,
+ libxl_device_disk *disk);
+ /* disk must have been initialised.
+ *
+ * On error, returns errno value. Bad strings cause EINVAL and
+ * print a message to cfg's report (that's all cfg is used for).
+ *
+ * Normally one would pass nspecs==1 and only specs[0]. But it is
+ * permitted to pass more strings in which case each is parsed as a
+ * string containing a collection of parameters (but they all refer
+ * to of the configuration for a single disk).
+ *
+ * nspecs==0 is permitted but since it does not specify some mandatory
+ * properties, it produces a run-time configuration error if the
+ * resulting disk struct is used with libxl.
+ */
+
+
#endif /* LIBXLUTIL_H */
--
1.5.6.5
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|