summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2008-04-15 18:30:49 +0100
committerRichard W.M. Jones <rjones@redhat.com>2008-04-15 18:30:49 +0100
commit81294675f6a5058a3381871f1dc99c806922d77c (patch)
tree63e8e8bcf5955405749559b45e39e2fc556bdc10
parentb9320ec4678a8a7bb88a8b8aa72805b79ce48daf (diff)
downloadvirt-top-81294675f6a5058a3381871f1dc99c806922d77c.zip
virt-top-81294675f6a5058a3381871f1dc99c806922d77c.tar.gz
virt-top-81294675f6a5058a3381871f1dc99c806922d77c.tar.xz
Metadata parser.
-rw-r--r--.hgignore3
-rw-r--r--Make.rules.in7
-rw-r--r--virt-df/.depend17
-rw-r--r--virt-df/Makefile.in8
-rw-r--r--virt-df/virt_df_lvm2.ml40
-rw-r--r--virt-df/virt_df_lvm2_lexer.mll165
-rw-r--r--virt-df/virt_df_lvm2_metadata.ml65
-rw-r--r--virt-df/virt_df_lvm2_metadata.mli38
-rw-r--r--virt-df/virt_df_lvm2_parser.mly70
9 files changed, 404 insertions, 9 deletions
diff --git a/.hgignore b/.hgignore
index f8063da..f78c6f6 100644
--- a/.hgignore
+++ b/.hgignore
@@ -41,3 +41,6 @@ virt-df/virt_df_gettext.ml
virt-top/virt_top_gettext.ml
po/*.mo
po/*.po.bak
+virt-df/virt_df_lvm2_lexer.ml
+virt-df/virt_df_lvm2_parser.ml
+virt-df/virt_df_lvm2_parser.mli \ No newline at end of file
diff --git a/Make.rules.in b/Make.rules.in
index b22fdf6..a25d485 100644
--- a/Make.rules.in
+++ b/Make.rules.in
@@ -40,6 +40,11 @@ else
$(OCAMLOPT) $(OCAMLOPTFLAGS) $(OCAMLOPTINCS) -c $<
endif
+%.ml %.mli: %.mly
+ ocamlyacc $<
+.mll.ml:
+ ocamllex $<
+
# Dependencies.
depend: .depend
@@ -60,4 +65,4 @@ endif
.PHONY: depend dist check-manifest dpkg doc
-.SUFFIXES: .cmo .cmi .cmx .ml .mli .mll
+.SUFFIXES: .cmo .cmi .cmx .ml .mli .mll .mly
diff --git a/virt-df/.depend b/virt-df/.depend
index d253040..e7cd81e 100644
--- a/virt-df/.depend
+++ b/virt-df/.depend
@@ -1,3 +1,4 @@
+virt_df_lvm2_parser.cmi: virt_df_lvm2_metadata.cmi
virt_df_ext2.cmo: virt_df_gettext.cmo virt_df.cmi \
/usr/lib64/ocaml/bitmatch/bitmatch.cmi virt_df_ext2.cmi
virt_df_ext2.cmx: virt_df_gettext.cmx virt_df.cmx \
@@ -6,10 +7,18 @@ virt_df_linux_swap.cmo: virt_df_gettext.cmo virt_df.cmi \
/usr/lib64/ocaml/bitmatch/bitmatch.cmi virt_df_linux_swap.cmi
virt_df_linux_swap.cmx: virt_df_gettext.cmx virt_df.cmx \
/usr/lib64/ocaml/bitmatch/bitmatch.cmi virt_df_linux_swap.cmi
-virt_df_lvm2.cmo: virt_df_gettext.cmo virt_df.cmi \
- /usr/lib64/ocaml/bitmatch/bitmatch.cmi virt_df_lvm2.cmi
-virt_df_lvm2.cmx: virt_df_gettext.cmx virt_df.cmx \
- /usr/lib64/ocaml/bitmatch/bitmatch.cmi virt_df_lvm2.cmi
+virt_df_lvm2_lexer.cmo: virt_df_lvm2_parser.cmi virt_df.cmi
+virt_df_lvm2_lexer.cmx: virt_df_lvm2_parser.cmx virt_df.cmx
+virt_df_lvm2_metadata.cmo: virt_df_lvm2_metadata.cmi
+virt_df_lvm2_metadata.cmx: virt_df_lvm2_metadata.cmi
+virt_df_lvm2.cmo: virt_df_lvm2_metadata.cmi virt_df_lvm2_lexer.cmo \
+ virt_df_gettext.cmo virt_df.cmi /usr/lib64/ocaml/bitmatch/bitmatch.cmi \
+ virt_df_lvm2.cmi
+virt_df_lvm2.cmx: virt_df_lvm2_metadata.cmx virt_df_lvm2_lexer.cmx \
+ virt_df_gettext.cmx virt_df.cmx /usr/lib64/ocaml/bitmatch/bitmatch.cmi \
+ virt_df_lvm2.cmi
+virt_df_lvm2_parser.cmo: virt_df_lvm2_metadata.cmi virt_df_lvm2_parser.cmi
+virt_df_lvm2_parser.cmx: virt_df_lvm2_metadata.cmx virt_df_lvm2_parser.cmi
virt_df_main.cmo: virt_df_gettext.cmo virt_df.cmi \
../libvirt/libvirt_version.cmi ../libvirt/libvirt.cmi
virt_df_main.cmx: virt_df_gettext.cmx virt_df.cmx \
diff --git a/virt-df/Makefile.in b/virt-df/Makefile.in
index 4a56d2d..4fb088c 100644
--- a/virt-df/Makefile.in
+++ b/virt-df/Makefile.in
@@ -39,6 +39,9 @@ OBJS := \
virt_df.cmo \
virt_df_ext2.cmo \
virt_df_linux_swap.cmo \
+ virt_df_lvm2_metadata.cmo \
+ virt_df_lvm2_parser.cmo \
+ virt_df_lvm2_lexer.cmo \
virt_df_lvm2.cmo \
virt_df_mbr.cmo \
virt_df_main.cmo
@@ -82,6 +85,11 @@ virt-df.opt: $(XOBJS)
$(OCAMLOPTPACKAGES) $(OCAMLOPTFLAGS) $(OCAMLOPTLIBS) \
../libvirt/mllibvirt.cmxa -o $@ $^
+# 'make depend' doesn't catch these dependencies because the .mli file
+# is auto-generated.
+virt_df_lvm2_parser.cmo: virt_df_lvm2_parser.mli
+virt_df_lvm2_parser.cmx: virt_df_lvm2_parser.mli
+
# Manual page.
ifeq ($(HAVE_PERLDOC),perldoc)
virt-df.1: virt-df.pod
diff --git a/virt-df/virt_df_lvm2.ml b/virt-df/virt_df_lvm2.ml
index 16d8e89..fcf1fd2 100644
--- a/virt-df/virt_df_lvm2.ml
+++ b/virt-df/virt_df_lvm2.ml
@@ -24,6 +24,8 @@ open Printf
open Virt_df_gettext.Gettext
open Virt_df
+open Virt_df_lvm2_metadata
+
let plugin_name = "LVM2"
let sector_size = 512
@@ -64,9 +66,16 @@ and read_pv_label dev =
metadata_length : 32 : littleendian (* length of metadata (bytes) *)
when Bitmatch.string_of_bitstring labelone = "LABELONE" &&
Bitmatch.string_of_bitstring lvm2_ver = "LVM2 001" ->
+
+ (* Metadata offset is relative to end of PV label. *)
let metadata_offset = metadata_offset +* 0x1000_l in
+ (* Metadata length appears to include the trailing \000 which
+ * we don't want.
+ *)
+ let metadata_length = metadata_length -* 1_l in
+
let metadata = read_metadata dev metadata_offset metadata_length in
- (*prerr_endline metadata;*)
+
let uuid = Bitmatch.string_of_bitstring uuid in
uuid, metadata
@@ -101,11 +110,34 @@ and read_metadata dev offset32 len32 =
* (as devices) and return them. Note that we don't try to detect
* what is on these LVs - that will be done in the main code.
*)
-let list_lvs devs =
- (* Read the UUID and metadata (again) from each device. *)
- let uuidmetas = List.map read_pv_label devs in
+let rec list_lvs devs =
+ (* Read the UUID and metadata (again) from each device to end up with
+ * an assoc list of PVs, keyed on the UUID.
+ *)
+ let pvs = List.map read_pv_label devs in
+
+ (* Parse the metadata using the external lexer/parser. *)
+ let pvs = List.map (
+ fun (uuid, metadata) ->
+ eprintf "parsing: %s\n<<<<\n" metadata;
+ uuid, Virt_df_lvm2_lexer.parse_lvm2_metadata_from_string metadata
+ ) pvs in
+
+ (* Print the parsed metadata. *)
+ List.iter (
+ fun (uuid, metadata) ->
+ eprintf "metadata for UUID %s:\n" uuid;
+ output_metadata stderr metadata
+ ) pvs;
+
[]
+
+
+
+
+
+
(* Register with main code. *)
let () =
lvm_type_register plugin_name probe_pv list_lvs
diff --git a/virt-df/virt_df_lvm2_lexer.mll b/virt-df/virt_df_lvm2_lexer.mll
new file mode 100644
index 0000000..2dbe7e5
--- /dev/null
+++ b/virt-df/virt_df_lvm2_lexer.mll
@@ -0,0 +1,165 @@
+(* 'df' command for virtual domains.
+ (C) Copyright 2007-2008 Richard W.M. Jones, Red Hat Inc.
+ http://libvirt.org/
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *)
+
+(* Scanner for LVM2 metadata.
+ * ocamllex tutorial:
+ * http://plus.kaist.ac.kr/~shoh/ocaml/ocamllex-ocamlyacc/ocamllex-tutorial/
+ *)
+
+{
+ open Printf
+ open Lexing
+
+ open Virt_df
+ open Virt_df_lvm2_parser
+
+ (* Temporary buffer used for parsing strings, etc. *)
+ let tmp = Buffer.create 80
+
+ exception Error of string
+}
+
+let digit = ['0'-'9']
+let alpha = ['a'-'z' 'A'-'Z']
+let alphau = ['a'-'z' 'A'-'Z' '_']
+let alnum = ['a'-'z' 'A'-'Z' '0'-'9']
+let alnumu = ['a'-'z' 'A'-'Z' '0'-'9' '_']
+let ident = alphau alnumu*
+
+let whitespace = [' ' '\t' '\r' '\n']+
+
+let escaped_char = '\\' _
+
+rule token = parse
+ (* ignore whitespace and comments *)
+ | whitespace
+ | '#' [^ '\n']*
+ { token lexbuf }
+
+ (* scan single character tokens *)
+ | '{' { LBRACE }
+ | '}' { RBRACE }
+ | '[' { LSQUARE }
+ | ']' { RSQUARE }
+ | '=' { EQ }
+ | ',' { COMMA }
+
+ (* strings - see LVM2/lib/config/config.c *)
+ | '"'
+ {
+ Buffer.reset tmp;
+ STRING (dq_string lexbuf)
+ }
+ | '\''
+ {
+ Buffer.reset tmp;
+ STRING (dq_string lexbuf)
+ }
+
+ (* floats *)
+ | ('-'? digit+ '.' digit*) as f
+ {
+ let f = float_of_string f in
+ FLOAT f
+ }
+
+ (* integers *)
+ | ('-'? digit+) as i
+ {
+ let i = Int64.of_string i in
+ INT i
+ }
+
+ (* identifiers *)
+ | ident as id
+ { IDENT id }
+
+ (* end of file *)
+ | eof
+ { EOF }
+
+ | _ as c
+ { raise (Error (sprintf "%c: invalid character in input" c)) }
+
+and dq_string = parse
+ | '"'
+ { Buffer.contents tmp }
+ | escaped_char as str
+ { Buffer.add_char tmp str.[1]; dq_string lexbuf }
+ | eof
+ { raise (Error "unterminated string in metadata") }
+ | _ as c
+ { Buffer.add_char tmp c; dq_string lexbuf }
+
+and q_string = parse
+ | '\''
+ { Buffer.contents tmp }
+ | escaped_char as str
+ { Buffer.add_char tmp str.[1]; q_string lexbuf }
+ | eof
+ { raise (Error "unterminated string in metadata") }
+ | _ as c
+ { Buffer.add_char tmp c; q_string lexbuf }
+
+{
+ (* Demonstration of how to wrap the token function
+ with extra debugging statements:
+ let token lexbuf =
+ try
+ let r = token lexbuf in
+ if debug then
+ eprintf "Lexer: token returned is %s\n"
+ (match r with
+ | LBRACE -> "LBRACE"
+ | RBRACE -> "RBRACE"
+ | LSQUARE -> "LSQUARE"
+ | RSQUARE -> "RSQUARE"
+ | EQ -> "EQ"
+ | COMMA -> "COMMA"
+ | STRING s -> sprintf "STRING(%S)" s
+ | INT i -> sprintf "INT(%Ld)" i
+ | FLOAT f -> sprintf "FLOAT(%g)" f
+ | IDENT s -> sprintf "IDENT(%s)" s
+ | EOF -> "EOF");
+ r
+ with
+ exn ->
+ prerr_endline (Printexc.to_string exn);
+ raise exn
+ *)
+
+ (* Lex and parse input.
+ *
+ * Return the parsed metadata structure if everything went to plan.
+ * Raises [Error msg] if there was some parsing problem.
+ *)
+ let rec parse_lvm2_metadata_from_string str =
+ let lexbuf = Lexing.from_string str in
+ parse_lvm2_metadata lexbuf
+ and parse_lvm2_metadata_from_channel chan =
+ let lexbuf = Lexing.from_channel chan in
+ parse_lvm2_metadata lexbuf
+ and parse_lvm2_metadata lexbuf =
+ try
+ input token lexbuf
+ with
+ | Error _ as exn -> raise exn
+ | Parsing.Parse_error -> raise (Error "Parse error")
+ | exn -> raise (Error ("Exception: " ^ Printexc.to_string exn))
+}
diff --git a/virt-df/virt_df_lvm2_metadata.ml b/virt-df/virt_df_lvm2_metadata.ml
new file mode 100644
index 0000000..d293577
--- /dev/null
+++ b/virt-df/virt_df_lvm2_metadata.ml
@@ -0,0 +1,65 @@
+(* 'df' command for virtual domains. -*- text -*-
+ (C) Copyright 2007-2008 Richard W.M. Jones, Red Hat Inc.
+ http://libvirt.org/
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *)
+
+(* Part of the parser for LVM2 metadata. *)
+
+type metadata = metastmt list
+
+and metastmt = string * metavalue
+
+and metavalue =
+ | Metadata of metadata (* name { ... } *)
+ | String of string (* name = "..." *)
+ | Int of int64
+ | Float of float
+ | List of metavalue list (* name = [...] *)
+
+let rec output_metadata chan md =
+ _output_metadata chan "" md
+
+and _output_metadata chan prefix = function
+ | [] -> ()
+ | (name, value) :: rest ->
+ output_string chan prefix;
+ output_string chan name;
+ output_string chan " = ";
+ output_metavalue chan prefix value;
+ output_string chan "\n";
+ _output_metadata chan prefix rest
+
+and output_metavalue chan prefix = function
+ | Metadata md ->
+ output_string chan "{\n";
+ _output_metadata chan (prefix ^ " ") md;
+ output_string chan prefix;
+ output_string chan "}\n";
+ | String str ->
+ output_char chan '"';
+ output_string chan str;
+ output_char chan '"';
+ | Int i ->
+ output_string chan (Int64.to_string i)
+ | Float f ->
+ output_string chan (string_of_float f)
+ | List [] -> ()
+ | List [x] -> output_metavalue chan prefix x
+ | List (x :: xs) ->
+ output_metavalue chan prefix x;
+ output_string chan ", ";
+ output_metavalue chan prefix (List xs)
diff --git a/virt-df/virt_df_lvm2_metadata.mli b/virt-df/virt_df_lvm2_metadata.mli
new file mode 100644
index 0000000..b7e821b
--- /dev/null
+++ b/virt-df/virt_df_lvm2_metadata.mli
@@ -0,0 +1,38 @@
+(* 'df' command for virtual domains. -*- text -*-
+ (C) Copyright 2007-2008 Richard W.M. Jones, Red Hat Inc.
+ http://libvirt.org/
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *)
+
+(* Part of the parser for LVM2 metadata. *)
+
+type metadata = metastmt list
+
+and metastmt = string * metavalue
+
+and metavalue =
+ | Metadata of metadata (* name { ... } *)
+ | String of string (* name = "..." *)
+ | Int of int64
+ | Float of float
+ | List of metavalue list (* name = [...] *)
+
+val output_metadata : out_channel -> metadata -> unit
+(** This function prints out the metadata on the selected channel.
+
+ The output format isn't particularly close to the input
+ format. This is just for debugging purposes.
+*)
diff --git a/virt-df/virt_df_lvm2_parser.mly b/virt-df/virt_df_lvm2_parser.mly
new file mode 100644
index 0000000..9f47ced
--- /dev/null
+++ b/virt-df/virt_df_lvm2_parser.mly
@@ -0,0 +1,70 @@
+/* 'df' command for virtual domains. -*- text -*-
+ (C) Copyright 2007-2008 Richard W.M. Jones, Red Hat Inc.
+ http://libvirt.org/
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Parser for LVM2 metadata.
+ ocamlyacc tutorial:
+ http://plus.kaist.ac.kr/~shoh/ocaml/ocamllex-ocamlyacc/ocamlyacc-tutorial/
+ */
+
+%{
+ open Virt_df_lvm2_metadata
+%}
+
+%token LBRACE RBRACE /* { } */
+%token LSQUARE RSQUARE /* [ ] */
+%token EQ /* = */
+%token COMMA /* , */
+%token <string> STRING /* "string" */
+%token <int64> INT /* an integer */
+%token <float> FLOAT /* a float */
+%token <string> IDENT /* a naked keyword/identifier */
+%token EOF /* end of file */
+
+%start input
+%type <Virt_df_lvm2_metadata.metadata> input
+
+%%
+
+input : lines EOF { List.rev $1 }
+ ;
+
+lines : /* empty */ { prerr_endline "empty line"; [] }
+ | lines line { prerr_endline "input line"; $2 :: $1 }
+ ;
+
+line : /* empty */ /* These dummy entries get removed after parsing. */
+ { ("", String "") }
+ | IDENT EQ value
+ { ($1, $3) }
+ | IDENT LBRACE lines RBRACE
+ { ($1, Metadata (List.rev $3)) }
+ ;
+
+value : STRING { String $1 }
+ | INT { Int $1 }
+ | FLOAT { Float $1 }
+ | LSQUARE list RSQUARE
+ { List (List.rev $2) }
+ ;
+
+list : /* empty */ { [] }
+ | value { [$1] }
+ | list COMMA value
+ { $3 :: $1 }
+ ;