From: "Richard W.M. Jones" <rjones(a)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