Update FSF address.
[libguestfs.git] / inspector / virt-inspector.c
index 68f8b46..09621ab 100644 (file)
@@ -1,5 +1,5 @@
 /* virt-inspector
- * Copyright (C) 2010 Red Hat Inc.
+ * Copyright (C) 2010-2011 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
  *
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #include <config.h>
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <inttypes.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <locale.h>
 #include <assert.h>
+#include <libintl.h>
 
 #include <libxml/xmlIO.h>
 #include <libxml/xmlwriter.h>
+#include <libxml/xpath.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlsave.h>
 
 #include "progname.h"
 #include "c-ctype.h"
 guestfs_h *g;
 
 int read_only = 1;
+int live = 0;
 int verbose = 0;
 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);
 static void output_root (xmlTextWriterPtr xo, char *root);
 static void output_mountpoints (xmlTextWriterPtr xo, char *root);
 static void output_filesystems (xmlTextWriterPtr xo, char *root);
+static void output_drive_mappings (xmlTextWriterPtr xo, char *root);
 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)
@@ -84,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);
@@ -114,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;
@@ -145,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);
@@ -226,11 +241,27 @@ main (int argc, char *argv[])
    */
   assert (read_only == 1);
   assert (inspector == 1);
+  assert (live == 0);
 
   /* Must be no extra arguments on the command line. */
   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);
@@ -334,6 +365,7 @@ output_root (xmlTextWriterPtr xo, char *root)
   int i, r;
   char buf[32];
   char canonical_root[strlen (root) + 1];
+  size_t size;
 
   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystem"));
 
@@ -370,6 +402,13 @@ output_root (xmlTextWriterPtr xo, char *root)
       xmlTextWriterWriteElement (xo, BAD_CAST "product_name", BAD_CAST str));
   free (str);
 
+  str = guestfs_inspect_get_product_variant (g, root);
+  if (!str) exit (EXIT_FAILURE);
+  if (STRNEQ (str, "unknown"))
+    XMLERROR (-1,
+      xmlTextWriterWriteElement (xo, BAD_CAST "product_variant", BAD_CAST str));
+  free (str);
+
   i = guestfs_inspect_get_major_version (g, root);
   snprintf (buf, sizeof buf, "%d", i);
   XMLERROR (-1,
@@ -406,6 +445,22 @@ output_root (xmlTextWriterPtr xo, char *root)
                                            BAD_CAST str));
     free (str);
   );
+  DISABLE_GUESTFS_ERRORS_FOR (
+    str = guestfs_inspect_get_windows_current_control_set (g, root);
+    if (str)
+      XMLERROR (-1,
+                xmlTextWriterWriteElement (xo, BAD_CAST "windows_current_control_set",
+                                           BAD_CAST str));
+    free (str);
+  );
+
+  str = guestfs_inspect_get_hostname (g, root);
+  if (!str) exit (EXIT_FAILURE);
+  if (STRNEQ (str, "unknown"))
+    XMLERROR (-1,
+      xmlTextWriterWriteElement (xo, BAD_CAST "hostname",
+                                 BAD_CAST str));
+  free (str);
 
   str = guestfs_inspect_get_format (g, root);
   if (!str) exit (EXIT_FAILURE);
@@ -440,8 +495,34 @@ output_root (xmlTextWriterPtr xo, char *root)
 
   output_filesystems (xo, root);
 
+  output_drive_mappings (xo, root);
+
+  /* We need to mount everything up in order to read out the list of
+   * applications and the icon, ie. everything below this point.
+   */
+  inspect_mount_root (root);
+
   output_applications (xo, root);
 
+  /* Don't return favicon.  XXX Should we? */
+  str = guestfs_inspect_get_icon (g, root, &size,
+                                  GUESTFS_INSPECT_GET_ICON_FAVICON, 0,
+                                  -1);
+  if (!str) exit (EXIT_FAILURE);
+  if (size > 0) {
+    XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "icon"));
+    XMLERROR (-1, xmlTextWriterWriteBase64 (xo, str, 0, size));
+    XMLERROR (-1, xmlTextWriterEndElement (xo));
+  }
+  /* Note we must free (str) even if size == 0, because that indicates
+   * there was no icon.
+   */
+  free (str);
+
+  /* Unmount (see inspect_mount_root above). */
+  if (guestfs_umount_all (g) == -1)
+    exit (EXIT_FAILURE);
+
   XMLERROR (-1, xmlTextWriterEndElement (xo));
 }
 
@@ -455,6 +536,15 @@ compare_keys (const void *p1, const void *p2)
 }
 
 static int
+compare_keys_nocase (const void *p1, const void *p2)
+{
+  const char *key1 = * (char * const *) p1;
+  const char *key2 = * (char * const *) p2;
+
+  return strcasecmp (key1, key2);
+}
+
+static int
 compare_keys_len (const void *p1, const void *p2)
 {
   const char *key1 = * (char * const *) p1;
@@ -565,24 +655,59 @@ output_filesystems (xmlTextWriterPtr xo, char *root)
 }
 
 static void
+output_drive_mappings (xmlTextWriterPtr xo, char *root)
+{
+  char **drive_mappings = NULL;
+  size_t i;
+
+  DISABLE_GUESTFS_ERRORS_FOR (
+    drive_mappings = guestfs_inspect_get_drive_mappings (g, root);
+  );
+  if (drive_mappings == NULL)
+    return;
+
+  if (drive_mappings[0] == NULL) {
+    free_strings (drive_mappings);
+    return;
+  }
+
+  /* Sort by key. */
+  qsort (drive_mappings,
+         count_strings (drive_mappings) / 2, 2 * sizeof (char *),
+         compare_keys_nocase);
+
+  XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "drive_mappings"));
+
+  for (i = 0; drive_mappings[i] != NULL; i += 2) {
+    canonicalize (drive_mappings[i+1]);
+
+    XMLERROR (-1,
+              xmlTextWriterStartElement (xo, BAD_CAST "drive_mapping"));
+    XMLERROR (-1,
+              xmlTextWriterWriteAttribute (xo, BAD_CAST "name",
+                                           BAD_CAST drive_mappings[i]));
+    XMLERROR (-1,
+              xmlTextWriterWriteString (xo, BAD_CAST drive_mappings[i+1]));
+    XMLERROR (-1, xmlTextWriterEndElement (xo));
+  }
+
+  XMLERROR (-1, xmlTextWriterEndElement (xo));
+
+  free_strings (drive_mappings);
+}
+
+static void
 output_applications (xmlTextWriterPtr xo, char *root)
 {
   struct guestfs_application_list *apps;
   size_t i;
 
-  /* We need to mount everything up in order to read out the list of
-   * applications.
-   */
-  inspect_mount_root (root);
-
   /* This returns an empty list if we simply couldn't determine the
    * applications, so if it returns NULL then it's a real error.
    */
   apps = guestfs_inspect_list_applications (g, root);
   if (apps == NULL)
     exit (EXIT_FAILURE);
-  if (guestfs_umount_all (g) == -1)
-    exit (EXIT_FAILURE);
 
   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "applications"));
 
@@ -682,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);
+}