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