inspector: Introductory documentation for XML format.
[libguestfs.git] / fish / virt.c
index 9c4ce1a..728f9c2 100644 (file)
@@ -1,4 +1,4 @@
-/* guestfish - the filesystem interactive shell
+/* libguestfs - guestfish and guestmount shared option parsing
  * Copyright (C) 2010 Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -30,9 +30,9 @@
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 
-#include "fish.h"
+#include "guestfs.h"
 
-static int add_drives_from_node_set (xmlDocPtr doc, xmlNodeSetPtr nodes);
+#include "options.h"
 
 /* Implements the guts of the '-d' option.
  *
@@ -56,7 +56,7 @@ add_libvirt_drives (const char *guest)
     LIBXML_TEST_VERSION;
   }
 
-  int r = -1, nr_added = 0;
+  int r = -1, nr_added = 0, i;
   virErrorPtr err;
   virConnectPtr conn = NULL;
   virDomainPtr dom = NULL;
@@ -69,29 +69,29 @@ add_libvirt_drives (const char *guest)
   conn = virConnectOpenReadOnly (libvirt_uri);
   if (!conn) {
     err = virGetLastError ();
-    fprintf (stderr, _("guestfish: could not connect to libvirt (code %d, domain %d): %s\n"),
-             err->code, err->domain, err->message);
+    fprintf (stderr, _("%s: could not connect to libvirt (code %d, domain %d): %s\n"),
+             program_name, err->code, err->domain, err->message);
     goto cleanup;
   }
 
   dom = virDomainLookupByName (conn, guest);
   if (!dom) {
     err = virConnGetLastError (conn);
-    fprintf (stderr, _("guestfish: no libvirt domain called '%s': %s\n"),
-             guest, err->message);
+    fprintf (stderr, _("%s: no libvirt domain called '%s': %s\n"),
+             program_name, guest, err->message);
     goto cleanup;
   }
   if (!read_only) {
     virDomainInfo info;
     if (virDomainGetInfo (dom, &info) == -1) {
       err = virConnGetLastError (conn);
-      fprintf (stderr, _("guestfish: error getting domain info about '%s': %s\n"),
-               guest, err->message);
+      fprintf (stderr, _("%s: error getting domain info about '%s': %s\n"),
+               program_name, guest, err->message);
       goto cleanup;
     }
     if (info.state != VIR_DOMAIN_SHUTOFF) {
-      fprintf (stderr, _("guestfish: error: '%s' is a live virtual machine.\nYou must use '--ro' because write access to a running virtual machine can\ncause disk corruption.\n"),
-               guest);
+      fprintf (stderr, _("%s: error: '%s' is a live virtual machine.\nYou must use '--ro' because write access to a running virtual machine can\ncause disk corruption.\n"),
+               program_name, guest);
       goto cleanup;
     }
   }
@@ -101,8 +101,8 @@ add_libvirt_drives (const char *guest)
 
   if (!xml) {
     err = virConnGetLastError (conn);
-    fprintf (stderr, _("guestfish: error reading libvirt XML information about '%s': %s\n"),
-             guest, err->message);
+    fprintf (stderr, _("%s: error reading libvirt XML information about '%s': %s\n"),
+             program_name, guest, err->message);
     goto cleanup;
   }
 
@@ -111,39 +111,98 @@ add_libvirt_drives (const char *guest)
    */
   doc = xmlParseMemory (xml, strlen (xml));
   if (doc == NULL) {
-    fprintf (stderr, _("guestfish: unable to parse XML information returned by libvirt\n"));
+    fprintf (stderr, _("%s: unable to parse XML information returned by libvirt\n"),
+             program_name);
     goto cleanup;
   }
 
   xpathCtx = xmlXPathNewContext (doc);
   if (xpathCtx == NULL) {
-    fprintf (stderr, _("guestfish: unable to create new XPath context\n"));
+    fprintf (stderr, _("%s: unable to create new XPath context\n"),
+             program_name);
     goto cleanup;
   }
 
-  xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk/source/@dev",
-                                     xpathCtx);
+  /* This gives us a set of all the <disk> nodes. */
+  xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx);
   if (xpathObj == NULL) {
-    fprintf (stderr, _("guestfish: unable to evaluate XPath expression\n"));
+    fprintf (stderr, _("%s: unable to evaluate XPath expression\n"),
+             program_name);
     goto cleanup;
   }
 
-  nr_added += add_drives_from_node_set (doc, xpathObj->nodesetval);
+  xmlNodeSetPtr nodes = xpathObj->nodesetval;
+  for (i = 0; i < nodes->nodeNr; ++i) {
+    xmlXPathObjectPtr xpfilename;
+    xmlXPathObjectPtr xpformat;
+
+    /* Change the context to the current <disk> node.
+     * DV advises to reset this before each search since older versions of
+     * libxml2 might overwrite it.
+     */
+    xpathCtx->node = nodes->nodeTab[i];
+
+    /* Filename can be in <source dev=..> or <source file=..> attribute. */
+    xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx);
+    if (xpfilename == NULL ||
+        xpfilename->nodesetval == NULL ||
+        xpfilename->nodesetval->nodeNr == 0) {
+      xmlXPathFreeObject (xpfilename);
+      xpathCtx->node = nodes->nodeTab[i];
+      xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx);
+      if (xpfilename == NULL ||
+          xpfilename->nodesetval == NULL ||
+          xpfilename->nodesetval->nodeNr == 0) {
+        xmlXPathFreeObject (xpfilename);
+        continue;               /* disk filename not found, skip this */
+      }
+    }
 
-  xmlXPathFreeObject (xpathObj); xpathObj = NULL;
+    assert (xpfilename->nodesetval->nodeTab[0]);
+    assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
+    xmlAttrPtr attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
+    char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
+
+    /* Get the disk format (may not be set). */
+    xpathCtx->node = nodes->nodeTab[i];
+    xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx);
+    char *format = NULL;
+    if (xpformat != NULL &&
+        xpformat->nodesetval &&
+        xpformat->nodesetval->nodeNr > 0) {
+      assert (xpformat->nodesetval->nodeTab[0]);
+      assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
+      attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0];
+      format = (char *) xmlNodeListGetString (doc, attr->children, 1);
+    }
 
-  xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk/source/@file",
-                                     xpathCtx);
-  if (xpathObj == NULL) {
-    fprintf (stderr, _("guestfish: unable to evaluate XPath expression\n"));
-    goto cleanup;
-  }
+    /* Add the disk, with optional format. */
+    struct guestfs_add_drive_opts_argv optargs = { .bitmask = 0 };
+    if (read_only) {
+      optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
+      optargs.readonly = read_only;
+    }
+    if (format) {
+      optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
+      optargs.format = format;
+    }
+
+    int t = guestfs_add_drive_opts_argv (g, filename, &optargs);
+
+    xmlFree (filename);
+    xmlFree (format);
+    xmlXPathFreeObject (xpfilename);
+    xmlXPathFreeObject (xpformat);
+
+    if (t == -1)
+      goto cleanup;
 
-  nr_added += add_drives_from_node_set (doc, xpathObj->nodesetval);
+    nr_added++;
+  }
 
   if (nr_added == 0) {
-    fprintf (stderr, _("guestfish: libvirt domain '%s' has no disks\n"),
-             guest);
+    fprintf (stderr, _("%s: libvirt domain '%s' has no disks\n"),
+             program_name, guest);
     goto cleanup;
   }
 
@@ -160,32 +219,3 @@ cleanup:
 
   return r;
 }
-
-static int
-add_drives_from_node_set (xmlDocPtr doc, xmlNodeSetPtr nodes)
-{
-  if (!nodes)
-    return 0;
-
-  int i;
-
-  for (i = 0; i < nodes->nodeNr; ++i) {
-    assert (nodes->nodeTab[i]);
-    assert (nodes->nodeTab[i]->type == XML_ATTRIBUTE_NODE);
-    xmlAttrPtr attr = (xmlAttrPtr) nodes->nodeTab[i];
-
-    char *device = (char *) xmlNodeListGetString (doc, attr->children, 1);
-
-    int r;
-    if (!read_only)
-      r = guestfs_add_drive (g, device);
-    else
-      r = guestfs_add_drive_ro (g, device);
-    if (r == -1)
-      exit (EXIT_FAILURE);
-
-    xmlFree (device);
-  }
-
-  return nodes->nodeNr;
-}