Fix infinite loop encountered when reading Windows disk in example program.
[libguestfs.git] / examples / to-xml.c
1 /* This inspects a block device and produces an XML representation of
2  * the partitions, LVM, filesystems that we find there.  This could be
3  * useful as example code of how to do this sort of probing, or to
4  * feed the XML to other programs.
5  *
6  * Usage:
7  *   to-xml guest.img [guest.img ...]
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdint.h>
14 #include <inttypes.h>
15 #include <unistd.h>
16 #include <ctype.h>
17
18 #include <guestfs.h>
19
20 /* Note that if any API call fails, we can just exit.  The
21  * standard error handler will have printed the error message
22  * to stderr already.
23  */
24 #define CALL(call,errcode)                      \
25   if ((call) == (errcode)) exit (1);
26
27 static void display_partition (guestfs_h *g, const char *dev);
28 static void display_partitions (guestfs_h *g, const char *dev);
29 static void display_ext23 (guestfs_h *g, const char *dev, const char *fstype);
30
31 int
32 main (int argc, char *argv[])
33 {
34   guestfs_h *g;
35   int i;
36
37   if (argc < 2 || access (argv[1], F_OK) != 0) {
38     fprintf (stderr, "Usage: to-xml guest.img [guest.img ...]\n");
39     exit (1);
40   }
41
42   if (!(g = guestfs_create ())) {
43     fprintf (stderr, "Cannot create libguestfs handle.\n");
44     exit (1);
45   }
46
47   for (i = 1; i < argc; ++i)
48     CALL (guestfs_add_drive (g, argv[i]), -1);
49
50   CALL (guestfs_launch (g), -1);
51   CALL (guestfs_wait_ready (g), -1);
52
53   printf ("<guestfs-system>\n");
54
55   /* list-devices should return the devices that we just attached?
56    * Better to find out what the kernel thinks are devices anyway ...
57    */
58   char **devices;
59   CALL (devices = guestfs_list_devices (g), NULL);
60   printf ("<devices>\n");
61   for (i = 0; devices[i] != NULL; ++i) {
62     int64_t size;
63     CALL (size = guestfs_blockdev_getsize64 (g, devices[i]), -1);
64     printf ("<device dev=\"%s\" size=\"%" PRIi64 "\">\n", devices[i], size);
65     display_partitions (g, devices[i]);
66     free (devices[i]);
67     printf ("</device>\n");
68   }
69   free (devices);
70   printf ("</devices>\n");
71
72   /* Now do the same for VGs and LVs.  Note that a VG may span
73    * multiple PVs / block devices, in arbitrary ways, which is
74    * why VGs are in a separate top-level XML class.
75    */
76   char **vgs;
77   char **lvs;
78   printf ("<volgroups>\n");
79   CALL (vgs = guestfs_vgs (g), NULL);
80   CALL (lvs = guestfs_lvs (g), NULL);
81   for (i = 0; vgs[i] != NULL; ++i) {
82     printf ("<volgroup name=\"%s\">\n", vgs[i]);
83
84     /* Just the LVs in this VG. */
85     int len = strlen (vgs[i]);
86     int j;
87     for (j = 0; lvs[j] != NULL; ++j) {
88       if (strncmp (lvs[j], "/dev/", 5) == 0 &&
89           strncmp (&lvs[j][5], vgs[i], len) == 0 &&
90           lvs[j][len+5] == '/') {
91         int64_t size;
92         CALL (size = guestfs_blockdev_getsize64 (g, lvs[j]), -1);
93         printf ("<logvol name=\"%s\" size=\"%" PRIi64 "\">\n", lvs[j], size);
94         display_partition (g, lvs[j]);
95         printf ("</logvol>\n");
96         free (lvs[j]);
97       }
98     }
99
100     free (vgs[i]);
101     printf ("</volgroup>\n");
102   }
103   free (vgs);
104   free (lvs);
105   printf ("</volgroups>\n");
106
107   guestfs_close (g);
108   printf ("</guestfs-system>\n");
109
110   return 0;
111 }
112
113 /* Display a partition or LV. */
114 static void
115 display_partition (guestfs_h *g, const char *dev)
116 {
117   char *what;
118
119   CALL (what = guestfs_file (g, dev), NULL);
120
121   if (strcmp (what, "x86 boot sector") == 0)
122     /* This is what 'file' program shows for Windows/NTFS partitions. */
123     printf ("<windows/>\n");
124   else if (strstr (what, "boot sector") != NULL)
125     display_partitions (g, dev);
126   else if (strncmp (what, "LVM2", 4) == 0)
127     printf ("<physvol/>\n");
128   else if (strstr (what, "ext2 filesystem data") != NULL)
129     display_ext23 (g, dev, "ext2");
130   else if (strstr (what, "ext3 filesystem data") != NULL)
131     display_ext23 (g, dev, "ext3");
132   else if (strstr (what, "Linux/i386 swap file") != NULL)
133     printf ("<linux-swap/>\n");
134   else
135     printf ("<unknown/>\n");
136
137   free (what);
138 }
139
140 /* Display an MBR-formatted boot sector. */
141 static void
142 display_partitions (guestfs_h *g, const char *dev)
143 {
144   /* We can't look into a boot sector which is an LV or partition.
145    * That's a limitation of sorts of the Linux kernel.  (Actually,
146    * we could do this if we add the kpartx program to libguestfs).
147    */
148   if (strncmp (dev, "/dev/sd", 7) != 0 || isdigit (dev[strlen(dev)-1])) {
149     printf ("<vm-image dev=\"%s\"/>\n", dev);
150     return;
151   }
152
153   char **parts;
154   int i, len;
155   CALL (parts = guestfs_list_partitions (g), NULL);
156   printf ("<partitions>\n");
157
158   len = strlen (dev);
159   for (i = 0; parts[i] != NULL; ++i) {
160     /* Only display partition if it's in the device. */
161     if (strncmp (parts[i], dev, len) == 0) {
162       int64_t size;
163       CALL (size = guestfs_blockdev_getsize64 (g, parts[i]), -1);
164       printf ("<partition dev=\"%s\" size=\"%" PRIi64 "\">\n", parts[i], size);
165       display_partition (g, parts[i]);
166       printf ("</partition>\n");
167     }
168
169     free (parts[i]);
170   }
171   free (parts);
172   printf ("</partitions>\n");
173 }
174
175 /* Display some details on the ext2/3 filesystem on dev. */
176 static void
177 display_ext23 (guestfs_h *g, const char *dev, const char *fstype)
178 {
179   char **sbfields;
180   int i;
181
182   printf ("<fs type=\"%s\">\n", fstype);
183   CALL (sbfields = guestfs_tune2fs_l (g, dev), NULL);
184
185   for (i = 0; sbfields[i] != NULL; i += 2) {
186     /* Just pick out a few important fields to display.  There
187      * is much more that could be displayed here.
188      */
189     if (strcmp (sbfields[i], "Filesystem UUID") == 0)
190       printf ("<uuid>%s</uuid>\n", sbfields[i+1]);
191     else if (strcmp (sbfields[i], "Block size") == 0)
192       printf ("<blocksize>%s</blocksize>\n", sbfields[i+1]);
193
194     free (sbfields[i]);
195     free (sbfields[i+1]);
196   }
197   free (sbfields);
198
199   printf ("</fs>\n");
200 }