mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
Add guestfs_add_domain 'live' flag.
This optional flag controls whether this API call will try to connect to a running virtual machine 'guestfsd' process. If the flag is given and the virtual machine is running, then the libvirt XML is parsed looking for a suitable <channel> element, and 'guestfs_set_attach_method' is called with the corresponding virtio-serial socket path.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/* guestfish - the filesystem interactive shell
|
||||
* Copyright (C) 2009-2010 Red Hat Inc.
|
||||
* Copyright (C) 2009-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
|
||||
|
||||
@@ -1074,7 +1074,7 @@ Please read L<guestfs(3)/INSPECTION> for more details.");
|
||||
This returns the internal QEMU command line. 'debug' commands are
|
||||
not part of the formal API and can be removed or changed at any time.");
|
||||
|
||||
("add_domain", (RInt "nrdisks", [String "dom"], [String "libvirturi"; Bool "readonly"; String "iface"]), -1, [FishAlias "domain"],
|
||||
("add_domain", (RInt "nrdisks", [String "dom"], [String "libvirturi"; Bool "readonly"; String "iface"; Bool "live"]), -1, [FishAlias "domain"],
|
||||
[],
|
||||
"add the disk(s) from a named libvirt domain",
|
||||
"\
|
||||
@@ -1101,12 +1101,18 @@ we connect to the default libvirt URI (or one set through an
|
||||
environment variable, see the libvirt documentation for full
|
||||
details).
|
||||
|
||||
The optional C<live> flag controls whether this call will try
|
||||
to connect to a running virtual machine C<guestfsd> process if
|
||||
it sees a suitable E<lt>channelE<gt> element in the libvirt
|
||||
XML definition. The default (if the flag is omitted) is never
|
||||
to try.
|
||||
|
||||
The other optional parameters are passed directly through to
|
||||
C<guestfs_add_drive_opts>.");
|
||||
|
||||
(*
|
||||
This interface is not quite baked yet. -- RWMJ 2010-11-11
|
||||
("add_libvirt_dom", (RInt "nrdisks", [Pointer ("virDomainPtr", "dom")], [Bool "readonly"; String "iface"]), -1, [NotInFish],
|
||||
("add_libvirt_dom", (RInt "nrdisks", [Pointer ("virDomainPtr", "dom")], [Bool "readonly"; String "iface"; Bool "live"]), -1, [NotInFish],
|
||||
[],
|
||||
"add the disk(s) from a libvirt domain",
|
||||
"\
|
||||
@@ -1129,7 +1135,13 @@ from a remote libvirt connection (see L<http://libvirt.org/remote.html>)
|
||||
will fail unless those disks are accessible via the same device path
|
||||
locally too.
|
||||
|
||||
The optional parameters are passed directly through to
|
||||
The optional C<live> flag controls whether this call will try
|
||||
to connect to a running virtual machine C<guestfsd> process if
|
||||
it sees a suitable E<lt>channelE<gt> element in the libvirt
|
||||
XML definition. The default (if the flag is omitted) is never
|
||||
to try.
|
||||
|
||||
The other optional parameters are passed directly through to
|
||||
C<guestfs_add_drive_opts>.");
|
||||
*)
|
||||
|
||||
|
||||
208
src/virt.c
208
src/virt.c
@@ -59,6 +59,8 @@ struct guestfs___add_libvirt_dom_argv {
|
||||
int readonly;
|
||||
#define GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK (UINT64_C(1)<<1)
|
||||
const char *iface;
|
||||
#define GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK (UINT64_C(1)<<2)
|
||||
int live;
|
||||
};
|
||||
|
||||
static int guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom, const struct guestfs___add_libvirt_dom_argv *optargs);
|
||||
@@ -73,6 +75,7 @@ guestfs__add_domain (guestfs_h *g, const char *domain_name,
|
||||
int r = -1;
|
||||
const char *libvirturi;
|
||||
int readonly;
|
||||
int live;
|
||||
const char *iface;
|
||||
struct guestfs___add_libvirt_dom_argv optargs2 = { .bitmask = 0 };
|
||||
|
||||
@@ -82,6 +85,13 @@ guestfs__add_domain (guestfs_h *g, const char *domain_name,
|
||||
? optargs->readonly : 0;
|
||||
iface = optargs->bitmask & GUESTFS_ADD_DOMAIN_IFACE_BITMASK
|
||||
? optargs->iface : NULL;
|
||||
live = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIVE_BITMASK
|
||||
? optargs->live : 0;
|
||||
|
||||
if (live && readonly) {
|
||||
error (g, _("you cannot set both live and readonly flags"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Connect to libvirt, find the domain. */
|
||||
conn = virConnectOpenReadOnly (libvirturi);
|
||||
@@ -108,6 +118,10 @@ guestfs__add_domain (guestfs_h *g, const char *domain_name,
|
||||
optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK;
|
||||
optargs2.iface = iface;
|
||||
}
|
||||
if (live) {
|
||||
optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK;
|
||||
optargs2.live = live;
|
||||
}
|
||||
|
||||
r = guestfs___add_libvirt_dom (g, dom, &optargs2);
|
||||
|
||||
@@ -282,6 +296,88 @@ guestfs___for_each_disk (guestfs_h *g,
|
||||
return r;
|
||||
}
|
||||
|
||||
/* 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 connect_live (guestfs_h *g, virDomainPtr dom);
|
||||
|
||||
static int
|
||||
guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
|
||||
const struct guestfs___add_libvirt_dom_argv *optargs)
|
||||
{
|
||||
int cmdline_pos;
|
||||
int r;
|
||||
int readonly;
|
||||
const char *iface;
|
||||
int live;
|
||||
|
||||
readonly =
|
||||
optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK
|
||||
? optargs->readonly : 0;
|
||||
iface =
|
||||
optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK
|
||||
? optargs->iface : NULL;
|
||||
live =
|
||||
optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK
|
||||
? optargs->live : 0;
|
||||
|
||||
if (live && readonly) {
|
||||
error (g, _("you cannot set both live and readonly flags"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!readonly) {
|
||||
virDomainInfo info;
|
||||
virErrorPtr err;
|
||||
int vm_running;
|
||||
|
||||
if (virDomainGetInfo (dom, &info) == -1) {
|
||||
err = virGetLastError ();
|
||||
error (g, _("error getting domain info: %s"), err->message);
|
||||
return -1;
|
||||
}
|
||||
vm_running = info.state != VIR_DOMAIN_SHUTOFF;
|
||||
|
||||
if (vm_running) {
|
||||
/* If the caller specified the 'live' flag, then they want us to
|
||||
* try to connect to guestfsd if the domain is running. Note
|
||||
* that live readonly connections are not possible.
|
||||
*/
|
||||
if (live)
|
||||
return connect_live (g, dom);
|
||||
|
||||
/* Dangerous to modify the disks of a running VM. */
|
||||
error (g, _("error: domain is a live virtual machine.\n"
|
||||
"Writing to the disks of a running virtual machine can cause disk corruption.\n"
|
||||
"Either use read-only access, or if the guest is running the guestfsd daemon\n"
|
||||
"specify live access. In most libguestfs tools these options are --ro or\n"
|
||||
"--live respectively. Consult the documentation for further information."));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
if (iface) {
|
||||
optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK;
|
||||
optargs2.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);
|
||||
if (r == -1)
|
||||
guestfs___rollback_cmdline (g, cmdline_pos);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
add_disk (guestfs_h *g, const char *filename, const char *format,
|
||||
void *optargs_vp)
|
||||
@@ -297,52 +393,98 @@ add_disk (guestfs_h *g, const char *filename, const char *format,
|
||||
return guestfs__add_drive_opts (g, filename, optargs);
|
||||
}
|
||||
|
||||
/* This was proposed as an external API, but it's not quite baked yet. */
|
||||
static int
|
||||
guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
|
||||
const struct guestfs___add_libvirt_dom_argv *optargs)
|
||||
connect_live (guestfs_h *g, virDomainPtr dom)
|
||||
{
|
||||
int r = -1;
|
||||
int i, r = -1;
|
||||
virErrorPtr err;
|
||||
int cmdline_pos;
|
||||
xmlDocPtr doc = NULL;
|
||||
xmlXPathContextPtr xpathCtx = NULL;
|
||||
xmlXPathObjectPtr xpathObj = NULL;
|
||||
char *xml = NULL;
|
||||
char *path = NULL;
|
||||
char *attach_method = NULL;
|
||||
|
||||
cmdline_pos = guestfs___checkpoint_cmdline (g);
|
||||
/* Domain XML. */
|
||||
xml = virDomainGetXMLDesc (dom, 0);
|
||||
|
||||
int readonly =
|
||||
optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK
|
||||
? optargs->readonly : 0;
|
||||
const char *iface =
|
||||
optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK
|
||||
? optargs->iface : NULL;
|
||||
if (!xml) {
|
||||
err = virGetLastError ();
|
||||
error (g, _("error reading libvirt XML information: %s"),
|
||||
err->message);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!readonly) {
|
||||
virDomainInfo info;
|
||||
if (virDomainGetInfo (dom, &info) == -1) {
|
||||
err = virGetLastError ();
|
||||
error (g, _("error getting domain info: %s"), err->message);
|
||||
goto cleanup;
|
||||
}
|
||||
if (info.state != VIR_DOMAIN_SHUTOFF) {
|
||||
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."));
|
||||
goto cleanup;
|
||||
/* Parse XML to document. */
|
||||
doc = xmlParseMemory (xml, strlen (xml));
|
||||
if (doc == NULL) {
|
||||
error (g, _("unable to parse XML information returned by libvirt"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
xpathCtx = xmlXPathNewContext (doc);
|
||||
if (xpathCtx == NULL) {
|
||||
error (g, _("unable to create new XPath context"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* This gives us a set of all the <channel> nodes related to the
|
||||
* guestfsd virtio-serial channel.
|
||||
*/
|
||||
xpathObj = xmlXPathEvalExpression (BAD_CAST
|
||||
"//devices/channel[@type=\"unix\" and "
|
||||
"./source/@mode=\"bind\" and "
|
||||
"./source/@path and "
|
||||
"./target/@type=\"virtio\" and "
|
||||
"./target/@name=\"org.libguestfs.channel.0\"]",
|
||||
xpathCtx);
|
||||
if (xpathObj == NULL) {
|
||||
error (g, _("unable to evaluate XPath expression"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
xmlNodeSetPtr nodes = xpathObj->nodesetval;
|
||||
for (i = 0; i < nodes->nodeNr; ++i) {
|
||||
xmlXPathObjectPtr xppath;
|
||||
|
||||
/* See note in function above. */
|
||||
xpathCtx->node = nodes->nodeTab[i];
|
||||
|
||||
/* The path is in <source path=..> attribute. */
|
||||
xppath = xmlXPathEvalExpression (BAD_CAST "./source/@path", xpathCtx);
|
||||
if (xppath == NULL ||
|
||||
xppath->nodesetval == NULL ||
|
||||
xppath->nodesetval->nodeNr == 0) {
|
||||
xmlXPathFreeObject (xppath);
|
||||
continue; /* no type attribute, skip it */
|
||||
}
|
||||
assert (xppath->nodesetval->nodeTab[0]);
|
||||
assert (xppath->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
|
||||
xmlAttrPtr attr = (xmlAttrPtr) xppath->nodesetval->nodeTab[0];
|
||||
path = (char *) xmlNodeListGetString (doc, attr->children, 1);
|
||||
xmlXPathFreeObject (xppath);
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
if (iface) {
|
||||
optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK;
|
||||
optargs2.iface = iface;
|
||||
if (path == NULL) {
|
||||
error (g, _("this guest has no libvirt <channel> definition for guestfsd"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
r = guestfs___for_each_disk (g, dom, add_disk, &optargs2);
|
||||
/* Got a path. */
|
||||
attach_method = safe_malloc (g, strlen (path) + 5 + 1);
|
||||
strcpy (attach_method, "unix:");
|
||||
strcat (attach_method, path);
|
||||
r = guestfs_set_attach_method (g, attach_method);
|
||||
|
||||
cleanup:
|
||||
if (r == -1) guestfs___rollback_cmdline (g, cmdline_pos);
|
||||
free (path);
|
||||
free (attach_method);
|
||||
free (xml);
|
||||
if (xpathObj) xmlXPathFreeObject (xpathObj);
|
||||
if (xpathCtx) xmlXPathFreeContext (xpathCtx);
|
||||
if (doc) xmlFreeDoc (doc);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user