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