1 /* guestfish - the filesystem interactive shell
2 * Copyright (C) 2010 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program 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
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <libvirt/libvirt.h>
27 #include <libvirt/virterror.h>
29 #include <libxml/xpath.h>
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
35 /* Implements the guts of the '-d' option.
37 * Note that we have to observe the '--ro' flag in two respects: by
38 * adding the drives read-only if the flag is set, and by restricting
39 * guests to shut down ones unless '--ro' is set.
41 * Returns the number of drives added (> 0), or -1 for failure.
44 add_libvirt_drives (const char *guest)
46 static int initialized = 0;
50 if (virInitialize () == -1)
57 int r = -1, nr_added = 0, i;
59 virConnectPtr conn = NULL;
60 virDomainPtr dom = NULL;
62 xmlXPathContextPtr xpathCtx = NULL;
63 xmlXPathObjectPtr xpathObj = NULL;
66 /* Connect to libvirt, find the domain. */
67 conn = virConnectOpenReadOnly (libvirt_uri);
69 err = virGetLastError ();
70 fprintf (stderr, _("guestfish: could not connect to libvirt (code %d, domain %d): %s\n"),
71 err->code, err->domain, err->message);
75 dom = virDomainLookupByName (conn, guest);
77 err = virConnGetLastError (conn);
78 fprintf (stderr, _("guestfish: no libvirt domain called '%s': %s\n"),
84 if (virDomainGetInfo (dom, &info) == -1) {
85 err = virConnGetLastError (conn);
86 fprintf (stderr, _("guestfish: error getting domain info about '%s': %s\n"),
90 if (info.state != VIR_DOMAIN_SHUTOFF) {
91 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"),
98 xml = virDomainGetXMLDesc (dom, 0);
101 err = virConnGetLastError (conn);
102 fprintf (stderr, _("guestfish: error reading libvirt XML information about '%s': %s\n"),
103 guest, err->message);
107 /* Now the horrible task of parsing out the fields we need from the XML.
108 * http://www.xmlsoft.org/examples/xpath1.c
110 doc = xmlParseMemory (xml, strlen (xml));
112 fprintf (stderr, _("guestfish: unable to parse XML information returned by libvirt\n"));
116 xpathCtx = xmlXPathNewContext (doc);
117 if (xpathCtx == NULL) {
118 fprintf (stderr, _("guestfish: unable to create new XPath context\n"));
122 /* This gives us a set of all the <disk> nodes. */
123 xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx);
124 if (xpathObj == NULL) {
125 fprintf (stderr, _("guestfish: unable to evaluate XPath expression\n"));
129 xmlNodeSetPtr nodes = xpathObj->nodesetval;
130 for (i = 0; i < nodes->nodeNr; ++i) {
131 xmlXPathObjectPtr xpfilename;
132 xmlXPathObjectPtr xpformat;
134 /* Change the context to the current <disk> node.
135 * DV advises to reset this before each search since older versions of
136 * libxml2 might overwrite it.
138 xpathCtx->node = nodes->nodeTab[i];
140 /* Filename can be in <source dev=..> or <source file=..> attribute. */
141 xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx);
142 if (xpfilename == NULL ||
143 xpfilename->nodesetval == NULL ||
144 xpfilename->nodesetval->nodeNr == 0) {
145 xmlXPathFreeObject (xpfilename);
146 xpathCtx->node = nodes->nodeTab[i];
147 xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx);
148 if (xpfilename == NULL ||
149 xpfilename->nodesetval == NULL ||
150 xpfilename->nodesetval->nodeNr == 0) {
151 xmlXPathFreeObject (xpfilename);
152 continue; /* disk filename not found, skip this */
156 assert (xpfilename->nodesetval->nodeTab[0]);
157 assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
158 xmlAttrPtr attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
159 char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
161 /* Get the disk format (may not be set). */
162 xpathCtx->node = nodes->nodeTab[i];
163 xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx);
165 if (xpformat != NULL &&
166 xpformat->nodesetval &&
167 xpformat->nodesetval->nodeNr > 0) {
168 assert (xpformat->nodesetval->nodeTab[0]);
169 assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
170 attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0];
171 format = (char *) xmlNodeListGetString (doc, attr->children, 1);
174 /* Add the disk, with optional format. */
175 struct guestfs_add_drive_opts_argv optargs = { .bitmask = 0 };
177 optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
178 optargs.readonly = read_only;
181 optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
182 optargs.format = format;
185 int t = guestfs_add_drive_opts_argv (g, filename, &optargs);
189 xmlXPathFreeObject (xpfilename);
190 xmlXPathFreeObject (xpformat);
199 fprintf (stderr, _("guestfish: libvirt domain '%s' has no disks\n"),
209 if (xpathObj) xmlXPathFreeObject (xpathObj);
210 if (xpathCtx) xmlXPathFreeContext (xpathCtx);
211 if (doc) xmlFreeDoc (doc);
212 if (dom) virDomainFree (dom);
213 if (conn) virConnectClose (conn);