ocaml: Compile OCaml bindings and tests with -warn-error.
[libguestfs.git] / src / virt.c
index a23ac0a..a95b029 100644 (file)
@@ -53,6 +53,12 @@ init_libxml2 (void)
   LIBXML_TEST_VERSION;
 }
 
+static void
+ignore_errors (void *ignore, virErrorPtr ignore2)
+{
+  /* empty */
+}
+
 struct guestfs___add_libvirt_dom_argv {
   uint64_t bitmask;
 #define GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK (UINT64_C(1)<<0)
@@ -61,6 +67,8 @@ struct guestfs___add_libvirt_dom_argv {
   const char *iface;
 #define GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK (UINT64_C(1)<<2)
   int live;
+#define GUESTFS___ADD_LIBVIRT_DOM_READONLYDISK_BITMASK (UINT64_C(1)<<3)
+  const char *readonlydisk;
 };
 
 static int guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom, const struct guestfs___add_libvirt_dom_argv *optargs);
@@ -76,6 +84,8 @@ guestfs__add_domain (guestfs_h *g, const char *domain_name,
   const char *libvirturi;
   int readonly;
   int live;
+  int allowuuid;
+  const char *readonlydisk;
   const char *iface;
   struct guestfs___add_libvirt_dom_argv optargs2 = { .bitmask = 0 };
 
@@ -87,6 +97,10 @@ guestfs__add_domain (guestfs_h *g, const char *domain_name,
           ? optargs->iface : NULL;
   live = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIVE_BITMASK
          ? optargs->live : 0;
+  allowuuid = optargs->bitmask & GUESTFS_ADD_DOMAIN_ALLOWUUID_BITMASK
+            ? optargs->allowuuid : 0;
+  readonlydisk = optargs->bitmask & GUESTFS_ADD_DOMAIN_READONLYDISK_BITMASK
+               ? optargs->readonlydisk : NULL;
 
   if (live && readonly) {
     error (g, _("you cannot set both live and readonly flags"));
@@ -102,7 +116,20 @@ guestfs__add_domain (guestfs_h *g, const char *domain_name,
     goto cleanup;
   }
 
-  dom = virDomainLookupByName (conn, domain_name);
+  /* Suppress default behaviour of printing errors to stderr.  Note
+   * you can't set this to NULL to ignore errors; setting it to NULL
+   * restores the default error handler ...
+   */
+  virConnSetErrorFunc (conn, NULL, ignore_errors);
+
+  /* Try UUID first. */
+  if (allowuuid)
+    dom = virDomainLookupByUUIDString (conn, domain_name);
+
+  /* Try ordinary domain name. */
+  if (!dom)
+    dom = virDomainLookupByName (conn, domain_name);
+
   if (!dom) {
     err = virGetLastError ();
     error (g, _("no libvirt domain called '%s': %s"),
@@ -122,6 +149,10 @@ guestfs__add_domain (guestfs_h *g, const char *domain_name,
     optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK;
     optargs2.live = live;
   }
+  if (readonlydisk) {
+    optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_READONLYDISK_BITMASK;
+    optargs2.readonlydisk = readonlydisk;
+  }
 
   r = guestfs___add_libvirt_dom (g, dom, &optargs2);
 
@@ -145,6 +176,7 @@ guestfs___for_each_disk (guestfs_h *g,
                          virDomainPtr dom,
                          int (*f) (guestfs_h *g,
                                    const char *filename, const char *format,
+                                   int readonly,
                                    void *data),
                          void *data)
 {
@@ -262,9 +294,20 @@ guestfs___for_each_disk (guestfs_h *g,
       format = (char *) xmlNodeListGetString (doc, attr->children, 1);
     }
 
+    /* Get the <readonly/> flag. */
+    xmlXPathObjectPtr xpreadonly;
+
+    xpathCtx->node = nodes->nodeTab[i];
+    xpreadonly = xmlXPathEvalExpression (BAD_CAST "./readonly", xpathCtx);
+    int readonly = 0;
+    if (xpreadonly != NULL &&
+        xpreadonly->nodesetval &&
+        xpreadonly->nodesetval->nodeNr > 0)
+      readonly = 1;
+
     int t;
     if (f)
-      t = f (g, filename, format, data);
+      t = f (g, filename, format, readonly, data);
     else
       t = 0;
 
@@ -272,6 +315,7 @@ guestfs___for_each_disk (guestfs_h *g,
     xmlFree (format);
     xmlXPathFreeObject (xpfilename);
     xmlXPathFreeObject (xpformat);
+    xmlXPathFreeObject (xpreadonly);
 
     if (t == -1)
       goto cleanup;
@@ -298,18 +342,34 @@ guestfs___for_each_disk (guestfs_h *g,
 
 /* This was proposed as an external API, but it's not quite baked yet. */
 
-static int add_disk (guestfs_h *g, const char *filename, const char *format, void *optargs_vp);
+static int add_disk (guestfs_h *g, const char *filename, const char *format, int readonly, void *data);
 static int connect_live (guestfs_h *g, virDomainPtr dom);
 
+enum readonlydisk {
+  readonlydisk_error,
+  readonlydisk_read,
+  readonlydisk_write,
+  readonlydisk_ignore,
+};
+
+struct add_disk_data {
+  int readonly;
+  enum readonlydisk readonlydisk;
+  /* Other args to pass through to add_drive_opts. */
+  struct guestfs_add_drive_opts_argv optargs;
+};
+
 static int
 guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
                            const struct guestfs___add_libvirt_dom_argv *optargs)
 {
-  int cmdline_pos;
+  size_t cmdline_pos;
   int r;
   int readonly;
   const char *iface;
   int live;
+  /* Default for back-compat reasons: */
+  enum readonlydisk readonlydisk = readonlydisk_write;
 
   readonly =
     optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK
@@ -321,6 +381,21 @@ guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
     optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK
     ? optargs->live : 0;
 
+  if ((optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_READONLYDISK_BITMASK)) {
+    if (STREQ (optargs->readonlydisk, "error"))
+      readonlydisk = readonlydisk_error;
+    else if (STREQ (optargs->readonlydisk, "read"))
+      readonlydisk = readonlydisk_read;
+    else if (STREQ (optargs->readonlydisk, "write"))
+      readonlydisk = readonlydisk_write;
+    else if (STREQ (optargs->readonlydisk, "ignore"))
+      readonlydisk = readonlydisk_ignore;
+    else {
+      error (g, _("unknown readonlydisk parameter"));
+      return -1;
+    }
+  }
+
   if (live && readonly) {
     error (g, _("you cannot set both live and readonly flags"));
     return -1;
@@ -357,40 +432,75 @@ guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
   }
 
   /* Add the disks. */
-  struct guestfs_add_drive_opts_argv optargs2 = { .bitmask = 0 };
-  if (readonly) {
-    optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
-    optargs2.readonly = readonly;
-  }
+  struct add_disk_data data;
+  data.optargs.bitmask = 0;
+  data.readonly = readonly;
+  data.readonlydisk = readonlydisk;
   if (iface) {
-    optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK;
-    optargs2.iface = iface;
+    data.optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK;
+    data.optargs.iface = iface;
   }
 
   /* Checkpoint the command line around the operation so that either
    * all disks are added or none are added.
    */
-  cmdline_pos = guestfs___checkpoint_cmdline (g);
-  r = guestfs___for_each_disk (g, dom, add_disk, &optargs2);
+  struct drive **cp = guestfs___checkpoint_drives (g);
+  r = guestfs___for_each_disk (g, dom, add_disk, &data);
   if (r == -1)
-    guestfs___rollback_cmdline (g, cmdline_pos);
+    guestfs___rollback_drives (g, cp);
 
   return r;
 }
 
 static int
-add_disk (guestfs_h *g, const char *filename, const char *format,
-          void *optargs_vp)
+add_disk (guestfs_h *g,
+          const char *filename, const char *format, int readonly_in_xml,
+          void *datavp)
 {
-  struct guestfs_add_drive_opts_argv *optargs = optargs_vp;
+  struct add_disk_data *data = datavp;
+  /* Copy whole struct so we can make local changes: */
+  struct guestfs_add_drive_opts_argv optargs = data->optargs;
+  int readonly, error = 0, skip = 0;
+
+  if (readonly_in_xml) {        /* <readonly/> appears in the XML */
+    if (data->readonly) {       /* asked to add disk read-only */
+      switch (data->readonlydisk) {
+      case readonlydisk_error: readonly = 1; break;
+      case readonlydisk_read: readonly = 1; break;
+      case readonlydisk_write: readonly = 1; break;
+      case readonlydisk_ignore: skip = 1; break;
+      default: abort ();
+      }
+    } else {                    /* asked to add disk for read/write */
+      switch (data->readonlydisk) {
+      case readonlydisk_error: error = 1; break;
+      case readonlydisk_read: readonly = 1; break;
+      case readonlydisk_write: readonly = 0; break;
+      case readonlydisk_ignore: skip = 1; break;
+      default: abort ();
+      }
+    }
+  } else                        /* no <readonly/> in XML */
+    readonly = data->readonly;
+
+  if (skip)
+    return 0;
+
+  if (error) {
+    error (g, _("%s: disk is marked <readonly/> in libvirt XML, and readonlydisk was set to \"error\""),
+           filename);
+    return -1;
+  }
+
+  optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
+  optargs.readonly = readonly;
 
   if (format) {
-    optargs->bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
-    optargs->format = format;
-  } else
-    optargs->bitmask &= ~GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
+    optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
+    optargs.format = format;
+  }
 
-  return guestfs__add_drive_opts (g, filename, optargs);
+  return guestfs__add_drive_opts (g, filename, &optargs);
 }
 
 static int