fish: Add guestfish --live, guestmount --live options.
[libguestfs.git] / inspector / virt-inspector.c
1 /* virt-inspector
2  * Copyright (C) 2010 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <inttypes.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include <assert.h>
27
28 #include <libxml/xmlIO.h>
29 #include <libxml/xmlwriter.h>
30
31 #include "progname.h"
32 #include "c-ctype.h"
33
34 #include "guestfs.h"
35 #include "options.h"
36
37 /* Currently open libguestfs handle. */
38 guestfs_h *g;
39
40 int read_only = 1;
41 int live = 0;
42 int verbose = 0;
43 int keys_from_stdin = 0;
44 int echo_keys = 0;
45 const char *libvirt_uri = NULL;
46 int inspector = 1;
47
48 static void output (char **roots);
49 static void output_roots (xmlTextWriterPtr xo, char **roots);
50 static void output_root (xmlTextWriterPtr xo, char *root);
51 static void output_mountpoints (xmlTextWriterPtr xo, char *root);
52 static void output_filesystems (xmlTextWriterPtr xo, char *root);
53 static void output_applications (xmlTextWriterPtr xo, char *root);
54 static void canonicalize (char *dev);
55 static void free_strings (char **argv);
56 static int count_strings (char *const*argv);
57
58 static inline char *
59 bad_cast (char const *s)
60 {
61   return (char *) s;
62 }
63
64 static void __attribute__((noreturn))
65 usage (int status)
66 {
67   if (status != EXIT_SUCCESS)
68     fprintf (stderr, _("Try `%s --help' for more information.\n"),
69              program_name);
70   else {
71     fprintf (stdout,
72            _("%s: display information about a virtual machine\n"
73              "Copyright (C) 2010 Red Hat Inc.\n"
74              "Usage:\n"
75              "  %s [--options] -d domname file [file ...]\n"
76              "  %s [--options] -a disk.img [-a disk.img ...] file [file ...]\n"
77              "Options:\n"
78              "  -a|--add image       Add image\n"
79              "  -c|--connect uri     Specify libvirt URI for -d option\n"
80              "  -d|--domain guest    Add disks from libvirt guest\n"
81              "  --echo-keys          Don't turn off echo for passphrases\n"
82              "  --format[=raw|..]    Force disk format for -a option\n"
83              "  --help               Display brief help\n"
84              "  --keys-from-stdin    Read passphrases from stdin\n"
85              "  -v|--verbose         Verbose messages\n"
86              "  -V|--version         Display version and exit\n"
87              "  -x                   Trace libguestfs API calls\n"
88              "For more information, see the manpage %s(1).\n"),
89              program_name, program_name, program_name,
90              program_name);
91   }
92   exit (status);
93 }
94
95 int
96 main (int argc, char *argv[])
97 {
98   /* Set global program name that is not polluted with libtool artifacts.  */
99   set_program_name (argv[0]);
100
101   setlocale (LC_ALL, "");
102   bindtextdomain (PACKAGE, LOCALEBASEDIR);
103   textdomain (PACKAGE);
104
105   enum { HELP_OPTION = CHAR_MAX + 1 };
106
107   static const char *options = "a:c:d:vVx";
108   static const struct option long_options[] = {
109     { "add", 1, 0, 'a' },
110     { "connect", 1, 0, 'c' },
111     { "domain", 1, 0, 'd' },
112     { "echo-keys", 0, 0, 0 },
113     { "format", 2, 0, 0 },
114     { "help", 0, 0, HELP_OPTION },
115     { "keys-from-stdin", 0, 0, 0 },
116     { "verbose", 0, 0, 'v' },
117     { "version", 0, 0, 'V' },
118     { 0, 0, 0, 0 }
119   };
120   struct drv *drvs = NULL;
121   struct drv *drv;
122   const char *format = NULL;
123   int c;
124   int option_index;
125
126   g = guestfs_create ();
127   if (g == NULL) {
128     fprintf (stderr, _("guestfs_create: failed to create handle\n"));
129     exit (EXIT_FAILURE);
130   }
131
132   argv[0] = bad_cast (program_name);
133
134   for (;;) {
135     c = getopt_long (argc, argv, options, long_options, &option_index);
136     if (c == -1) break;
137
138     switch (c) {
139     case 0:                     /* options which are long only */
140       if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
141         keys_from_stdin = 1;
142       } else if (STREQ (long_options[option_index].name, "echo-keys")) {
143         echo_keys = 1;
144       } else if (STREQ (long_options[option_index].name, "format")) {
145         if (!optarg || STREQ (optarg, ""))
146           format = NULL;
147         else
148           format = optarg;
149       } else {
150         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
151                  program_name, long_options[option_index].name, option_index);
152         exit (EXIT_FAILURE);
153       }
154       break;
155
156     case 'a':
157       OPTION_a;
158       break;
159
160     case 'c':
161       OPTION_c;
162       break;
163
164     case 'd':
165       OPTION_d;
166       break;
167
168     case 'h':
169       usage (EXIT_SUCCESS);
170
171     case 'v':
172       OPTION_v;
173       break;
174
175     case 'V':
176       OPTION_V;
177       break;
178
179     case 'x':
180       OPTION_x;
181       break;
182
183     case HELP_OPTION:
184       usage (EXIT_SUCCESS);
185
186     default:
187       usage (EXIT_FAILURE);
188     }
189   }
190
191   /* Old-style syntax?  There were no -a or -d options in the old
192    * virt-inspector which is how we detect this.
193    */
194   if (drvs == NULL) {
195     while (optind < argc) {
196       if (strchr (argv[optind], '/') ||
197           access (argv[optind], F_OK) == 0) { /* simulate -a option */
198         drv = malloc (sizeof (struct drv));
199         if (!drv) {
200           perror ("malloc");
201           exit (EXIT_FAILURE);
202         }
203         drv->type = drv_a;
204         drv->a.filename = argv[optind];
205         drv->a.format = NULL;
206         drv->next = drvs;
207         drvs = drv;
208       } else {                  /* simulate -d option */
209         drv = malloc (sizeof (struct drv));
210         if (!drv) {
211           perror ("malloc");
212           exit (EXIT_FAILURE);
213         }
214         drv->type = drv_d;
215         drv->d.guest = argv[optind];
216         drv->next = drvs;
217         drvs = drv;
218       }
219
220       optind++;
221     }
222   }
223
224   /* These are really constants, but they have to be variables for the
225    * options parsing code.  Assert here that they have known-good
226    * values.
227    */
228   assert (read_only == 1);
229   assert (inspector == 1);
230   assert (live == 0);
231
232   /* Must be no extra arguments on the command line. */
233   if (optind != argc)
234     usage (EXIT_FAILURE);
235
236   /* User must have specified some drives. */
237   if (drvs == NULL)
238     usage (EXIT_FAILURE);
239
240   /* Add drives, inspect and mount.  Note that inspector is always true,
241    * and there is no -m option.
242    */
243   add_drives (drvs, 'a');
244
245   if (guestfs_launch (g) == -1)
246     exit (EXIT_FAILURE);
247
248   /* Free up data structures, no longer needed after this point. */
249   free_drives (drvs);
250
251   /* NB. Can't call inspect_mount () here (ie. normal processing of
252    * the -i option) because it can only handle a single root.  So we
253    * use low-level APIs.
254    */
255   inspect_do_decrypt ();
256
257   char **roots = guestfs_inspect_os (g);
258   if (roots == NULL) {
259     fprintf (stderr, _("%s: no operating system could be detected inside this disk image.\n\nThis may be because the file is not a disk image, or is not a virtual machine\nimage, or because the OS type is not understood by libguestfs.\n\nNOTE for Red Hat Enterprise Linux 6 users: for Windows guest support you must\ninstall the separate libguestfs-winsupport package.\n\nIf you feel this is an error, please file a bug report including as much\ninformation about the disk image as possible.\n"),
260              program_name);
261     exit (EXIT_FAILURE);
262   }
263
264   output (roots);
265
266   free_strings (roots);
267
268   guestfs_close (g);
269
270   exit (EXIT_SUCCESS);
271 }
272
273 #define DISABLE_GUESTFS_ERRORS_FOR(stmt) do {                           \
274     guestfs_error_handler_cb old_error_cb;                              \
275     void *old_error_data;                                               \
276     old_error_cb = guestfs_get_error_handler (g, &old_error_data);      \
277     guestfs_set_error_handler (g, NULL, NULL);                          \
278     stmt;                                                               \
279     guestfs_set_error_handler (g, old_error_cb, old_error_data);        \
280   } while (0)
281
282 #define XMLERROR(code,e) do {                                           \
283     if ((e) == (code)) {                                                \
284       fprintf (stderr, _("%s: XML write error at \"%s\": %m\n"),        \
285                #e, program_name);                                       \
286       exit (EXIT_FAILURE);                                              \
287     }                                                                   \
288   } while (0)
289
290 static void
291 output (char **roots)
292 {
293   xmlOutputBufferPtr ob = xmlOutputBufferCreateFd (1, NULL);
294   if (ob == NULL) {
295     fprintf (stderr,
296              _("%s: xmlOutputBufferCreateFd: failed to open stdout\n"),
297              program_name);
298     exit (EXIT_FAILURE);
299   }
300
301   xmlTextWriterPtr xo = xmlNewTextWriter (ob);
302   if (xo == NULL) {
303     fprintf (stderr,
304              _("%s: xmlNewTextWriter: failed to create libxml2 writer\n"),
305              program_name);
306     exit (EXIT_FAILURE);
307   }
308
309   /* Pretty-print the output. */
310   XMLERROR (-1, xmlTextWriterSetIndent (xo, 1));
311   XMLERROR (-1, xmlTextWriterSetIndentString (xo, BAD_CAST "  "));
312
313   XMLERROR (-1, xmlTextWriterStartDocument (xo, NULL, NULL, NULL));
314   output_roots (xo, roots);
315   XMLERROR (-1, xmlTextWriterEndDocument (xo));
316
317   /* 'ob' is freed by this too. */
318   xmlFreeTextWriter (xo);
319 }
320
321 static void
322 output_roots (xmlTextWriterPtr xo, char **roots)
323 {
324   size_t i;
325
326   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystems"));
327   for (i = 0; roots[i] != NULL; ++i)
328     output_root (xo, roots[i]);
329   XMLERROR (-1, xmlTextWriterEndElement (xo));
330 }
331
332 static void
333 output_root (xmlTextWriterPtr xo, char *root)
334 {
335   char *str;
336   int i, r;
337   char buf[32];
338   char canonical_root[strlen (root) + 1];
339
340   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystem"));
341
342   strcpy (canonical_root, root);
343   canonicalize (canonical_root);
344   XMLERROR (-1,
345     xmlTextWriterWriteElement (xo, BAD_CAST "root", BAD_CAST canonical_root));
346
347   str = guestfs_inspect_get_type (g, root);
348   if (!str) exit (EXIT_FAILURE);
349   if (STRNEQ (str, "unknown"))
350     XMLERROR (-1,
351       xmlTextWriterWriteElement (xo, BAD_CAST "name", BAD_CAST str));
352   free (str);
353
354   str = guestfs_inspect_get_arch (g, root);
355   if (!str) exit (EXIT_FAILURE);
356   if (STRNEQ (str, "unknown"))
357     XMLERROR (-1,
358       xmlTextWriterWriteElement (xo, BAD_CAST "arch", BAD_CAST str));
359   free (str);
360
361   str = guestfs_inspect_get_distro (g, root);
362   if (!str) exit (EXIT_FAILURE);
363   if (STRNEQ (str, "unknown"))
364     XMLERROR (-1,
365       xmlTextWriterWriteElement (xo, BAD_CAST "distro", BAD_CAST str));
366   free (str);
367
368   str = guestfs_inspect_get_product_name (g, root);
369   if (!str) exit (EXIT_FAILURE);
370   if (STRNEQ (str, "unknown"))
371     XMLERROR (-1,
372       xmlTextWriterWriteElement (xo, BAD_CAST "product_name", BAD_CAST str));
373   free (str);
374
375   i = guestfs_inspect_get_major_version (g, root);
376   snprintf (buf, sizeof buf, "%d", i);
377   XMLERROR (-1,
378     xmlTextWriterWriteElement (xo, BAD_CAST "major_version", BAD_CAST buf));
379   i = guestfs_inspect_get_minor_version (g, root);
380   snprintf (buf, sizeof buf, "%d", i);
381   XMLERROR (-1,
382     xmlTextWriterWriteElement (xo, BAD_CAST "minor_version", BAD_CAST buf));
383
384   str = guestfs_inspect_get_package_format (g, root);
385   if (!str) exit (EXIT_FAILURE);
386   if (STRNEQ (str, "unknown"))
387     XMLERROR (-1,
388       xmlTextWriterWriteElement (xo, BAD_CAST "package_format", BAD_CAST str));
389   free (str);
390
391   str = guestfs_inspect_get_package_management (g, root);
392   if (!str) exit (EXIT_FAILURE);
393   if (STRNEQ (str, "unknown"))
394     XMLERROR (-1,
395       xmlTextWriterWriteElement (xo, BAD_CAST "package_management",
396                                  BAD_CAST str));
397   free (str);
398
399   /* inspect-get-windows-systemroot will fail with non-windows guests,
400    * or if the systemroot could not be determined for a windows guest.
401    * Disable error output around this call.
402    */
403   DISABLE_GUESTFS_ERRORS_FOR (
404     str = guestfs_inspect_get_windows_systemroot (g, root);
405     if (str)
406       XMLERROR (-1,
407                 xmlTextWriterWriteElement (xo, BAD_CAST "windows_systemroot",
408                                            BAD_CAST str));
409     free (str);
410   );
411
412   str = guestfs_inspect_get_format (g, root);
413   if (!str) exit (EXIT_FAILURE);
414   if (STRNEQ (str, "unknown"))
415     XMLERROR (-1,
416       xmlTextWriterWriteElement (xo, BAD_CAST "format",
417                                  BAD_CAST str));
418   free (str);
419
420   r = guestfs_inspect_is_live (g, root);
421   if (r > 0) {
422     XMLERROR (-1,
423               xmlTextWriterStartElement (xo, BAD_CAST "live"));
424     XMLERROR (-1, xmlTextWriterEndElement (xo));
425   }
426
427   r = guestfs_inspect_is_netinst (g, root);
428   if (r > 0) {
429     XMLERROR (-1,
430               xmlTextWriterStartElement (xo, BAD_CAST "netinst"));
431     XMLERROR (-1, xmlTextWriterEndElement (xo));
432   }
433
434   r = guestfs_inspect_is_multipart (g, root);
435   if (r > 0) {
436     XMLERROR (-1,
437               xmlTextWriterStartElement (xo, BAD_CAST "multipart"));
438     XMLERROR (-1, xmlTextWriterEndElement (xo));
439   }
440
441   output_mountpoints (xo, root);
442
443   output_filesystems (xo, root);
444
445   output_applications (xo, root);
446
447   XMLERROR (-1, xmlTextWriterEndElement (xo));
448 }
449
450 static int
451 compare_keys (const void *p1, const void *p2)
452 {
453   const char *key1 = * (char * const *) p1;
454   const char *key2 = * (char * const *) p2;
455
456   return strcmp (key1, key2);
457 }
458
459 static int
460 compare_keys_len (const void *p1, const void *p2)
461 {
462   const char *key1 = * (char * const *) p1;
463   const char *key2 = * (char * const *) p2;
464   int c;
465
466   c = strlen (key1) - strlen (key2);
467   if (c != 0)
468     return c;
469
470   return compare_keys (p1, p2);
471 }
472
473 static void
474 output_mountpoints (xmlTextWriterPtr xo, char *root)
475 {
476   char **mountpoints;
477   size_t i;
478
479   mountpoints = guestfs_inspect_get_mountpoints (g, root);
480   if (mountpoints == NULL)
481     exit (EXIT_FAILURE);
482
483   /* Sort by key length, shortest key first, and then name, so the
484    * output is stable.
485    */
486   qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
487          compare_keys_len);
488
489   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "mountpoints"));
490
491   for (i = 0; mountpoints[i] != NULL; i += 2) {
492     canonicalize (mountpoints[i+1]);
493
494     XMLERROR (-1,
495               xmlTextWriterStartElement (xo, BAD_CAST "mountpoint"));
496     XMLERROR (-1,
497               xmlTextWriterWriteAttribute (xo, BAD_CAST "dev",
498                                            BAD_CAST mountpoints[i+1]));
499     XMLERROR (-1,
500               xmlTextWriterWriteString (xo, BAD_CAST mountpoints[i]));
501     XMLERROR (-1, xmlTextWriterEndElement (xo));
502   }
503
504   XMLERROR (-1, xmlTextWriterEndElement (xo));
505
506   free_strings (mountpoints);
507 }
508
509 static void
510 output_filesystems (xmlTextWriterPtr xo, char *root)
511 {
512   char **filesystems;
513   char *str;
514   size_t i;
515
516   filesystems = guestfs_inspect_get_filesystems (g, root);
517   if (filesystems == NULL)
518     exit (EXIT_FAILURE);
519
520   /* Sort by name so the output is stable. */
521   qsort (filesystems, count_strings (filesystems), sizeof (char *),
522          compare_keys);
523
524   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystems"));
525
526   for (i = 0; filesystems[i] != NULL; ++i) {
527     canonicalize (filesystems[i]);
528
529     XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystem"));
530     XMLERROR (-1,
531               xmlTextWriterWriteAttribute (xo, BAD_CAST "dev",
532                                            BAD_CAST filesystems[i]));
533
534     DISABLE_GUESTFS_ERRORS_FOR (
535       str = guestfs_vfs_type (g, filesystems[i]);
536       if (str && str[0])
537         XMLERROR (-1,
538                   xmlTextWriterWriteElement (xo, BAD_CAST "type",
539                                              BAD_CAST str));
540       free (str);
541     );
542
543     DISABLE_GUESTFS_ERRORS_FOR (
544       str = guestfs_vfs_label (g, filesystems[i]);
545       if (str && str[0])
546         XMLERROR (-1,
547                   xmlTextWriterWriteElement (xo, BAD_CAST "label",
548                                              BAD_CAST str));
549       free (str);
550     );
551
552     DISABLE_GUESTFS_ERRORS_FOR (
553       str = guestfs_vfs_uuid (g, filesystems[i]);
554       if (str && str[0])
555         XMLERROR (-1,
556                   xmlTextWriterWriteElement (xo, BAD_CAST "uuid",
557                                              BAD_CAST str));
558       free (str);
559     );
560
561     XMLERROR (-1, xmlTextWriterEndElement (xo));
562   }
563
564   XMLERROR (-1, xmlTextWriterEndElement (xo));
565
566   free_strings (filesystems);
567 }
568
569 static void
570 output_applications (xmlTextWriterPtr xo, char *root)
571 {
572   struct guestfs_application_list *apps;
573   size_t i;
574
575   /* We need to mount everything up in order to read out the list of
576    * applications.
577    */
578   inspect_mount_root (root);
579
580   /* This returns an empty list if we simply couldn't determine the
581    * applications, so if it returns NULL then it's a real error.
582    */
583   apps = guestfs_inspect_list_applications (g, root);
584   if (apps == NULL)
585     exit (EXIT_FAILURE);
586   if (guestfs_umount_all (g) == -1)
587     exit (EXIT_FAILURE);
588
589   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "applications"));
590
591   for (i = 0; i < apps->len; ++i) {
592     XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "application"));
593
594     assert (apps->val[i].app_name && apps->val[i].app_name[0]);
595     XMLERROR (-1,
596               xmlTextWriterWriteElement (xo, BAD_CAST "name",
597                                          BAD_CAST apps->val[i].app_name));
598
599     if (apps->val[i].app_display_name && apps->val[i].app_display_name[0])
600       XMLERROR (-1,
601         xmlTextWriterWriteElement (xo, BAD_CAST "display_name",
602                                    BAD_CAST apps->val[i].app_display_name));
603
604     if (apps->val[i].app_epoch != 0) {
605       char buf[32];
606
607       snprintf (buf, sizeof buf, "%d", apps->val[i].app_epoch);
608
609       XMLERROR (-1,
610         xmlTextWriterWriteElement (xo, BAD_CAST "epoch", BAD_CAST buf));
611     }
612
613     if (apps->val[i].app_version && apps->val[i].app_version[0])
614       XMLERROR (-1,
615         xmlTextWriterWriteElement (xo, BAD_CAST "version",
616                                    BAD_CAST apps->val[i].app_version));
617     if (apps->val[i].app_release && apps->val[i].app_release[0])
618       XMLERROR (-1,
619         xmlTextWriterWriteElement (xo, BAD_CAST "release",
620                                    BAD_CAST apps->val[i].app_release));
621     if (apps->val[i].app_install_path && apps->val[i].app_install_path[0])
622       XMLERROR (-1,
623         xmlTextWriterWriteElement (xo, BAD_CAST "install_path",
624                                    BAD_CAST apps->val[i].app_install_path));
625     if (apps->val[i].app_publisher && apps->val[i].app_publisher[0])
626       XMLERROR (-1,
627         xmlTextWriterWriteElement (xo, BAD_CAST "publisher",
628                                    BAD_CAST apps->val[i].app_publisher));
629     if (apps->val[i].app_url && apps->val[i].app_url[0])
630       XMLERROR (-1,
631         xmlTextWriterWriteElement (xo, BAD_CAST "url",
632                                    BAD_CAST apps->val[i].app_url));
633     if (apps->val[i].app_source_package && apps->val[i].app_source_package[0])
634       XMLERROR (-1,
635         xmlTextWriterWriteElement (xo, BAD_CAST "source_package",
636                                    BAD_CAST apps->val[i].app_source_package));
637     if (apps->val[i].app_summary && apps->val[i].app_summary[0])
638       XMLERROR (-1,
639         xmlTextWriterWriteElement (xo, BAD_CAST "summary",
640                                    BAD_CAST apps->val[i].app_summary));
641     if (apps->val[i].app_description && apps->val[i].app_description[0])
642       XMLERROR (-1,
643         xmlTextWriterWriteElement (xo, BAD_CAST "description",
644                                    BAD_CAST apps->val[i].app_description));
645
646     XMLERROR (-1, xmlTextWriterEndElement (xo));
647   }
648
649   XMLERROR (-1, xmlTextWriterEndElement (xo));
650
651   guestfs_free_application_list (apps);
652 }
653
654 /* "/dev/vda1" -> "/dev/sda1"
655  * See BLOCK DEVICE NAMING in guestfs(3).
656  */
657 static void
658 canonicalize (char *dev)
659 {
660   if (STRPREFIX (dev, "/dev/") &&
661       (dev[5] == 'h' || dev[5] == 'v') &&
662       dev[6] == 'd' &&
663       c_isalpha (dev[7]) &&
664       (c_isdigit (dev[8]) || dev[8] == '\0'))
665     dev[5] = 's';
666 }
667
668 static void
669 free_strings (char **argv)
670 {
671   int argc;
672
673   for (argc = 0; argv[argc] != NULL; ++argc)
674     free (argv[argc]);
675   free (argv);
676 }
677
678 static int
679 count_strings (char *const *argv)
680 {
681   int c;
682
683   for (c = 0; argv[c]; ++c)
684     ;
685   return c;
686 }