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 ... */
57 ignore_errors (void *ignore, virErrorPtr ignore2)
62 struct guestfs___add_libvirt_dom_argv {
64 #define GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK (UINT64_C(1)<<0)
66 #define GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK (UINT64_C(1)<<1)
68 #define GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK (UINT64_C(1)<<2)
72 static int guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom, const struct guestfs___add_libvirt_dom_argv *optargs);
75 guestfs__add_domain (guestfs_h *g, const char *domain_name,
76 const struct guestfs_add_domain_argv *optargs)
79 virConnectPtr conn = NULL;
80 virDomainPtr dom = NULL;
82 const char *libvirturi;
87 struct guestfs___add_libvirt_dom_argv optargs2 = { .bitmask = 0 };
89 libvirturi = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIBVIRTURI_BITMASK
90 ? optargs->libvirturi : NULL;
91 readonly = optargs->bitmask & GUESTFS_ADD_DOMAIN_READONLY_BITMASK
92 ? optargs->readonly : 0;
93 iface = optargs->bitmask & GUESTFS_ADD_DOMAIN_IFACE_BITMASK
94 ? optargs->iface : NULL;
95 live = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIVE_BITMASK
97 allowuuid = optargs->bitmask & GUESTFS_ADD_DOMAIN_ALLOWUUID_BITMASK
98 ? optargs->allowuuid : 0;
100 if (live && readonly) {
101 error (g, _("you cannot set both live and readonly flags"));
105 /* Connect to libvirt, find the domain. */
106 conn = virConnectOpenReadOnly (libvirturi);
108 err = virGetLastError ();
109 error (g, _("could not connect to libvirt (code %d, domain %d): %s"),
110 err->code, err->domain, err->message);
114 /* Suppress default behaviour of printing errors to stderr. Note
115 * you can't set this to NULL to ignore errors; setting it to NULL
116 * restores the default error handler ...
118 virConnSetErrorFunc (conn, NULL, ignore_errors);
120 /* Try UUID first. */
122 dom = virDomainLookupByUUIDString (conn, domain_name);
124 /* Try ordinary domain name. */
126 dom = virDomainLookupByName (conn, domain_name);
129 err = virGetLastError ();
130 error (g, _("no libvirt domain called '%s': %s"),
131 domain_name, err->message);
136 optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK;
137 optargs2.readonly = readonly;
140 optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK;
141 optargs2.iface = iface;
144 optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK;
145 optargs2.live = live;
148 r = guestfs___add_libvirt_dom (g, dom, &optargs2);
151 if (dom) virDomainFree (dom);
152 if (conn) virConnectClose (conn);
157 /* This function is also used in virt-df to avoid having all that
158 * stupid XPath code repeated. This is something that libvirt should
161 * The callback function 'f' is called once for each disk.
163 * Returns number of disks, or -1 if there was an error.
166 guestfs___for_each_disk (guestfs_h *g,
168 int (*f) (guestfs_h *g,
169 const char *filename, const char *format,
174 int i, nr_added = 0, r = -1;
176 xmlDocPtr doc = NULL;
177 xmlXPathContextPtr xpathCtx = NULL;
178 xmlXPathObjectPtr xpathObj = NULL;
182 xml = virDomainGetXMLDesc (dom, 0);
185 err = virGetLastError ();
186 error (g, _("error reading libvirt XML information: %s"),
191 /* Now the horrible task of parsing out the fields we need from the XML.
192 * http://www.xmlsoft.org/examples/xpath1.c
194 doc = xmlParseMemory (xml, strlen (xml));
196 error (g, _("unable to parse XML information returned by libvirt"));
200 xpathCtx = xmlXPathNewContext (doc);
201 if (xpathCtx == NULL) {
202 error (g, _("unable to create new XPath context"));
206 /* This gives us a set of all the <disk> nodes. */
207 xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx);
208 if (xpathObj == NULL) {
209 error (g, _("unable to evaluate XPath expression"));
213 xmlNodeSetPtr nodes = xpathObj->nodesetval;
214 for (i = 0; i < nodes->nodeNr; ++i) {
215 xmlXPathObjectPtr xptype;
217 /* Change the context to the current <disk> node.
218 * DV advises to reset this before each search since older versions of
219 * libxml2 might overwrite it.
221 xpathCtx->node = nodes->nodeTab[i];
223 /* Filename can be in <source dev=..> or <source file=..> attribute.
224 * Check the <disk type=..> attribute first to find out which one.
226 xptype = xmlXPathEvalExpression (BAD_CAST "./@type", xpathCtx);
227 if (xptype == NULL ||
228 xptype->nodesetval == NULL ||
229 xptype->nodesetval->nodeNr == 0) {
230 xmlXPathFreeObject (xptype);
231 continue; /* no type attribute, skip it */
233 assert (xptype->nodesetval->nodeTab[0]);
234 assert (xptype->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
235 xmlAttrPtr attr = (xmlAttrPtr) xptype->nodesetval->nodeTab[0];
236 char *type = (char *) xmlNodeListGetString (doc, attr->children, 1);
237 xmlXPathFreeObject (xptype);
239 xmlXPathObjectPtr xpfilename;
241 if (STREQ (type, "file")) { /* type = "file" so look at source/@file */
244 xpathCtx->node = nodes->nodeTab[i];
245 xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx);
246 if (xpfilename == NULL ||
247 xpfilename->nodesetval == NULL ||
248 xpfilename->nodesetval->nodeNr == 0) {
249 xmlXPathFreeObject (xpfilename);
250 continue; /* disk filename not found, skip this */
252 } else if (STREQ (type, "block")) { /* type = "block", use source/@dev */
255 xpathCtx->node = nodes->nodeTab[i];
256 xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx);
257 if (xpfilename == NULL ||
258 xpfilename->nodesetval == NULL ||
259 xpfilename->nodesetval->nodeNr == 0) {
260 xmlXPathFreeObject (xpfilename);
261 continue; /* disk filename not found, skip this */
265 continue; /* type <> "file" or "block", skip it */
268 assert (xpfilename->nodesetval->nodeTab[0]);
269 assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
270 attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
271 char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
273 /* Get the disk format (may not be set). */
274 xmlXPathObjectPtr xpformat;
276 xpathCtx->node = nodes->nodeTab[i];
277 xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx);
279 if (xpformat != NULL &&
280 xpformat->nodesetval &&
281 xpformat->nodesetval->nodeNr > 0) {
282 assert (xpformat->nodesetval->nodeTab[0]);
283 assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
284 attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0];
285 format = (char *) xmlNodeListGetString (doc, attr->children, 1);
288 /* Get the <readonly/> flag. */
289 xmlXPathObjectPtr xpreadonly;
291 xpathCtx->node = nodes->nodeTab[i];
292 xpreadonly = xmlXPathEvalExpression (BAD_CAST "./readonly", xpathCtx);
294 if (xpreadonly != NULL &&
295 xpreadonly->nodesetval &&
296 xpreadonly->nodesetval->nodeNr > 0)
301 t = f (g, filename, format, readonly, data);
307 xmlXPathFreeObject (xpfilename);
308 xmlXPathFreeObject (xpformat);
309 xmlXPathFreeObject (xpreadonly);
318 error (g, _("libvirt domain has no disks"));
327 if (xpathObj) xmlXPathFreeObject (xpathObj);
328 if (xpathCtx) xmlXPathFreeContext (xpathCtx);
329 if (doc) xmlFreeDoc (doc);
334 /* This was proposed as an external API, but it's not quite baked yet. */
336 static int add_disk (guestfs_h *g, const char *filename, const char *format, int readonly, void *optargs_vp);
337 static int connect_live (guestfs_h *g, virDomainPtr dom);
340 guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
341 const struct guestfs___add_libvirt_dom_argv *optargs)
350 optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK
351 ? optargs->readonly : 0;
353 optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK
354 ? optargs->iface : NULL;
356 optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK
359 if (live && readonly) {
360 error (g, _("you cannot set both live and readonly flags"));
369 if (virDomainGetInfo (dom, &info) == -1) {
370 err = virGetLastError ();
371 error (g, _("error getting domain info: %s"), err->message);
374 vm_running = info.state != VIR_DOMAIN_SHUTOFF;
377 /* If the caller specified the 'live' flag, then they want us to
378 * try to connect to guestfsd if the domain is running. Note
379 * that live readonly connections are not possible.
382 return connect_live (g, dom);
384 /* Dangerous to modify the disks of a running VM. */
385 error (g, _("error: domain is a live virtual machine.\n"
386 "Writing to the disks of a running virtual machine can cause disk corruption.\n"
387 "Either use read-only access, or if the guest is running the guestfsd daemon\n"
388 "specify live access. In most libguestfs tools these options are --ro or\n"
389 "--live respectively. Consult the documentation for further information."));
395 struct guestfs_add_drive_opts_argv optargs2 = { .bitmask = 0 };
397 optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
398 optargs2.readonly = readonly;
401 optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK;
402 optargs2.iface = iface;
405 /* Checkpoint the command line around the operation so that either
406 * all disks are added or none are added.
408 struct drive **cp = guestfs___checkpoint_drives (g);
409 r = guestfs___for_each_disk (g, dom, add_disk, &optargs2);
411 guestfs___rollback_drives (g, cp);
417 add_disk (guestfs_h *g,
418 const char *filename, const char *format, int readonly,
421 struct guestfs_add_drive_opts_argv *optargs = optargs_vp;
424 optargs->bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
425 optargs->format = format;
427 optargs->bitmask &= ~GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
429 return guestfs__add_drive_opts (g, filename, optargs);
433 connect_live (guestfs_h *g, virDomainPtr dom)
437 xmlDocPtr doc = NULL;
438 xmlXPathContextPtr xpathCtx = NULL;
439 xmlXPathObjectPtr xpathObj = NULL;
442 char *attach_method = NULL;
445 xml = virDomainGetXMLDesc (dom, 0);
448 err = virGetLastError ();
449 error (g, _("error reading libvirt XML information: %s"),
454 /* Parse XML to document. */
455 doc = xmlParseMemory (xml, strlen (xml));
457 error (g, _("unable to parse XML information returned by libvirt"));
461 xpathCtx = xmlXPathNewContext (doc);
462 if (xpathCtx == NULL) {
463 error (g, _("unable to create new XPath context"));
467 /* This gives us a set of all the <channel> nodes related to the
468 * guestfsd virtio-serial channel.
470 xpathObj = xmlXPathEvalExpression (BAD_CAST
471 "//devices/channel[@type=\"unix\" and "
472 "./source/@mode=\"bind\" and "
473 "./source/@path and "
474 "./target/@type=\"virtio\" and "
475 "./target/@name=\"org.libguestfs.channel.0\"]",
477 if (xpathObj == NULL) {
478 error (g, _("unable to evaluate XPath expression"));
482 xmlNodeSetPtr nodes = xpathObj->nodesetval;
483 for (i = 0; i < nodes->nodeNr; ++i) {
484 xmlXPathObjectPtr xppath;
486 /* See note in function above. */
487 xpathCtx->node = nodes->nodeTab[i];
489 /* The path is in <source path=..> attribute. */
490 xppath = xmlXPathEvalExpression (BAD_CAST "./source/@path", xpathCtx);
491 if (xppath == NULL ||
492 xppath->nodesetval == NULL ||
493 xppath->nodesetval->nodeNr == 0) {
494 xmlXPathFreeObject (xppath);
495 continue; /* no type attribute, skip it */
497 assert (xppath->nodesetval->nodeTab[0]);
498 assert (xppath->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
499 xmlAttrPtr attr = (xmlAttrPtr) xppath->nodesetval->nodeTab[0];
500 path = (char *) xmlNodeListGetString (doc, attr->children, 1);
501 xmlXPathFreeObject (xppath);
506 error (g, _("this guest has no libvirt <channel> definition for guestfsd\n"
507 "See ATTACHING TO RUNNING DAEMONS in guestfs(3) for further information."));
512 attach_method = safe_malloc (g, strlen (path) + 5 + 1);
513 strcpy (attach_method, "unix:");
514 strcat (attach_method, path);
515 r = guestfs_set_attach_method (g, attach_method);
519 free (attach_method);
521 if (xpathObj) xmlXPathFreeObject (xpathObj);
522 if (xpathCtx) xmlXPathFreeContext (xpathCtx);
523 if (doc) xmlFreeDoc (doc);
528 #else /* no libvirt or libxml2 at compile time */
530 #define NOT_IMPL(r) \
531 error (g, _("add-domain API not available since this version of libguestfs was compiled without libvirt or libxml2")); \
535 guestfs__add_domain (guestfs_h *g, const char *dom,
536 const struct guestfs_add_domain_argv *optargs)
541 #endif /* no libvirt or libxml2 at compile time */