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