Richard W.M. Jones
2012-Dec-07  16:28 UTC
[Libguestfs] [PATCH] Add support for Windows dynamic disks (libldm / ldmtool).
This is just an initial version of the patch, not to be applied. It implements just the diskgroup functions, ie. corresponding to these ldmtool commands: * ldmtool scan * ldmtool show diskgroup <guid> I have chosen yajl as the JSON parsing library (don't worry, this is optional). You will also, of course, need ldmtool which is not packaged in anything except Fedora. Rich.
Richard W.M. Jones
2012-Dec-07  16:28 UTC
[Libguestfs] [PATCH] Add support for Windows dynamic disks (libldm / ldmtool).
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
Matthew Booth
2012-Dec-10  09:27 UTC
[Libguestfs] [PATCH] Add support for Windows dynamic disks (libldm / ldmtool).
On 07/12/12 16:28, Richard W.M. Jones wrote:> This is just an initial version of the patch, not to be > applied. It implements just the diskgroup functions, ie. > corresponding to these ldmtool commands: > > * ldmtool scan > * ldmtool show diskgroup <guid> > > I have chosen yajl as the JSON parsing library (don't worry, > this is optional). You will also, of course, need ldmtool > which is not packaged in anything except Fedora.There's no need to parse JSON. Why aren't you using the C api? Matt -- Matthew Booth, RHCA, RHCSS Red Hat Engineering, Virtualisation Team GPG ID: D33C3490 GPG FPR: 3733 612D 2D05 5458 8A8A 1600 3441 EA19 D33C 3490