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 static int add_drives_from_node_set (xmlDocPtr doc, xmlNodeSetPtr nodes);
37 /* Implements the guts of the '-d' option.
39 * Note that we have to observe the '--ro' flag in two respects: by
40 * adding the drives read-only if the flag is set, and by restricting
41 * guests to shut down ones unless '--ro' is set.
43 * Returns the number of drives added (> 0), or -1 for failure.
46 add_libvirt_drives (const char *guest)
48 static int initialized = 0;
52 if (virInitialize () == -1)
59 int r = -1, nr_added = 0;
61 virConnectPtr conn = NULL;
62 virDomainPtr dom = NULL;
64 xmlXPathContextPtr xpathCtx = NULL;
65 xmlXPathObjectPtr xpathObj = NULL;
68 /* Connect to libvirt, find the domain. */
69 conn = virConnectOpenReadOnly (libvirt_uri);
71 err = virGetLastError ();
72 fprintf (stderr, _("guestfish: could not connect to libvirt (code %d, domain %d): %s\n"),
73 err->code, err->domain, err->message);
77 dom = virDomainLookupByName (conn, guest);
79 err = virConnGetLastError (conn);
80 fprintf (stderr, _("guestfish: no libvirt domain called '%s': %s\n"),
86 if (virDomainGetInfo (dom, &info) == -1) {
87 err = virConnGetLastError (conn);
88 fprintf (stderr, _("guestfish: error getting domain info about '%s': %s\n"),
92 if (info.state != VIR_DOMAIN_SHUTOFF) {
93 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"),
100 xml = virDomainGetXMLDesc (dom, 0);
103 err = virConnGetLastError (conn);
104 fprintf (stderr, _("guestfish: error reading libvirt XML information about '%s': %s\n"),
105 guest, err->message);
109 /* Now the horrible task of parsing out the fields we need from the XML.
110 * http://www.xmlsoft.org/examples/xpath1.c
112 doc = xmlParseMemory (xml, strlen (xml));
114 fprintf (stderr, _("guestfish: unable to parse XML information returned by libvirt\n"));
118 xpathCtx = xmlXPathNewContext (doc);
119 if (xpathCtx == NULL) {
120 fprintf (stderr, _("guestfish: unable to create new XPath context\n"));
124 xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk/source/@dev",
126 if (xpathObj == NULL) {
127 fprintf (stderr, _("guestfish: unable to evaluate XPath expression\n"));
131 nr_added += add_drives_from_node_set (doc, xpathObj->nodesetval);
133 xmlXPathFreeObject (xpathObj); xpathObj = NULL;
135 xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk/source/@file",
137 if (xpathObj == NULL) {
138 fprintf (stderr, _("guestfish: unable to evaluate XPath expression\n"));
142 nr_added += add_drives_from_node_set (doc, xpathObj->nodesetval);
145 fprintf (stderr, _("guestfish: libvirt domain '%s' has no disks\n"),
155 if (xpathObj) xmlXPathFreeObject (xpathObj);
156 if (xpathCtx) xmlXPathFreeContext (xpathCtx);
157 if (doc) xmlFreeDoc (doc);
158 if (dom) virDomainFree (dom);
159 if (conn) virConnectClose (conn);
165 add_drives_from_node_set (xmlDocPtr doc, xmlNodeSetPtr nodes)
172 for (i = 0; i < nodes->nodeNr; ++i) {
173 assert (nodes->nodeTab[i]);
174 assert (nodes->nodeTab[i]->type == XML_ATTRIBUTE_NODE);
175 xmlAttrPtr attr = (xmlAttrPtr) nodes->nodeTab[i];
177 char *device = (char *) xmlNodeListGetString (doc, attr->children, 1);
181 r = guestfs_add_drive (g, device);
183 r = guestfs_add_drive_ro (g, device);
190 return nodes->nodeNr;