[Libguestfs] [PATCH v6 27/41] daemon: Reimplement ‘lvs’ API in OCaml.

Richard W.M. Jones
Thu, 15 Jun 2017 17:06:17 -0000

---
 daemon/Makefile.am        |   2 +
 daemon/lvm.c              | 151 ----------------------------------------------
 daemon/lvm.ml             |  92 ++++++++++++++++++++++++++++
 daemon/lvm.mli            |  19 ++++++
 generator/actions_core.ml |   1 +
 5 files changed, 114 insertions(+), 151 deletions(-)

diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index fde09c34b..0525b4d72 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -267,6 +267,7 @@ SOURCES_MLI = \
 	is.mli \
 	ldm.mli \
 	link.mli \
+	lvm.mli \
 	mount.mli \
 	mountable.mli \
 	parted.mli \
@@ -286,6 +287,7 @@ SOURCES_ML = \
 	is.ml \
 	ldm.ml \
 	link.ml \
+	lvm.ml \
 	mount.ml \
 	parted.ml \
 	realpath.ml \
diff --git a/daemon/lvm.c b/daemon/lvm.c
index 5d12b009f..072bf53b4 100644
--- a/daemon/lvm.c
+++ b/daemon/lvm.c
@@ -103,89 +103,6 @@ convert_lvm_output (char *out, const char *prefix)
   return take_stringsbuf (&ret);
 }

-/* Filter a colon-separated output of
- *   lvs -o lv_attr,vg_name,lv_name
- * removing thin layouts, and building the device path as we expect it.
- *
- * This is used only when lvm has no -S.
- */
-static char **
-filter_convert_old_lvs_output (char *out)
-{
-  char *p, *pend;
-  CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
-
-  p = out;
-  while (p) {
-    size_t len;
-    char *saveptr;
-    char *lv_attr, *vg_name, *lv_name;
-
-    pend = strchr (p, '\n');	/* Get the next line of output. */
-    if (pend) {
-      *pend = '\0';
-      pend++;
-    }
-
-    while (*p && c_isspace (*p))	/* Skip any leading whitespace. */
-      p++;
-
-    /* Sigh, skip trailing whitespace too.  "pvs", I'm looking at
you. */
-    len = strlen (p)-1;
-    while (*p && c_isspace (p[len]))
-      p[len--] = '\0';
-
-    if (!*p) {			/* Empty line?  Skip it. */
-    skip_line:
-      p = pend;
-      continue;
-    }
-
-    lv_attr = strtok_r (p, ":", &saveptr);
-    if (!lv_attr)
-      goto skip_line;
-
-    vg_name = strtok_r (NULL, ":", &saveptr);
-    if (!vg_name)
-      goto skip_line;
-
-    lv_name = strtok_r (NULL, ":", &saveptr);
-    if (!lv_name)
-      goto skip_line;
-
-    /* Ignore thin layouts (RHBZ#1278878). */
-    if (lv_attr[0] == 't')
-      goto skip_line;
-
-    /* Ignore activationskip (RHBZ#1306666). */
-    if (strlen (lv_attr) >= 10 && lv_attr[9] == 'k')
-      goto skip_line;
-
-    /* Ignore "unknown device" message (RHBZ#1054761). */
-    if (STRNEQ (p, "unknown device")) {
-      char buf[256];
-
-      snprintf (buf, sizeof buf, "/dev/%s/%s", vg_name, lv_name);
-      if (add_string (&ret, buf) == -1) {
-        free (out);
-        return NULL;
-      }
-    }
-
-    p = pend;
-  }
-
-  free (out);
-
-  if (ret.size > 0)
-    sort_strings (ret.argv, ret.size);
-
-  if (end_stringsbuf (&ret) == -1)
-    return NULL;
-
-  return take_stringsbuf (&ret);
-}
-
 char **
 do_pvs (void)
 {
@@ -222,74 +139,6 @@ do_vgs (void)
   return convert_lvm_output (out, NULL);
 }

-/* Check whether lvs has -S to filter its output.
- * It is available only in lvm2 >= 2.02.107.
- */
-static int
-test_lvs_has_S_opt (void)
-{
-  static int result = -1;
-  if (result != -1)
-    return result;
-
-  CLEANUP_FREE char *out = NULL;
-  CLEANUP_FREE char *err = NULL;
-
-  int r = command (&out, &err, str_lvm, "lvs",
"--help", NULL);
-  if (r == -1) {
-    reply_with_error ("lvm lvs --help: %s", err);
-    return -1;
-  }
-
-  if (strstr (out, "-S") == NULL)
-    result = 0;
-  else
-    result = 1;
-
-  return result;
-}
-
-char **
-do_lvs (void)
-{
-  char *out;
-  CLEANUP_FREE char *err = NULL;
-  int r;
-  const int has_S = test_lvs_has_S_opt ();
-
-  if (has_S < 0)
-    return NULL;
-
-  if (has_S > 0) {
-    r = command (&out, &err,
-                 str_lvm, "lvs",
-                 "-o", "vg_name,lv_name",
-                 "-S", "lv_role=public &&
lv_skip_activation!=yes",
-                 "--noheadings",
-                 "--separator", "/", NULL);
-    if (r == -1) {
-      reply_with_error ("%s", err);
-      free (out);
-      return NULL;
-    }
-
-    return convert_lvm_output (out, "/dev/");
-  } else {
-    r = command (&out, &err,
-                 str_lvm, "lvs",
-                 "-o", "lv_attr,vg_name,lv_name",
-                 "--noheadings",
-                 "--separator", ":", NULL);
-    if (r == -1) {
-      reply_with_error ("%s", err);
-      free (out);
-      return NULL;
-    }
-
-    return filter_convert_old_lvs_output (out);
-  }
-}
-
 /* These were so complex to implement that I ended up auto-generating
  * the code.  That code is in stubs.c, and it is generated as usual
  * by generator.ml.
diff --git a/daemon/lvm.ml b/daemon/lvm.ml
new file mode 100644
index 000000000..55421b628
--- /dev/null
+++ b/daemon/lvm.ml
@@ -0,0 +1,92 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 Red Hat Inc.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Std_utils
+
+open Utils
+
+let lvs_has_S_opt = lazy (
+  let out = command "lvm" ["lvs"; "--help"] in
+  String.find out "-S" >= 0
+)
+
+let rec lvs () +  let has_S = Lazy.force lvs_has_S_opt in
+  if has_S then (
+    let out = command "lvm" ["lvs";
+                             "-o"; "vg_name,lv_name";
+                             "-S"; "lv_role=public &&
lv_skip_activation!=yes";
+                             "--noheadings";
+                             "--separator"; "/"] in
+    convert_lvm_output ~prefix:"/dev/" out
+  )
+  else (
+    let out = command "lvm" ["lvs";
+                             "-o";
"lv_attr,vg_name,lv_name";
+                             "--noheadings";
+                             "--separator"; ":"] in
+    filter_convert_old_lvs_output out
+  )
+
+and convert_lvm_output ?(prefix = "") out +  let lines =
String.nsplit "\n" out in
+
+  (* Skip leading and trailing ("pvs", I'm looking at you)
whitespace. *)
+  let lines = List.map String.trim lines in
+
+  (* Skip empty lines. *)
+  let lines = List.filter ((<>) "") lines in
+
+  (* Ignore "unknown device" message (RHBZ#1054761). *)
+  let lines = List.filter ((<>) "unknown device") lines in
+
+  (* Add a prefix? *)
+  let lines = List.map ((^) prefix) lines in
+
+  (* Sort and return. *)
+  List.sort compare lines
+
+(* Filter a colon-separated output of
+ *   lvs -o lv_attr,vg_name,lv_name
+ * removing thin layouts, and building the device path as we expect it.
+ *
+ * This is used only when lvm has no -S.
+ *)
+and filter_convert_old_lvs_output out +  let lines = String.nsplit
"\n" out in
+  let lines = List.map String.trim lines in
+  let lines = List.filter ((<>) "") lines in
+  let lines = List.filter ((<>) "unknown device") lines in
+
+  let lines = filter_map (
+    fun line ->
+      match String.nsplit ":" line with
+      | [ lv_attr; vg_name; lv_name ] ->
+         (* Ignore thin layouts (RHBZ#1278878). *)
+         if String.length lv_attr > 0 && lv_attr.[0] = 't'
then None
+         (* Ignore activationskip (RHBZ#1306666). *)
+         else if String.length lv_attr > 9 && lv_attr.[9] =
'k' then None
+         else
+           Some (sprintf "/dev/%s/%s" vg_name lv_name)
+      | _ ->
+         None
+  ) lines in
+
+  List.sort compare lines
diff --git a/daemon/lvm.mli b/daemon/lvm.mli
new file mode 100644
index 000000000..f254728cb
--- /dev/null
+++ b/daemon/lvm.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 Red Hat Inc.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+val lvs : unit -> string list
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 331a5feb1..f6f006eee 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -1732,6 +1732,7 @@ See also C<guestfs_vgs_full>." };
   { defaults with
     name = "lvs"; added = (0, 0, 4);
     style = RStringList (RDevice, "logvols"), [], [];
+    impl = OCaml "Lvm.lvs";
     optional = Some "lvm2";
     tests = [
       InitBasicFSonLVM, Always, TestResult (
--
2.13.0