inspector: Remove some unused local variables.
[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;
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   output_mountpoints (xo, root);
411
412   output_filesystems (xo, root);
413
414   output_applications (xo, root);
415
416   XMLERROR (-1, xmlTextWriterEndElement (xo));
417 }
418
419 static int
420 compare_keys (const void *p1, const void *p2)
421 {
422   const char *key1 = * (char * const *) p1;
423   const char *key2 = * (char * const *) p2;
424
425   return strcmp (key1, key2);
426 }
427
428 static int
429 compare_keys_len (const void *p1, const void *p2)
430 {
431   const char *key1 = * (char * const *) p1;
432   const char *key2 = * (char * const *) p2;
433   int c;
434
435   c = strlen (key1) - strlen (key2);
436   if (c != 0)
437     return c;
438
439   return compare_keys (p1, p2);
440 }
441
442 static void
443 output_mountpoints (xmlTextWriterPtr xo, char *root)
444 {
445   char **mountpoints;
446   size_t i;
447
448   mountpoints = guestfs_inspect_get_mountpoints (g, root);
449   if (mountpoints == NULL)
450     exit (EXIT_FAILURE);
451
452   /* Sort by key length, shortest key first, and then name, so the
453    * output is stable.
454    */
455   qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
456          compare_keys_len);
457
458   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "mountpoints"));
459
460   for (i = 0; mountpoints[i] != NULL; i += 2) {
461     canonicalize (mountpoints[i+1]);
462
463     XMLERROR (-1,
464               xmlTextWriterStartElement (xo, BAD_CAST "mountpoint"));
465     XMLERROR (-1,
466               xmlTextWriterWriteAttribute (xo, BAD_CAST "dev",
467                                            BAD_CAST mountpoints[i+1]));
468     XMLERROR (-1,
469               xmlTextWriterWriteString (xo, BAD_CAST mountpoints[i]));
470     XMLERROR (-1, xmlTextWriterEndElement (xo));
471   }
472
473   XMLERROR (-1, xmlTextWriterEndElement (xo));
474
475   free_strings (mountpoints);
476 }
477
478 static void
479 output_filesystems (xmlTextWriterPtr xo, char *root)
480 {
481   char **filesystems;
482   char *str;
483   size_t i;
484
485   filesystems = guestfs_inspect_get_filesystems (g, root);
486   if (filesystems == NULL)
487     exit (EXIT_FAILURE);
488
489   /* Sort by name so the output is stable. */
490   qsort (filesystems, count_strings (filesystems), sizeof (char *),
491          compare_keys);
492
493   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystems"));
494
495   for (i = 0; filesystems[i] != NULL; ++i) {
496     canonicalize (filesystems[i]);
497
498     XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystem"));
499     XMLERROR (-1,
500               xmlTextWriterWriteAttribute (xo, BAD_CAST "dev",
501                                            BAD_CAST filesystems[i]));
502
503     DISABLE_GUESTFS_ERRORS_FOR (
504       str = guestfs_vfs_type (g, filesystems[i]);
505       if (str && str[0])
506         XMLERROR (-1,
507                   xmlTextWriterWriteElement (xo, BAD_CAST "type",
508                                              BAD_CAST str));
509       free (str);
510     );
511
512     DISABLE_GUESTFS_ERRORS_FOR (
513       str = guestfs_vfs_label (g, filesystems[i]);
514       if (str && str[0])
515         XMLERROR (-1,
516                   xmlTextWriterWriteElement (xo, BAD_CAST "label",
517                                              BAD_CAST str));
518       free (str);
519     );
520
521     DISABLE_GUESTFS_ERRORS_FOR (
522       str = guestfs_vfs_uuid (g, filesystems[i]);
523       if (str && str[0])
524         XMLERROR (-1,
525                   xmlTextWriterWriteElement (xo, BAD_CAST "uuid",
526                                              BAD_CAST str));
527       free (str);
528     );
529
530     XMLERROR (-1, xmlTextWriterEndElement (xo));
531   }
532
533   XMLERROR (-1, xmlTextWriterEndElement (xo));
534
535   free_strings (filesystems);
536 }
537
538 static void
539 output_applications (xmlTextWriterPtr xo, char *root)
540 {
541   struct guestfs_application_list *apps;
542   size_t i;
543
544   /* We need to mount everything up in order to read out the list of
545    * applications.
546    */
547   inspect_mount_root (root);
548
549   /* This returns an empty list if we simply couldn't determine the
550    * applications, so if it returns NULL then it's a real error.
551    */
552   apps = guestfs_inspect_list_applications (g, root);
553   if (apps == NULL)
554     exit (EXIT_FAILURE);
555   if (guestfs_umount_all (g) == -1)
556     exit (EXIT_FAILURE);
557
558   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "applications"));
559
560   for (i = 0; i < apps->len; ++i) {
561     XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "application"));
562
563     assert (apps->val[i].app_name && apps->val[i].app_name[0]);
564     XMLERROR (-1,
565               xmlTextWriterWriteElement (xo, BAD_CAST "name",
566                                          BAD_CAST apps->val[i].app_name));
567
568     if (apps->val[i].app_display_name && apps->val[i].app_display_name[0])
569       XMLERROR (-1,
570         xmlTextWriterWriteElement (xo, BAD_CAST "display_name",
571                                    BAD_CAST apps->val[i].app_display_name));
572
573     if (apps->val[i].app_epoch != 0) {
574       char buf[32];
575
576       snprintf (buf, sizeof buf, "%d", apps->val[i].app_epoch);
577
578       XMLERROR (-1,
579         xmlTextWriterWriteElement (xo, BAD_CAST "epoch", BAD_CAST buf));
580     }
581
582     if (apps->val[i].app_version && apps->val[i].app_version[0])
583       XMLERROR (-1,
584         xmlTextWriterWriteElement (xo, BAD_CAST "version",
585                                    BAD_CAST apps->val[i].app_version));
586     if (apps->val[i].app_release && apps->val[i].app_release[0])
587       XMLERROR (-1,
588         xmlTextWriterWriteElement (xo, BAD_CAST "release",
589                                    BAD_CAST apps->val[i].app_release));
590     if (apps->val[i].app_install_path && apps->val[i].app_install_path[0])
591       XMLERROR (-1,
592         xmlTextWriterWriteElement (xo, BAD_CAST "install_path",
593                                    BAD_CAST apps->val[i].app_install_path));
594     if (apps->val[i].app_publisher && apps->val[i].app_publisher[0])
595       XMLERROR (-1,
596         xmlTextWriterWriteElement (xo, BAD_CAST "publisher",
597                                    BAD_CAST apps->val[i].app_publisher));
598     if (apps->val[i].app_url && apps->val[i].app_url[0])
599       XMLERROR (-1,
600         xmlTextWriterWriteElement (xo, BAD_CAST "url",
601                                    BAD_CAST apps->val[i].app_url));
602     if (apps->val[i].app_source_package && apps->val[i].app_source_package[0])
603       XMLERROR (-1,
604         xmlTextWriterWriteElement (xo, BAD_CAST "source_package",
605                                    BAD_CAST apps->val[i].app_source_package));
606     if (apps->val[i].app_summary && apps->val[i].app_summary[0])
607       XMLERROR (-1,
608         xmlTextWriterWriteElement (xo, BAD_CAST "summary",
609                                    BAD_CAST apps->val[i].app_summary));
610     if (apps->val[i].app_description && apps->val[i].app_description[0])
611       XMLERROR (-1,
612         xmlTextWriterWriteElement (xo, BAD_CAST "description",
613                                    BAD_CAST apps->val[i].app_description));
614
615     XMLERROR (-1, xmlTextWriterEndElement (xo));
616   }
617
618   XMLERROR (-1, xmlTextWriterEndElement (xo));
619
620   guestfs_free_application_list (apps);
621 }
622
623 /* "/dev/vda1" -> "/dev/sda1"
624  * See BLOCK DEVICE NAMING in guestfs(3).
625  */
626 static void
627 canonicalize (char *dev)
628 {
629   if (STRPREFIX (dev, "/dev/") &&
630       (dev[5] == 'h' || dev[5] == 'v') &&
631       dev[6] == 'd' &&
632       c_isalpha (dev[7]) &&
633       (c_isdigit (dev[8]) || dev[8] == '\0'))
634     dev[5] = 's';
635 }
636
637 static void
638 free_strings (char **argv)
639 {
640   int argc;
641
642   for (argc = 0; argv[argc] != NULL; ++argc)
643     free (argv[argc]);
644   free (argv);
645 }
646
647 static int
648 count_strings (char *const *argv)
649 {
650   int c;
651
652   for (c = 0; argv[c]; ++c)
653     ;
654   return c;
655 }