From mboxrd@z Thu Jan 1 00:00:00 1970 From: Ian Jackson Subject: [PATCH 08/14] libxl: disks: new xlu_disk_parse function Date: Thu, 12 May 2011 15:36:38 +0100 Message-ID: <1305211004-31687-9-git-send-email-ian.jackson@eu.citrix.com> References: <1305211004-31687-1-git-send-email-ian.jackson@eu.citrix.com> <1305211004-31687-2-git-send-email-ian.jackson@eu.citrix.com> <1305211004-31687-3-git-send-email-ian.jackson@eu.citrix.com> <1305211004-31687-4-git-send-email-ian.jackson@eu.citrix.com> <1305211004-31687-5-git-send-email-ian.jackson@eu.citrix.com> <1305211004-31687-6-git-send-email-ian.jackson@eu.citrix.com> <1305211004-31687-7-git-send-email-ian.jackson@eu.citrix.com> <1305211004-31687-8-git-send-email-ian.jackson@eu.citrix.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: In-Reply-To: <1305211004-31687-8-git-send-email-ian.jackson@eu.citrix.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: xen-devel@lists.xensource.com Cc: Ian Jackson , Ian Jackson List-Id: xen-devel@lists.xenproject.org From: Ian Jackson 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 --- tools/libxl/Makefile | 3 +- tools/libxl/libxlu_disk.c | 91 ++++++++++++++++++ tools/libxl/libxlu_disk_i.h | 21 ++++ tools/libxl/libxlu_disk_l.l | 217 +++++++++++++++++++++++++++++++++++++++++++ tools/libxl/libxlutil.h | 23 +++++ 5 files changed, 354 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 538cd16..3c10051 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..52780e6 --- /dev/null +++ b/tools/libxl/libxlu_disk.c @@ -0,0 +1,91 @@ +#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; + + for (i=0; iformat == 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..8e511ea --- /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 + * + * 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 based on the string value. This ought to be an enum. */ +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 based on the string value. This ought to be an enum. */ +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(); +} +.* { + 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.7.2.5