From 73364364b62090d8f820f91688fa940e508641fe Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 3 Nov 2011 13:06:25 +0000 Subject: [PATCH] Add virt-inspector --xpath to run XPath queries directly. xmlstarlet is good, but not available in Red Hat Enterprise Linux. Build a simple but sane XPath query parser into virt-inspector directly so that we don't need any external tools. (cherry picked from commit d1ee71782ace98a11c5aabaf1f9fd5f601e08367) --- inspector/virt-inspector.c | 124 +++++++++++++++++++++++++++++++++++++++++++ inspector/virt-inspector.pod | 29 +++++++--- 2 files changed, 146 insertions(+), 7 deletions(-) diff --git a/inspector/virt-inspector.c b/inspector/virt-inspector.c index 4afce0e..c505fa4 100644 --- a/inspector/virt-inspector.c +++ b/inspector/virt-inspector.c @@ -30,6 +30,10 @@ #include #include +#include +#include +#include +#include #include "progname.h" #include "c-ctype.h" @@ -47,6 +51,7 @@ int keys_from_stdin = 0; int echo_keys = 0; const char *libvirt_uri = NULL; int inspector = 1; +static const char *xpath = NULL; static void output (char **roots); static void output_roots (xmlTextWriterPtr xo, char **roots); @@ -58,6 +63,7 @@ static void output_applications (xmlTextWriterPtr xo, char *root); static void canonicalize (char *dev); static void free_strings (char **argv); static int count_strings (char *const*argv); +static void do_xpath (const char *query); static inline char * bad_cast (char const *s) @@ -89,6 +95,7 @@ usage (int status) " -v|--verbose Verbose messages\n" " -V|--version Display version and exit\n" " -x Trace libguestfs API calls\n" + " --xpath query Perform an XPath query\n" "For more information, see the manpage %s(1).\n"), program_name, program_name, program_name, program_name); @@ -119,6 +126,7 @@ main (int argc, char *argv[]) { "keys-from-stdin", 0, 0, 0 }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, + { "xpath", 1, 0, 0 }, { 0, 0, 0, 0 } }; struct drv *drvs = NULL; @@ -150,6 +158,8 @@ main (int argc, char *argv[]) format = NULL; else format = optarg; + } else if (STREQ (long_options[option_index].name, "xpath")) { + xpath = optarg; } else { fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), program_name, long_options[option_index].name, option_index); @@ -237,6 +247,21 @@ main (int argc, char *argv[]) if (optind != argc) usage (EXIT_FAILURE); + /* XPath is modal: no drives should be specified. There must be + * one extra parameter on the command line. + */ + if (xpath) { + if (drvs != NULL) { + fprintf (stderr, _("%s: cannot use --xpath together with other options.\n"), + program_name); + exit (EXIT_FAILURE); + } + + do_xpath (xpath); + + exit (EXIT_SUCCESS); + } + /* User must have specified some drives. */ if (drvs == NULL) usage (EXIT_FAILURE); @@ -782,3 +807,102 @@ count_strings (char *const *argv) ; return c; } + +/* Run an XPath query on XML on stdin, print results to stdout. */ +static void +do_xpath (const char *query) +{ + xmlDocPtr doc; + xmlXPathContextPtr xpathCtx; + xmlXPathObjectPtr xpathObj; + xmlNodeSetPtr nodes; + char *r; + size_t i; + xmlSaveCtxtPtr saveCtx; + xmlDocPtr wrdoc; + xmlNodePtr wrnode; + + doc = xmlReadFd (STDIN_FILENO, NULL, "utf8", 0); + if (doc == NULL) { + fprintf (stderr, _("%s: unable to parse XML from stdin\n"), program_name); + exit (EXIT_FAILURE); + } + + xpathCtx = xmlXPathNewContext (doc); + if (xpathCtx == NULL) { + fprintf (stderr, _("%s: unable to create new XPath context\n"), + program_name); + exit (EXIT_FAILURE); + } + + xpathObj = xmlXPathEvalExpression (BAD_CAST query, xpathCtx); + if (xpathObj == NULL) { + fprintf (stderr, _("%s: unable to evaluate XPath expression\n"), + program_name); + exit (EXIT_FAILURE); + } + + switch (xpathObj->type) { + case XPATH_NODESET: + nodes = xpathObj->nodesetval; + + saveCtx = xmlSaveToFd (STDOUT_FILENO, NULL, XML_SAVE_NO_DECL); + if (saveCtx == NULL) { + fprintf (stderr, _("%s: xmlSaveToFd failed\n"), program_name); + exit (EXIT_FAILURE); + } + + for (i = 0; i < (size_t) nodes->nodeNr; ++i) { + wrdoc = xmlNewDoc (BAD_CAST "1.0"); + if (wrdoc == NULL) { + fprintf (stderr, _("%s: xmlNewDoc failed\n"), program_name); + exit (EXIT_FAILURE); + } + wrnode = xmlCopyNode (nodes->nodeTab[i], 1); + if (wrnode == NULL) { + fprintf (stderr, _("%s: xmlCopyNode failed\n"), program_name); + exit (EXIT_FAILURE); + } + + xmlDocSetRootElement (wrdoc, wrnode); + + if (xmlSaveDoc (saveCtx, wrdoc) == -1) { + fprintf (stderr, _("%s: xmlSaveDoc failed\n"), program_name); + exit (EXIT_FAILURE); + } + + xmlFreeDoc (wrdoc); + } + + xmlSaveClose (saveCtx); + + break; + + case XPATH_STRING: + r = (char *) xpathObj->stringval; + printf ("%s", r); + i = strlen (r); + if (i > 0 && r[i-1] != '\n') + printf ("\n"); + break; + + case XPATH_UNDEFINED: /* grrrrr ... switch-enum is a useless warning */ + case XPATH_BOOLEAN: + case XPATH_NUMBER: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + case XPATH_USERS: + case XPATH_XSLT_TREE: + default: + r = (char *) xmlXPathCastToString (xpathObj); + printf ("%s\n", r); + free (r); + } + + xmlXPathFreeObject (xpathObj); + xmlXPathFreeContext (xpathCtx); + xmlFreeDoc (doc); + + exit (EXIT_SUCCESS); +} diff --git a/inspector/virt-inspector.pod b/inspector/virt-inspector.pod index df9dfda..9a43e94 100755 --- a/inspector/virt-inspector.pod +++ b/inspector/virt-inspector.pod @@ -132,6 +132,13 @@ Display version number and exit. Enable tracing of libguestfs API calls. +=item B<--xpath> query + +Perform an XPath query on the XML on stdin, and print the result on +stdout. In this mode virt-inspector simply runs an XPath query; all +other inspection functions are disabled. See L below +for some examples. + =back =head1 OLD-STYLE COMMAND LINE ARGUMENTS @@ -327,26 +334,34 @@ installer, or one part of a multipart CD. For example: installer -=head1 USING XPATH +=head1 XPATH QUERIES + +Virt-inspector includes built in support for running XPath queries. +The reason for including XPath support directly in virt-inspector is +simply that there are no good and widely available command line +programs that can do XPath queries. The only good one is +L and that is not available on Red Hat Enterprise +Linux. -You can use the XPath query language to select parts of the XML. We -recommend using C to perform XPath queries from the -command line. +To perform an XPath query, use the I<--xpath> option. Note that in +this mode, virt-inspector simply reads XML from stdin and outputs the +query result on stdout. All other inspection features are disabled in +this mode. For example: - $ virt-inspector -d Guest | xmlstarlet sel -t -c '//filesystems' + $ virt-inspector -d Guest | virt-inspector --xpath '//filesystems' ext4 [...] $ virt-inspector -d Guest | \ - xmlstarlet sel -t -c "string(//filesystem[@dev='/dev/sda1']/type)" + virt-inspector --xpath "string(//filesystem[@dev='/dev/sda1']/type)" ext4 $ virt-inspector -d Guest | \ - xmlstarlet sel -t -v '//icon' | base64 -i -d | display - + virt-inspector --xpath 'string(//icon)' | base64 -i -d | display - [displays the guest icon, if there is one] =head1 SHELL QUOTING -- 1.8.3.1