[Libguestfs] [PATCH] Add support for Windows dynamic disks (libldm / ldmtool).

Richard W.M. Jones rjones at redhat.com
Fri Dec 7 16:28:01 UTC 2012


From: "Richard W.M. Jones" <rjones at redhat.com>

New APIs:
  ldmtool-scan
  ldmtool-diskgroup-name
  ldmtool-diskgroup-volumes
  ldmtool-diskgroup-disks
---
 README                   |   6 +
 appliance/packagelist.in |   6 +-
 configure.ac             |   7 ++
 daemon/Makefile.am       |   5 +-
 daemon/ldm.c             | 299 +++++++++++++++++++++++++++++++++++++++++++++++
 generator/actions.ml     |  48 ++++++++
 po/POTFILES              |   1 +
 src/MAX_PROC_NR          |   2 +-
 8 files changed, 371 insertions(+), 3 deletions(-)
 create mode 100644 daemon/ldm.c

diff --git a/README b/README
index 5380415..aa9fc8e 100644
--- a/README
+++ b/README
@@ -112,6 +112,12 @@ For basic functionality and the C tools:
 
 - Linux capabilities library (libcap) (optional)
 
+- libldm and ldmtool (optional)
+  This is used to handle Windows dynamic disks.
+
+- yajl >= 2 (optional)
+  JSON parser, needed to handle the output of ldmtool.
+
 - netpbm, icoutils (optional)
   These programs are used to render icons from guests.
 
diff --git a/appliance/packagelist.in b/appliance/packagelist.in
index 9fc73c9..3ad343b 100644
--- a/appliance/packagelist.in
+++ b/appliance/packagelist.in
@@ -35,6 +35,7 @@
   iproute
   iputils
   kernel
+  libldm /* only Fedora has this for now, but we should add it to others later*/
   MAKEDEV
   nilfs-utils
   ntfsprogs
@@ -45,6 +46,7 @@
   systemd /* for /sbin/reboot and udevd */
   vim-minimal
   xz
+  yajl
   zfs-fuse
 #endif /* REDHAT */
 
@@ -61,6 +63,7 @@
   iproute
   libaugeas0
   libhivex0
+  libyajl2
   linux-image
   nilfs-tools
   ntfs-3g
@@ -91,8 +94,9 @@
   reiserfsprogs
   systemd
   vim
-  zfs-fuse
   xz
+  yajl
+  zfs-fuse
 #endif /* ARCHLINUX */
 
 acl
diff --git a/configure.ac b/configure.ac
index 5f373d3..2703b82 100644
--- a/configure.ac
+++ b/configure.ac
@@ -785,6 +785,13 @@ AS_IF([test "x$enable_fuse" != "xno"],
         ])
 AM_CONDITIONAL([HAVE_FUSE],[test "x$enable_fuse" != "xno"])
 
+dnl Check for yajl JSON library (optional).
+PKG_CHECK_MODULES([YAJL], [yajl >= 2], [
+    AC_SUBST([YAJL_CFLAGS])
+    AC_SUBST([YAJL_LIBS])
+    AC_DEFINE([HAVE_YAJL],[1],[Define to 1 if you have yajl.])
+    ],[AC_MSG_WARN([yajl not found, some features will be disabled])])
+
 dnl Check for C++ (optional, we just use this to test the header works).
 AC_PROG_CXX
 
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 46fa7c5..a05771e 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -136,6 +136,7 @@ guestfsd_SOURCES = \
 	is.c \
 	isoinfo.c \
 	labels.c \
+	ldm.c \
 	link.c \
 	ls.c \
 	luks.c \
@@ -185,6 +186,7 @@ guestfsd_LDADD = \
 	libprotocol.a \
 	$(ACL_LIBS) \
 	$(CAP_LIBS) \
+	$(YAJL_LIBS) \
 	$(SELINUX_LIB) \
 	$(AUGEAS_LIBS) \
 	$(HIVEX_LIBS) \
@@ -201,7 +203,8 @@ guestfsd_CPPFLAGS = -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib
 guestfsd_CFLAGS = \
 	$(WARN_CFLAGS) $(WERROR_CFLAGS) \
 	$(AUGEAS_CFLAGS) \
-	$(HIVEX_CFLAGS)
+	$(HIVEX_CFLAGS) \
+	$(YAJL_CFLAGS)
 
 # Manual pages and HTML files for the website.
 man_MANS = guestfsd.8
diff --git a/daemon/ldm.c b/daemon/ldm.c
new file mode 100644
index 0000000..71eaa86
--- /dev/null
+++ b/daemon/ldm.c
@@ -0,0 +1,299 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2012 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if HAVE_YAJL
+#include <yajl/yajl_tree.h>
+#endif
+
+#include "daemon.h"
+#include "actions.h"
+#include "optgroups.h"
+
+#if HAVE_YAJL
+
+GUESTFSD_EXT_CMD(str_ldmtool, ldmtool);
+
+int
+optgroup_ldm_available (void)
+{
+  return prog_exists (str_ldmtool);
+}
+
+static yajl_val
+parse_json (const char *json, const char *func)
+{
+  yajl_val tree;
+  char parse_error[1024];
+
+  if (verbose)
+    fprintf (stderr, "%s: parsing json: %s\n", func, json);
+
+  tree = yajl_tree_parse (json, parse_error, sizeof parse_error);
+  if (tree == NULL) {
+    reply_with_error ("parse error: %s",
+                      strlen (parse_error) ? parse_error : "unknown error");
+    return NULL;
+  }
+
+  /* Caller should free this by doing 'yajl_tree_free (tree);'. */
+  return tree;
+}
+
+#define TYPE_ERROR ((char **) -1)
+
+static char **
+json_value_to_string_list (yajl_val node)
+{
+  DECLARE_STRINGSBUF (strs);
+  yajl_val n;
+  size_t i, len;
+
+  if (! YAJL_IS_ARRAY (node))
+    return TYPE_ERROR;
+
+  len = YAJL_GET_ARRAY(node)->len;
+  for (i = 0; i < len; ++i) {
+    n = YAJL_GET_ARRAY(node)->values[i];
+    if (! YAJL_IS_STRING (n))
+      return TYPE_ERROR;
+    if (add_string (&strs, YAJL_GET_STRING (n)) == -1)
+      return NULL;
+  }
+  if (end_stringsbuf (&strs) == -1)
+    return NULL;
+
+  return strs.argv;
+}
+
+static char **
+parse_json_get_string_list (const char *json,
+                            const char *func, const char *cmd)
+{
+  char **ret;
+  yajl_val tree = NULL;
+
+  tree = parse_json (json, func);
+  if (tree == NULL)
+    return NULL;
+
+  ret = json_value_to_string_list (tree);
+  yajl_tree_free (tree);
+  if (ret == TYPE_ERROR) {
+    reply_with_error ("output of '%s' was not a JSON array of strings", cmd);
+    return NULL;
+  }
+  return ret;
+}
+
+static char *
+parse_json_get_object_string (const char *json, const char *key,
+                              const char *func, const char *cmd)
+{
+  char *str, *ret;
+  yajl_val tree = NULL, node;
+  size_t i, len;
+
+  tree = parse_json (json, func);
+  if (tree == NULL)
+    return NULL;
+
+  if (! YAJL_IS_OBJECT (tree))
+    goto bad_type;
+
+  len = YAJL_GET_OBJECT(tree)->len;
+  for (i = 0; i < len; ++i) {
+    if (STREQ (YAJL_GET_OBJECT(tree)->keys[i], key)) {
+      node = YAJL_GET_OBJECT(tree)->values[i];
+      str = YAJL_GET_STRING (node);
+      if (str == NULL)
+        goto bad_type;
+      ret = strdup (str);
+      if (ret == NULL)
+        reply_with_perror ("strdup");
+      yajl_tree_free (tree);
+      return ret;
+    }
+  }
+
+ bad_type:
+  reply_with_error ("output of '%s' was not a JSON object "
+                    "containing a key '%s' of type string", cmd, key);
+  yajl_tree_free (tree);
+  return NULL;
+}
+
+static char **
+parse_json_get_object_string_list (const char *json, const char *key,
+                                   const char *func, const char *cmd)
+{
+  char **ret;
+  yajl_val tree, node;
+  size_t i, len;
+
+  tree = parse_json (json, func);
+  if (tree == NULL)
+    return NULL;
+
+  if (! YAJL_IS_OBJECT (tree))
+    goto bad_type;
+
+  len = YAJL_GET_OBJECT(tree)->len;
+  for (i = 0; i < len; ++i) {
+    if (STREQ (YAJL_GET_OBJECT(tree)->keys[i], key)) {
+      node = YAJL_GET_OBJECT(tree)->values[i];
+      ret = json_value_to_string_list (node);
+      if (ret == TYPE_ERROR)
+        goto bad_type;
+      yajl_tree_free (tree);
+      return ret;
+    }
+  }
+
+ bad_type:
+  reply_with_error ("output of '%s' was not a JSON object "
+                    "containing a key '%s' of type array of strings",
+                    cmd, key);
+  yajl_tree_free (tree);
+  return NULL;
+}
+
+char **
+do_ldmtool_scan (void)
+{
+  char **ret;
+  int r;
+  char *out, *err;
+
+  r = command (&out, &err, str_ldmtool, "scan", NULL);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+  free (err);
+
+  ret = parse_json_get_string_list (out, __func__, "ldmtool scan");
+  free (out);
+  return ret;
+}
+
+char *
+do_ldmtool_diskgroup_name (const char *diskgroup)
+{
+  char *ret;
+  int r;
+  char *out, *err;
+
+  r = command (&out, &err, str_ldmtool, "show", "diskgroup", diskgroup, NULL);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+  free (err);
+
+  ret = parse_json_get_object_string (out, "name",
+                                      __func__, "ldmtool show diskgroup");
+  free (out);
+  return ret;
+}
+
+char **
+do_ldmtool_diskgroup_volumes (const char *diskgroup)
+{
+  char **ret;
+  int r;
+  char *out, *err;
+
+  r = command (&out, &err, str_ldmtool, "show", "diskgroup", diskgroup, NULL);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+  free (err);
+
+  ret = parse_json_get_object_string_list (out, "volumes",
+                                           __func__, "ldmtool show diskgroup");
+  free (out);
+  return ret;
+}
+
+char **
+do_ldmtool_diskgroup_disks (const char *diskgroup)
+{
+  char **ret;
+  int r;
+  char *out, *err;
+
+  r = command (&out, &err, str_ldmtool, "show", "diskgroup", diskgroup, NULL);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+  free (err);
+
+  ret = parse_json_get_object_string_list (out, "disks",
+                                           __func__, "ldmtool show diskgroup");
+  free (out);
+  return ret;
+}
+
+#else /* !HAVE_YAJL */
+
+int
+optgroup_ldm_available (void)
+{
+  return 0;
+}
+
+char ** __attribute__((noreturn))
+do_ldmtool_scan (void)
+{
+  abort ();
+}
+
+char * __attribute__((noreturn))
+do_ldmtool_diskgroup_name (const char *diskgroup)
+{
+  abort ();
+}
+
+char ** __attribute__((noreturn))
+do_ldmtool_diskgroup_volumes (const char *diskgroup)
+{
+  abort ();
+}
+
+char ** __attribute__((noreturn))
+do_ldmtool_diskgroup_disks (const char *diskgroup)
+{
+  abort ();
+}
+
+#endif
diff --git a/generator/actions.ml b/generator/actions.ml
index b906ff6..beb4b82 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -10526,6 +10526,54 @@ This function sets the Linux capabilities attached to C<path>.
 The capabilities set C<cap> should be passed in text form
 (see L<cap_from_text(3)>)." };
 
+  { defaults with
+    name = "ldmtool_scan";
+    style = RStringList "guids", [], [];
+    proc_nr = Some 380;
+    optional = Some "ldm";
+    tests = [];
+    shortdesc = "scan for Windows dynamic disks";
+    longdesc = "\
+This function scans for Windows dynamic disks.  It returns a list
+of identifiers (GUIDs) for all disk groups that were found.  These
+identifiers can be passed to other C<guestfs_ldmtool_*> functions." };
+
+  { defaults with
+    name = "ldmtool_diskgroup_name";
+    style = RString "name", [String "diskgroup"], [];
+    proc_nr = Some 381;
+    optional = Some "ldm";
+    tests = [];
+    shortdesc = "return the name of a Windows dynamic disk group";
+    longdesc = "\
+Return the name of a Windows dynamic disk group.  The C<diskgroup>
+parameter should be the GUID of a disk group, one element from
+the list returned by C<guestfs_ldmtool_scan>." };
+
+  { defaults with
+    name = "ldmtool_diskgroup_volumes";
+    style = RStringList "volumes", [String "diskgroup"], [];
+    proc_nr = Some 382;
+    optional = Some "ldm";
+    tests = [];
+    shortdesc = "return the volumes in a Windows dynamic disk group";
+    longdesc = "\
+Return the volumes in a Windows dynamic disk group.  The C<diskgroup>
+parameter should be the GUID of a disk group, one element from
+the list returned by C<guestfs_ldmtool_scan>." };
+
+  { defaults with
+    name = "ldmtool_diskgroup_disks";
+    style = RStringList "disks", [String "diskgroup"], [];
+    proc_nr = Some 383;
+    optional = Some "ldm";
+    tests = [];
+    shortdesc = "return the disks in a Windows dynamic disk group";
+    longdesc = "\
+Return the disks in a Windows dynamic disk group.  The C<diskgroup>
+parameter should be the GUID of a disk group, one element from
+the list returned by C<guestfs_ldmtool_scan>." };
+
 ]
 
 (* Non-API meta-commands available only in guestfish.
diff --git a/po/POTFILES b/po/POTFILES
index 675cb8d..272b62c 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -52,6 +52,7 @@ daemon/internal.c
 daemon/is.c
 daemon/isoinfo.c
 daemon/labels.c
+daemon/ldm.c
 daemon/link.c
 daemon/ls.c
 daemon/luks.c
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index 3b2f92e..f138657 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-379
+383
-- 
1.8.0.1




More information about the Libguestfs mailing list