fish: Declare run_* functions in a generated header file.
[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 <locale.h>
27 #include <assert.h>
28
29 #include <libxml/xmlIO.h>
30 #include <libxml/xmlwriter.h>
31
32 #include "progname.h"
33 #include "c-ctype.h"
34
35 #include "guestfs.h"
36 #include "options.h"
37
38 /* Currently open libguestfs handle. */
39 guestfs_h *g;
40
41 int read_only = 1;
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
231   /* Must be no extra arguments on the command line. */
232   if (optind != argc)
233     usage (EXIT_FAILURE);
234
235   /* User must have specified some drives. */
236   if (drvs == NULL)
237     usage (EXIT_FAILURE);
238
239   /* Add drives, inspect and mount.  Note that inspector is always true,
240    * and there is no -m option.
241    */
242   add_drives (drvs, 'a');
243
244   if (guestfs_launch (g) == -1)
245     exit (EXIT_FAILURE);
246
247   /* Free up data structures, no longer needed after this point. */
248   free_drives (drvs);
249
250   /* NB. Can't call inspect_mount () here (ie. normal processing of
251    * the -i option) because it can only handle a single root.  So we
252    * use low-level APIs.
253    */
254   inspect_do_decrypt ();
255
256   char **roots = guestfs_inspect_os (g);
257   if (roots == NULL) {
258     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"),
259              program_name);
260     exit (EXIT_FAILURE);
261   }
262
263   output (roots);
264
265   free_strings (roots);
266
267   guestfs_close (g);
268
269   exit (EXIT_SUCCESS);
270 }
271
272 #define DISABLE_GUESTFS_ERRORS_FOR(stmt) do {                           \
273     guestfs_error_handler_cb old_error_cb;                              \
274     void *old_error_data;                                               \
275     old_error_cb = guestfs_get_error_handler (g, &old_error_data);      \
276     guestfs_set_error_handler (g, NULL, NULL);                          \
277     stmt;                                                               \
278     guestfs_set_error_handler (g, old_error_cb, old_error_data);        \
279   } while (0)
280
281 #define XMLERROR(code,e) do {                                           \
282     if ((e) == (code)) {                                                \
283       fprintf (stderr, _("%s: XML write error at \"%s\": %m\n"),        \
284                #e, program_name);                                       \
285       exit (EXIT_FAILURE);                                              \
286     }                                                                   \
287   } while (0)
288
289 static void
290 output (char **roots)
291 {
292   xmlOutputBufferPtr ob = xmlOutputBufferCreateFd (1, NULL);
293   if (ob == NULL) {
294     fprintf (stderr,
295              _("%s: xmlOutputBufferCreateFd: failed to open stdout\n"),
296              program_name);
297     exit (EXIT_FAILURE);
298   }
299
300   xmlTextWriterPtr xo = xmlNewTextWriter (ob);
301   if (xo == NULL) {
302     fprintf (stderr,
303              _("%s: xmlNewTextWriter: failed to create libxml2 writer\n"),
304              program_name);
305     exit (EXIT_FAILURE);
306   }
307
308   /* Pretty-print the output. */
309   XMLERROR (-1, xmlTextWriterSetIndent (xo, 1));
310   XMLERROR (-1, xmlTextWriterSetIndentString (xo, BAD_CAST "  "));
311
312   XMLERROR (-1, xmlTextWriterStartDocument (xo, NULL, NULL, NULL));
313   output_roots (xo, roots);
314   XMLERROR (-1, xmlTextWriterEndDocument (xo));
315
316   /* 'ob' is freed by this too. */
317   xmlFreeTextWriter (xo);
318 }
319
320 static void
321 output_roots (xmlTextWriterPtr xo, char **roots)
322 {
323   size_t i;
324
325   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystems"));
326   for (i = 0; roots[i] != NULL; ++i)
327     output_root (xo, roots[i]);
328   XMLERROR (-1, xmlTextWriterEndElement (xo));
329 }
330
331 static void
332 output_root (xmlTextWriterPtr xo, char *root)
333 {
334   char *str;
335   int i;
336   char buf[32];
337   char canonical_root[strlen (root) + 1];
338
339   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystem"));
340
341   strcpy (canonical_root, root);
342   canonicalize (canonical_root);
343   XMLERROR (-1,
344     xmlTextWriterWriteElement (xo, BAD_CAST "root", BAD_CAST canonical_root));
345
346   str = guestfs_inspect_get_type (g, root);
347   if (!str) exit (EXIT_FAILURE);
348   if (STRNEQ (str, "unknown"))
349     XMLERROR (-1,
350       xmlTextWriterWriteElement (xo, BAD_CAST "name", BAD_CAST str));
351   free (str);
352
353   str = guestfs_inspect_get_arch (g, root);
354   if (!str) exit (EXIT_FAILURE);
355   if (STRNEQ (str, "unknown"))
356     XMLERROR (-1,
357       xmlTextWriterWriteElement (xo, BAD_CAST "arch", BAD_CAST str));
358   free (str);
359
360   str = guestfs_inspect_get_distro (g, root);
361   if (!str) exit (EXIT_FAILURE);
362   if (STRNEQ (str, "unknown"))
363     XMLERROR (-1,
364       xmlTextWriterWriteElement (xo, BAD_CAST "distro", BAD_CAST str));
365   free (str);
366
367   str = guestfs_inspect_get_product_name (g, root);
368   if (!str) exit (EXIT_FAILURE);
369   if (STRNEQ (str, "unknown"))
370     XMLERROR (-1,
371       xmlTextWriterWriteElement (xo, BAD_CAST "product_name", BAD_CAST str));
372   free (str);
373
374   i = guestfs_inspect_get_major_version (g, root);
375   snprintf (buf, sizeof buf, "%d", i);
376   XMLERROR (-1,
377     xmlTextWriterWriteElement (xo, BAD_CAST "major_version", BAD_CAST buf));
378   i = guestfs_inspect_get_minor_version (g, root);
379   snprintf (buf, sizeof buf, "%d", i);
380   XMLERROR (-1,
381     xmlTextWriterWriteElement (xo, BAD_CAST "minor_version", BAD_CAST buf));
382
383   str = guestfs_inspect_get_package_format (g, root);
384   if (!str) exit (EXIT_FAILURE);
385   if (STRNEQ (str, "unknown"))
386     XMLERROR (-1,
387       xmlTextWriterWriteElement (xo, BAD_CAST "package_format", BAD_CAST str));
388   free (str);
389
390   str = guestfs_inspect_get_package_management (g, root);
391   if (!str) exit (EXIT_FAILURE);
392   if (STRNEQ (str, "unknown"))
393     XMLERROR (-1,
394       xmlTextWriterWriteElement (xo, BAD_CAST "package_management",
395                                  BAD_CAST str));
396   free (str);
397
398   /* inspect-get-windows-systemroot will fail with non-windows guests,
399    * or if the systemroot could not be determined for a windows guest.
400    * Disable error output around this call.
401    */
402   DISABLE_GUESTFS_ERRORS_FOR (
403     str = guestfs_inspect_get_windows_systemroot (g, root);
404     if (str)
405       XMLERROR (-1,
406                 xmlTextWriterWriteElement (xo, BAD_CAST "windows_systemroot",
407                                            BAD_CAST str));
408     free (str);
409   );
410
411   str = guestfs_inspect_get_hostname (g, root);
412   if (!str) exit (EXIT_FAILURE);
413   if (STRNEQ (str, "unknown"))
414     XMLERROR (-1,
415       xmlTextWriterWriteElement (xo, BAD_CAST "hostname",
416                                  BAD_CAST str));
417   free (str);
418
419   output_mountpoints (xo, root);
420
421   output_filesystems (xo, root);
422
423   output_applications (xo, root);
424
425   XMLERROR (-1, xmlTextWriterEndElement (xo));
426 }
427
428 static int
429 compare_keys (const void *p1, const void *p2)
430 {
431   const char *key1 = * (char * const *) p1;
432   const char *key2 = * (char * const *) p2;
433
434   return strcmp (key1, key2);
435 }
436
437 static int
438 compare_keys_len (const void *p1, const void *p2)
439 {
440   const char *key1 = * (char * const *) p1;
441   const char *key2 = * (char * const *) p2;
442   int c;
443
444   c = strlen (key1) - strlen (key2);
445   if (c != 0)
446     return c;
447
448   return compare_keys (p1, p2);
449 }
450
451 static void
452 output_mountpoints (xmlTextWriterPtr xo, char *root)
453 {
454   char **mountpoints;
455   size_t i;
456
457   mountpoints = guestfs_inspect_get_mountpoints (g, root);
458   if (mountpoints == NULL)
459     exit (EXIT_FAILURE);
460
461   /* Sort by key length, shortest key first, and then name, so the
462    * output is stable.
463    */
464   qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
465          compare_keys_len);
466
467   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "mountpoints"));
468
469   for (i = 0; mountpoints[i] != NULL; i += 2) {
470     canonicalize (mountpoints[i+1]);
471
472     XMLERROR (-1,
473               xmlTextWriterStartElement (xo, BAD_CAST "mountpoint"));
474     XMLERROR (-1,
475               xmlTextWriterWriteAttribute (xo, BAD_CAST "dev",
476                                            BAD_CAST mountpoints[i+1]));
477     XMLERROR (-1,
478               xmlTextWriterWriteString (xo, BAD_CAST mountpoints[i]));
479     XMLERROR (-1, xmlTextWriterEndElement (xo));
480   }
481
482   XMLERROR (-1, xmlTextWriterEndElement (xo));
483
484   free_strings (mountpoints);
485 }
486
487 static void
488 output_filesystems (xmlTextWriterPtr xo, char *root)
489 {
490   char **filesystems;
491   char *str;
492   size_t i;
493
494   filesystems = guestfs_inspect_get_filesystems (g, root);
495   if (filesystems == NULL)
496     exit (EXIT_FAILURE);
497
498   /* Sort by name so the output is stable. */
499   qsort (filesystems, count_strings (filesystems), sizeof (char *),
500          compare_keys);
501
502   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystems"));
503
504   for (i = 0; filesystems[i] != NULL; ++i) {
505     canonicalize (filesystems[i]);
506
507     XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystem"));
508     XMLERROR (-1,
509               xmlTextWriterWriteAttribute (xo, BAD_CAST "dev",
510                                            BAD_CAST filesystems[i]));
511
512     DISABLE_GUESTFS_ERRORS_FOR (
513       str = guestfs_vfs_type (g, filesystems[i]);
514       if (str && str[0])
515         XMLERROR (-1,
516                   xmlTextWriterWriteElement (xo, BAD_CAST "type",
517                                              BAD_CAST str));
518       free (str);
519     );
520
521     DISABLE_GUESTFS_ERRORS_FOR (
522       str = guestfs_vfs_label (g, filesystems[i]);
523       if (str && str[0])
524         XMLERROR (-1,
525                   xmlTextWriterWriteElement (xo, BAD_CAST "label",
526                                              BAD_CAST str));
527       free (str);
528     );
529
530     DISABLE_GUESTFS_ERRORS_FOR (
531       str = guestfs_vfs_uuid (g, filesystems[i]);
532       if (str && str[0])
533         XMLERROR (-1,
534                   xmlTextWriterWriteElement (xo, BAD_CAST "uuid",
535                                              BAD_CAST str));
536       free (str);
537     );
538
539     XMLERROR (-1, xmlTextWriterEndElement (xo));
540   }
541
542   XMLERROR (-1, xmlTextWriterEndElement (xo));
543
544   free_strings (filesystems);
545 }
546
547 static void
548 output_applications (xmlTextWriterPtr xo, char *root)
549 {
550   struct guestfs_application_list *apps;
551   size_t i;
552
553   /* We need to mount everything up in order to read out the list of
554    * applications.
555    */
556   inspect_mount_root (root);
557
558   /* This returns an empty list if we simply couldn't determine the
559    * applications, so if it returns NULL then it's a real error.
560    */
561   apps = guestfs_inspect_list_applications (g, root);
562   if (apps == NULL)
563     exit (EXIT_FAILURE);
564   if (guestfs_umount_all (g) == -1)
565     exit (EXIT_FAILURE);
566
567   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "applications"));
568
569   for (i = 0; i < apps->len; ++i) {
570     XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "application"));
571
572     assert (apps->val[i].app_name && apps->val[i].app_name[0]);
573     XMLERROR (-1,
574               xmlTextWriterWriteElement (xo, BAD_CAST "name",
575                                          BAD_CAST apps->val[i].app_name));
576
577     if (apps->val[i].app_display_name && apps->val[i].app_display_name[0])
578       XMLERROR (-1,
579         xmlTextWriterWriteElement (xo, BAD_CAST "display_name",
580                                    BAD_CAST apps->val[i].app_display_name));
581
582     if (apps->val[i].app_epoch != 0) {
583       char buf[32];
584
585       snprintf (buf, sizeof buf, "%d", apps->val[i].app_epoch);
586
587       XMLERROR (-1,
588         xmlTextWriterWriteElement (xo, BAD_CAST "epoch", BAD_CAST buf));
589     }
590
591     if (apps->val[i].app_version && apps->val[i].app_version[0])
592       XMLERROR (-1,
593         xmlTextWriterWriteElement (xo, BAD_CAST "version",
594                                    BAD_CAST apps->val[i].app_version));
595     if (apps->val[i].app_release && apps->val[i].app_release[0])
596       XMLERROR (-1,
597         xmlTextWriterWriteElement (xo, BAD_CAST "release",
598                                    BAD_CAST apps->val[i].app_release));
599     if (apps->val[i].app_install_path && apps->val[i].app_install_path[0])
600       XMLERROR (-1,
601         xmlTextWriterWriteElement (xo, BAD_CAST "install_path",
602                                    BAD_CAST apps->val[i].app_install_path));
603     if (apps->val[i].app_publisher && apps->val[i].app_publisher[0])
604       XMLERROR (-1,
605         xmlTextWriterWriteElement (xo, BAD_CAST "publisher",
606                                    BAD_CAST apps->val[i].app_publisher));
607     if (apps->val[i].app_url && apps->val[i].app_url[0])
608       XMLERROR (-1,
609         xmlTextWriterWriteElement (xo, BAD_CAST "url",
610                                    BAD_CAST apps->val[i].app_url));
611     if (apps->val[i].app_source_package && apps->val[i].app_source_package[0])
612       XMLERROR (-1,
613         xmlTextWriterWriteElement (xo, BAD_CAST "source_package",
614                                    BAD_CAST apps->val[i].app_source_package));
615     if (apps->val[i].app_summary && apps->val[i].app_summary[0])
616       XMLERROR (-1,
617         xmlTextWriterWriteElement (xo, BAD_CAST "summary",
618                                    BAD_CAST apps->val[i].app_summary));
619     if (apps->val[i].app_description && apps->val[i].app_description[0])
620       XMLERROR (-1,
621         xmlTextWriterWriteElement (xo, BAD_CAST "description",
622                                    BAD_CAST apps->val[i].app_description));
623
624     XMLERROR (-1, xmlTextWriterEndElement (xo));
625   }
626
627   XMLERROR (-1, xmlTextWriterEndElement (xo));
628
629   guestfs_free_application_list (apps);
630 }
631
632 /* "/dev/vda1" -> "/dev/sda1"
633  * See BLOCK DEVICE NAMING in guestfs(3).
634  */
635 static void
636 canonicalize (char *dev)
637 {
638   if (STRPREFIX (dev, "/dev/") &&
639       (dev[5] == 'h' || dev[5] == 'v') &&
640       dev[6] == 'd' &&
641       c_isalpha (dev[7]) &&
642       (c_isdigit (dev[8]) || dev[8] == '\0'))
643     dev[5] = 's';
644 }
645
646 static void
647 free_strings (char **argv)
648 {
649   int argc;
650
651   for (argc = 0; argv[argc] != NULL; ++argc)
652     free (argv[argc]);
653   free (argv);
654 }
655
656 static int
657 count_strings (char *const *argv)
658 {
659   int c;
660
661   for (c = 0; argv[c]; ++c)
662     ;
663   return c;
664 }