a23ac0a58237f7f27ed9ca0bdaab005705f92b64
[libguestfs.git] / src / virt.c
1 /* libguestfs
2  * Copyright (C) 2010 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <assert.h>
24
25 #ifdef HAVE_LIBVIRT
26 #include <libvirt/libvirt.h>
27 #include <libvirt/virterror.h>
28 #endif
29
30 #ifdef HAVE_LIBXML2
31 #include <libxml/xpath.h>
32 #include <libxml/parser.h>
33 #include <libxml/tree.h>
34 #endif
35
36 #define GUESTFS_PRIVATE_FOR_EACH_DISK 1
37
38 #include "guestfs.h"
39 #include "guestfs-internal.h"
40 #include "guestfs-internal-actions.h"
41 #include "guestfs_protocol.h"
42
43 #if defined(HAVE_LIBVIRT) && defined(HAVE_LIBXML2)
44
45 static void init_libxml2 (void) __attribute__((constructor));
46
47 static void
48 init_libxml2 (void)
49 {
50   /* I am told that you don't really need to call virInitialize ... */
51
52   xmlInitParser ();
53   LIBXML_TEST_VERSION;
54 }
55
56 struct guestfs___add_libvirt_dom_argv {
57   uint64_t bitmask;
58 #define GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK (UINT64_C(1)<<0)
59   int readonly;
60 #define GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK (UINT64_C(1)<<1)
61   const char *iface;
62 #define GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK (UINT64_C(1)<<2)
63   int live;
64 };
65
66 static int guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom, const struct guestfs___add_libvirt_dom_argv *optargs);
67
68 int
69 guestfs__add_domain (guestfs_h *g, const char *domain_name,
70                      const struct guestfs_add_domain_argv *optargs)
71 {
72   virErrorPtr err;
73   virConnectPtr conn = NULL;
74   virDomainPtr dom = NULL;
75   int r = -1;
76   const char *libvirturi;
77   int readonly;
78   int live;
79   const char *iface;
80   struct guestfs___add_libvirt_dom_argv optargs2 = { .bitmask = 0 };
81
82   libvirturi = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIBVIRTURI_BITMASK
83                ? optargs->libvirturi : NULL;
84   readonly = optargs->bitmask & GUESTFS_ADD_DOMAIN_READONLY_BITMASK
85              ? optargs->readonly : 0;
86   iface = optargs->bitmask & GUESTFS_ADD_DOMAIN_IFACE_BITMASK
87           ? optargs->iface : NULL;
88   live = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIVE_BITMASK
89          ? optargs->live : 0;
90
91   if (live && readonly) {
92     error (g, _("you cannot set both live and readonly flags"));
93     return -1;
94   }
95
96   /* Connect to libvirt, find the domain. */
97   conn = virConnectOpenReadOnly (libvirturi);
98   if (!conn) {
99     err = virGetLastError ();
100     error (g, _("could not connect to libvirt (code %d, domain %d): %s"),
101            err->code, err->domain, err->message);
102     goto cleanup;
103   }
104
105   dom = virDomainLookupByName (conn, domain_name);
106   if (!dom) {
107     err = virGetLastError ();
108     error (g, _("no libvirt domain called '%s': %s"),
109            domain_name, err->message);
110     goto cleanup;
111   }
112
113   if (readonly) {
114     optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK;
115     optargs2.readonly = readonly;
116   }
117   if (iface) {
118     optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK;
119     optargs2.iface = iface;
120   }
121   if (live) {
122     optargs2.bitmask |= GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK;
123     optargs2.live = live;
124   }
125
126   r = guestfs___add_libvirt_dom (g, dom, &optargs2);
127
128  cleanup:
129   if (dom) virDomainFree (dom);
130   if (conn) virConnectClose (conn);
131
132   return r;
133 }
134
135 /* This function is also used in virt-df to avoid having all that
136  * stupid XPath code repeated.  This is something that libvirt should
137  * really provide.
138  *
139  * The callback function 'f' is called once for each disk.
140  *
141  * Returns number of disks, or -1 if there was an error.
142  */
143 int
144 guestfs___for_each_disk (guestfs_h *g,
145                          virDomainPtr dom,
146                          int (*f) (guestfs_h *g,
147                                    const char *filename, const char *format,
148                                    void *data),
149                          void *data)
150 {
151   int i, nr_added = 0, r = -1;
152   virErrorPtr err;
153   xmlDocPtr doc = NULL;
154   xmlXPathContextPtr xpathCtx = NULL;
155   xmlXPathObjectPtr xpathObj = NULL;
156   char *xml = NULL;
157
158   /* Domain XML. */
159   xml = virDomainGetXMLDesc (dom, 0);
160
161   if (!xml) {
162     err = virGetLastError ();
163     error (g, _("error reading libvirt XML information: %s"),
164            err->message);
165     goto cleanup;
166   }
167
168   /* Now the horrible task of parsing out the fields we need from the XML.
169    * http://www.xmlsoft.org/examples/xpath1.c
170    */
171   doc = xmlParseMemory (xml, strlen (xml));
172   if (doc == NULL) {
173     error (g, _("unable to parse XML information returned by libvirt"));
174     goto cleanup;
175   }
176
177   xpathCtx = xmlXPathNewContext (doc);
178   if (xpathCtx == NULL) {
179     error (g, _("unable to create new XPath context"));
180     goto cleanup;
181   }
182
183   /* This gives us a set of all the <disk> nodes. */
184   xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx);
185   if (xpathObj == NULL) {
186     error (g, _("unable to evaluate XPath expression"));
187     goto cleanup;
188   }
189
190   xmlNodeSetPtr nodes = xpathObj->nodesetval;
191   for (i = 0; i < nodes->nodeNr; ++i) {
192     xmlXPathObjectPtr xptype;
193
194     /* Change the context to the current <disk> node.
195      * DV advises to reset this before each search since older versions of
196      * libxml2 might overwrite it.
197      */
198     xpathCtx->node = nodes->nodeTab[i];
199
200     /* Filename can be in <source dev=..> or <source file=..> attribute.
201      * Check the <disk type=..> attribute first to find out which one.
202      */
203     xptype = xmlXPathEvalExpression (BAD_CAST "./@type", xpathCtx);
204     if (xptype == NULL ||
205         xptype->nodesetval == NULL ||
206         xptype->nodesetval->nodeNr == 0) {
207       xmlXPathFreeObject (xptype);
208       continue;                 /* no type attribute, skip it */
209     }
210     assert (xptype->nodesetval->nodeTab[0]);
211     assert (xptype->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
212     xmlAttrPtr attr = (xmlAttrPtr) xptype->nodesetval->nodeTab[0];
213     char *type = (char *) xmlNodeListGetString (doc, attr->children, 1);
214     xmlXPathFreeObject (xptype);
215
216     xmlXPathObjectPtr xpfilename;
217
218     if (STREQ (type, "file")) { /* type = "file" so look at source/@file */
219       free (type);
220
221       xpathCtx->node = nodes->nodeTab[i];
222       xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx);
223       if (xpfilename == NULL ||
224           xpfilename->nodesetval == NULL ||
225           xpfilename->nodesetval->nodeNr == 0) {
226         xmlXPathFreeObject (xpfilename);
227         continue;             /* disk filename not found, skip this */
228       }
229     } else if (STREQ (type, "block")) { /* type = "block", use source/@dev */
230       free (type);
231
232       xpathCtx->node = nodes->nodeTab[i];
233       xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx);
234       if (xpfilename == NULL ||
235           xpfilename->nodesetval == NULL ||
236           xpfilename->nodesetval->nodeNr == 0) {
237         xmlXPathFreeObject (xpfilename);
238         continue;             /* disk filename not found, skip this */
239       }
240     } else {
241       free (type);
242       continue;               /* type <> "file" or "block", skip it */
243     }
244
245     assert (xpfilename->nodesetval->nodeTab[0]);
246     assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
247     attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
248     char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
249
250     /* Get the disk format (may not be set). */
251     xmlXPathObjectPtr xpformat;
252
253     xpathCtx->node = nodes->nodeTab[i];
254     xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx);
255     char *format = NULL;
256     if (xpformat != NULL &&
257         xpformat->nodesetval &&
258         xpformat->nodesetval->nodeNr > 0) {
259       assert (xpformat->nodesetval->nodeTab[0]);
260       assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
261       attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0];
262       format = (char *) xmlNodeListGetString (doc, attr->children, 1);
263     }
264
265     int t;
266     if (f)
267       t = f (g, filename, format, data);
268     else
269       t = 0;
270
271     xmlFree (filename);
272     xmlFree (format);
273     xmlXPathFreeObject (xpfilename);
274     xmlXPathFreeObject (xpformat);
275
276     if (t == -1)
277       goto cleanup;
278
279     nr_added++;
280   }
281
282   if (nr_added == 0) {
283     error (g, _("libvirt domain has no disks"));
284     goto cleanup;
285   }
286
287   /* Successful. */
288   r = nr_added;
289
290  cleanup:
291   free (xml);
292   if (xpathObj) xmlXPathFreeObject (xpathObj);
293   if (xpathCtx) xmlXPathFreeContext (xpathCtx);
294   if (doc) xmlFreeDoc (doc);
295
296   return r;
297 }
298
299 /* This was proposed as an external API, but it's not quite baked yet. */
300
301 static int add_disk (guestfs_h *g, const char *filename, const char *format, void *optargs_vp);
302 static int connect_live (guestfs_h *g, virDomainPtr dom);
303
304 static int
305 guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
306                            const struct guestfs___add_libvirt_dom_argv *optargs)
307 {
308   int cmdline_pos;
309   int r;
310   int readonly;
311   const char *iface;
312   int live;
313
314   readonly =
315     optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_READONLY_BITMASK
316     ? optargs->readonly : 0;
317   iface =
318     optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_IFACE_BITMASK
319     ? optargs->iface : NULL;
320   live =
321     optargs->bitmask & GUESTFS___ADD_LIBVIRT_DOM_LIVE_BITMASK
322     ? optargs->live : 0;
323
324   if (live && readonly) {
325     error (g, _("you cannot set both live and readonly flags"));
326     return -1;
327   }
328
329   if (!readonly) {
330     virDomainInfo info;
331     virErrorPtr err;
332     int vm_running;
333
334     if (virDomainGetInfo (dom, &info) == -1) {
335       err = virGetLastError ();
336       error (g, _("error getting domain info: %s"), err->message);
337       return -1;
338     }
339     vm_running = info.state != VIR_DOMAIN_SHUTOFF;
340
341     if (vm_running) {
342       /* If the caller specified the 'live' flag, then they want us to
343        * try to connect to guestfsd if the domain is running.  Note
344        * that live readonly connections are not possible.
345        */
346       if (live)
347         return connect_live (g, dom);
348
349       /* Dangerous to modify the disks of a running VM. */
350       error (g, _("error: domain is a live virtual machine.\n"
351                   "Writing to the disks of a running virtual machine can cause disk corruption.\n"
352                   "Either use read-only access, or if the guest is running the guestfsd daemon\n"
353                   "specify live access.  In most libguestfs tools these options are --ro or\n"
354                   "--live respectively.  Consult the documentation for further information."));
355       return -1;
356     }
357   }
358
359   /* Add the disks. */
360   struct guestfs_add_drive_opts_argv optargs2 = { .bitmask = 0 };
361   if (readonly) {
362     optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
363     optargs2.readonly = readonly;
364   }
365   if (iface) {
366     optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK;
367     optargs2.iface = iface;
368   }
369
370   /* Checkpoint the command line around the operation so that either
371    * all disks are added or none are added.
372    */
373   cmdline_pos = guestfs___checkpoint_cmdline (g);
374   r = guestfs___for_each_disk (g, dom, add_disk, &optargs2);
375   if (r == -1)
376     guestfs___rollback_cmdline (g, cmdline_pos);
377
378   return r;
379 }
380
381 static int
382 add_disk (guestfs_h *g, const char *filename, const char *format,
383           void *optargs_vp)
384 {
385   struct guestfs_add_drive_opts_argv *optargs = optargs_vp;
386
387   if (format) {
388     optargs->bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
389     optargs->format = format;
390   } else
391     optargs->bitmask &= ~GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
392
393   return guestfs__add_drive_opts (g, filename, optargs);
394 }
395
396 static int
397 connect_live (guestfs_h *g, virDomainPtr dom)
398 {
399   int i, r = -1;
400   virErrorPtr err;
401   xmlDocPtr doc = NULL;
402   xmlXPathContextPtr xpathCtx = NULL;
403   xmlXPathObjectPtr xpathObj = NULL;
404   char *xml = NULL;
405   char *path = NULL;
406   char *attach_method = NULL;
407
408   /* Domain XML. */
409   xml = virDomainGetXMLDesc (dom, 0);
410
411   if (!xml) {
412     err = virGetLastError ();
413     error (g, _("error reading libvirt XML information: %s"),
414            err->message);
415     goto cleanup;
416   }
417
418   /* Parse XML to document. */
419   doc = xmlParseMemory (xml, strlen (xml));
420   if (doc == NULL) {
421     error (g, _("unable to parse XML information returned by libvirt"));
422     goto cleanup;
423   }
424
425   xpathCtx = xmlXPathNewContext (doc);
426   if (xpathCtx == NULL) {
427     error (g, _("unable to create new XPath context"));
428     goto cleanup;
429   }
430
431   /* This gives us a set of all the <channel> nodes related to the
432    * guestfsd virtio-serial channel.
433    */
434   xpathObj = xmlXPathEvalExpression (BAD_CAST
435       "//devices/channel[@type=\"unix\" and "
436                         "./source/@mode=\"bind\" and "
437                         "./source/@path and "
438                         "./target/@type=\"virtio\" and "
439                         "./target/@name=\"org.libguestfs.channel.0\"]",
440                                      xpathCtx);
441   if (xpathObj == NULL) {
442     error (g, _("unable to evaluate XPath expression"));
443     goto cleanup;
444   }
445
446   xmlNodeSetPtr nodes = xpathObj->nodesetval;
447   for (i = 0; i < nodes->nodeNr; ++i) {
448     xmlXPathObjectPtr xppath;
449
450     /* See note in function above. */
451     xpathCtx->node = nodes->nodeTab[i];
452
453     /* The path is in <source path=..> attribute. */
454     xppath = xmlXPathEvalExpression (BAD_CAST "./source/@path", xpathCtx);
455     if (xppath == NULL ||
456         xppath->nodesetval == NULL ||
457         xppath->nodesetval->nodeNr == 0) {
458       xmlXPathFreeObject (xppath);
459       continue;                 /* no type attribute, skip it */
460     }
461     assert (xppath->nodesetval->nodeTab[0]);
462     assert (xppath->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
463     xmlAttrPtr attr = (xmlAttrPtr) xppath->nodesetval->nodeTab[0];
464     path = (char *) xmlNodeListGetString (doc, attr->children, 1);
465     xmlXPathFreeObject (xppath);
466     break;
467   }
468
469   if (path == NULL) {
470     error (g, _("this guest has no libvirt <channel> definition for guestfsd\n"
471                 "See ATTACHING TO RUNNING DAEMONS in guestfs(3) for further information."));
472     goto cleanup;
473   }
474
475   /* Got a path. */
476   attach_method = safe_malloc (g, strlen (path) + 5 + 1);
477   strcpy (attach_method, "unix:");
478   strcat (attach_method, path);
479   r = guestfs_set_attach_method (g, attach_method);
480
481  cleanup:
482   free (path);
483   free (attach_method);
484   free (xml);
485   if (xpathObj) xmlXPathFreeObject (xpathObj);
486   if (xpathCtx) xmlXPathFreeContext (xpathCtx);
487   if (doc) xmlFreeDoc (doc);
488
489   return r;
490 }
491
492 #else /* no libvirt or libxml2 at compile time */
493
494 #define NOT_IMPL(r)                                                     \
495   error (g, _("add-domain API not available since this version of libguestfs was compiled without libvirt or libxml2")); \
496   return r
497
498 int
499 guestfs__add_domain (guestfs_h *g, const char *dom,
500                      const struct guestfs_add_domain_argv *optargs)
501 {
502   NOT_IMPL(-1);
503 }
504
505 #endif /* no libvirt or libxml2 at compile time */