2 * Copyright (C) 2010 Red Hat Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #include <libvirt/libvirt.h>
27 #include <libvirt/virterror.h>
31 #include <libxml/xpath.h>
32 #include <libxml/parser.h>
33 #include <libxml/tree.h>
36 #define GUESTFS_PRIVATE_FOR_EACH_DISK 1
39 #include "guestfs-internal.h"
40 #include "guestfs-internal-actions.h"
41 #include "guestfs_protocol.h"
43 #if defined(HAVE_LIBVIRT) && defined(HAVE_LIBXML2)
45 static void init_libxml2 (void) __attribute__((constructor));
50 /* I am told that you don't really need to call virInitialize ... */
56 struct guestfs___add_libvirt_dom_argv {
58 #define GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK (UINT64_C(1)<<0)
60 #define GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK (UINT64_C(1)<<1)
64 static int guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom, const struct guestfs___add_libvirt_dom_argv *optargs);
67 guestfs__add_domain (guestfs_h *g, const char *domain_name,
68 const struct guestfs_add_domain_argv *optargs)
71 virConnectPtr conn = NULL;
72 virDomainPtr dom = NULL;
74 const char *libvirturi;
77 struct guestfs___add_libvirt_dom_argv optargs2 = { .bitmask = 0 };
79 libvirturi = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIBVIRTURI_BITMASK
80 ? optargs->libvirturi : NULL;
81 readonly = optargs->bitmask & GUESTFS_ADD_DOMAIN_READONLY_BITMASK
82 ? optargs->readonly : 0;
83 iface = optargs->bitmask & GUESTFS_ADD_DOMAIN_IFACE_BITMASK
84 ? optargs->iface : NULL;
86 /* Connect to libvirt, find the domain. */
87 conn = virConnectOpenReadOnly (libvirturi);
89 err = virGetLastError ();
90 error (g, _("could not connect to libvirt (code %d, domain %d): %s"),
91 err->code, err->domain, err->message);
95 dom = virDomainLookupByName (conn, domain_name);
97 err = virGetLastError ();
98 error (g, _("no libvirt domain called '%s': %s"),
99 domain_name, err->message);
104 optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK;
105 optargs2.readonly = readonly;
108 optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK;
109 optargs2.iface = iface;
112 r = guestfs___add_libvirt_dom (g, dom, &optargs2);
115 if (dom) virDomainFree (dom);
116 if (conn) virConnectClose (conn);
121 /* This function is also used in virt-df to avoid having all that
122 * stupid XPath code repeated. This is something that libvirt should
125 * The callback function 'f' is called once for each disk.
127 * Returns number of disks, or -1 if there was an error.
130 guestfs___for_each_disk (guestfs_h *g,
132 int (*f) (guestfs_h *g,
133 const char *filename, const char *format,
137 int i, nr_added = 0, r = -1;
139 xmlDocPtr doc = NULL;
140 xmlXPathContextPtr xpathCtx = NULL;
141 xmlXPathObjectPtr xpathObj = NULL;
145 xml = virDomainGetXMLDesc (dom, 0);
148 err = virGetLastError ();
149 error (g, _("error reading libvirt XML information: %s"),
154 /* Now the horrible task of parsing out the fields we need from the XML.
155 * http://www.xmlsoft.org/examples/xpath1.c
157 doc = xmlParseMemory (xml, strlen (xml));
159 error (g, _("unable to parse XML information returned by libvirt"));
163 xpathCtx = xmlXPathNewContext (doc);
164 if (xpathCtx == NULL) {
165 error (g, _("unable to create new XPath context"));
169 /* This gives us a set of all the <disk> nodes. */
170 xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx);
171 if (xpathObj == NULL) {
172 error (g, _("unable to evaluate XPath expression"));
176 xmlNodeSetPtr nodes = xpathObj->nodesetval;
177 for (i = 0; i < nodes->nodeNr; ++i) {
178 xmlXPathObjectPtr xptype;
180 /* Change the context to the current <disk> node.
181 * DV advises to reset this before each search since older versions of
182 * libxml2 might overwrite it.
184 xpathCtx->node = nodes->nodeTab[i];
186 /* Filename can be in <source dev=..> or <source file=..> attribute.
187 * Check the <disk type=..> attribute first to find out which one.
189 xptype = xmlXPathEvalExpression (BAD_CAST "./@type", xpathCtx);
190 if (xptype == NULL ||
191 xptype->nodesetval == NULL ||
192 xptype->nodesetval->nodeNr == 0) {
193 xmlXPathFreeObject (xptype);
194 continue; /* no type attribute, skip it */
196 assert (xptype->nodesetval->nodeTab[0]);
197 assert (xptype->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
198 xmlAttrPtr attr = (xmlAttrPtr) xptype->nodesetval->nodeTab[0];
199 xmlXPathFreeObject (xptype);
200 char *type = (char *) xmlNodeListGetString (doc, attr->children, 1);
202 xmlXPathObjectPtr xpfilename;
204 if (STREQ (type, "file")) { /* type = "file" so look at source/@file */
207 xpathCtx->node = nodes->nodeTab[i];
208 xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx);
209 if (xpfilename == NULL ||
210 xpfilename->nodesetval == NULL ||
211 xpfilename->nodesetval->nodeNr == 0) {
212 xmlXPathFreeObject (xpfilename);
213 continue; /* disk filename not found, skip this */
215 } else if (STREQ (type, "block")) { /* type = "block", use source/@dev */
218 xpathCtx->node = nodes->nodeTab[i];
219 xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx);
220 if (xpfilename == NULL ||
221 xpfilename->nodesetval == NULL ||
222 xpfilename->nodesetval->nodeNr == 0) {
223 xmlXPathFreeObject (xpfilename);
224 continue; /* disk filename not found, skip this */
228 continue; /* type <> "file" or "block", skip it */
231 assert (xpfilename->nodesetval->nodeTab[0]);
232 assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
233 attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
234 char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
236 /* Get the disk format (may not be set). */
237 xmlXPathObjectPtr xpformat;
239 xpathCtx->node = nodes->nodeTab[i];
240 xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx);
242 if (xpformat != NULL &&
243 xpformat->nodesetval &&
244 xpformat->nodesetval->nodeNr > 0) {
245 assert (xpformat->nodesetval->nodeTab[0]);
246 assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
247 attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0];
248 format = (char *) xmlNodeListGetString (doc, attr->children, 1);
253 t = f (g, filename, format, data);
259 xmlXPathFreeObject (xpfilename);
260 xmlXPathFreeObject (xpformat);
269 error (g, _("libvirt domain has no disks"));
278 if (xpathObj) xmlXPathFreeObject (xpathObj);
279 if (xpathCtx) xmlXPathFreeContext (xpathCtx);
280 if (doc) xmlFreeDoc (doc);
286 add_disk (guestfs_h *g, const char *filename, const char *format,
289 struct guestfs_add_drive_opts_argv *optargs = optargs_vp;
292 optargs->bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
293 optargs->format = format;
295 optargs->bitmask &= ~GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
297 return guestfs__add_drive_opts (g, filename, optargs);
300 /* This was proposed as an external API, but it's not quite baked yet. */
302 guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
303 const struct guestfs___add_libvirt_dom_argv *optargs)
309 cmdline_pos = guestfs___checkpoint_cmdline (g);
312 optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK
313 ? optargs->readonly : 0;
315 optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK
316 ? optargs->iface : NULL;
320 if (virDomainGetInfo (dom, &info) == -1) {
321 err = virGetLastError ();
322 error (g, _("error getting domain info: %s"), err->message);
325 if (info.state != VIR_DOMAIN_SHUTOFF) {
326 error (g, _("error: domain is a live virtual machine.\nYou must use readonly access because write access to a running virtual machine\ncan cause disk corruption."));
332 struct guestfs_add_drive_opts_argv optargs2 = { .bitmask = 0 };
334 optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
335 optargs2.readonly = readonly;
338 optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK;
339 optargs2.iface = iface;
342 r = guestfs___for_each_disk (g, dom, add_disk, &optargs2);
345 if (r == -1) guestfs___rollback_cmdline (g, cmdline_pos);
349 #else /* no libvirt or libxml2 at compile time */
351 #define NOT_IMPL(r) \
352 error (g, _("add-domain API not available since this version of libguestfs was compiled without libvirt or libxml2")); \
356 guestfs__add_domain (guestfs_h *g, const char *dom,
357 const struct guestfs_add_domain_argv *optargs)
362 #endif /* no libvirt or libxml2 at compile time */