1 /* libguestfs - guestfish and guestmount shared option parsing
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>
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, i;
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, _("%s: could not connect to libvirt (code %d, domain %d): %s\n"),
73 program_name, err->code, err->domain, err->message);
77 dom = virDomainLookupByName (conn, guest);
79 err = virConnGetLastError (conn);
80 fprintf (stderr, _("%s: no libvirt domain called '%s': %s\n"),
81 program_name, guest, err->message);
86 if (virDomainGetInfo (dom, &info) == -1) {
87 err = virConnGetLastError (conn);
88 fprintf (stderr, _("%s: error getting domain info about '%s': %s\n"),
89 program_name, guest, err->message);
92 if (info.state != VIR_DOMAIN_SHUTOFF) {
93 fprintf (stderr, _("%s: 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, _("%s: error reading libvirt XML information about '%s': %s\n"),
105 program_name, 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, _("%s: unable to parse XML information returned by libvirt\n"),
119 xpathCtx = xmlXPathNewContext (doc);
120 if (xpathCtx == NULL) {
121 fprintf (stderr, _("%s: unable to create new XPath context\n"),
126 /* This gives us a set of all the <disk> nodes. */
127 xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx);
128 if (xpathObj == NULL) {
129 fprintf (stderr, _("%s: unable to evaluate XPath expression\n"),
134 xmlNodeSetPtr nodes = xpathObj->nodesetval;
135 for (i = 0; i < nodes->nodeNr; ++i) {
136 xmlXPathObjectPtr xpfilename;
137 xmlXPathObjectPtr xpformat;
139 /* Change the context to the current <disk> node.
140 * DV advises to reset this before each search since older versions of
141 * libxml2 might overwrite it.
143 xpathCtx->node = nodes->nodeTab[i];
145 /* Filename can be in <source dev=..> or <source file=..> attribute. */
146 xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx);
147 if (xpfilename == NULL ||
148 xpfilename->nodesetval == NULL ||
149 xpfilename->nodesetval->nodeNr == 0) {
150 xmlXPathFreeObject (xpfilename);
151 xpathCtx->node = nodes->nodeTab[i];
152 xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx);
153 if (xpfilename == NULL ||
154 xpfilename->nodesetval == NULL ||
155 xpfilename->nodesetval->nodeNr == 0) {
156 xmlXPathFreeObject (xpfilename);
157 continue; /* disk filename not found, skip this */
161 assert (xpfilename->nodesetval->nodeTab[0]);
162 assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
163 xmlAttrPtr attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
164 char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
166 /* Get the disk format (may not be set). */
167 xpathCtx->node = nodes->nodeTab[i];
168 xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx);
170 if (xpformat != NULL &&
171 xpformat->nodesetval &&
172 xpformat->nodesetval->nodeNr > 0) {
173 assert (xpformat->nodesetval->nodeTab[0]);
174 assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
175 attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0];
176 format = (char *) xmlNodeListGetString (doc, attr->children, 1);
179 /* Add the disk, with optional format. */
180 struct guestfs_add_drive_opts_argv optargs = { .bitmask = 0 };
182 optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
183 optargs.readonly = read_only;
186 optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
187 optargs.format = format;
190 int t = guestfs_add_drive_opts_argv (g, filename, &optargs);
194 xmlXPathFreeObject (xpfilename);
195 xmlXPathFreeObject (xpformat);
204 fprintf (stderr, _("%s: libvirt domain '%s' has no disks\n"),
205 program_name, guest);
214 if (xpathObj) xmlXPathFreeObject (xpathObj);
215 if (xpathCtx) xmlXPathFreeContext (xpathCtx);
216 if (doc) xmlFreeDoc (doc);
217 if (dom) virDomainFree (dom);
218 if (conn) virConnectClose (conn);