diff options
Diffstat (limited to 'align')
-rw-r--r-- | align/Makefile.am | 4 | ||||
-rw-r--r-- | align/domains.c | 339 | ||||
-rw-r--r-- | align/scan.c | 71 | ||||
-rw-r--r-- | align/scan.h | 30 | ||||
-rwxr-xr-x | align/virt-alignment-scan.pod | 27 |
5 files changed, 446 insertions, 25 deletions
diff --git a/align/Makefile.am b/align/Makefile.am index d1d13255..eccb05c0 100644 --- a/align/Makefile.am +++ b/align/Makefile.am @@ -34,7 +34,9 @@ SHARED_SOURCE_FILES = \ virt_alignment_scan_SOURCES = \ $(SHARED_SOURCE_FILES) \ - scan.c + domains.c \ + scan.c \ + scan.h virt_alignment_scan_CFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ diff --git a/align/domains.c b/align/domains.c new file mode 100644 index 00000000..4ade3f66 --- /dev/null +++ b/align/domains.c @@ -0,0 +1,339 @@ +/* virt-alignment-scan + * Copyright (C) 2012 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_LIBVIRT +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> +#endif + +#include "progname.h" + +#define GUESTFS_PRIVATE_FOR_EACH_DISK 1 + +#include "guestfs.h" +#include "options.h" +#include "scan.h" + +#if defined(HAVE_LIBVIRT) && defined(HAVE_LIBXML2) + +/* The list of domains and disks that we build up in + * get_domains_from_libvirt. + */ +struct disk { + struct disk *next; + char *filename; + char *format; /* could be NULL */ +}; + +struct domain { + char *name; + char *uuid; + struct disk *disks; + size_t nr_disks; +}; + +struct domain *domains = NULL; +size_t nr_domains; + +static int +compare_domain_names (const void *p1, const void *p2) +{ + const struct domain *d1 = p1; + const struct domain *d2 = p2; + + return strcmp (d1->name, d2->name); +} + +static void +free_domain (struct domain *domain) +{ + struct disk *disk, *next; + + for (disk = domain->disks; disk; disk = next) { + next = disk->next; + free (disk->filename); + free (disk->format); + free (disk); + } + + free (domain->name); + free (domain->uuid); +} + +static void add_domains_by_id (virConnectPtr conn, int *ids, size_t n); +static void add_domains_by_name (virConnectPtr conn, char **names, size_t n); +static void add_domain (virDomainPtr dom); +static int add_disk (guestfs_h *g, const char *filename, const char *format, int readonly, void *domain_vp); +static void add_disks_to_handle_reverse (struct disk *disk); +static void reset_guestfs_handle (void); + +void +get_domains_from_libvirt (int uuid, size_t *worst_alignment_ptr) +{ + virErrorPtr err; + virConnectPtr conn; + int n; + size_t i; + const char *prefix; + + nr_domains = 0; + domains = NULL; + + /* Get the list of all domains. */ + conn = virConnectOpenReadOnly (libvirt_uri); + if (!conn) { + err = virGetLastError (); + fprintf (stderr, + _("%s: could not connect to libvirt (code %d, domain %d): %s"), + program_name, err->code, err->domain, err->message); + exit (EXIT_FAILURE); + } + + n = virConnectNumOfDomains (conn); + if (n == -1) { + err = virGetLastError (); + fprintf (stderr, + _("%s: could not get number of running domains (code %d, domain %d): %s"), + program_name, err->code, err->domain, err->message); + exit (EXIT_FAILURE); + } + + int ids[n]; + n = virConnectListDomains (conn, ids, n); + if (n == -1) { + err = virGetLastError (); + fprintf (stderr, + _("%s: could not list running domains (code %d, domain %d): %s"), + program_name, err->code, err->domain, err->message); + exit (EXIT_FAILURE); + } + + add_domains_by_id (conn, ids, n); + + n = virConnectNumOfDefinedDomains (conn); + if (n == -1) { + err = virGetLastError (); + fprintf (stderr, + _("%s: could not get number of inactive domains (code %d, domain %d): %s"), + program_name, err->code, err->domain, err->message); + exit (EXIT_FAILURE); + } + + char *names[n]; + n = virConnectListDefinedDomains (conn, names, n); + if (n == -1) { + err = virGetLastError (); + fprintf (stderr, + _("%s: could not list inactive domains (code %d, domain %d): %s"), + program_name, err->code, err->domain, err->message); + exit (EXIT_FAILURE); + } + + add_domains_by_name (conn, names, n); + + /* You must free these even though the libvirt documentation doesn't + * mention it. + */ + for (i = 0; i < (size_t) n; ++i) + free (names[i]); + + virConnectClose (conn); + + /* No domains? */ + if (nr_domains == 0) + return; + + /* Sort the domains alphabetically by name for display. */ + qsort (domains, nr_domains, sizeof (struct domain), compare_domain_names); + + for (i = 0; i < nr_domains; ++i) { + add_disks_to_handle_reverse (domains[i].disks); + + if (guestfs_launch (g) == -1) + exit (EXIT_FAILURE); + + prefix = !uuid ? domains[i].name : domains[i].uuid; + + /* Perform the scan. */ + scan (worst_alignment_ptr, prefix); + + if (i < nr_domains - 1) + reset_guestfs_handle (); + } + + /* Free up domains structure. */ + for (i = 0; i < nr_domains; ++i) + free_domain (&domains[i]); + free (domains); +} + +static void +add_domains_by_id (virConnectPtr conn, int *ids, size_t n) +{ + size_t i; + virDomainPtr dom; + + for (i = 0; i < n; ++i) { + if (ids[i] != 0) { /* RHBZ#538041 */ + dom = virDomainLookupByID (conn, ids[i]); + if (dom) { /* transient errors are possible here, ignore them */ + add_domain (dom); + virDomainFree (dom); + } + } + } +} + +static void +add_domains_by_name (virConnectPtr conn, char **names, size_t n) +{ + size_t i; + virDomainPtr dom; + + for (i = 0; i < n; ++i) { + dom = virDomainLookupByName (conn, names[i]); + if (dom) { /* transient errors are possible here, ignore them */ + add_domain (dom); + virDomainFree (dom); + } + } +} + +static void +add_domain (virDomainPtr dom) +{ + struct domain *domain; + + domains = realloc (domains, (nr_domains + 1) * sizeof (struct domain)); + if (domains == NULL) { + perror ("realloc"); + exit (EXIT_FAILURE); + } + + domain = &domains[nr_domains]; + nr_domains++; + + domain->name = strdup (virDomainGetName (dom)); + if (domain->name == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + + char uuid[VIR_UUID_STRING_BUFLEN]; + if (virDomainGetUUIDString (dom, uuid) == 0) { + domain->uuid = strdup (uuid); + if (domain->uuid == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + } + else + domain->uuid = NULL; + + domain->disks = NULL; + int n = guestfs___for_each_disk (g, dom, add_disk, domain); + if (n == -1) + exit (EXIT_FAILURE); + domain->nr_disks = n; +} + +static int +add_disk (guestfs_h *g, + const char *filename, const char *format, int readonly, + void *domain_vp) +{ + struct domain *domain = domain_vp; + struct disk *disk; + + disk = malloc (sizeof *disk); + if (disk == NULL) { + perror ("malloc"); + return -1; + } + + disk->next = domain->disks; + domain->disks = disk; + + disk->filename = strdup (filename); + if (disk->filename == NULL) { + perror ("malloc"); + return -1; + } + if (format) { + disk->format = strdup (format); + if (disk->format == NULL) { + perror ("malloc"); + return -1; + } + } + else + disk->format = NULL; + + return 0; +} + +static void +add_disks_to_handle_reverse (struct disk *disk) +{ + if (disk == NULL) + return; + + add_disks_to_handle_reverse (disk->next); + + struct guestfs_add_drive_opts_argv optargs = { .bitmask = 0 }; + + optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK; + optargs.readonly = 1; + + if (disk->format) { + optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK; + optargs.format = disk->format; + } + + if (guestfs_add_drive_opts_argv (g, disk->filename, &optargs) == -1) + exit (EXIT_FAILURE); +} + +/* Close and reopen the libguestfs handle. */ +static void +reset_guestfs_handle (void) +{ + /* Copy the settings from the old handle. */ + int verbose = guestfs_get_verbose (g); + int trace = guestfs_get_trace (g); + + guestfs_close (g); + + g = guestfs_create (); + if (g == NULL) { + fprintf (stderr, _("guestfs_create: failed to create handle\n")); + exit (EXIT_FAILURE); + } + + guestfs_set_verbose (g, verbose); + guestfs_set_trace (g, trace); +} + +#endif diff --git a/align/scan.c b/align/scan.c index 7b4631ab..e1fd0d04 100644 --- a/align/scan.c +++ b/align/scan.c @@ -39,6 +39,7 @@ #include "guestfs.h" #include "options.h" +#include "scan.h" /* These globals are shared with options.c. */ guestfs_h *g; @@ -53,8 +54,6 @@ int inspector = 0; static int quiet = 0; /* --quiet */ -static int scan (void); - static inline char * bad_cast (char const *s) { @@ -111,6 +110,7 @@ main (int argc, char *argv[]) { "format", 2, 0, 0 }, { "help", 0, 0, HELP_OPTION }, { "quiet", 0, 0, 'q' }, + { "uuid", 0, 0, 0, }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 } @@ -120,6 +120,9 @@ main (int argc, char *argv[]) const char *format = NULL; int c; int option_index; + int uuid = 0; + /* This just needs to be larger than any alignment we care about. */ + size_t worst_alignment = UINT_MAX; int exit_code; g = guestfs_create (); @@ -141,6 +144,8 @@ main (int argc, char *argv[]) format = NULL; else format = optarg; + } else if (STREQ (long_options[option_index].name, "uuid")) { + uuid = 1; } else { fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), program_name, long_options[option_index].name, option_index); @@ -197,30 +202,50 @@ main (int argc, char *argv[]) usage (EXIT_FAILURE); /* The user didn't specify any drives to scan. */ - if (drvs == NULL) - usage (EXIT_FAILURE); + if (drvs == NULL) { +#if defined(HAVE_LIBVIRT) && defined(HAVE_LIBXML2) + get_domains_from_libvirt (uuid, &worst_alignment); +#else + fprintf (stderr, _("%s: compiled without support for libvirt and/or libxml2.\n"), + program_name); + exit (EXIT_FAILURE); +#endif + } else { + if (uuid) { + fprintf (stderr, _("%s: --uuid option cannot be used with -a or -d\n"), + program_name); + exit (EXIT_FAILURE); + } - /* Add domains/drives from the command line (for a single guest). */ - add_drives (drvs, 'a'); + /* Add domains/drives from the command line (for a single guest). */ + add_drives (drvs, 'a'); - if (guestfs_launch (g) == -1) - exit (EXIT_FAILURE); + if (guestfs_launch (g) == -1) + exit (EXIT_FAILURE); + + /* Free up data structures, no longer needed after this point. */ + free_drives (drvs); - /* Free up data structures, no longer needed after this point. */ - free_drives (drvs); + /* Perform the scan. */ + scan (&worst_alignment, NULL); - /* Perform the scan. */ - exit_code = scan (); + guestfs_close (g); + } - guestfs_close (g); + /* Decide on an appropriate exit code. */ + if (worst_alignment < 4096) + exit_code = 3; + else if (worst_alignment < 65536) + exit_code = 2; + else + exit_code = 0; exit (exit_code); } -static int -scan (void) +void +scan (size_t *worst_alignment, const char *prefix) { - int exit_code = 0; char **devices; size_t i, j; size_t alignment; @@ -247,9 +272,13 @@ scan (void) /* Start offset of the partition in bytes. */ start = parts->val[j].part_start; - if (!quiet) + if (!quiet) { + if (prefix) + printf ("%s:", prefix); + printf ("%s%d %12" PRIu64 " ", devices[i], (int) parts->val[j].part_num, start); + } /* What's the alignment? */ if (start == 0) /* Probably not possible, but anyway. */ @@ -267,13 +296,13 @@ scan (void) printf ("- "); } + if (alignment < *worst_alignment) + *worst_alignment = alignment; + if (alignment < 12) { /* Bad in general: < 4K alignment */ - exit_code = 3; if (!quiet) printf ("bad (%s)\n", _("alignment < 4K")); } else if (alignment < 16) { /* Bad on NetApps: < 64K alignment */ - if (exit_code < 2) - exit_code = 2; if (!quiet) printf ("bad (%s)\n", _("alignment < 64K")); } else { @@ -286,6 +315,4 @@ scan (void) free (devices[i]); } free (devices); - - return exit_code; } diff --git a/align/scan.h b/align/scan.h new file mode 100644 index 00000000..1678d8d2 --- /dev/null +++ b/align/scan.h @@ -0,0 +1,30 @@ +/* virt-alignment-scan + * Copyright (C) 2012 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef GUESTFS_VIRT_ALIGNMENT_SCAN_H_ +#define GUESTFS_VIRT_ALIGNMENT_SCAN_H_ + +/* domains.c */ +#if defined(HAVE_LIBVIRT) && defined(HAVE_LIBXML2) +extern void get_domains_from_libvirt (int uuid, size_t *worst_alignment); +#endif + +/* scan.c */ +extern void scan (size_t *worst_alignment, const char *prefix); + +#endif /* GUESTFS_VIRT_ALIGNMENT_SCAN_H_ */ diff --git a/align/virt-alignment-scan.pod b/align/virt-alignment-scan.pod index 56c51abd..3be721b0 100755 --- a/align/virt-alignment-scan.pod +++ b/align/virt-alignment-scan.pod @@ -10,6 +10,8 @@ virt-alignment-scan - Check alignment of virtual machine partitions virt-alignment-scan [--options] -a disk.img [-a disk.img ...] + virt-alignment-scan [--options] + =head1 DESCRIPTION When older operating systems install themselves, the partitioning @@ -51,6 +53,14 @@ possibly the I<-c> option: /dev/sda2 105906176 1024K ok /dev/sdb1 65536 64K ok +Run virt-alignment-scan without any I<-a> or I<-d> options to scan all +libvirt domains. + + # virt-alignment-scan + F16x64:/dev/sda1 1048576 1024K ok + F16x64:/dev/sda2 2097152 2048K ok + F16x64:/dev/sda3 526385152 2048K ok + The output consists of 4 or more whitespace-separated columns. Only the first 4 columns are significant if you want to parse this from a program. The columns are: @@ -59,8 +69,12 @@ program. The columns are: =item col 1 -the device and partition name (eg. C</dev/sda1> meaning the -first partition on the first block device) +The device and partition name (eg. C</dev/sda1> meaning the +first partition on the first block device). + +When listing all libvirt domains (no I<-a> or I<-d> option given) this +column is prefixed by the libvirt name or UUID (if I<--uuid> is +given). eg: C<WinXP:/dev/sda1> =item col 2 @@ -151,6 +165,15 @@ security problem with malicious guests (CVE-2010-3851). Don't produce any output. Just set the exit code (see L</EXIT STATUS> below). +=item B<--uuid> + +Print UUIDs instead of names. This is useful for following a guest +even when the guest is migrated or renamed, or when two guests happen +to have the same name. + +This option only applies when listing all libvirt domains (when no +I<-a> or I<-d> options are specified). + =item B<-v> =item B<--verbose> |