a409654a3410bc235a6a9e48cffbb83d0eabe94a
[libguestfs.git] / align / scan.c
1 /* virt-alignment-scan
2  * Copyright (C) 2011 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 <stdint.h>
24 #include <string.h>
25 #include <inttypes.h>
26 #include <unistd.h>
27 #include <getopt.h>
28 #include <locale.h>
29 #include <assert.h>
30 #include <libintl.h>
31
32 #ifdef HAVE_LIBVIRT
33 #include <libvirt/libvirt.h>
34 #include <libvirt/virterror.h>
35 #endif
36
37 #include "progname.h"
38 #include "c-ctype.h"
39
40 #include "guestfs.h"
41 #include "options.h"
42
43 /* These globals are shared with options.c. */
44 guestfs_h *g;
45
46 int read_only = 1;
47 int live = 0;
48 int verbose = 0;
49 int keys_from_stdin = 0;
50 int echo_keys = 0;
51 const char *libvirt_uri = NULL;
52 int inspector = 0;
53
54 static int quiet = 0;           /* --quiet */
55
56 static int scan (void);
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: check alignment of virtual machine partitions\n"
73              "Copyright (C) 2011 Red Hat Inc.\n"
74              "Usage:\n"
75              "  %s [--options] -d domname\n"
76              "  %s [--options] -a disk.img [-a disk.img ...]\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              "  --format[=raw|..]    Force disk format for -a option\n"
82              "  --help               Display brief help\n"
83              "  -q|--quiet           No output, just exit code\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:qvVx";
107   static const struct option long_options[] = {
108     { "add", 1, 0, 'a' },
109     { "connect", 1, 0, 'c' },
110     { "domain", 1, 0, 'd' },
111     { "format", 2, 0, 0 },
112     { "help", 0, 0, HELP_OPTION },
113     { "quiet", 0, 0, 'q' },
114     { "verbose", 0, 0, 'v' },
115     { "version", 0, 0, 'V' },
116     { 0, 0, 0, 0 }
117   };
118   struct drv *drvs = NULL;
119   struct drv *drv;
120   const char *format = NULL;
121   int c;
122   int option_index;
123   int exit_code;
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, "format")) {
140         if (!optarg || STREQ (optarg, ""))
141           format = NULL;
142         else
143           format = optarg;
144       } else {
145         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
146                  program_name, long_options[option_index].name, option_index);
147         exit (EXIT_FAILURE);
148       }
149       break;
150
151     case 'a':
152       OPTION_a;
153       break;
154
155     case 'c':
156       OPTION_c;
157       break;
158
159     case 'd':
160       OPTION_d;
161       break;
162
163     case 'q':
164       quiet = 1;
165       break;
166
167     case 'v':
168       OPTION_v;
169       break;
170
171     case 'V':
172       OPTION_V;
173       break;
174
175     case 'x':
176       OPTION_x;
177       break;
178
179     case HELP_OPTION:
180       usage (EXIT_SUCCESS);
181
182     default:
183       usage (EXIT_FAILURE);
184     }
185   }
186
187   /* These are really constants, but they have to be variables for the
188    * options parsing code.  Assert here that they have known-good
189    * values.
190    */
191   assert (read_only == 1);
192   assert (inspector == 0);
193   assert (live == 0);
194
195   /* Must be no extra arguments on the command line. */
196   if (optind != argc)
197     usage (EXIT_FAILURE);
198
199   /* The user didn't specify any drives to scan. */
200   if (drvs == NULL)
201     usage (EXIT_FAILURE);
202
203   /* Add domains/drives from the command line (for a single guest). */
204   add_drives (drvs, 'a');
205
206   if (guestfs_launch (g) == -1)
207     exit (EXIT_FAILURE);
208
209   /* Free up data structures, no longer needed after this point. */
210   free_drives (drvs);
211
212   /* Perform the scan. */
213   exit_code = scan ();
214
215   guestfs_close (g);
216
217   exit (exit_code);
218 }
219
220 static int
221 scan (void)
222 {
223   int exit_code = 0;
224   char **devices;
225   size_t i, j;
226   size_t alignment;
227   uint64_t start;
228   struct guestfs_partition_list *parts;
229
230   devices = guestfs_list_devices (g);
231
232   for (i = 0; devices[i] != NULL; ++i) {
233     parts = guestfs_part_list (g, devices[i]);
234
235     /* Canonicalize the name of the device for printing. */
236     if (STRPREFIX (devices[i], "/dev/") &&
237         (devices[i][5] == 'h' || devices[i][5] == 'v') &&
238         devices[i][6] == 'd' &&
239         c_isalpha (devices[i][7]))
240       devices[i][5] = 's';
241
242     for (j = 0; j < parts->len; ++j) {
243       /* Start offset of the partition in bytes. */
244       start = parts->val[j].part_start;
245
246       if (!quiet)
247         printf ("%s%d %12" PRIu64 " ",
248                 devices[i], (int) parts->val[j].part_num, start);
249
250       /* What's the alignment? */
251       if (start == 0)           /* Probably not possible, but anyway. */
252         alignment = 64;
253       else
254         for (alignment = 0; (start & 1) == 0; alignment++, start /= 2)
255           ;
256
257       if (!quiet) {
258         if (alignment < 10)
259           printf ("%12" PRIu64 "    ", UINT64_C(1) << alignment);
260         else if (alignment < 64)
261           printf ("%12" PRIu64 "K   ", UINT64_C(1) << (alignment - 10));
262         else
263           printf ("- ");
264       }
265
266       if (alignment < 12) {     /* Bad in general: < 4K alignment */
267         exit_code = 3;
268         if (!quiet)
269           printf ("bad (%s)\n", _("alignment < 4K"));
270       } else if (alignment < 16) { /* Bad on NetApps: < 64K alignment */
271         if (exit_code < 2)
272           exit_code = 2;
273         if (!quiet)
274           printf ("bad (%s)\n", _("alignment < 64K"));
275       } else {
276         if (!quiet)
277           printf ("ok\n");
278       }
279     }
280
281     guestfs_free_partition_list (parts);
282     free (devices[i]);
283   }
284   free (devices);
285
286   return exit_code;
287 }